More Info
Private Name Tags
ContractCreator
TokenTracker
Latest 25 from a total of 1,200 transactions
| Transaction Hash |
|
Block
|
From
|
To
|
|||||
|---|---|---|---|---|---|---|---|---|---|
| Report | 52574511 | 88 days ago | IN | 0 S | 0.0160268 | ||||
| Rebalance Strate... | 52574149 | 88 days ago | IN | 0 S | 0.11048595 | ||||
| Set Target Ltv | 52574149 | 88 days ago | IN | 0 S | 0.00306486 | ||||
| Rebalance Strate... | 52573902 | 88 days ago | IN | 0 S | 0.15461935 | ||||
| Set Target Ltv | 52573902 | 88 days ago | IN | 0 S | 0.0027157 | ||||
| Rebalance Strate... | 52573071 | 88 days ago | IN | 0 S | 0.1545022 | ||||
| Set Target Ltv | 52573071 | 88 days ago | IN | 0 S | 0.0025871 | ||||
| Rebalance Strate... | 52571697 | 88 days ago | IN | 0 S | 0.1451786 | ||||
| Rebalance Strate... | 52571697 | 88 days ago | IN | 0 S | 0.15484435 | ||||
| Rebalance Strate... | 52571555 | 88 days ago | IN | 0 S | 0.14397665 | ||||
| Rebalance Strate... | 52571555 | 88 days ago | IN | 0 S | 0.1546103 | ||||
| Rebalance Strate... | 52571411 | 88 days ago | IN | 0 S | 0.1442707 | ||||
| Rebalance Strate... | 52571411 | 88 days ago | IN | 0 S | 0.14395205 | ||||
| Rebalance Strate... | 52571411 | 88 days ago | IN | 0 S | 0.1439907 | ||||
| Rebalance Strate... | 52571411 | 88 days ago | IN | 0 S | 0.15456895 | ||||
| Rebalance Strate... | 52571009 | 88 days ago | IN | 0 S | 0.15457295 | ||||
| Rebalance Strate... | 52570689 | 88 days ago | IN | 0 S | 0.1548678 | ||||
| Rebalance Strate... | 52569818 | 88 days ago | IN | 0 S | 0.20296376 | ||||
| Rebalance Strate... | 52569323 | 88 days ago | IN | 0 S | 0.20424508 | ||||
| Rebalance Strate... | 52569233 | 88 days ago | IN | 0 S | 0.20465511 | ||||
| Rebalance Strate... | 52421258 | 89 days ago | IN | 0 S | 0.17453155 | ||||
| Rebalance Strate... | 52420603 | 89 days ago | IN | 0 S | 0.1613337 | ||||
| Deposit Idle Str... | 52402795 | 89 days ago | IN | 0 S | 0.1414791 | ||||
| Rebalance Strate... | 52209206 | 90 days ago | IN | 0 S | 0.0860868 | ||||
| Rebalance Strate... | 52102012 | 91 days ago | IN | 0 S | 0.08294245 |
Latest 1 internal transaction
Advanced mode:
| Parent Transaction Hash | Block | From | To | |||
|---|---|---|---|---|---|---|
| 46413972 | 138 days ago | Contract Creation | 0 S |
Cross-Chain Transactions
Loading...
Loading
Contract Name:
CeresLeveragedStrategy
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: AGPL-3.0
pragma solidity 0.8.28;
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";
import {BaseStrategy} from "@tokenized-strategy/BaseStrategy.sol";
import {TokenizedStrategyStorageAccess} from "./TokenizedStrategyStorageAccess.sol";
// Pendle
import {IPMarket, IStandardizedYield, IPPrincipalToken} from "@pendle/core-v2/contracts/interfaces/IPMarket.sol";
import {PendlePYOracleLib} from "@pendle/core-v2/contracts/oracles/PtYtLpOracle/PendlePYOracleLib.sol";
// Silo
import {ISilo} from "silo-core/contracts/interfaces/ISilo.sol";
import {ISiloOracle} from "silo-core/contracts/interfaces/ISiloOracle.sol";
import {ISiloConfig} from "silo-core/contracts/interfaces/ISiloConfig.sol";
import {ISiloLens} from "silo-core/contracts/SiloLens.sol";
import {IERC3156FlashBorrower} from "silo-core/contracts/interfaces/IERC3156FlashBorrower.sol";
import {LibError} from "./libraries/LibError.sol";
import {ICeresSwapper} from "./interfaces/ICeresSwapper.sol";
contract CeresLeveragedStrategy is BaseStrategy, TokenizedStrategyStorageAccess, IERC3156FlashBorrower {
using Math for uint256;
using SafeERC20 for IERC20;
// Pendle config
address public immutable PENDLE_MARKET;
address public immutable PT;
// Silo
address public immutable SILO_CONFIG;
address public immutable DEPOSIT_SILO;
address public immutable BORROW_SILO;
address public immutable SILO_BORROW_TOKEN;
uint8 public immutable BORROW_TOKEN_DECIMALS;
uint8 public immutable PT_TOKEN_DECIMALS;
ISilo.CollateralType public immutable SILO_COLLATERAL_TYPE;
ICeresSwapper public ceresSwapper;
// Constants
uint256 private constant MAX_BPS = 10_000;
address private constant SILO_LENS_SONIC = 0x925D5466d4D5b01995E20e1245924aDa6415126a;
uint256 private constant WAD = 1e18;
uint256 private constant MIN_TWAP_DURATION = 5 minutes;
uint256 private constant MAX_SLIPPAGE_LIMIT = 500; // 5% max slippage limit
uint256 private constant DEFAULT_LTV_BUFFER = 5 * 1e16; // 5% buffer (0.05), 1e18 = 1 (100%)
// Strategy config with default values
uint256 public configTargetLtv = 50 * 1e16; // 50% LTV by default, 1e18 = 1 (100%)
uint256 public configMaxSlippageBps = 100; // Maximum slippage in basis points, 1% = 100 bps
uint32 public configTwapDuration = 30 minutes; // Default to a TWAP duration of 30 mins
uint256 public depositLimit = type(uint256).max; // Default value
uint256 public withdrawLimit = type(uint256).max; // Default value
//Events
event LossOnWithdrawal(uint256 lossAmount);
event FlashLoan(uint256 amountRepaid);
event StrategyRebalance();
event MaxSlippageSet(uint256 slippage);
event TargetLtvSet(uint256 targetLtv);
event SwapperSet(address updatedSwapper);
event DepositLimitSet(uint256);
event WithdrawLimitSet(uint256);
event TwapDurationSet(uint32);
event StrategyFundsDeallocated();
event TokensRecovered(address indexed token, uint256 amount);
event DepositIdleAssets(uint256 assetAmount, uint256 ptAmount, uint256 borrowTokenBalance);
/// @notice Constructor for the Strategy contract.
/// @param _asset The address of the underlying asset token.
/// @param _name The name of the strategy.
/// @param _pendleMarket The address of the Pendle market contract.
/// @param _siloMarketConfig The address of the Silo market config contract.
constructor(
address _asset,
string memory _name,
address _ceresSwapper,
address _pendleMarket,
address _siloMarketConfig,
bool isCollateralProtected
) BaseStrategy(_asset, _name) {
if (_ceresSwapper == address(0) || _pendleMarket == address(0) || _siloMarketConfig == address(0)) {
revert LibError.InvalidAddress();
}
// Pendle
_validatePendleMarket(_pendleMarket);
PENDLE_MARKET = _pendleMarket;
(, IPPrincipalToken ptToken, ) = IPMarket(PENDLE_MARKET).readTokens();
PT = address(ptToken);
// Silo
(DEPOSIT_SILO, BORROW_SILO) = _validateSiloMarket(_siloMarketConfig);
SILO_CONFIG = _siloMarketConfig;
SILO_BORROW_TOKEN = ISilo(BORROW_SILO).asset();
BORROW_TOKEN_DECIMALS = IERC20Metadata(SILO_BORROW_TOKEN).decimals();
PT_TOKEN_DECIMALS = IERC20Metadata(PT).decimals();
// Asset and Pendle PT token decimals should be the same
if (IERC20Metadata(_asset).decimals() != PT_TOKEN_DECIMALS) {
revert LibError.InvalidPendleMarket();
}
if (isCollateralProtected) {
SILO_COLLATERAL_TYPE = ISilo.CollateralType.Protected;
} else {
SILO_COLLATERAL_TYPE = ISilo.CollateralType.Collateral;
}
ceresSwapper = ICeresSwapper(_ceresSwapper);
_setSwapperApprovals();
}
/*//////////////////////////////////////////////////////////////
External/Public Functions
//////////////////////////////////////////////////////////////*/
/// @notice Deposits idle asset tokens to get Pendle PT and supply to Silo
function depositIdleStrategyAssets() external nonReentrant onlyKeepers {
_depositIdleStrategyAssets();
}
/// @notice Rebalances the strategy using a flash loan with specified amount and direction.
/// @param flashLoanAmount The amount for the flash loan.
/// @param isLeverageUp True if leveraging up, false if leveraging down.
/// @param shouldDepositIdleAssets If true, checks all token balances and deposits assets before rebalancing
/// @param swapData External aggregator swap data
function rebalanceStrategy(
uint256 flashLoanAmount,
bool isLeverageUp,
bool shouldDepositIdleAssets,
bytes calldata swapData
) external nonReentrant onlyKeepers {
if (TokenizedStrategy.isShutdown()) {
_deallocateAllFunds();
} else {
if (shouldDepositIdleAssets) {
_depositIdleStrategyAssets();
}
_rebalanceStrategy(flashLoanAmount, isLeverageUp, swapData);
}
}
/// @notice Rebalances the strategy by calculating the required flash loan amount for leveraging up or down.
/// @dev This function is called by keepers to maintain the target LTV.
/// @param ltvBuffer The buffer to apply when checking if rebalancing is needed.
/// @param shouldDepositIdleAssets If true, checks all token balances and deposits assets before rebalancing
function rebalanceStrategy(uint256 ltvBuffer, bool shouldDepositIdleAssets) external nonReentrant onlyKeepers {
if (TokenizedStrategy.isShutdown()) {
_deallocateAllFunds();
} else {
if (shouldDepositIdleAssets) {
_depositIdleStrategyAssets();
}
_rebalanceStrategy(ltvBuffer);
}
}
/// @notice Handles the flash loan callback from the Silo protocol.
/// The function cannot be marked as nonReentrant, as during callback,
/// the reentrant flag will already be set by `rebalanceStrategy` function
/// Additional validations on the contract initiator to be the strategy contract,
/// validations on caller address and the token are in place
/// @param initiator The address that initiated the flash loan.
/// @param token The address of the token that was flash loaned.
/// @param amount The amount of the token that was flash loaned.
/// @param fee The fee for the flash loan.
/// @param data Additional data passed to the flash loan callback.
/// @return bytes32 The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan".
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32) {
if (msg.sender != BORROW_SILO) revert LibError.InvalidFlashLoanCaller();
if (initiator != address(this)) revert LibError.InvalidFlashLoanInitiator();
if (token != SILO_BORROW_TOKEN) revert LibError.InvalidFlashLoanToken();
// Decode the data
(uint256 flashLoanAmount, bool leverageUp, bytes memory swapData) = abi.decode(data, (uint256, bool, bytes));
if (flashLoanAmount != amount) revert LibError.InvalidFlashLoanAmount();
uint256 flashLoanAmountWithFee = amount + fee;
if (leverageUp) {
_leverageUpOnFlashLoan(amount, flashLoanAmountWithFee, swapData);
} else {
_leverageDownOnFlashLoan(amount, flashLoanAmountWithFee, swapData);
}
// Repay flash loan
emit FlashLoan(flashLoanAmountWithFee);
IERC20(SILO_BORROW_TOKEN).forceApprove(BORROW_SILO, flashLoanAmountWithFee);
return keccak256("ERC3156FlashBorrower.onFlashLoan");
}
/// @notice Rescues tokens from the strategy contract.
/// @dev This function allows the emergency authorized address to rescue tokens that are not part of the strategy.
/// @param _token The address of the token to rescue.
function rescueTokens(address _token) external onlyEmergencyAuthorized {
// Rescuing tokens is allowed only for tokens that are not part of the strategy
if (_token == address(asset) || _token == PT || _token == SILO_BORROW_TOKEN) {
revert LibError.InvalidToken();
}
uint256 amount = IERC20(_token).balanceOf(address(this));
IERC20(_token).safeTransfer(msg.sender, amount);
emit TokensRecovered(_token, amount);
}
///////////////////// Permissioned Setter Functions ///////////////////
/// @notice Sets the deposit limit for the strategy.
/// @param _depositLimit The new deposit limit.
function setDepositLimit(uint256 _depositLimit) external onlyManagement {
depositLimit = _depositLimit;
emit DepositLimitSet(_depositLimit);
}
/// @notice Sets the withdrawal limit for the strategy.
/// @param _withdrawLimit The new withdrawal limit.
function setWithdrawLimit(uint256 _withdrawLimit) external onlyManagement {
withdrawLimit = _withdrawLimit;
emit WithdrawLimitSet(_withdrawLimit);
}
/// @notice Sets the maximum slippage in basis points for swaps.
/// @param _maxSlippageBps The maximum slippage in basis points (e.g., 100 for 1%).
function setMaxSlippageBps(uint256 _maxSlippageBps) external onlyManagement {
if (_maxSlippageBps > MAX_SLIPPAGE_LIMIT) revert LibError.AboveMaxValue(MAX_SLIPPAGE_LIMIT);
configMaxSlippageBps = _maxSlippageBps;
emit MaxSlippageSet(_maxSlippageBps);
}
/// @notice Sets the target Loan-to-Value (LTV) for the strategy.
/// @dev Keeper (and management) can call this function to update the LTV after rebalance
/// @param _targetLtv The target LTV in 1e18 format (e.g., 50 * 1e16 for 50%).
function setTargetLtv(uint256 _targetLtv) external onlyKeepers {
// Target LTV should be less than WAD and maxLtv allowed
if (_targetLtv >= WAD) revert LibError.AboveMaxValue(WAD);
uint256 maxLtv = getMaxLtvSilo();
if (_targetLtv + DEFAULT_LTV_BUFFER > maxLtv) revert LibError.AboveMaxValue(maxLtv);
configTargetLtv = _targetLtv;
emit TargetLtvSet(_targetLtv);
}
/// @notice Sets the address of the swapper contract used for token swaps.
/// @param _swapper The address of the swapper contract.
function setSwapper(address _swapper) external onlyManagement {
if (_swapper == address(0)) revert LibError.InvalidAddress();
// Revoke approvals to the previous swapper
_revokeSwapperApprovals();
// Update swapper address and set approvals
ceresSwapper = ICeresSwapper(_swapper);
_setSwapperApprovals();
emit SwapperSet(_swapper);
}
/// @notice Sets approvals to the swapper contract for token swaps.
function setSwapperApprovals() external onlyManagement {
_setSwapperApprovals();
}
/// @notice Revoke approvals from the swapper contract
function revokeSwapperApprovals() external onlyManagement {
_revokeSwapperApprovals();
}
/// @notice Sets the TWAP duration for oracle price.
/// @param _twapDuration Duration to use for the Time Weighted Average Price (TWAP) of oracle
function setTwapDuration(uint32 _twapDuration) external onlyManagement {
if (_twapDuration < MIN_TWAP_DURATION) revert LibError.InvalidValue();
configTwapDuration = _twapDuration;
emit TwapDurationSet(_twapDuration);
}
//////////////////////// Getter Functions //////////////////////
/// @notice Returns the available deposit limit for the strategy.
/// @return Deposit limit.
function availableDepositLimit(address /* _owner */) public view override returns (uint256) {
return depositLimit;
}
/// @notice Returns the available withdrawal limit for the strategy.
/// @return Withdrawal limit.
function availableWithdrawLimit(address /* _owner */) public view override returns (uint256) {
return withdrawLimit;
}
/// @notice Gets the maximum allowed Loan-to-Value (LTV) for the deposit silo.
/// @return maxLtv The maximum LTV in 1e18 format.
function getMaxLtvSilo() public view returns (uint256 maxLtv) {
// Max LTV is calculated using the depositSilo, it is 0 for borrowSilo
maxLtv = ISiloLens(SILO_LENS_SONIC).getMaxLtv(ISilo(DEPOSIT_SILO));
}
/// @notice Gets the current Loan-to-Value (LTV) of the strategy's position in the borrow silo.
/// @return positionLtv The current LTV in 1e18 format.
function getPositionLtv() public view returns (uint256 positionLtv) {
positionLtv = ISiloLens(SILO_LENS_SONIC).getLtv(ISilo(BORROW_SILO), address(this));
}
/// @notice Gets the collateral balance of the strategy in the deposit silo.
/// @return collateralBalance The collateral balance in Pendle PT units.
function getPositionCollateral() public view returns (uint256 collateralBalance) {
return _getSiloCollateralBalance();
}
/// @notice Gets the debt balance of the strategy in the borrow silo.
/// @return debtBalance The debt balance in Silo borrow token units.
function getPositionDebt() public view returns (uint256 debtBalance) {
return _getSiloDebtBalance();
}
/// @notice Gets the net real asset balance of the strategy without looping,
// The returned value is the net value in assets of the strategy
/// @return totalAssets Value of the strategy in asset token
function getRealAssetBalance()
external
view
returns (uint256 totalAssets, uint256 totalCollateral, uint256 totalDebt)
{
return _getRealAssetBalance();
}
/// @notice Gets the price of the collateral token (PT) in terms of the borrow token from the Silo oracle.
/// @return price The price of the collateral token in borrow token units (1e18 format).
function getSiloCollateralTokenPrice() external view returns (uint256 price) {
return _getSiloCollateralTokenPrice();
}
/// @notice Converts an amount of the strategy's asset to Pendle PT tokens based on the current rate.
/// @param assetAmount The amount of strategy asset tokens to convert.
/// @return ptAmount The equivalent amount of Pendle PT tokens.
function convertAssetToPt(uint256 assetAmount) external view returns (uint256 ptAmount) {
return _convertAssetToPt(assetAmount);
}
/// @notice Converts an amount of Pendle PT tokens to the strategy's asset based on the current rate.
/// @param ptAmount The amount of Pendle PT tokens to convert.
/// @return assetAmount The equivalent amount of strategy asset tokens.
function convertPtToAsset(uint256 ptAmount) external view returns (uint256 assetAmount) {
return _convertPtToAsset(ptAmount);
}
/// @notice Converts an amount of Silo borrow tokens to Pendle PT tokens based on the current price.
/// @param borrowTokenAmount The amount of Silo borrow tokens to convert.
/// @return ptAmount The equivalent amount of Pendle PT tokens.
function convertBorrowTokenToPt(uint256 borrowTokenAmount) external view returns (uint256 ptAmount) {
return _convertBorrowTokenToPt(borrowTokenAmount);
}
/// @notice Converts an amount of Pendle PT tokens to Silo borrow tokens based on the current price.
/// @param ptAmount The amount of Pendle PT tokens to convert.
/// @return borrowTokenAmount The equivalent amount of Silo borrow tokens.
function convertPtToBorrowToken(uint256 ptAmount) external view returns (uint256 borrowTokenAmount) {
return _convertPtToBorrowToken(ptAmount);
}
/// @notice Converts an amount of Silo Borrow tokens to strategy asset tokens based on the current price.
/// @param tokenAmount The amount of SiloBorrowToken to convert.
/// @return assetAmount The equivalent amount of asset tokens.
function convertBorrowTokenToAsset(uint256 tokenAmount) external view returns (uint256 assetAmount) {
return _convertBorrowTokenToAsset(tokenAmount);
}
/// @notice Converts an amount of strategy Asset tokens to Silo borrow tokens based on the current price.
/// @param assetAmount The amount of asset tokens to convert to SiloBorrowToken.
/// @return borrowTokenAmount The equivalent amount of Silo borrow tokens (SILO_BORROW_TOKEN).
function convertAssetToBorrowToken(uint256 assetAmount) external view returns (uint256 borrowTokenAmount) {
return _convertAssetToBorrowToken(assetAmount);
}
/// @notice Calculates the required flash loan amount and direction (leverage up or down) for rebalancing.
/// @param ltvBuffer The buffer to apply when calculating FL amount
/// @return flashLoanAmount The calculated amount for the flash loan.
/// @return isLeverageUp True if leveraging up, false if leveraging down.
function calculateFlashLoanAmount(
uint256 ltvBuffer
) external view returns (uint256 flashLoanAmount, bool isLeverageUp) {
return _calculateFlashLoanAmount(ltvBuffer);
}
/*//////////////////////////////////////////////////////////////
Internal Yearn override functions
//////////////////////////////////////////////////////////////*/
/// @notice Funds deposited to the strategy are deployed during rebalance
/// This approach is used to avoid swaps on user deposits
function _deployFunds(uint256 /* _amount */) internal override {}
/// @notice Attempts to free a specified amount of the strategy's asset from the yield source.
/// @dev This function is an internal override from the BaseStrategy.
/// @param _amount The amount of 'asset' to be freed.
function _freeFunds(uint256 _amount) internal override {
uint256 amountToWithdrawInPt = _convertAssetToPt(_amount);
uint256 ptBalance = _getSelfBalance(PT);
// If strategy already has enough PT tokens, no need to withdraw from Silo
if (ptBalance >= amountToWithdrawInPt) {
// Swap PT to asset
_swapPendlePtForAsset(amountToWithdrawInPt);
return;
} else {
amountToWithdrawInPt -= ptBalance;
}
(uint256 totalAssets, , uint256 totalDebt) = _getRealAssetBalance();
// If amount to withdraw is more than totalAssets, set amount to totalAssets
if (_amount > totalAssets) {
_amount = totalAssets;
}
uint256 targetDebt = (_convertAssetToBorrowToken(totalAssets - _amount) * configTargetLtv) /
(WAD - configTargetLtv);
// If the updated debt is less than the current debt,
// we need wind-down the position to repay debt to Silo
if (targetDebt < totalDebt) {
uint256 repayAmount = totalDebt - targetDebt;
bytes memory flashLoanData = abi.encode(repayAmount, false, "");
// The flashloan is used to repay the Silo debt and reduce the LTV of the position
// This is needed to ensure that the position is healthy and can be unwound safely
// After flashloan, the strategy needs to withdraw collateral from Silo and swap the
// received PT tokens to asset
ISilo(BORROW_SILO).flashLoan(this, SILO_BORROW_TOKEN, repayAmount, flashLoanData);
}
// If the max withdraw amount is less than the withdraw amount, withdraw maxWithdrawAmount and book a loss
uint256 maxWithdraw = ISilo(DEPOSIT_SILO).maxWithdraw(address(this), SILO_COLLATERAL_TYPE);
if (amountToWithdrawInPt > maxWithdraw) {
amountToWithdrawInPt = maxWithdraw;
}
// Withdraw and swap the required PT tokens to asset
_withdrawCollateralFromSilo(amountToWithdrawInPt);
uint256 assetsReceived = _swapPendlePtForAsset(_getSelfBalance(PT));
if (assetsReceived < _amount) {
emit LossOnWithdrawal(_amount - assetsReceived);
}
}
/**
* @dev Internal function to harvest all rewards, redeploy any idle
* funds and return an accurate accounting of all funds currently
* held by the Strategy.
* @return _totalAssets A trusted and accurate account for the total
* amount of 'asset' the strategy currently holds including idle funds.
*/
function _harvestAndReport() internal override returns (uint256 _totalAssets) {
if (_isLtvOutOfRange()) {
_depositIdleStrategyAssets();
_rebalanceStrategy(DEFAULT_LTV_BUFFER);
}
(_totalAssets, , ) = _getRealAssetBalance();
}
/// @notice Rebalances the strategy
function _tend(uint256 /* _totalIdle */) internal override {
_rebalanceStrategy(DEFAULT_LTV_BUFFER);
}
/// @notice Checks if the strategy needs to be tended (rebalanced).
/// @return True if tending is required, false otherwise.
function _tendTrigger() internal view override returns (bool) {
// _tend needs to be triggered if the strategy LTV is outside configured range
return _isLtvOutOfRange();
}
/// @notice Handles emergency withdrawals of assets from the strategy during shutdown.
/// @dev This function is an internal override from the BaseStrategy.
/// @param _amount The amount of 'asset' to withdraw.
function _emergencyWithdraw(uint256 _amount) internal override {
if (_amount == type(uint256).max) {
_deallocateAllFunds();
return;
}
_freeFunds(_amount);
}
/*//////////////////////////////////////////////////////////////
Internal Functions with external interactions
//////////////////////////////////////////////////////////////*/
//////////////////////// Silo Functions ////////////////////////
/// @notice Supplies a specified amount of Pendle PT tokens as collateral to the deposit silo.
/// @param amount The amount of Pendle PT tokens to supply.
function _supplyCollateralToSilo(uint256 amount) internal {
IERC20(PT).forceApprove(DEPOSIT_SILO, amount);
ISilo(DEPOSIT_SILO).deposit(amount, address(this), SILO_COLLATERAL_TYPE);
}
/// @notice Borrows a specified amount of tokens from the borrow silo.
/// @param amount The amount of tokens to borrow.
function _borrowFromSilo(uint256 amount) internal {
ISilo(BORROW_SILO).borrow(amount, address(this), address(this));
}
/// @notice Repays a specified amount of debt to the borrow silo.
/// @param repayAmount The amount of debt to repay.
/// @return amountRepaid The actual amount of debt repaid.
function _repaySiloDebt(uint256 repayAmount) internal returns (uint256 amountRepaid) {
// Check the max loan amount of Silo
uint256 existingBorrow = ISiloLens(SILO_LENS_SONIC).getBorrowAmount(ISilo(BORROW_SILO), address(this));
if (repayAmount > existingBorrow) {
repayAmount = existingBorrow;
}
if (repayAmount == 0) {
// No debt to repay, return
return 0;
}
IERC20(SILO_BORROW_TOKEN).forceApprove(BORROW_SILO, repayAmount);
ISilo(BORROW_SILO).repay(repayAmount, address(this));
return repayAmount;
}
/// @notice Withdraws a specified amount of collateral from the deposit silo.
/// The function reverts if the withdrawal results in the strategy position being insolvent
/// @param collateralAmount The amount of collateral to withdraw in Pendle PT units.
/// @return amountWithdrawn The actual amount of collateral withdrawn.
function _withdrawCollateralFromSilo(uint256 collateralAmount) internal returns (uint256 amountWithdrawn) {
uint256 collateralBalance = _getSiloCollateralBalance();
if (collateralAmount > collateralBalance) {
// If the requested amount is more than the available collateral, withdraw all available collateral
collateralAmount = collateralBalance;
}
ISilo(DEPOSIT_SILO).withdraw(collateralAmount, address(this), address(this), SILO_COLLATERAL_TYPE);
return collateralAmount;
}
//////////////////////// Swapper Functions //////////////////////
/// @notice Swap Pendle Asset tokens for Pendle PT tokens
/// @dev NOTE: The strategy asset token is not the same as the pendle asset token
/// For Ceres strategy, Pendle yieldToken is the strategy asset token
function _swapAssetForPendlePt(uint256 assetAmount) internal returns (uint256) {
uint256 minPtOut = _getSlippageAdjustedAmount(_convertAssetToPt(assetAmount));
uint256 tokensReceived = ceresSwapper.swapFrom(
address(asset),
PT,
assetAmount,
minPtOut,
address(this),
abi.encode(PENDLE_MARKET)
);
if (tokensReceived < minPtOut) revert LibError.SlippageLimitExceeded();
return tokensReceived;
}
/// @notice Swap Pendle Asset tokens for Pendle PT tokens
/// @dev NOTE: The strategy asset token is not the same as the pendle asset token
/// For Ceres strategy, Pendle yieldToken is the strategy asset token
function _swapPendlePtForAsset(uint256 ptAmount) internal returns (uint256) {
uint256 minAssetOut = _getSlippageAdjustedAmount(_convertPtToAsset(ptAmount));
uint256 tokensReceived = ceresSwapper.swapFrom(
PT,
address(asset),
ptAmount,
minAssetOut,
address(this),
abi.encode(PENDLE_MARKET)
);
if (tokensReceived < minAssetOut) revert LibError.SlippageLimitExceeded();
return tokensReceived;
}
/// @notice Swaps borrowed tokens for the strategy's asset tokens.
/// @param _amountIn The amount of borrowed tokens to swap.
/// @return The amount of strategy asset tokens received.
function _swapBorrowTokenToAsset(uint256 _amountIn) internal returns (uint256) {
uint256 minAmountOut = _getSlippageAdjustedAmount(_convertBorrowTokenToAsset(_amountIn));
uint256 tokensReceived = ceresSwapper.swapFrom(
SILO_BORROW_TOKEN,
address(asset),
_amountIn,
minAmountOut,
address(this),
""
);
if (tokensReceived < minAmountOut) revert LibError.SlippageLimitExceeded();
return tokensReceived;
}
/*//////////////////////////////////////////////////////////////
Internal functions for conversions
//////////////////////////////////////////////////////////////*/
/// @notice Converts an amount of the strategy's asset to Pendle PT tokens based on the current rate.
/// @param assetAmount The amount of strategy asset tokens to convert.
/// @return ptAmount The equivalent amount of Pendle PT tokens.
function _convertAssetToPt(uint256 assetAmount) internal view returns (uint256 ptAmount) {
if (assetAmount > 0) {
// PT to SY(Strategy asset) rate is in 18 decimals.
// For example, 1 PT-wstkscUSD => 0.98 wstkscUSD (formatted) or 0.98 * 10 ** 18 (raw)
uint256 ptPriceInAsset = PendlePYOracleLib.getPtToSyRate(IPMarket(PENDLE_MARKET), configTwapDuration);
if (ptPriceInAsset == 0) revert LibError.InvalidPrice();
ptAmount = assetAmount.mulDiv(WAD, ptPriceInAsset, Math.Rounding.Floor);
}
}
/// @notice Converts an amount of Pendle PT tokens to the strategy's asset based on the current rate.
/// @param ptAmount The amount of Pendle PT tokens to convert.
/// @return assetAmount The equivalent amount of strategy asset tokens.
function _convertPtToAsset(uint256 ptAmount) internal view returns (uint256 assetAmount) {
if (ptAmount > 0) {
// PT to SY(Strategy asset) rate is in 18 decimals.
// For example, 1 PT-wstkscUSD => 0.98 wstkscUSD (formatted) or 0.98 * 10 ** 18 (raw)
uint256 ptPriceInAsset = PendlePYOracleLib.getPtToSyRate(IPMarket(PENDLE_MARKET), configTwapDuration);
if (ptPriceInAsset == 0) revert LibError.InvalidPrice();
assetAmount = ptAmount.mulDiv(ptPriceInAsset, WAD, Math.Rounding.Floor);
}
}
/// @notice Converts an amount of Silo borrow tokens to Pendle PT tokens based on the current price.
/// @param borrowTokenAmount The amount of Silo borrow tokens to convert.
/// @return ptAmount The equivalent amount of Pendle PT tokens.
function _convertBorrowTokenToPt(uint256 borrowTokenAmount) internal view returns (uint256 ptAmount) {
if (borrowTokenAmount > 0) {
uint256 collateralTokenPrice = _getSiloCollateralTokenPrice();
ptAmount =
(borrowTokenAmount * 10 ** PT_TOKEN_DECIMALS * WAD) /
(collateralTokenPrice * 10 ** BORROW_TOKEN_DECIMALS);
}
}
/// @notice Converts an amount of Pendle PT tokens to Silo borrow tokens based on the current price.
/// @param ptAmount The amount of Pendle PT tokens to convert.
/// @return borrowTokenAmount The equivalent amount of Silo borrow tokens.
function _convertPtToBorrowToken(uint256 ptAmount) internal view returns (uint256 borrowTokenAmount) {
if (ptAmount > 0) {
uint256 collateralTokenPrice = _getSiloCollateralTokenPrice();
borrowTokenAmount =
(ptAmount * collateralTokenPrice * 10 ** BORROW_TOKEN_DECIMALS) /
(WAD * 10 ** PT_TOKEN_DECIMALS);
}
}
/// @notice Converts an amount of Silo Borrow tokens to strategy asset tokens based on the current price.
/// @param tokenAmount The amount of SiloBorrowToken to convert.
/// @return assetAmount The equivalent amount of asset tokens.
function _convertBorrowTokenToAsset(uint256 tokenAmount) internal view returns (uint256 assetAmount) {
if (tokenAmount > 0) {
// As direct oracle for Silo Borrow token to strategy Asset is not available
// convert the tokenAmount in 2 steps: Silo Borrow Token -> PT -> Asset
uint256 tokenAmountInPt = _convertBorrowTokenToPt(tokenAmount);
assetAmount = _convertPtToAsset(tokenAmountInPt);
}
}
/// @notice Converts an amount of strategy Asset tokens to Silo borrow tokens based on the current price.
/// @param assetAmount The amount of asset tokens to convert to SiloBorrowToken.
/// @return borrowTokenAmount The equivalent amount of Silo borrow tokens (SILO_BORROW_TOKEN).
function _convertAssetToBorrowToken(uint256 assetAmount) internal view returns (uint256 borrowTokenAmount) {
if (assetAmount > 0) {
// As direct oracle for strategy Asset to Silo Borrow token is not available
// convert the assetAmount in 2 steps: Asset -> PT -> Silo Borrow Token
uint256 assetAmountInPt = _convertAssetToPt(assetAmount);
borrowTokenAmount = _convertPtToBorrowToken(assetAmountInPt);
}
}
/// @notice Calculates the slippage-adjusted amount based on the configured maximum slippage.
/// @param amount The original amount.
/// @return adjustedAmount The amount adjusted for slippage.
function _getSlippageAdjustedAmount(uint256 amount) internal view returns (uint256 adjustedAmount) {
return amount.mulDiv(MAX_BPS - configMaxSlippageBps, MAX_BPS, Math.Rounding.Ceil);
}
/*//////////////////////////////////////////////////////////////
Internal misc. functions and validations
//////////////////////////////////////////////////////////////*/
/// @notice Validates the Pendle market configuration.
/// @param _market The address of the Pendle market contract.
function _validatePendleMarket(address _market) internal view {
(IStandardizedYield stdYield, , ) = IPMarket(_market).readTokens();
address yieldToken = stdYield.yieldToken();
// Strategy asset must be the same as the Pendle yield token
// SY-token is a wrapped version of the yield token
// For example, for PT-sUSDe, yield token is sUSDe, etc.
// More details here: https://docs.pendle.finance/Developers/Contracts/StandardizedYield#standard-sys
if (address(asset) != yieldToken) {
revert LibError.InvalidAsset();
}
}
/// @notice Validates the Silo market configuration and identifies the deposit and borrow silos.
/// @param _siloMarketConfig The address of the Silo market config contract.
/// @return depositSiloAddress The address of the deposit silo.
/// @return borrowSiloAddress The address of the borrow silo.
function _validateSiloMarket(
address _siloMarketConfig
) internal view returns (address depositSiloAddress, address borrowSiloAddress) {
(address _silo0, address _silo1) = ISiloConfig(_siloMarketConfig).getSilos();
if (PT == ISilo(_silo0).asset()) {
depositSiloAddress = _silo0;
borrowSiloAddress = _silo1;
} else if (PT == ISilo(_silo1).asset()) {
depositSiloAddress = _silo1;
borrowSiloAddress = _silo0;
} else {
revert LibError.InvalidSiloMarket();
}
}
/// @notice Internal function to rebalance the strategy using a flash loan.
/// @param flashLoanAmount The amount for the flash loan.
/// @param isLeverageUp True if leveraging up, false if leveraging down.
function _rebalanceStrategy(uint256 flashLoanAmount, bool isLeverageUp, bytes memory swapData) internal {
if (flashLoanAmount > 0) {
// FlashLoan data contains the borrow amount and a flag to indicate that we are leveraging up
// The flag is used to differentiate between leverage up and leverage down operations
bytes memory flashLoanData = abi.encode(flashLoanAmount, isLeverageUp, swapData);
ISilo(BORROW_SILO).flashLoan(this, SILO_BORROW_TOKEN, flashLoanAmount, flashLoanData);
emit StrategyRebalance();
}
}
/// @notice Rebalances the strategy by calculating the required flash loan amount and direction.
/// @dev This internal function is called by the public `rebalanceStrategy` function.
function _rebalanceStrategy(uint256 ltvBuffer) internal {
(uint256 flashLoanAmount, bool isLeverageUp) = _calculateFlashLoanAmount(ltvBuffer);
_rebalanceStrategy(flashLoanAmount, isLeverageUp, "");
}
/// @notice Calculates the required flash loan amount and direction (leverage up or down) for internal rebalancing.
/// @return flashLoanAmount The calculated amount for the flash loan.
/// @return isLeverageUp True if leveraging up, false if leveraging down.
function _calculateFlashLoanAmount(
uint256 ltvBuffer
) internal view returns (uint256 flashLoanAmount, bool isLeverageUp) {
(uint256 totalAssets, , uint256 totalDebt) = _getRealAssetBalance();
// Total max deposit amount for a given LTV is calculated as follows:
// Max Deposit = Initial Deposit (Actual assets deposited) + Max flash loan amount (flMax)
// where:
// Initial Deposit = Total assets balance of the strategy
// flMax = Total assets * Target LTV / (1 - Target LTV)
uint256 totalAssetsInBorrowToken = _convertAssetToBorrowToken(totalAssets);
uint256 maxLoan = (totalAssetsInBorrowToken * configTargetLtv) / (WAD - configTargetLtv);
uint256 positionLtv = getPositionLtv();
// Leverage up the position only if the position is below the target LTV range
if (positionLtv + ltvBuffer < configTargetLtv) {
return (maxLoan - totalDebt, true);
} else if (positionLtv > configTargetLtv + ltvBuffer) {
// Leverage down the position only if the position is above the target LTV range
return (totalDebt - maxLoan, false);
} else {
return (0, false);
}
}
/// @notice Leverages up the position onFlashLoan callback.
/// @param amount The amount of tokens received on flashLoan
/// @param amountWithFee The amount of tokens on flashLoan, including the flash loan fee.
/// @param swapData Optional: Aggregator swap data
function _leverageUpOnFlashLoan(uint256 amount, uint256 amountWithFee, bytes memory swapData) internal {
// If aggregator swap data is provided, swap SiloBorrowToken to asset token,
// else swap using on-chain dex
uint256 assetsReceived;
if (swapData.length > 0) {
uint256 minAmountOut = _getSlippageAdjustedAmount(_convertBorrowTokenToAsset(amount));
assetsReceived = ceresSwapper.swapUsingAggregator(
SILO_BORROW_TOKEN,
address(asset),
amount,
minAmountOut,
address(this),
swapData
);
} else {
assetsReceived = _swapBorrowTokenToAsset(amount);
}
// Swap the asset to Pendle PT tokens
uint256 ptReceived = _swapAssetForPendlePt(assetsReceived);
// Supply the PT tokens to Silo
_supplyCollateralToSilo(ptReceived);
// Borrow from Silo to repay the flash loan
_borrowFromSilo(amountWithFee);
}
/// @notice Leverages down the position onFlashLoan callback.
/// @param amount The amount of tokens received on flashLoan
/// @param amountWithFee The amount of tokens on flashLoan, including the flash loan fee.
/// @param swapData Optional: Aggregator swap data
function _leverageDownOnFlashLoan(uint256 amount, uint256 amountWithFee, bytes memory swapData) internal {
// Repay silo debt
_repaySiloDebt(amount);
// Add some buffer to the PT amount for slippage
uint256 ptAmount = (_convertBorrowTokenToPt(amountWithFee) * (MAX_BPS + configMaxSlippageBps)) / MAX_BPS;
// Withdraw collateral from Silo
uint256 amountWithdrawn = _withdrawCollateralFromSilo(ptAmount);
// Swap the PT tokens to strategy asset
uint256 assetAmount = _swapPendlePtForAsset(amountWithdrawn);
// Swap the asset to borrowed token using Aggregator if swapData is provided,
// else swap using on-chain dex
if (swapData.length > 0) {
uint256 minAmountOut = _getSlippageAdjustedAmount(_convertAssetToBorrowToken(assetAmount));
ceresSwapper.swapUsingAggregator(
address(asset),
SILO_BORROW_TOKEN,
assetAmount,
minAmountOut,
address(this),
swapData
);
} else {
// Swap and receive Exact amount of BORROW_TOKEN to repay the flash loan
ceresSwapper.swapTo(address(asset), SILO_BORROW_TOKEN, amountWithFee, assetAmount, address(this), "");
}
}
/// @notice Checks if the strategy is out of the configured LTV range.
/// @return True if the LTV is out of range, false otherwise.
function _isLtvOutOfRange() internal view returns (bool) {
uint256 positionLtv = getPositionLtv();
if (
TokenizedStrategy.totalAssets() != 0 &&
(positionLtv + DEFAULT_LTV_BUFFER < configTargetLtv || positionLtv > configTargetLtv + DEFAULT_LTV_BUFFER)
) {
return true;
}
return false;
}
/// @notice Deposits any idle asset or PT tokens held by the strategy
/// Swaps any idle asset tokens for Pendle PT tokens and supplies PT tokens to Silo.
function _depositIdleStrategyAssets() internal {
// Swap any idle Borrow token balance to assets
uint256 borrowTokenBalance = _getSelfBalance(SILO_BORROW_TOKEN);
if (borrowTokenBalance > 0) {
_swapBorrowTokenToAsset(borrowTokenBalance);
}
// Deposit idle assets in the strategy
uint256 assetBalance = _getSelfBalance(address(asset));
if (assetBalance > 0) {
_swapAssetForPendlePt(assetBalance);
}
// Deposit PT tokens in the strategy
uint256 ptBalance = _getSelfBalance(PT);
if (ptBalance > 0) {
_supplyCollateralToSilo(ptBalance);
}
emit DepositIdleAssets(assetBalance, ptBalance, borrowTokenBalance);
}
/// @notice Set approvals for the swapper contract
function _setSwapperApprovals() internal {
address swapperAddress = address(ceresSwapper);
if (swapperAddress == address(0)) return;
IERC20(address(asset)).forceApprove(swapperAddress, type(uint256).max);
IERC20(PT).forceApprove(swapperAddress, type(uint256).max);
IERC20(SILO_BORROW_TOKEN).forceApprove(swapperAddress, type(uint256).max);
}
/// @notice Revoke approvals for the swapper contract
function _revokeSwapperApprovals() internal {
address swapperAddress = address(ceresSwapper);
if (swapperAddress == address(0)) return;
IERC20(address(asset)).forceApprove(swapperAddress, 0);
IERC20(PT).forceApprove(swapperAddress, 0);
IERC20(SILO_BORROW_TOKEN).forceApprove(swapperAddress, 0);
}
/// @notice Deallocates all funds from the strategy positions and converts them to the strategy asset.
/// @dev This function completely unwinds the leveraged position by:
/// 1. Repaying all outstanding debt using a flash loan
/// 2. Withdrawing all collateral from the Silo deposit pool
/// 3. Converting all Pendle PT tokens back to the strategy's base asset
/// 4. Converting any remaining borrow tokens back to the base asset
/// @dev This function is typically called during strategy migration or shutdown or emergency procedures
/// to ensure all positions are closed and funds are liquid.
/// @dev After execution, the strategy should hold only the base asset tokens with no
/// outstanding positions in Silo or Pendle markets.
function _deallocateAllFunds() internal {
// Set target LTV to 0
configTargetLtv = 0;
uint256 strategyDebt = _getSiloDebtBalance();
// Repay the entire debt using FlashLoan
_rebalanceStrategy(strategyDebt, false, "");
// Withdraw all collateral from the deposit silo
uint256 collateralBalance = _getSiloCollateralBalance();
if (collateralBalance > 0) {
uint256 maxWithdraw = ISilo(DEPOSIT_SILO).maxWithdraw(address(this), SILO_COLLATERAL_TYPE);
// To account of any remaining dust amount of debt or losses due to slippage during rebalance
if (collateralBalance > maxWithdraw) {
collateralBalance = maxWithdraw;
}
_withdrawCollateralFromSilo(collateralBalance);
}
// Swap all PT tokens to asset
uint256 ptBalance = _getSelfBalance(PT);
if (ptBalance > 0) {
_swapPendlePtForAsset(ptBalance);
}
// Swap all Borrow tokens to asset
uint256 borrowTokenBalance = _getSelfBalance(SILO_BORROW_TOKEN);
if (borrowTokenBalance > 0) {
_swapBorrowTokenToAsset(borrowTokenBalance);
}
emit StrategyFundsDeallocated();
}
/*//////////////////////////////////////////////////////////////
Internal getter functions
//////////////////////////////////////////////////////////////*/
/// @notice Gets the total real asset balance of the strategy, including assets, PT tokens, and Silo positions.
/// @return totalAssets The total real asset balance in strategy asset units.
/// @return totalCollateral Total collateral balance in Pendle PT units that the strategy holds.
/// Includes deposited collateral + strategy PT balance
/// @return totalDebt The debt balance of the strategy in SILO_BORROW_TOKEN units.
/// Includes the Silo Debt of the strategy minus any token balance of the strategy
function _getRealAssetBalance()
internal
view
returns (uint256 totalAssets, uint256 totalCollateral, uint256 totalDebt)
{
// The total real asset balance of the strategy is calculated as follows:
// Real balance = Asset_Bal + PT_Bal + PT_Deposited + BT_Bal - BT_Debt
// Where:
// Asset_Bal = Asset balance of the strategy
// PT_Bal = PT balance of the strategy (converted to Asset)
// PT_Deposited = PT tokens deposited to Silo as Collateral (converted to Asset)
// BT_Bal = Silo Borrow Token balance of the strategy (converted to Asset)
// BT_Debt = Total Debt (borrowed amount in SiloBorrowToken) (converted to Asset)
totalCollateral = _getSelfBalance(PT) + _getSiloCollateralBalance();
uint256 debt = _getSiloDebtBalance();
uint256 btBalance = _getSelfBalance(SILO_BORROW_TOKEN);
if (debt > btBalance) {
totalDebt = debt - btBalance;
totalAssets =
_getSelfBalance(address(asset)) +
_convertPtToAsset(totalCollateral) -
_convertBorrowTokenToAsset(totalDebt);
} else {
// If the strategy has more SiloBorrowToken than debt, we can ignore the debt
totalDebt = 0;
totalAssets =
_getSelfBalance(address(asset)) +
_convertPtToAsset(totalCollateral) +
_convertBorrowTokenToAsset(btBalance - debt);
}
}
/// @notice Gets the collateral balance of the strategy in the deposit silo.
/// @return collateralBalance The collateral balance in Pendle PT units.
function _getSiloCollateralBalance() internal view returns (uint256 collateralBalance) {
collateralBalance = ISiloLens(SILO_LENS_SONIC).collateralBalanceOfUnderlying(
ISilo(DEPOSIT_SILO),
address(this)
);
}
/// @notice Gets the debt balance of the strategy in the borrow silo.
/// @return debtBalance The debt balance in Silo borrow token units.
function _getSiloDebtBalance() internal view returns (uint256 debtBalance) {
debtBalance = ISiloLens(SILO_LENS_SONIC).debtBalanceOfUnderlying(ISilo(BORROW_SILO), address(this));
}
/// @notice Gets the price of the collateral token (PT) in terms of the borrow token from the Silo oracle.
/// @return collateralTokenPrice The price of the collateral token in borrow token units
function _getSiloCollateralTokenPrice() internal view returns (uint256 collateralTokenPrice) {
address depositOracle = ISiloConfig(SILO_CONFIG).getConfig(DEPOSIT_SILO).solvencyOracle;
// If only one oracle is set, Oracle must return the token's price in the exact decimals of the other token
// that will not use an oracle. In this case, the Other token price is represented by its token amounts.
// https://docs.silo.finance/docs/developers/dev-tutorials/oracles/#one-oracle-is-set
if (depositOracle == address(0)) {
collateralTokenPrice = 10 ** BORROW_TOKEN_DECIMALS; // Default price for the deposit token
} else {
collateralTokenPrice = ISiloOracle(depositOracle).quote(10 ** PT_TOKEN_DECIMALS, PT);
}
if (collateralTokenPrice == 0) revert LibError.InvalidPrice();
return collateralTokenPrice;
}
/// @notice Gets the balance of a specific token held by the strategy contract.
/// @param token The address of the token to check the balance of.
/// @return The balance of the token held by the strategy.
function _getSelfBalance(address token) internal view returns (uint256) {
return IERC20(token).balanceOf(address(this));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
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.1.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 ERC-20 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.3.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC1363} from "../../../interfaces/IERC1363.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 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 {
/**
* @dev An operation with an ERC-20 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 Variant of {safeTransfer} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransfer(IERC20 token, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Variant of {safeTransferFrom} that returns a bool instead of reverting if the operation is not successful.
*/
function trySafeTransferFrom(IERC20 token, address from, address to, uint256 value) internal returns (bool) {
return _callOptionalReturnBool(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.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
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.
*
* IMPORTANT: If the token implements ERC-7674 (ERC-20 with temporary allowance), and if the "client"
* smart contract uses ERC-7674 to set temporary allowances, then the "client" smart contract should avoid using
* this function. Performing a {safeIncreaseAllowance} or {safeDecreaseAllowance} operation on a token contract
* that has a non-zero temporary allowance (for that particular owner-spender) will result in unexpected behavior.
*/
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.
*
* NOTE: If the token implements ERC-7674, this function will not modify any temporary allowance. This function
* only sets the "standard" allowance. Any temporary allowance will remain active, in addition to the value being
* set here.
*/
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 Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
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 {_callOptionalReturnBool} that reverts if call fails to meet the requirements.
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
let success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
// bubble errors
if iszero(success) {
let ptr := mload(0x40)
returndatacopy(ptr, 0, returndatasize())
revert(ptr, returndatasize())
}
returnSize := returndatasize()
returnValue := mload(0)
}
if (returnSize == 0 ? address(token).code.length == 0 : returnValue != 1) {
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 silently catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
bool success;
uint256 returnSize;
uint256 returnValue;
assembly ("memory-safe") {
success := call(gas(), token, 0, add(data, 0x20), mload(data), 0, 0x20)
returnSize := returndatasize()
returnValue := mload(0)
}
return success && (returnSize == 0 ? address(token).code.length > 0 : returnValue == 1);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Return the 512-bit addition of two uint256.
*
* The result is stored in two 256 variables such that sum = high * 2²⁵⁶ + low.
*/
function add512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
assembly ("memory-safe") {
low := add(a, b)
high := lt(low, a)
}
}
/**
* @dev Return the 512-bit multiplication of two uint256.
*
* The result is stored in two 256 variables such that product = high * 2²⁵⁶ + low.
*/
function mul512(uint256 a, uint256 b) internal pure returns (uint256 high, uint256 low) {
// 512-bit multiply [high low] = x * y. Compute the product mod 2²⁵⁶ and mod 2²⁵⁶ - 1, then use
// the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = high * 2²⁵⁶ + low.
assembly ("memory-safe") {
let mm := mulmod(a, b, not(0))
low := mul(a, b)
high := sub(sub(mm, low), lt(mm, low))
}
}
/**
* @dev Returns the addition of two unsigned integers, with a success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
success = c >= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with a success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a - b;
success = c <= a;
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with a success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a * b;
assembly ("memory-safe") {
// Only true when the multiplication doesn't overflow
// (c / a == b) || (a == 0)
success := or(eq(div(c, a), b), iszero(a))
}
// equivalent to: success ? c : 0
result = c * SafeCast.toUint(success);
}
}
/**
* @dev Returns the division of two unsigned integers, with a success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `DIV` opcode returns zero when the denominator is 0.
result := div(a, b)
}
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
success = b > 0;
assembly ("memory-safe") {
// The `MOD` opcode returns zero when the denominator is 0.
result := mod(a, b)
}
}
}
/**
* @dev Unsigned saturating addition, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingAdd(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryAdd(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Unsigned saturating subtraction, bounds to zero instead of overflowing.
*/
function saturatingSub(uint256 a, uint256 b) internal pure returns (uint256) {
(, uint256 result) = trySub(a, b);
return result;
}
/**
* @dev Unsigned saturating multiplication, bounds to `2²⁵⁶ - 1` instead of overflowing.
*/
function saturatingMul(uint256 a, uint256 b) internal pure returns (uint256) {
(bool success, uint256 result) = tryMul(a, b);
return ternary(success, result, type(uint256).max);
}
/**
* @dev Branchless ternary evaluation for `a ? b : c`. Gas costs are constant.
*
* IMPORTANT: This function may reduce bytecode size and consume less gas when used standalone.
* However, the compiler may optimize Solidity ternary operations (i.e. `a ? b : c`) to only compute
* one branch when needed, making this function more expensive.
*/
function ternary(bool condition, uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
// branchless ternary works because:
// b ^ (a ^ b) == a
// b ^ 0 == b
return b ^ ((a ^ b) * SafeCast.toUint(condition));
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(a > b, a, b);
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return ternary(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.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return SafeCast.toUint(a > 0) * ((a - 1) / b + 1);
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* 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 {
(uint256 high, uint256 low) = mul512(x, y);
// Handle non-overflow cases, 256 by 256 division.
if (high == 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 low / denominator;
}
// Make sure the result is less than 2²⁵⁶. Also prevents denominator == 0.
if (denominator <= high) {
Panic.panic(ternary(denominator == 0, Panic.DIVISION_BY_ZERO, Panic.UNDER_OVERFLOW));
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [high low].
uint256 remainder;
assembly ("memory-safe") {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
high := sub(high, gt(remainder, low))
low := sub(low, 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 ("memory-safe") {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [high low] by twos.
low := div(low, twos)
// Flip twos such that it is 2²⁵⁶ / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from high into low.
low |= high * twos;
// Invert denominator mod 2²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
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⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// 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²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, this is the final result. We don't need to compute the high bits of the result and high
// is no longer required.
result = low * inverse;
return result;
}
}
/**
* @dev 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) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculates floor(x * y >> n) with full precision. Throws if result overflows a uint256.
*/
function mulShr(uint256 x, uint256 y, uint8 n) internal pure returns (uint256 result) {
unchecked {
(uint256 high, uint256 low) = mul512(x, y);
if (high >= 1 << n) {
Panic.panic(Panic.UNDER_OVERFLOW);
}
return (high << (256 - n)) | (low >> n);
}
}
/**
* @dev Calculates x * y >> n with full precision, following the selected rounding direction.
*/
function mulShr(uint256 x, uint256 y, uint8 n, Rounding rounding) internal pure returns (uint256) {
return mulShr(x, y, n) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, 1 << n) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, except 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Fermat's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`. See {invModPrime}.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return ternary(x < 0, n - uint256(-x), uint256(x)); // Wrap the result if it's negative.
}
}
/**
* @dev Variant of {invMod}. More efficient, but only works if `p` is known to be a prime greater than `2`.
*
* From https://en.wikipedia.org/wiki/Fermat%27s_little_theorem[Fermat's little theorem], we know that if p is
* prime, then `a**(p-1) ≡ 1 mod p`. As a consequence, we have `a * a**(p-2) ≡ 1 mod p`, which means that
* `a**(p-2)` is the modular multiplicative inverse of a in Fp.
*
* NOTE: this function does NOT check that `p` is a prime greater than `2`.
*/
function invModPrime(uint256 a, uint256 p) internal view returns (uint256) {
unchecked {
return Math.modExp(a, p - 2, p);
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked as failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
assembly ("memory-safe") {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
assembly ("memory-safe") {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLen))
}
}
/**
* @dev Returns whether the provided byte array is zero.
*/
function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
for (uint256 i = 0; i < byteArray.length; ++i) {
if (byteArray[i] != 0) {
return false;
}
}
return true;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* This method is based on Newton's method for computing square roots; the algorithm is restricted to only
* using integer operations.
*/
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
// Take care of easy edge cases when a == 0 or a == 1
if (a <= 1) {
return a;
}
// In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
// sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
// the current value as `ε_n = | x_n - sqrt(a) |`.
//
// For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
// of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
// bigger than any uint256.
//
// By noticing that
// `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
// we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
// to the msb function.
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
// We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
//
// We can refine our estimation by noticing that the middle of that interval minimizes the error.
// If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
// This is going to be our x_0 (and ε_0)
xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
// From here, Newton's method give us:
// x_{n+1} = (x_n + a / x_n) / 2
//
// One should note that:
// x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
// = ((x_n² + a) / (2 * x_n))² - a
// = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
// = (x_n² - a)² / (2 * x_n)²
// = ((x_n² - a) / (2 * x_n))²
// ≥ 0
// Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
//
// This gives us the proof of quadratic convergence of the sequence:
// ε_{n+1} = | x_{n+1} - sqrt(a) |
// = | (x_n + a / x_n) / 2 - sqrt(a) |
// = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
// = | (x_n - sqrt(a))² / (2 * x_n) |
// = | ε_n² / (2 * x_n) |
// = ε_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// ε_1 = ε_0² / | (2 * x_0) |
// ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
// ≤ 2**(2*e-4) / (3 * 2**(e-1))
// ≤ 2**(e-3) / 3
// ≤ 2**(e-3-log2(3))
// ≤ 2**(e-4.5)
//
// For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
// ε_{n+1} = ε_n² / | (2 * x_n) |
// ≤ (2**(e-k))² / (2 * 2**(e-1))
// ≤ 2**(2*e-2*k) / 2**e
// ≤ 2**(e-2*k)
xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72
// Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
// ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
// sqrt(a) or sqrt(a) + 1.
return xn - SafeCast.toUint(xn > a / xn);
}
}
/**
* @dev 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// If upper 8 bits of 16-bit half set, add 8 to result
r |= SafeCast.toUint((x >> r) > 0xff) << 3;
// If upper 4 bits of 8-bit half set, add 4 to result
r |= SafeCast.toUint((x >> r) > 0xf) << 2;
// Shifts value right by the current result and use it as an index into this lookup table:
//
// | x (4 bits) | index | table[index] = MSB position |
// |------------|---------|-----------------------------|
// | 0000 | 0 | table[0] = 0 |
// | 0001 | 1 | table[1] = 0 |
// | 0010 | 2 | table[2] = 1 |
// | 0011 | 3 | table[3] = 1 |
// | 0100 | 4 | table[4] = 2 |
// | 0101 | 5 | table[5] = 2 |
// | 0110 | 6 | table[6] = 2 |
// | 0111 | 7 | table[7] = 2 |
// | 1000 | 8 | table[8] = 3 |
// | 1001 | 9 | table[9] = 3 |
// | 1010 | 10 | table[10] = 3 |
// | 1011 | 11 | table[11] = 3 |
// | 1100 | 12 | table[12] = 3 |
// | 1101 | 13 | table[13] = 3 |
// | 1110 | 14 | table[14] = 3 |
// | 1111 | 15 | table[15] = 3 |
//
// The lookup table is represented as a 32-byte value with the MSB positions for 0-15 in the last 16 bytes.
assembly ("memory-safe") {
r := or(r, byte(shr(r, x), 0x0000010102020202030303030303030300000000000000000000000000000000))
}
}
/**
* @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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
}
}
/**
* @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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
}
}
/**
* @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 x) internal pure returns (uint256 r) {
// If value has upper 128 bits set, log2 result is at least 128
r = SafeCast.toUint(x > 0xffffffffffffffffffffffffffffffff) << 7;
// If upper 64 bits of 128-bit half set, add 64 to result
r |= SafeCast.toUint((x >> r) > 0xffffffffffffffff) << 6;
// If upper 32 bits of 64-bit half set, add 32 to result
r |= SafeCast.toUint((x >> r) > 0xffffffff) << 5;
// If upper 16 bits of 32-bit half set, add 16 to result
r |= SafeCast.toUint((x >> r) > 0xffff) << 4;
// Add 1 if upper 8 bits of 16-bit half set, and divide accumulated result by 8
return (r >> 3) | SafeCast.toUint((x >> r) > 0xff);
}
/**
* @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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
}
}
/**
* @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: AGPL-3.0
pragma solidity >=0.8.18;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
// TokenizedStrategy interface used for internal view delegateCalls.
import {ITokenizedStrategy} from "./interfaces/ITokenizedStrategy.sol";
/**
* @title YearnV3 Base Strategy
* @author yearn.finance
* @notice
* BaseStrategy implements all of the required functionality to
* seamlessly integrate with the `TokenizedStrategy` implementation contract
* allowing anyone to easily build a fully permissionless ERC-4626 compliant
* Vault by inheriting this contract and overriding three simple functions.
* It utilizes an immutable proxy pattern that allows the BaseStrategy
* to remain simple and small. All standard logic is held within the
* `TokenizedStrategy` and is reused over any n strategies all using the
* `fallback` function to delegatecall the implementation so that strategists
* can only be concerned with writing their strategy specific code.
*
* This contract should be inherited and the three main abstract methods
* `_deployFunds`, `_freeFunds` and `_harvestAndReport` implemented to adapt
* the Strategy to the particular needs it has to generate yield. There are
* other optional methods that can be implemented to further customize
* the strategy if desired.
*
* All default storage for the strategy is controlled and updated by the
* `TokenizedStrategy`. The implementation holds a storage struct that
* contains all needed global variables in a manual storage slot. This
* means strategists can feel free to implement their own custom storage
* variables as they need with no concern of collisions. All global variables
* can be viewed within the Strategy by a simple call using the
* `TokenizedStrategy` variable. IE: TokenizedStrategy.globalVariable();.
*/
abstract contract BaseStrategy {
/*//////////////////////////////////////////////////////////////
MODIFIERS
//////////////////////////////////////////////////////////////*/
/**
* @dev Used on TokenizedStrategy callback functions to make sure it is post
* a delegateCall from this address to the TokenizedStrategy.
*/
modifier onlySelf() {
_onlySelf();
_;
}
/**
* @dev Use to assure that the call is coming from the strategies management.
*/
modifier onlyManagement() {
TokenizedStrategy.requireManagement(msg.sender);
_;
}
/**
* @dev Use to assure that the call is coming from either the strategies
* management or the keeper.
*/
modifier onlyKeepers() {
TokenizedStrategy.requireKeeperOrManagement(msg.sender);
_;
}
/**
* @dev Use to assure that the call is coming from either the strategies
* management or the emergency admin.
*/
modifier onlyEmergencyAuthorized() {
TokenizedStrategy.requireEmergencyAuthorized(msg.sender);
_;
}
/**
* @dev Require that the msg.sender is this address.
*/
function _onlySelf() internal view {
require(msg.sender == address(this), "!self");
}
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/**
* @dev This is the address of the TokenizedStrategy implementation
* contract that will be used by all strategies to handle the
* accounting, logic, storage etc.
*
* Any external calls to the that don't hit one of the functions
* defined in this base or the strategy will end up being forwarded
* through the fallback function, which will delegateCall this address.
*
* This address should be the same for every strategy, never be adjusted
* and always be checked before any integration with the Strategy.
*/
address public constant tokenizedStrategyAddress =
0xD377919FA87120584B21279a491F82D5265A139c;
/*//////////////////////////////////////////////////////////////
IMMUTABLES
//////////////////////////////////////////////////////////////*/
/**
* @dev Underlying asset the Strategy is earning yield on.
* Stored here for cheap retrievals within the strategy.
*/
ERC20 internal immutable asset;
/**
* @dev This variable is set to address(this) during initialization of each strategy.
*
* This can be used to retrieve storage data within the strategy
* contract as if it were a linked library.
*
* i.e. uint256 totalAssets = TokenizedStrategy.totalAssets()
*
* Using address(this) will mean any calls using this variable will lead
* to a call to itself. Which will hit the fallback function and
* delegateCall that to the actual TokenizedStrategy.
*/
ITokenizedStrategy internal immutable TokenizedStrategy;
/**
* @notice Used to initialize the strategy on deployment.
*
* This will set the `TokenizedStrategy` variable for easy
* internal view calls to the implementation. As well as
* initializing the default storage variables based on the
* parameters and using the deployer for the permissioned roles.
*
* @param _asset Address of the underlying asset.
* @param _name Name the strategy will use.
*/
constructor(address _asset, string memory _name) {
asset = ERC20(_asset);
// Set instance of the implementation for internal use.
TokenizedStrategy = ITokenizedStrategy(address(this));
// Initialize the strategy's storage variables.
_delegateCall(
abi.encodeCall(
ITokenizedStrategy.initialize,
(_asset, _name, msg.sender, msg.sender, msg.sender)
)
);
// Store the tokenizedStrategyAddress at the standard implementation
// address storage slot so etherscan picks up the interface. This gets
// stored on initialization and never updated.
assembly {
sstore(
// keccak256('eip1967.proxy.implementation' - 1)
0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc,
tokenizedStrategyAddress
)
}
}
/*//////////////////////////////////////////////////////////////
NEEDED TO BE OVERRIDDEN BY STRATEGIST
//////////////////////////////////////////////////////////////*/
/**
* @dev Can deploy up to '_amount' of 'asset' in the yield source.
*
* This function is called at the end of a {deposit} or {mint}
* call. Meaning that unless a whitelist is implemented it will
* be entirely permissionless and thus can be sandwiched or otherwise
* manipulated.
*
* @param _amount The amount of 'asset' that the strategy can attempt
* to deposit in the yield source.
*/
function _deployFunds(uint256 _amount) internal virtual;
/**
* @dev Should attempt to free the '_amount' of 'asset'.
*
* NOTE: The amount of 'asset' that is already loose has already
* been accounted for.
*
* This function is called during {withdraw} and {redeem} calls.
* Meaning that unless a whitelist is implemented it will be
* entirely permissionless and thus can be sandwiched or otherwise
* manipulated.
*
* Should not rely on asset.balanceOf(address(this)) calls other than
* for diff accounting purposes.
*
* Any difference between `_amount` and what is actually freed will be
* counted as a loss and passed on to the withdrawer. This means
* care should be taken in times of illiquidity. It may be better to revert
* if withdraws are simply illiquid so not to realize incorrect losses.
*
* @param _amount, The amount of 'asset' to be freed.
*/
function _freeFunds(uint256 _amount) internal virtual;
/**
* @dev Internal function to harvest all rewards, redeploy any idle
* funds and return an accurate accounting of all funds currently
* held by the Strategy.
*
* This should do any needed harvesting, rewards selling, accrual,
* redepositing etc. to get the most accurate view of current assets.
*
* NOTE: All applicable assets including loose assets should be
* accounted for in this function.
*
* Care should be taken when relying on oracles or swap values rather
* than actual amounts as all Strategy profit/loss accounting will
* be done based on this returned value.
*
* This can still be called post a shutdown, a strategist can check
* `TokenizedStrategy.isShutdown()` to decide if funds should be
* redeployed or simply realize any profits/losses.
*
* @return _totalAssets A trusted and accurate account for the total
* amount of 'asset' the strategy currently holds including idle funds.
*/
function _harvestAndReport()
internal
virtual
returns (uint256 _totalAssets);
/*//////////////////////////////////////////////////////////////
OPTIONAL TO OVERRIDE BY STRATEGIST
//////////////////////////////////////////////////////////////*/
/**
* @dev Optional function for strategist to override that can
* be called in between reports.
*
* If '_tend' is used tendTrigger() will also need to be overridden.
*
* This call can only be called by a permissioned role so may be
* through protected relays.
*
* This can be used to harvest and compound rewards, deposit idle funds,
* perform needed position maintenance or anything else that doesn't need
* a full report for.
*
* EX: A strategy that can not deposit funds without getting
* sandwiched can use the tend when a certain threshold
* of idle to totalAssets has been reached.
*
* This will have no effect on PPS of the strategy till report() is called.
*
* @param _totalIdle The current amount of idle funds that are available to deploy.
*/
function _tend(uint256 _totalIdle) internal virtual {}
/**
* @dev Optional trigger to override if tend() will be used by the strategy.
* This must be implemented if the strategy hopes to invoke _tend().
*
* @return . Should return true if tend() should be called by keeper or false if not.
*/
function _tendTrigger() internal view virtual returns (bool) {
return false;
}
/**
* @notice Returns if tend() should be called by a keeper.
*
* @return . Should return true if tend() should be called by keeper or false if not.
* @return . Calldata for the tend call.
*/
function tendTrigger() external view virtual returns (bool, bytes memory) {
return (
// Return the status of the tend trigger.
_tendTrigger(),
// And the needed calldata either way.
abi.encodeWithSelector(ITokenizedStrategy.tend.selector)
);
}
/**
* @notice Gets the max amount of `asset` that an address can deposit.
* @dev Defaults to an unlimited amount for any address. But can
* be overridden by strategists.
*
* This function will be called before any deposit or mints to enforce
* any limits desired by the strategist. This can be used for either a
* traditional deposit limit or for implementing a whitelist etc.
*
* EX:
* if(isAllowed[_owner]) return super.availableDepositLimit(_owner);
*
* This does not need to take into account any conversion rates
* from shares to assets. But should know that any non max uint256
* amounts may be converted to shares. So it is recommended to keep
* custom amounts low enough as not to cause overflow when multiplied
* by `totalSupply`.
*
* @param . The address that is depositing into the strategy.
* @return . The available amount the `_owner` can deposit in terms of `asset`
*/
function availableDepositLimit(
address /*_owner*/
) public view virtual returns (uint256) {
return type(uint256).max;
}
/**
* @notice Gets the max amount of `asset` that can be withdrawn.
* @dev Defaults to an unlimited amount for any address. But can
* be overridden by strategists.
*
* This function will be called before any withdraw or redeem to enforce
* any limits desired by the strategist. This can be used for illiquid
* or sandwichable strategies. It should never be lower than `totalIdle`.
*
* EX:
* return TokenIzedStrategy.totalIdle();
*
* This does not need to take into account the `_owner`'s share balance
* or conversion rates from shares to assets.
*
* @param . The address that is withdrawing from the strategy.
* @return . The available amount that can be withdrawn in terms of `asset`
*/
function availableWithdrawLimit(
address /*_owner*/
) public view virtual returns (uint256) {
return type(uint256).max;
}
/**
* @dev Optional function for a strategist to override that will
* allow management to manually withdraw deployed funds from the
* yield source if a strategy is shutdown.
*
* This should attempt to free `_amount`, noting that `_amount` may
* be more than is currently deployed.
*
* NOTE: This will not realize any profits or losses. A separate
* {report} will be needed in order to record any profit/loss. If
* a report may need to be called after a shutdown it is important
* to check if the strategy is shutdown during {_harvestAndReport}
* so that it does not simply re-deploy all funds that had been freed.
*
* EX:
* if(freeAsset > 0 && !TokenizedStrategy.isShutdown()) {
* depositFunds...
* }
*
* @param _amount The amount of asset to attempt to free.
*/
function _emergencyWithdraw(uint256 _amount) internal virtual {}
/*//////////////////////////////////////////////////////////////
TokenizedStrategy HOOKS
//////////////////////////////////////////////////////////////*/
/**
* @notice Can deploy up to '_amount' of 'asset' in yield source.
* @dev Callback for the TokenizedStrategy to call during a {deposit}
* or {mint} to tell the strategy it can deploy funds.
*
* Since this can only be called after a {deposit} or {mint}
* delegateCall to the TokenizedStrategy msg.sender == address(this).
*
* Unless a whitelist is implemented this will be entirely permissionless
* and thus can be sandwiched or otherwise manipulated.
*
* @param _amount The amount of 'asset' that the strategy can
* attempt to deposit in the yield source.
*/
function deployFunds(uint256 _amount) external virtual onlySelf {
_deployFunds(_amount);
}
/**
* @notice Should attempt to free the '_amount' of 'asset'.
* @dev Callback for the TokenizedStrategy to call during a withdraw
* or redeem to free the needed funds to service the withdraw.
*
* This can only be called after a 'withdraw' or 'redeem' delegateCall
* to the TokenizedStrategy so msg.sender == address(this).
*
* @param _amount The amount of 'asset' that the strategy should attempt to free up.
*/
function freeFunds(uint256 _amount) external virtual onlySelf {
_freeFunds(_amount);
}
/**
* @notice Returns the accurate amount of all funds currently
* held by the Strategy.
* @dev Callback for the TokenizedStrategy to call during a report to
* get an accurate accounting of assets the strategy controls.
*
* This can only be called after a report() delegateCall to the
* TokenizedStrategy so msg.sender == address(this).
*
* @return . A trusted and accurate account for the total amount
* of 'asset' the strategy currently holds including idle funds.
*/
function harvestAndReport() external virtual onlySelf returns (uint256) {
return _harvestAndReport();
}
/**
* @notice Will call the internal '_tend' when a keeper tends the strategy.
* @dev Callback for the TokenizedStrategy to initiate a _tend call in the strategy.
*
* This can only be called after a tend() delegateCall to the TokenizedStrategy
* so msg.sender == address(this).
*
* We name the function `tendThis` so that `tend` calls are forwarded to
* the TokenizedStrategy.
* @param _totalIdle The amount of current idle funds that can be
* deployed during the tend
*/
function tendThis(uint256 _totalIdle) external virtual onlySelf {
_tend(_totalIdle);
}
/**
* @notice Will call the internal '_emergencyWithdraw' function.
* @dev Callback for the TokenizedStrategy during an emergency withdraw.
*
* This can only be called after a emergencyWithdraw() delegateCall to
* the TokenizedStrategy so msg.sender == address(this).
*
* We name the function `shutdownWithdraw` so that `emergencyWithdraw`
* calls are forwarded to the TokenizedStrategy.
*
* @param _amount The amount of asset to attempt to free.
*/
function shutdownWithdraw(uint256 _amount) external virtual onlySelf {
_emergencyWithdraw(_amount);
}
/**
* @dev Function used to delegate call the TokenizedStrategy with
* certain `_calldata` and return any return values.
*
* This is used to setup the initial storage of the strategy, and
* can be used by strategist to forward any other call to the
* TokenizedStrategy implementation.
*
* @param _calldata The abi encoded calldata to use in delegatecall.
* @return . The return value if the call was successful in bytes.
*/
function _delegateCall(
bytes memory _calldata
) internal returns (bytes memory) {
// Delegate call the tokenized strategy with provided calldata.
(bool success, bytes memory result) = tokenizedStrategyAddress
.delegatecall(_calldata);
// If the call reverted. Return the error.
if (!success) {
assembly {
let ptr := mload(0x40)
let size := returndatasize()
returndatacopy(ptr, 0, size)
revert(ptr, size)
}
}
// Return the result.
return result;
}
/**
* @dev Execute a function on the TokenizedStrategy and return any value.
*
* This fallback function will be executed when any of the standard functions
* defined in the TokenizedStrategy are called since they wont be defined in
* this contract.
*
* It will delegatecall the TokenizedStrategy implementation with the exact
* calldata and return any relevant values.
*
*/
fallback() external {
// load our target address
address _tokenizedStrategyAddress = tokenizedStrategyAddress;
// Execute external function using delegatecall and return any value.
assembly {
// Copy function selector and any arguments.
calldatacopy(0, 0, calldatasize())
// Execute function delegatecall.
let result := delegatecall(
gas(),
_tokenizedStrategyAddress,
0,
calldatasize(),
0,
0
)
// Get any return value
returndatacopy(0, 0, returndatasize())
// Return any return value or error back to the caller
switch result
case 0 {
revert(0, returndatasize())
}
default {
return(0, returndatasize())
}
}
}
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.28;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
/// @notice This contract is used to access the storage of TokenizedStrategy variables
/// that are not directly accessible from the strategy contract.
/// The contract duplicates the struct and data from the original TokenizedStrategy contract
/// to expose the `nonReentrant` modifier.
contract TokenizedStrategyStorageAccess {
/*//////////////////////////////////////////////////////////////
STORAGE STRUCT
//////////////////////////////////////////////////////////////*/
/**
* @dev The struct that will hold all the storage data for each strategy
* that uses this implementation.
*
* This replaces all state variables for a traditional contract. This
* full struct will be initialized on the creation of the strategy
* and continually updated and read from for the life of the contract.
*
* We combine all the variables into one struct to limit the amount of
* times the custom storage slots need to be loaded during complex functions.
*
* Loading the corresponding storage slot for the struct does not
* load any of the contents of the struct into memory. So the size
* will not increase memory related gas usage.
*/
// prettier-ignore
struct StrategyData {
// The ERC20 compliant underlying asset that will be
// used by the Strategy
ERC20 asset;
// These are the corresponding ERC20 variables needed for the
// strategies token that is issued and burned on each deposit or withdraw.
uint8 decimals; // The amount of decimals that `asset` and strategy use.
string name; // The name of the token for the strategy.
uint256 totalSupply; // The total amount of shares currently issued.
mapping(address => uint256) nonces; // Mapping of nonces used for permit functions.
mapping(address => uint256) balances; // Mapping to track current balances for each account that holds shares.
mapping(address => mapping(address => uint256)) allowances; // Mapping to track the allowances for the strategies shares.
// We manually track `totalAssets` to prevent PPS manipulation through airdrops.
uint256 totalAssets;
// Variables for profit reporting and locking.
// We use uint96 for timestamps to fit in the same slot as an address. That overflows in 2.5e+21 years.
// I know Yearn moves slowly but surely V4 will be out by then.
// If the timestamps ever overflow tell the cyborgs still using this code I'm sorry for being cheap.
uint256 profitUnlockingRate; // The rate at which locked profit is unlocking.
uint96 fullProfitUnlockDate; // The timestamp at which all locked shares will unlock.
address keeper; // Address given permission to call {report} and {tend}.
uint32 profitMaxUnlockTime; // The amount of seconds that the reported profit unlocks over.
uint16 performanceFee; // The percent in basis points of profit that is charged as a fee.
address performanceFeeRecipient; // The address to pay the `performanceFee` to.
uint96 lastReport; // The last time a {report} was called.
// Access management variables.
address management; // Main address that can set all configurable variables.
address pendingManagement; // Address that is pending to take over `management`.
address emergencyAdmin; // Address to act in emergencies as well as `management`.
// Strategy Status
uint8 entered; // To prevent reentrancy. Use uint8 for gas savings.
bool shutdown; // Bool that can be used to stop deposits into the strategy.
}
/*//////////////////////////////////////////////////////////////
MODIFIERS
//////////////////////////////////////////////////////////////*/
/**
* @dev Prevents a contract from calling itself, directly or indirectly.
* Placed over all state changing functions for increased safety.
*/
modifier nonReentrant() {
StrategyData storage S = _strategyStorage();
// On the first call to nonReentrant, `entered` will be false (2)
// Modified from the original Yearn implementation (Original implementation checks that `S.entered != ENTERED`)
// The change below checks reentrancy, and also validates that the storage slot is correct.
// With the original Yearn check, if the storage slot was not correct, it would bypass the check as the value would be the default value of 0.
// This ensures that the storage slot is correct and that the reentrancy check is valid.
require(S.entered == NOT_ENTERED, "ReentrancyGuard: reentrant call");
// Any calls to nonReentrant after this point will fail
S.entered = ENTERED;
_;
// Reset to false (1) once call has finished.
S.entered = NOT_ENTERED;
}
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
/// @notice API version this TokenizedStrategy implements.
string internal constant API_VERSION = "3.0.4";
/// @notice Value to set the `entered` flag to during a call.
uint8 internal constant ENTERED = 2;
/// @notice Value to set the `entered` flag to at the end of the call.
uint8 internal constant NOT_ENTERED = 1;
/**
* @dev Custom storage slot that will be used to store the
* `StrategyData` struct that holds each strategies
* specific storage variables.
*
* Any storage updates done by the TokenizedStrategy actually update
* the storage of the calling contract. This variable points
* to the specific location that will be used to store the
* struct that holds all that data.
*
* We use a custom string in order to get a random
* storage slot that will allow for strategists to use any
* amount of storage in their strategy without worrying
* about collisions.
*/
bytes32 internal constant BASE_STRATEGY_STORAGE = bytes32(uint256(keccak256("yearn.base.strategy.storage")) - 1);
/*//////////////////////////////////////////////////////////////
STORAGE GETTER
//////////////////////////////////////////////////////////////*/
/**
* @dev will return the actual storage slot where the strategy
* specific `StrategyData` struct is stored for both read
* and write operations.
*
* This loads just the slot location, not the full struct
* so it can be used in a gas efficient manner.
*/
function _strategyStorage() internal pure returns (StrategyData storage S) {
// Since STORAGE_SLOT is a constant, we have to put a variable
// on the stack to access it from an inline assembly block.
bytes32 slot = BASE_STRATEGY_STORAGE;
assembly {
S.slot := slot
}
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "./IPPrincipalToken.sol";
import "./IPYieldToken.sol";
import "./IStandardizedYield.sol";
import "./IPGauge.sol";
import "../core/Market/MarketMathCore.sol";
interface IPMarket is IERC20Metadata, IPGauge {
event Mint(address indexed receiver, uint256 netLpMinted, uint256 netSyUsed, uint256 netPtUsed);
event Burn(
address indexed receiverSy,
address indexed receiverPt,
uint256 netLpBurned,
uint256 netSyOut,
uint256 netPtOut
);
event Swap(
address indexed caller,
address indexed receiver,
int256 netPtOut,
int256 netSyOut,
uint256 netSyFee,
uint256 netSyToReserve
);
event UpdateImpliedRate(uint256 indexed timestamp, uint256 lnLastImpliedRate);
event IncreaseObservationCardinalityNext(
uint16 observationCardinalityNextOld,
uint16 observationCardinalityNextNew
);
function mint(
address receiver,
uint256 netSyDesired,
uint256 netPtDesired
) external returns (uint256 netLpOut, uint256 netSyUsed, uint256 netPtUsed);
function burn(
address receiverSy,
address receiverPt,
uint256 netLpToBurn
) external returns (uint256 netSyOut, uint256 netPtOut);
function swapExactPtForSy(
address receiver,
uint256 exactPtIn,
bytes calldata data
) external returns (uint256 netSyOut, uint256 netSyFee);
function swapSyForExactPt(
address receiver,
uint256 exactPtOut,
bytes calldata data
) external returns (uint256 netSyIn, uint256 netSyFee);
function redeemRewards(address user) external returns (uint256[] memory);
function readState(address router) external view returns (MarketState memory market);
function observe(uint32[] memory secondsAgos) external view returns (uint216[] memory lnImpliedRateCumulative);
function increaseObservationsCardinalityNext(uint16 cardinalityNext) external;
function readTokens() external view returns (IStandardizedYield _SY, IPPrincipalToken _PT, IPYieldToken _YT);
function getRewardTokens() external view returns (address[] memory);
function isExpired() external view returns (bool);
function expiry() external view returns (uint256);
function observations(
uint256 index
) external view returns (uint32 blockTimestamp, uint216 lnImpliedRateCumulative, bool initialized);
function _storage()
external
view
returns (
int128 totalPt,
int128 totalSy,
uint96 lastLnImpliedRate,
uint16 observationIndex,
uint16 observationCardinality,
uint16 observationCardinalityNext
);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "../../interfaces/IPMarket.sol";
import "../../core/libraries/math/PMath.sol";
// This library can & should be integrated directly for optimal gas usage.
// If you prefer not to integrate it directly, the PendlePtOracle contract (a pre-deployed version of this contract) can be used.
library PendlePYOracleLib {
using PMath for uint256;
using PMath for int256;
/**
* This function returns the twap rate PT/Asset on market, but take into account the current rate of SY
This is to account for special cases where underlying asset becomes insolvent and has decreasing exchangeRate
* @param market market to get rate from
* @param duration twap duration
*/
function getPtToAssetRate(IPMarket market, uint32 duration) internal view returns (uint256) {
(uint256 syIndex, uint256 pyIndex) = getSYandPYIndexCurrent(market);
if (syIndex >= pyIndex) {
return getPtToAssetRateRaw(market, duration);
} else {
return (getPtToAssetRateRaw(market, duration) * syIndex) / pyIndex;
}
}
/**
* This function returns the twap rate YT/Asset on market, but take into account the current rate of SY
This is to account for special cases where underlying asset becomes insolvent and has decreasing exchangeRate
* @param market market to get rate from
* @param duration twap duration
*/
function getYtToAssetRate(IPMarket market, uint32 duration) internal view returns (uint256) {
(uint256 syIndex, uint256 pyIndex) = getSYandPYIndexCurrent(market);
if (syIndex >= pyIndex) {
return getYtToAssetRateRaw(market, duration);
} else {
return (getYtToAssetRateRaw(market, duration) * syIndex) / pyIndex;
}
}
/// @notice Similar to getPtToAsset but returns the rate in SY instead
function getPtToSyRate(IPMarket market, uint32 duration) internal view returns (uint256) {
(uint256 syIndex, uint256 pyIndex) = getSYandPYIndexCurrent(market);
if (syIndex >= pyIndex) {
return getPtToAssetRateRaw(market, duration).divDown(syIndex);
} else {
return getPtToAssetRateRaw(market, duration).divDown(pyIndex);
}
}
/// @notice Similar to getPtToAsset but returns the rate in SY instead
function getYtToSyRate(IPMarket market, uint32 duration) internal view returns (uint256) {
(uint256 syIndex, uint256 pyIndex) = getSYandPYIndexCurrent(market);
if (syIndex >= pyIndex) {
return getYtToAssetRateRaw(market, duration).divDown(syIndex);
} else {
return getYtToAssetRateRaw(market, duration).divDown(pyIndex);
}
}
/// @notice returns the raw rate without taking into account whether SY is solvent
function getPtToAssetRateRaw(IPMarket market, uint32 duration) internal view returns (uint256) {
uint256 expiry = market.expiry();
if (expiry <= block.timestamp) {
return PMath.ONE;
} else {
uint256 lnImpliedRate = getMarketLnImpliedRate(market, duration);
uint256 timeToExpiry = expiry - block.timestamp;
uint256 assetToPtRate = MarketMathCore._getExchangeRateFromImpliedRate(lnImpliedRate, timeToExpiry).Uint();
return PMath.ONE.divDown(assetToPtRate);
}
}
/// @notice returns the raw rate without taking into account whether SY is solvent
function getYtToAssetRateRaw(IPMarket market, uint32 duration) internal view returns (uint256) {
return PMath.ONE - getPtToAssetRateRaw(market, duration);
}
function getSYandPYIndexCurrent(IPMarket market) internal view returns (uint256 syIndex, uint256 pyIndex) {
(IStandardizedYield SY, , IPYieldToken YT) = market.readTokens();
syIndex = SY.exchangeRate();
uint256 pyIndexStored = YT.pyIndexStored();
if (YT.doCacheIndexSameBlock() && YT.pyIndexLastUpdatedBlock() == block.number) {
pyIndex = pyIndexStored;
} else {
pyIndex = PMath.max(syIndex, pyIndexStored);
}
}
function getMarketLnImpliedRate(IPMarket market, uint32 duration) internal view returns (uint256) {
if (duration == 0) {
(, , uint96 lnImpliedRate, , , ) = IPMarket(market)._storage();
return uint256(lnImpliedRate);
}
uint32[] memory durations = new uint32[](2);
durations[0] = duration;
uint216[] memory lnImpliedRateCumulative = market.observe(durations);
return (lnImpliedRateCumulative[1] - lnImpliedRateCumulative[0]) / duration;
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
import {IERC4626, IERC20, IERC20Metadata} from "openzeppelin5/interfaces/IERC4626.sol";
import {IERC3156FlashLender} from "./IERC3156FlashLender.sol";
import {ISiloConfig} from "./ISiloConfig.sol";
import {ISiloFactory} from "./ISiloFactory.sol";
import {IHookReceiver} from "./IHookReceiver.sol";
// solhint-disable ordering
interface ISilo is IERC20, IERC4626, IERC3156FlashLender {
/// @dev Interest accrual happens on each deposit/withdraw/borrow/repay. View methods work on storage that might be
/// outdate. Some calculations require accrued interest to return current state of Silo. This struct is used
/// to make a decision inside functions if interest should be accrued in memory to work on updated values.
enum AccrueInterestInMemory {
No,
Yes
}
/// @dev Silo has two separate oracles for solvency and maxLtv calculations. MaxLtv oracle is optional. Solvency
/// oracle can also be optional if asset is used as denominator in Silo config. For example, in ETH/USDC Silo
/// one could setup only solvency oracle for ETH that returns price in USDC. Then USDC does not need an oracle
/// because it's used as denominator for ETH and it's "price" can be assume as 1.
enum OracleType {
Solvency,
MaxLtv
}
/// @dev There are 3 types of accounting in the system: for non-borrowable collateral deposit called "protected",
/// for borrowable collateral deposit called "collateral" and for borrowed tokens called "debt". System does
/// identical calculations for each type of accounting but it uses different data. To avoid code duplication
/// this enum is used to decide which data should be read.
enum AssetType {
Protected, // default
Collateral,
Debt
}
/// @dev There are 2 types of accounting in the system: for non-borrowable collateral deposit called "protected" and
/// for borrowable collateral deposit called "collateral". System does
/// identical calculations for each type of accounting but it uses different data. To avoid code duplication
/// this enum is used to decide which data should be read.
enum CollateralType {
Protected, // default
Collateral
}
/// @dev Types of calls that can be made by the hook receiver on behalf of Silo via `callOnBehalfOfSilo` fn
enum CallType {
Call, // default
Delegatecall
}
/// @param _assets Amount of assets the user wishes to withdraw. Use 0 if shares are provided.
/// @param _shares Shares the user wishes to burn in exchange for the withdrawal. Use 0 if assets are provided.
/// @param _receiver Address receiving the withdrawn assets
/// @param _owner Address of the owner of the shares being burned
/// @param _spender Address executing the withdrawal; may be different than `_owner` if an allowance was set
/// @param _collateralType Type of the asset being withdrawn (Collateral or Protected)
struct WithdrawArgs {
uint256 assets;
uint256 shares;
address receiver;
address owner;
address spender;
ISilo.CollateralType collateralType;
}
/// @param assets Number of assets the borrower intends to borrow. Use 0 if shares are provided.
/// @param shares Number of shares corresponding to the assets that the borrower intends to borrow. Use 0 if
/// assets are provided.
/// @param receiver Address that will receive the borrowed assets
/// @param borrower The user who is borrowing the assets
struct BorrowArgs {
uint256 assets;
uint256 shares;
address receiver;
address borrower;
}
/// @param shares Amount of shares the user wishes to transit.
/// @param owner owner of the shares after transition.
/// @param transitionFrom type of collateral that will be transitioned.
struct TransitionCollateralArgs {
uint256 shares;
address owner;
ISilo.CollateralType transitionFrom;
}
struct UtilizationData {
/// @dev COLLATERAL: Amount of asset token that has been deposited to Silo plus interest earned by depositors.
/// It also includes token amount that has been borrowed.
uint256 collateralAssets;
/// @dev DEBT: Amount of asset token that has been borrowed plus accrued interest.
uint256 debtAssets;
/// @dev timestamp of the last interest accrual
uint64 interestRateTimestamp;
}
/// @dev Interest and revenue may be rounded down to zero if the underlying token's decimal is low.
/// Because of that, we need to store fractions for further calculation to minimize losses.
struct Fractions {
/// @dev interest value that we could not convert to full token in 36 decimals, max value for it is 1e18.
/// this value was not yet apply as interest for borrowers
uint64 interest;
/// @dev revenue value that we could not convert to full token in 36 decimals, max value for it is 1e18.
uint64 revenue;
}
struct SiloStorage {
/// @param daoAndDeployerRevenue Current amount of assets (fees) accrued by DAO and Deployer
/// but not yet withdrawn
uint192 daoAndDeployerRevenue;
/// @dev timestamp of the last interest accrual
uint64 interestRateTimestamp;
/// @dev Interest and revenue fractions for more precise calculations
Fractions fractions;
/// @dev silo is just for one asset,
/// but this one asset can be of three types: mapping key is uint256(AssetType), so we store `assets` by type.
/// Assets based on type:
/// - PROTECTED COLLATERAL: Amount of asset token that has been deposited to Silo that can be ONLY used
/// as collateral. These deposits do NOT earn interest and CANNOT be borrowed.
/// - COLLATERAL: Amount of asset token that has been deposited to Silo plus interest earned by depositors.
/// It also includes token amount that has been borrowed.
/// - DEBT: Amount of asset token that has been borrowed plus accrued interest.
/// `totalAssets` can have outdated value (without interest), if you doing view call (of off-chain call)
/// please use getters eg `getCollateralAssets()` to fetch value that includes interest.
mapping(AssetType assetType => uint256 assets) totalAssets;
}
/// @notice Emitted on protected deposit
/// @param sender wallet address that deposited asset
/// @param owner wallet address that received shares in Silo
/// @param assets amount of asset that was deposited
/// @param shares amount of shares that was minted
event DepositProtected(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
/// @notice Emitted on protected withdraw
/// @param sender wallet address that sent transaction
/// @param receiver wallet address that received asset
/// @param owner wallet address that owned asset
/// @param assets amount of asset that was withdrew
/// @param shares amount of shares that was burn
event WithdrawProtected(
address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares
);
/// @notice Emitted on borrow
/// @param sender wallet address that sent transaction
/// @param receiver wallet address that received asset
/// @param owner wallet address that owes assets
/// @param assets amount of asset that was borrowed
/// @param shares amount of shares that was minted
event Borrow(
address indexed sender, address indexed receiver, address indexed owner, uint256 assets, uint256 shares
);
/// @notice Emitted on repayment
/// @param sender wallet address that repaid asset
/// @param owner wallet address that owed asset
/// @param assets amount of asset that was repaid
/// @param shares amount of shares that was burn
event Repay(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
/// @notice emitted only when collateral has been switched to other one
event CollateralTypeChanged(address indexed borrower);
event HooksUpdated(uint24 hooksBefore, uint24 hooksAfter);
event AccruedInterest(uint256 hooksBefore);
event FlashLoan(uint256 amount);
event WithdrawnFees(uint256 daoFees, uint256 deployerFees, bool redirectedDeployerFees);
event DeployerFeesRedirected(uint256 deployerFees);
error UnsupportedFlashloanToken();
error FlashloanAmountTooBig();
error NothingToWithdraw();
error ProtectedProtection();
error NotEnoughLiquidity();
error NotSolvent();
error BorrowNotPossible();
error EarnedZero();
error FlashloanFailed();
error AboveMaxLtv();
error SiloInitialized();
error OnlyHookReceiver();
error NoLiquidity();
error InputCanBeAssetsOrShares();
error CollateralSiloAlreadySet();
error RepayTooHigh();
error ZeroAmount();
error InputZeroShares();
error ReturnZeroAssets();
error ReturnZeroShares();
/// @return siloFactory The associated factory of the silo
function factory() external view returns (ISiloFactory siloFactory);
/// @notice Method for HookReceiver only to call on behalf of Silo
/// @param _target address of the contract to call
/// @param _value amount of ETH to send
/// @param _callType type of the call (Call or Delegatecall)
/// @param _input calldata for the call
function callOnBehalfOfSilo(address _target, uint256 _value, CallType _callType, bytes calldata _input)
external
payable
returns (bool success, bytes memory result);
/// @notice Initialize Silo
/// @param _siloConfig address of ISiloConfig with full config for this Silo
function initialize(ISiloConfig _siloConfig) external;
/// @notice Update hooks configuration for Silo
/// @dev This function must be called after the hooks configuration is changed in the hook receiver
function updateHooks() external;
/// @notice Fetches the silo configuration contract
/// @return siloConfig Address of the configuration contract associated with the silo
function config() external view returns (ISiloConfig siloConfig);
/// @notice Fetches the utilization data of the silo used by IRM
function utilizationData() external view returns (UtilizationData memory utilizationData);
/// @notice Fetches the real (available to borrow) liquidity in the silo, it does include interest
/// @return liquidity The amount of liquidity
function getLiquidity() external view returns (uint256 liquidity);
/// @notice Determines if a borrower is solvent
/// @param _borrower Address of the borrower to check for solvency
/// @return True if the borrower is solvent, otherwise false
function isSolvent(address _borrower) external view returns (bool);
/// @notice Retrieves the raw total amount of assets based on provided type (direct storage access)
function getTotalAssetsStorage(AssetType _assetType) external view returns (uint256);
/// @notice Direct storage access to silo storage
/// @dev See struct `SiloStorage` for more details
function getSiloStorage()
external
view
returns (
uint192 daoAndDeployerRevenue,
uint64 interestRateTimestamp,
uint256 protectedAssets,
uint256 collateralAssets,
uint256 debtAssets
);
/// @notice Direct access to silo storage fractions variables
function getFractionsStorage() external view returns (Fractions memory fractions);
/// @notice Retrieves the total amount of collateral (borrowable) assets with interest
/// @return totalCollateralAssets The total amount of assets of type 'Collateral'
function getCollateralAssets() external view returns (uint256 totalCollateralAssets);
/// @notice Retrieves the total amount of debt assets with interest
/// @return totalDebtAssets The total amount of assets of type 'Debt'
function getDebtAssets() external view returns (uint256 totalDebtAssets);
/// @notice Retrieves the total amounts of collateral and protected (non-borrowable) assets
/// @return totalCollateralAssets The total amount of assets of type 'Collateral'
/// @return totalProtectedAssets The total amount of protected (non-borrowable) assets
function getCollateralAndProtectedTotalsStorage()
external
view
returns (uint256 totalCollateralAssets, uint256 totalProtectedAssets);
/// @notice Retrieves the total amounts of collateral and debt assets
/// @return totalCollateralAssets The total amount of assets of type 'Collateral'
/// @return totalDebtAssets The total amount of debt assets of type 'Debt'
function getCollateralAndDebtTotalsStorage()
external
view
returns (uint256 totalCollateralAssets, uint256 totalDebtAssets);
/// @notice Implements IERC4626.convertToShares for each asset type
function convertToShares(uint256 _assets, AssetType _assetType) external view returns (uint256 shares);
/// @notice Implements IERC4626.convertToAssets for each asset type
function convertToAssets(uint256 _shares, AssetType _assetType) external view returns (uint256 assets);
/// @notice Implements IERC4626.previewDeposit for protected (non-borrowable) collateral and collateral
/// @dev Reverts for debt asset type
function previewDeposit(uint256 _assets, CollateralType _collateralType) external view returns (uint256 shares);
/// @notice Implements IERC4626.deposit for protected (non-borrowable) collateral and collateral
/// @dev Reverts for debt asset type
function deposit(uint256 _assets, address _receiver, CollateralType _collateralType)
external
returns (uint256 shares);
/// @notice Implements IERC4626.previewMint for protected (non-borrowable) collateral and collateral
/// @dev Reverts for debt asset type
function previewMint(uint256 _shares, CollateralType _collateralType) external view returns (uint256 assets);
/// @notice Implements IERC4626.mint for protected (non-borrowable) collateral and collateral
/// @dev Reverts for debt asset type
function mint(uint256 _shares, address _receiver, CollateralType _collateralType) external returns (uint256 assets);
/// @notice Implements IERC4626.maxWithdraw for protected (non-borrowable) collateral and collateral
/// @dev Reverts for debt asset type
function maxWithdraw(address _owner, CollateralType _collateralType) external view returns (uint256 maxAssets);
/// @notice Implements IERC4626.previewWithdraw for protected (non-borrowable) collateral and collateral
/// @dev Reverts for debt asset type
function previewWithdraw(uint256 _assets, CollateralType _collateralType) external view returns (uint256 shares);
/// @notice Implements IERC4626.withdraw for protected (non-borrowable) collateral and collateral
/// @dev Reverts for debt asset type
function withdraw(uint256 _assets, address _receiver, address _owner, CollateralType _collateralType)
external
returns (uint256 shares);
/// @notice Implements IERC4626.maxRedeem for protected (non-borrowable) collateral and collateral
/// @dev Reverts for debt asset type
function maxRedeem(address _owner, CollateralType _collateralType) external view returns (uint256 maxShares);
/// @notice Implements IERC4626.previewRedeem for protected (non-borrowable) collateral and collateral
/// @dev Reverts for debt asset type
function previewRedeem(uint256 _shares, CollateralType _collateralType) external view returns (uint256 assets);
/// @notice Implements IERC4626.redeem for protected (non-borrowable) collateral and collateral
/// @dev Reverts for debt asset type
function redeem(uint256 _shares, address _receiver, address _owner, CollateralType _collateralType)
external
returns (uint256 assets);
/// @notice Calculates the maximum amount of assets that can be borrowed by the given address
/// @param _borrower Address of the potential borrower
/// @return maxAssets Maximum amount of assets that the borrower can borrow, this value is underestimated
/// That means, in some cases when you borrow maxAssets, you will be able to borrow again eg. up to 2wei
/// Reason for underestimation is to return value that will not cause borrow revert
function maxBorrow(address _borrower) external view returns (uint256 maxAssets);
/// @notice Previews the amount of shares equivalent to the given asset amount for borrowing
/// @param _assets Amount of assets to preview the equivalent shares for
/// @return shares Amount of shares equivalent to the provided asset amount
function previewBorrow(uint256 _assets) external view returns (uint256 shares);
/// @notice Allows an address to borrow a specified amount of assets
/// @param _assets Amount of assets to borrow
/// @param _receiver Address receiving the borrowed assets
/// @param _borrower Address responsible for the borrowed assets
/// @return shares Amount of shares equivalent to the borrowed assets
function borrow(uint256 _assets, address _receiver, address _borrower)
external returns (uint256 shares);
/// @notice Calculates the maximum amount of shares that can be borrowed by the given address
/// @param _borrower Address of the potential borrower
/// @return maxShares Maximum number of shares that the borrower can borrow
function maxBorrowShares(address _borrower) external view returns (uint256 maxShares);
/// @notice Previews the amount of assets equivalent to the given share amount for borrowing
/// @param _shares Amount of shares to preview the equivalent assets for
/// @return assets Amount of assets equivalent to the provided share amount
function previewBorrowShares(uint256 _shares) external view returns (uint256 assets);
/// @notice Calculates the maximum amount of assets that can be borrowed by the given address
/// @param _borrower Address of the potential borrower
/// @return maxAssets Maximum amount of assets that the borrower can borrow, this value is underestimated
/// That means, in some cases when you borrow maxAssets, you will be able to borrow again eg. up to 2wei
/// Reason for underestimation is to return value that will not cause borrow revert
function maxBorrowSameAsset(address _borrower) external view returns (uint256 maxAssets);
/// @notice Allows an address to borrow a specified amount of assets that will be back up with deposit made with the
/// same asset
/// @param _assets Amount of assets to borrow
/// @param _receiver Address receiving the borrowed assets
/// @param _borrower Address responsible for the borrowed assets
/// @return shares Amount of shares equivalent to the borrowed assets
function borrowSameAsset(uint256 _assets, address _receiver, address _borrower)
external returns (uint256 shares);
/// @notice Allows a user to borrow assets based on the provided share amount
/// @param _shares Amount of shares to borrow against
/// @param _receiver Address to receive the borrowed assets
/// @param _borrower Address responsible for the borrowed assets
/// @return assets Amount of assets borrowed
function borrowShares(uint256 _shares, address _receiver, address _borrower)
external
returns (uint256 assets);
/// @notice Calculates the maximum amount an address can repay based on their debt shares
/// @param _borrower Address of the borrower
/// @return assets Maximum amount of assets the borrower can repay
function maxRepay(address _borrower) external view returns (uint256 assets);
/// @notice Provides an estimation of the number of shares equivalent to a given asset amount for repayment
/// @param _assets Amount of assets to be repaid
/// @return shares Estimated number of shares equivalent to the provided asset amount
function previewRepay(uint256 _assets) external view returns (uint256 shares);
/// @notice Repays a given asset amount and returns the equivalent number of shares
/// @param _assets Amount of assets to be repaid
/// @param _borrower Address of the borrower whose debt is being repaid
/// @return shares The equivalent number of shares for the provided asset amount
function repay(uint256 _assets, address _borrower) external returns (uint256 shares);
/// @notice Calculates the maximum number of shares that can be repaid for a given borrower
/// @param _borrower Address of the borrower
/// @return shares The maximum number of shares that can be repaid for the borrower
function maxRepayShares(address _borrower) external view returns (uint256 shares);
/// @notice Provides a preview of the equivalent assets for a given number of shares to repay
/// @param _shares Number of shares to preview repayment for
/// @return assets Equivalent assets for the provided shares
function previewRepayShares(uint256 _shares) external view returns (uint256 assets);
/// @notice Allows a user to repay a loan using shares instead of assets
/// @param _shares The number of shares the borrower wants to repay with
/// @param _borrower The address of the borrower for whom to repay the loan
/// @return assets The equivalent assets amount for the provided shares
function repayShares(uint256 _shares, address _borrower) external returns (uint256 assets);
/// @notice Transitions assets between borrowable (collateral) and non-borrowable (protected) states
/// @dev This function allows assets to move between collateral and protected (non-borrowable) states without
/// leaving the protocol
/// @param _shares Amount of shares to be transitioned
/// @param _owner Owner of the assets being transitioned
/// @param _transitionFrom Specifies if the transition is from collateral or protected assets
/// @return assets Amount of assets transitioned
function transitionCollateral(uint256 _shares, address _owner, CollateralType _transitionFrom)
external
returns (uint256 assets);
/// @notice Switches the collateral silo to this silo
/// @dev Revert if the collateral silo is already set
function switchCollateralToThisSilo() external;
/// @notice Accrues interest for the asset and returns the accrued interest amount
/// @return accruedInterest The total interest accrued during this operation
function accrueInterest() external returns (uint256 accruedInterest);
/// @notice only for SiloConfig
function accrueInterestForConfig(
address _interestRateModel,
uint256 _daoFee,
uint256 _deployerFee
) external;
/// @notice Withdraws earned fees and distributes them to the DAO and deployer fee receivers
function withdrawFees() external;
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
interface ISiloOracle {
/// @notice Hook function to call before `quote` function reads price
/// @dev This hook function can be used to change state right before the price is read. For example it can be used
/// for curve read only reentrancy protection. In majority of implementations this will be an empty function.
/// WARNING: reverts are propagated to Silo so if `beforeQuote` reverts, Silo reverts as well.
/// @param _baseToken Address of priced token
function beforeQuote(address _baseToken) external;
/// @return quoteAmount Returns quote price for _baseAmount of _baseToken
/// @param _baseAmount Amount of priced token
/// @param _baseToken Address of priced token
function quote(uint256 _baseAmount, address _baseToken) external view returns (uint256 quoteAmount);
/// @return address of token in which quote (price) is denominated
function quoteToken() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
import {ISilo} from "./ISilo.sol";
import {ICrossReentrancyGuard} from "./ICrossReentrancyGuard.sol";
interface ISiloConfig is ICrossReentrancyGuard {
struct InitData {
/// @notice Can be address zero if deployer fees are not to be collected. If deployer address is zero then
/// deployer fee must be zero as well. Deployer will be minted an NFT that gives the right to claim deployer
/// fees. NFT can be transferred with the right to claim.
address deployer;
/// @notice Address of the hook receiver called on every before/after action on Silo. Hook contract also
/// implements liquidation logic and veSilo gauge connection.
address hookReceiver;
/// @notice Deployer's fee in 18 decimals points. Deployer will earn this fee based on the interest earned
/// by the Silo. Max deployer fee is set by the DAO. At deployment it is 15%.
uint256 deployerFee;
/// @notice DAO's fee in 18 decimals points. DAO will earn this fee based on the interest earned
/// by the Silo. Acceptable fee range fee is set by the DAO. Default at deployment is 5% - 50%.
uint256 daoFee;
/// @notice Address of the first token
address token0;
/// @notice Address of the solvency oracle. Solvency oracle is used to calculate LTV when deciding if borrower
/// is solvent or should be liquidated. Solvency oracle is optional and if not set price of 1 will be assumed.
address solvencyOracle0;
/// @notice Address of the maxLtv oracle. Max LTV oracle is used to calculate LTV when deciding if borrower
/// can borrow given amount of assets. Max LTV oracle is optional and if not set it defaults to solvency
/// oracle. If neither is set price of 1 will be assumed.
address maxLtvOracle0;
/// @notice Address of the interest rate model
address interestRateModel0;
/// @notice Maximum LTV for first token. maxLTV is in 18 decimals points and is used to determine, if borrower
/// can borrow given amount of assets. MaxLtv is in 18 decimals points. MaxLtv must be lower or equal to LT.
uint256 maxLtv0;
/// @notice Liquidation threshold for first token. LT is used to calculate solvency. LT is in 18 decimals
/// points. LT must not be lower than maxLTV.
uint256 lt0;
/// @notice minimal acceptable LTV after liquidation, in 18 decimals points
uint256 liquidationTargetLtv0;
/// @notice Liquidation fee for the first token in 18 decimals points. Liquidation fee is what liquidator earns
/// for repaying insolvent loan.
uint256 liquidationFee0;
/// @notice Flashloan fee sets the cost of taking a flashloan in 18 decimals points
uint256 flashloanFee0;
/// @notice Indicates if a beforeQuote on oracle contract should be called before quoting price
bool callBeforeQuote0;
/// @notice Address of the second token
address token1;
/// @notice Address of the solvency oracle. Solvency oracle is used to calculate LTV when deciding if borrower
/// is solvent or should be liquidated. Solvency oracle is optional and if not set price of 1 will be assumed.
address solvencyOracle1;
/// @notice Address of the maxLtv oracle. Max LTV oracle is used to calculate LTV when deciding if borrower
/// can borrow given amount of assets. Max LTV oracle is optional and if not set it defaults to solvency
/// oracle. If neither is set price of 1 will be assumed.
address maxLtvOracle1;
/// @notice Address of the interest rate model
address interestRateModel1;
/// @notice Maximum LTV for first token. maxLTV is in 18 decimals points and is used to determine,
/// if borrower can borrow given amount of assets. maxLtv is in 18 decimals points
uint256 maxLtv1;
/// @notice Liquidation threshold for first token. LT is used to calculate solvency. LT is in 18 decimals points
uint256 lt1;
/// @notice minimal acceptable LTV after liquidation, in 18 decimals points
uint256 liquidationTargetLtv1;
/// @notice Liquidation fee is what liquidator earns for repaying insolvent loan.
uint256 liquidationFee1;
/// @notice Flashloan fee sets the cost of taking a flashloan in 18 decimals points
uint256 flashloanFee1;
/// @notice Indicates if a beforeQuote on oracle contract should be called before quoting price
bool callBeforeQuote1;
}
struct ConfigData {
uint256 daoFee;
uint256 deployerFee;
address silo;
address token;
address protectedShareToken;
address collateralShareToken;
address debtShareToken;
address solvencyOracle;
address maxLtvOracle;
address interestRateModel;
uint256 maxLtv;
uint256 lt;
uint256 liquidationTargetLtv;
uint256 liquidationFee;
uint256 flashloanFee;
address hookReceiver;
bool callBeforeQuote;
}
struct DepositConfig {
address silo;
address token;
address collateralShareToken;
address protectedShareToken;
uint256 daoFee;
uint256 deployerFee;
address interestRateModel;
}
error OnlySilo();
error OnlySiloOrTokenOrHookReceiver();
error WrongSilo();
error OnlyDebtShareToken();
error DebtExistInOtherSilo();
error FeeTooHigh();
/// @dev It should be called on debt transfer (debt share token transfer).
/// In the case if the`_recipient` doesn't have configured a collateral silo,
/// it will be set to the collateral silo of the `_sender`.
/// @param _sender sender address
/// @param _recipient recipient address
function onDebtTransfer(address _sender, address _recipient) external;
/// @notice Set collateral silo.
/// @dev Revert if msg.sender is not a SILO_0 or SILO_1.
/// @dev Always set collateral silo the same as msg.sender.
/// @param _borrower borrower address
/// @return collateralSiloChanged TRUE if collateral silo changed
function setThisSiloAsCollateralSilo(address _borrower) external returns (bool collateralSiloChanged);
/// @notice Set collateral silo
/// @dev Revert if msg.sender is not a SILO_0 or SILO_1.
/// @dev Always set collateral silo opposite to the msg.sender.
/// @param _borrower borrower address
/// @return collateralSiloChanged TRUE if collateral silo changed
function setOtherSiloAsCollateralSilo(address _borrower) external returns (bool collateralSiloChanged);
/// @notice Accrue interest for the silo
/// @param _silo silo for which accrue interest
function accrueInterestForSilo(address _silo) external;
/// @notice Accrue interest for both silos (SILO_0 and SILO_1 in a config)
function accrueInterestForBothSilos() external;
/// @notice Retrieves the collateral silo for a specific borrower.
/// @dev As a user can deposit into `Silo0` and `Silo1`, this property specifies which Silo
/// will be used as collateral for the debt. Later on, it will be used for max LTV and solvency checks.
/// After being set, the collateral silo is never set to `address(0)` again but such getters as
/// `getConfigsForSolvency`, `getConfigsForBorrow`, `getConfigsForWithdraw` will return empty
/// collateral silo config if borrower doesn't have debt.
///
/// In the SiloConfig collateral silo is set by the following functions:
/// `onDebtTransfer` - only if the recipient doesn't have collateral silo set (inherits it from the sender)
/// This function is called on debt share token transfer (debt transfer).
/// `setThisSiloAsCollateralSilo` - sets the same silo as the one that calls the function.
/// `setOtherSiloAsCollateralSilo` - sets the opposite silo as collateral from the one that calls the function.
///
/// In the Silo collateral silo is set by the following functions:
/// `borrow` - always sets opposite silo as collateral.
/// If Silo0 borrows, then Silo1 will be collateral and vice versa.
/// `borrowSameAsset` - always sets the same silo as collateral.
/// `switchCollateralToThisSilo` - always sets the same silo as collateral.
/// @param _borrower The address of the borrower for which the collateral silo is being retrieved
/// @return collateralSilo The address of the collateral silo for the specified borrower
function borrowerCollateralSilo(address _borrower) external view returns (address collateralSilo);
/// @notice Retrieves the silo ID
/// @dev Each silo is assigned a unique ID. ERC-721 token is minted with identical ID to deployer.
/// An owner of that token receives the deployer fees.
/// @return siloId The ID of the silo
function SILO_ID() external view returns (uint256 siloId); // solhint-disable-line func-name-mixedcase
/// @notice Retrieves the addresses of the two silos
/// @return silo0 The address of the first silo
/// @return silo1 The address of the second silo
function getSilos() external view returns (address silo0, address silo1);
/// @notice Retrieves the asset associated with a specific silo
/// @dev This function reverts for incorrect silo address input
/// @param _silo The address of the silo for which the associated asset is being retrieved
/// @return asset The address of the asset associated with the specified silo
function getAssetForSilo(address _silo) external view returns (address asset);
/// @notice Verifies if the borrower has debt in other silo by checking the debt share token balance
/// @param _thisSilo The address of the silo in respect of which the debt is checked
/// @param _borrower The address of the borrower for which the debt is checked
/// @return hasDebt true if the borrower has debt in other silo
function hasDebtInOtherSilo(address _thisSilo, address _borrower) external view returns (bool hasDebt);
/// @notice Retrieves the debt silo associated with a specific borrower
/// @dev This function reverts if debt present in two silo (should not happen)
/// @param _borrower The address of the borrower for which the debt silo is being retrieved
function getDebtSilo(address _borrower) external view returns (address debtSilo);
/// @notice Retrieves configuration data for both silos. First config is for the silo that is asking for configs.
/// @param borrower borrower address for which debtConfig will be returned
/// @return collateralConfig The configuration data for collateral silo (empty if there is no debt).
/// @return debtConfig The configuration data for debt silo (empty if there is no debt).
function getConfigsForSolvency(address borrower)
external
view
returns (ConfigData memory collateralConfig, ConfigData memory debtConfig);
/// @notice Retrieves configuration data for a specific silo
/// @dev This function reverts for incorrect silo address input.
/// @param _silo The address of the silo for which configuration data is being retrieved
/// @return config The configuration data for the specified silo
function getConfig(address _silo) external view returns (ConfigData memory config);
/// @notice Retrieves configuration data for a specific silo for withdraw fn.
/// @dev This function reverts for incorrect silo address input.
/// @param _silo The address of the silo for which configuration data is being retrieved
/// @return depositConfig The configuration data for the specified silo (always config for `_silo`)
/// @return collateralConfig The configuration data for the collateral silo (empty if there is no debt)
/// @return debtConfig The configuration data for the debt silo (empty if there is no debt)
function getConfigsForWithdraw(address _silo, address _borrower) external view returns (
DepositConfig memory depositConfig,
ConfigData memory collateralConfig,
ConfigData memory debtConfig
);
/// @notice Retrieves configuration data for a specific silo for borrow fn.
/// @dev This function reverts for incorrect silo address input.
/// @param _debtSilo The address of the silo for which configuration data is being retrieved
/// @return collateralConfig The configuration data for the collateral silo (always other than `_debtSilo`)
/// @return debtConfig The configuration data for the debt silo (always config for `_debtSilo`)
function getConfigsForBorrow(address _debtSilo)
external
view
returns (ConfigData memory collateralConfig, ConfigData memory debtConfig);
/// @notice Retrieves fee-related information for a specific silo
/// @dev This function reverts for incorrect silo address input
/// @param _silo The address of the silo for which fee-related information is being retrieved.
/// @return daoFee The DAO fee percentage in 18 decimals points.
/// @return deployerFee The deployer fee percentage in 18 decimals points.
/// @return flashloanFee The flashloan fee percentage in 18 decimals points.
/// @return asset The address of the asset associated with the specified silo.
function getFeesWithAsset(address _silo)
external
view
returns (uint256 daoFee, uint256 deployerFee, uint256 flashloanFee, address asset);
/// @notice Retrieves share tokens associated with a specific silo
/// @dev This function reverts for incorrect silo address input
/// @param _silo The address of the silo for which share tokens are being retrieved
/// @return protectedShareToken The address of the protected (non-borrowable) share token
/// @return collateralShareToken The address of the collateral share token
/// @return debtShareToken The address of the debt share token
function getShareTokens(address _silo)
external
view
returns (address protectedShareToken, address collateralShareToken, address debtShareToken);
/// @notice Retrieves the share token and the silo token associated with a specific silo
/// @param _silo The address of the silo for which the share token and silo token are being retrieved
/// @param _collateralType The type of collateral
/// @return shareToken The address of the share token (collateral or protected collateral)
/// @return asset The address of the silo token
function getCollateralShareTokenAndAsset(address _silo, ISilo.CollateralType _collateralType)
external
view
returns (address shareToken, address asset);
/// @notice Retrieves the share token and the silo token associated with a specific silo
/// @param _silo The address of the silo for which the share token and silo token are being retrieved
/// @return shareToken The address of the share token (debt)
/// @return asset The address of the silo token
function getDebtShareTokenAndAsset(address _silo)
external
view
returns (address shareToken, address asset);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.28;
import {Strings} from "openzeppelin5/utils/Strings.sol";
import {IERC20} from "openzeppelin5/token/ERC20/IERC20.sol";
import {Utils} from "silo-foundry-utils/lib/Utils.sol";
import {ISiloLens, ISilo} from "./interfaces/ISiloLens.sol";
import {IShareToken} from "./interfaces/IShareToken.sol";
import {ISiloConfig} from "./interfaces/ISiloConfig.sol";
import {IPartialLiquidation} from "./interfaces/IPartialLiquidation.sol";
import {IInterestRateModel} from "./interfaces/IInterestRateModel.sol";
import {SiloLensLib} from "./lib/SiloLensLib.sol";
import {SiloStdLib} from "./lib/SiloStdLib.sol";
import {IPartialLiquidation} from "./interfaces/IPartialLiquidation.sol";
import {IDistributionManager} from "silo-core/contracts/incentives/interfaces/IDistributionManager.sol";
/// @title SiloLens is a helper contract for integrations and UI
contract SiloLens is ISiloLens {
uint256 internal constant _PRECISION_DECIMALS = 1e18;
/// @inheritdoc ISiloLens
function isSolvent(ISilo _silo, address _borrower) external view returns (bool) {
return _silo.isSolvent(_borrower);
}
/// @inheritdoc ISiloLens
function liquidity(ISilo _silo) external view returns (uint256) {
return _silo.getLiquidity();
}
/// @inheritdoc ISiloLens
function getRawLiquidity(ISilo _silo) external view virtual returns (uint256 liquidity) {
return SiloLensLib.getRawLiquidity(_silo);
}
/// @inheritdoc ISiloLens
function getMaxLtv(ISilo _silo) external view virtual returns (uint256 maxLtv) {
return SiloLensLib.getMaxLtv(_silo);
}
/// @inheritdoc ISiloLens
function getLt(ISilo _silo) external view virtual returns (uint256 lt) {
lt = SiloLensLib.getLt(_silo);
}
/// @inheritdoc ISiloLens
function getUserLT(ISilo _silo, address _borrower) external view returns (uint256 userLT) {
return SiloLensLib.getUserLt(_silo, _borrower);
}
function getUsersLT(Borrower[] calldata _borrowers) external view returns (uint256[] memory usersLTs) {
usersLTs = new uint256[](_borrowers.length);
for (uint256 i; i < _borrowers.length; i++) {
Borrower memory borrower = _borrowers[i];
usersLTs[i] = SiloLensLib.getUserLt(borrower.silo, borrower.wallet);
}
}
function getUsersHealth(Borrower[] calldata _borrowers) external view returns (BorrowerHealth[] memory healths) {
healths = new BorrowerHealth[](_borrowers.length);
for (uint256 i; i < _borrowers.length; i++) {
Borrower memory borrower = _borrowers[i];
BorrowerHealth memory health = healths[i];
(health.ltv, health.lt) = SiloLensLib.getLtvAndLt(borrower.silo, borrower.wallet);
}
}
/// @inheritdoc ISiloLens
function getUserLTV(ISilo _silo, address _borrower) external view returns (uint256 userLTV) {
return SiloLensLib.getLtv(_silo, _borrower);
}
/// @inheritdoc ISiloLens
function getLtv(ISilo _silo, address _borrower) external view virtual returns (uint256 ltv) {
return SiloLensLib.getLtv(_silo, _borrower);
}
/// @inheritdoc ISiloLens
function hasPosition(ISiloConfig _siloConfig, address _borrower) external view virtual returns (bool has) {
has = SiloLensLib.hasPosition(_siloConfig, _borrower);
}
/// @inheritdoc ISiloLens
function inDebt(ISiloConfig _siloConfig, address _borrower) external view returns (bool hasDebt) {
hasDebt = SiloLensLib.inDebt(_siloConfig, _borrower);
}
/// @inheritdoc ISiloLens
function getFeesAndFeeReceivers(ISilo _silo)
external
view
virtual
returns (address daoFeeReceiver, address deployerFeeReceiver, uint256 daoFee, uint256 deployerFee)
{
(daoFeeReceiver, deployerFeeReceiver, daoFee, deployerFee,) = SiloStdLib.getFeesAndFeeReceiversWithAsset(_silo);
}
/// @inheritdoc ISiloLens
function collateralBalanceOfUnderlying(ISilo _silo, address _borrower)
external
view
virtual
returns (uint256 borrowerCollateral)
{
return SiloLensLib.collateralBalanceOfUnderlying(_silo, _borrower);
}
/// @inheritdoc ISiloLens
function debtBalanceOfUnderlying(ISilo _silo, address _borrower) external view virtual returns (uint256) {
return _silo.maxRepay(_borrower);
}
/// @inheritdoc ISiloLens
function maxLiquidation(ISilo _silo, IPartialLiquidation _hook, address _borrower)
external
view
virtual
returns (uint256 collateralToLiquidate, uint256 debtToRepay, bool sTokenRequired, bool fullLiquidation)
{
(collateralToLiquidate, debtToRepay, sTokenRequired) = _hook.maxLiquidation(_borrower);
uint256 maxRepay = _silo.maxRepay(_borrower);
fullLiquidation = maxRepay == debtToRepay;
}
/// @inheritdoc ISiloLens
function totalDeposits(ISilo _silo) external view returns (uint256 totalDeposits) {
totalDeposits = _silo.getTotalAssetsStorage(ISilo.AssetType.Collateral);
}
/// @inheritdoc ISiloLens
function totalDepositsWithInterest(ISilo _silo) external view returns (uint256 amount) {
amount = _silo.totalAssets();
}
function totalBorrowAmountWithInterest(ISilo _silo) external view returns (uint256 amount) {
amount = _silo.getDebtAssets();
}
/// @inheritdoc ISiloLens
function collateralOnlyDeposits(ISilo _silo) external view returns (uint256) {
return _silo.getTotalAssetsStorage(ISilo.AssetType.Protected);
}
/// @inheritdoc ISiloLens
function getDepositAmount(ISilo _silo, address _borrower)
external
view
returns (uint256 borrowerDeposits)
{
borrowerDeposits = _silo.previewRedeem(_silo.balanceOf(_borrower));
}
/// @inheritdoc ISiloLens
function totalBorrowAmount(ISilo _silo) external view returns (uint256) {
return _silo.getTotalAssetsStorage(ISilo.AssetType.Debt);
}
/// @inheritdoc ISiloLens
function totalBorrowShare(ISilo _silo) external view returns (uint256) {
return SiloLensLib.totalBorrowShare(_silo);
}
/// @inheritdoc ISiloLens
function getBorrowAmount(ISilo _silo, address _borrower)
external
view
returns (uint256 maxRepay)
{
maxRepay = _silo.maxRepay(_borrower);
}
/// @inheritdoc ISiloLens
function borrowShare(ISilo _silo, address _borrower) external view returns (uint256) {
return SiloLensLib.borrowShare(_silo, _borrower);
}
/// @inheritdoc ISiloLens
function protocolFees(ISilo _silo) external view returns (uint256 daoAndDeployerRevenue) {
(daoAndDeployerRevenue,,,,) = _silo.getSiloStorage();
}
/// @inheritdoc ISiloLens
function calculateCollateralValue(ISiloConfig _siloConfig, address _borrower)
external
view
returns (uint256 collateralValue)
{
(collateralValue,) = SiloLensLib.calculateValues(_siloConfig, _borrower);
}
/// @inheritdoc ISiloLens
function calculateBorrowValue(ISiloConfig _siloConfig, address _borrower)
external
view
returns (uint256 borrowValue)
{
(, borrowValue) = SiloLensLib.calculateValues(_siloConfig, _borrower);
}
/// @inheritdoc ISiloLens
function getUtilization(ISilo _silo) external view returns (uint256) {
ISilo.UtilizationData memory data = _silo.utilizationData();
if (data.collateralAssets != 0) {
return data.debtAssets * _PRECISION_DECIMALS / data.collateralAssets;
}
}
/// @inheritdoc ISiloLens
function getInterestRateModel(ISilo _silo) external view virtual returns (address irm) {
return SiloLensLib.getInterestRateModel(_silo);
}
/// @inheritdoc ISiloLens
function getBorrowAPR(ISilo _silo) external view virtual returns (uint256 borrowAPR) {
return SiloLensLib.getBorrowAPR(_silo);
}
/// @inheritdoc ISiloLens
function getDepositAPR(ISilo _silo) external view virtual returns (uint256 depositAPR) {
return SiloLensLib.getDepositAPR(_silo);
}
/// @inheritdoc ISiloLens
function getAPRs(ISilo[] calldata _silos) external view virtual returns (APR[] memory aprs) {
aprs = new APR[](_silos.length);
for (uint256 i; i < _silos.length; i++) {
ISilo silo = _silos[i];
aprs[i] = APR({
borrowAPR: SiloLensLib.getBorrowAPR(silo),
depositAPR: SiloLensLib.getDepositAPR(silo)
});
}
}
function getModel(ISilo _silo) public view returns (IInterestRateModel irm) {
irm = IInterestRateModel(_silo.config().getConfig(address(_silo)).interestRateModel);
}
function getSiloIncentivesControllerProgramsNames(
address _siloIncentivesController
) public view returns (string[] memory programsNames) {
IDistributionManager distributionManager = IDistributionManager(_siloIncentivesController);
string[] memory originalProgramsNames = distributionManager.getAllProgramsNames();
programsNames = new string[](originalProgramsNames.length);
for (uint256 i; i < originalProgramsNames.length; i++) {
bytes memory originalProgramName = bytes(originalProgramsNames[i]);
if (isTokenAddress(originalProgramName)) {
address token = address(bytes20(originalProgramName));
programsNames[i] = Strings.toHexString(token);
} else {
programsNames[i] = originalProgramsNames[i];
}
}
}
function isTokenAddress(bytes memory _name) private view returns (bool isToken) {
if (_name.length != 20) return false;
address token = address(bytes20(_name));
if (Utils.getCodeAt(token).length == 0) return false;
// Sanity check to be sure that it is a token
try IERC20(token).balanceOf(address(this)) returns (uint256) {
isToken = true;
} catch {}
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
interface IERC3156FlashBorrower {
/// @notice During the execution of the flashloan, Silo methods are not taking into consideration the fact,
/// that some (or all) tokens were transferred as flashloan, therefore some methods can return invalid state
/// eg. maxWithdraw can return amount that are not available to withdraw during flashlon.
/// @dev Receive a flash loan.
/// @param _initiator The initiator of the loan.
/// @param _token The loan currency.
/// @param _amount The amount of tokens lent.
/// @param _fee The additional amount of tokens to repay.
/// @param _data Arbitrary data structure, intended to contain user-defined parameters.
/// @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan"
function onFlashLoan(address _initiator, address _token, uint256 _amount, uint256 _fee, bytes calldata _data)
external
returns (bytes32);
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.18;
library LibError {
// Common errors
error InvalidAsset();
error InvalidValue();
error InvalidAddress();
error InvalidPermissions();
error InvalidToken();
error AboveMaxValue(uint256 maxValue);
// Swap errors
error SwapFailed();
error InvalidPrice();
error InvalidSwapPath();
error InvalidSwapConfig();
error ScaledInputFailed();
error SlippageLimitExceeded();
// Pendle errors
error InvalidPendleMarket();
// Silo erros
error InvalidSiloMarket();
error InvalidFlashLoanCaller();
error InvalidFlashLoanInitiator();
error InvalidFlashLoanAmount();
error InvalidFlashLoanToken();
error AboveMaxWithdraw(uint256 currentAmount, uint256 maxAmount);
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity ^0.8.18;
interface ICeresSwapper {
enum SwapType {
PENDLE_TOKEN_TO_PT,
PENDLE_PT_TO_TOKEN,
ALGEBRA_V4_SWAPX,
KYBERSWAP_CACHED_PATH,
KYBERSWAP_AGGREGATOR,
BALANCER_V3_SWAP_MULTI
}
struct SwapProvider {
SwapType swapType;
address router;
}
function getTokenPairHash(address _tokenIn, address _tokenOut) external pure returns (bytes32);
function isSwapPathSet(bytes32 _tokenPairHash, SwapType _swapType) external view returns (bool);
function isSwapPathSet(address _tokenIn, address _tokenOut, SwapType _swapType) external view returns (bool);
function setStrategyApproval(address strategyAddress, bool status) external;
function setSwapPath(address _tokenIn, address _tokenOut, SwapType _swapType, bytes memory _path) external;
function setSwapProvider(address _tokenIn, address _tokenOut, SwapProvider calldata _provider) external;
function setSwapAggregator(address _tokenIn, address _tokenOut, SwapProvider calldata _provider) external;
function swapFrom(
address fromToken,
address toToken,
uint256 amountIn,
uint256 minAmountOut,
address receiver,
bytes calldata extraData
) external returns (uint256 tokensReceived);
function swapTo(
address fromToken,
address toToken,
uint256 amountOut,
uint256 maxAmountIn,
address receiver,
bytes calldata extraData
) external returns (uint256 actualAmountIn);
function swapProvider(bytes32 tokenPairHash) external view returns (SwapType swapType, address router);
function swapAggregator(bytes32 tokenPairHash) external view returns (SwapType swapType, address router);
function swapUsingAggregator(
address fromToken,
address toToken,
uint256 amountIn,
uint256 minAmountOut,
address receiver,
bytes calldata encodedSwapData
) external returns (uint256 tokensReceived);
function tokenSwapPath(bytes32 tokenPairHash, SwapType swapType) external view returns (bytes memory);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/Panic.sol)
pragma solidity ^0.8.20;
/**
* @dev Helper library for emitting standardized panic codes.
*
* ```solidity
* contract Example {
* using Panic for uint256;
*
* // Use any of the declared internal constants
* function foo() { Panic.GENERIC.panic(); }
*
* // Alternatively
* function foo() { Panic.panic(Panic.GENERIC); }
* }
* ```
*
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
*
* _Available since v5.1._
*/
// slither-disable-next-line unused-state
library Panic {
/// @dev generic / unspecified error
uint256 internal constant GENERIC = 0x00;
/// @dev used by the assert() builtin
uint256 internal constant ASSERT = 0x01;
/// @dev arithmetic underflow or overflow
uint256 internal constant UNDER_OVERFLOW = 0x11;
/// @dev division or modulo by zero
uint256 internal constant DIVISION_BY_ZERO = 0x12;
/// @dev enum conversion error
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
/// @dev invalid encoding in storage
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
/// @dev empty array pop
uint256 internal constant EMPTY_ARRAY_POP = 0x31;
/// @dev array out of bounds access
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
/// @dev resource error (too large allocation or too large array)
uint256 internal constant RESOURCE_ERROR = 0x41;
/// @dev calling invalid internal function
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
/// @dev Reverts with a panic code. Recommended to use with
/// the internal constants with predefined codes.
function panic(uint256 code) internal pure {
assembly ("memory-safe") {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
assembly ("memory-safe") {
u := iszero(iszero(b))
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC20Metadata} from "./extensions/IERC20Metadata.sol";
import {Context} from "../../utils/Context.sol";
import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC-20
* applications.
*/
abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors {
mapping(address account => uint256) private _balances;
mapping(address account => mapping(address spender => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* Both values are immutable: they can only be set once during construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the default value returned by this function, unless
* it's overridden.
*
* NOTE: This information is only used for _display_ purposes: it in
* no way affects any of the arithmetic of the contract, including
* {IERC20-balanceOf} and {IERC20-transfer}.
*/
function decimals() public view virtual returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `value`.
*/
function transfer(address to, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_transfer(owner, to, value);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `value` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 value) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, value);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Skips emitting an {Approval} event indicating an allowance update. This is not
* required by the ERC. See {xref-ERC20-_approve-address-address-uint256-bool-}[_approve].
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `value`.
* - the caller must have allowance for ``from``'s tokens of at least
* `value`.
*/
function transferFrom(address from, address to, uint256 value) public virtual returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
/**
* @dev Moves a `value` amount of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _transfer(address from, address to, uint256 value) internal {
if (from == address(0)) {
revert ERC20InvalidSender(address(0));
}
if (to == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(from, to, value);
}
/**
* @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from`
* (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding
* this function.
*
* Emits a {Transfer} event.
*/
function _update(address from, address to, uint256 value) internal virtual {
if (from == address(0)) {
// Overflow check required: The rest of the code assumes that totalSupply never overflows
_totalSupply += value;
} else {
uint256 fromBalance = _balances[from];
if (fromBalance < value) {
revert ERC20InsufficientBalance(from, fromBalance, value);
}
unchecked {
// Overflow not possible: value <= fromBalance <= totalSupply.
_balances[from] = fromBalance - value;
}
}
if (to == address(0)) {
unchecked {
// Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply.
_totalSupply -= value;
}
} else {
unchecked {
// Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256.
_balances[to] += value;
}
}
emit Transfer(from, to, value);
}
/**
* @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0).
* Relies on the `_update` mechanism
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead.
*/
function _mint(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidReceiver(address(0));
}
_update(address(0), account, value);
}
/**
* @dev Destroys a `value` amount of tokens from `account`, lowering the total supply.
* Relies on the `_update` mechanism.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* NOTE: This function is not virtual, {_update} should be overridden instead
*/
function _burn(address account, uint256 value) internal {
if (account == address(0)) {
revert ERC20InvalidSender(address(0));
}
_update(account, address(0), value);
}
/**
* @dev Sets `value` as the allowance of `spender` over the `owner`'s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*
* Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument.
*/
function _approve(address owner, address spender, uint256 value) internal {
_approve(owner, spender, value, true);
}
/**
* @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event.
*
* By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by
* `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any
* `Approval` event during `transferFrom` operations.
*
* Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to
* true using the following override:
*
* ```solidity
* function _approve(address owner, address spender, uint256 value, bool) internal virtual override {
* super._approve(owner, spender, value, true);
* }
* ```
*
* Requirements are the same as {_approve}.
*/
function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual {
if (owner == address(0)) {
revert ERC20InvalidApprover(address(0));
}
if (spender == address(0)) {
revert ERC20InvalidSpender(address(0));
}
_allowances[owner][spender] = value;
if (emitEvent) {
emit Approval(owner, spender, value);
}
}
/**
* @dev Updates `owner`'s allowance for `spender` based on spent `value`.
*
* Does not update the allowance value in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Does not emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 value) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance < type(uint256).max) {
if (currentAllowance < value) {
revert ERC20InsufficientAllowance(spender, currentAllowance, value);
}
unchecked {
_approve(owner, spender, currentAllowance - value, false);
}
}
}
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
// Interface that implements the 4626 standard and the implementation functions
interface ITokenizedStrategy is IERC4626, IERC20Permit {
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event StrategyShutdown();
event NewTokenizedStrategy(
address indexed strategy,
address indexed asset,
string apiVersion
);
event Reported(
uint256 profit,
uint256 loss,
uint256 protocolFees,
uint256 performanceFees
);
event UpdatePerformanceFeeRecipient(
address indexed newPerformanceFeeRecipient
);
event UpdateKeeper(address indexed newKeeper);
event UpdatePerformanceFee(uint16 newPerformanceFee);
event UpdateManagement(address indexed newManagement);
event UpdateEmergencyAdmin(address indexed newEmergencyAdmin);
event UpdateProfitMaxUnlockTime(uint256 newProfitMaxUnlockTime);
event UpdatePendingManagement(address indexed newPendingManagement);
/*//////////////////////////////////////////////////////////////
INITIALIZATION
//////////////////////////////////////////////////////////////*/
function initialize(
address _asset,
string memory _name,
address _management,
address _performanceFeeRecipient,
address _keeper
) external;
/*//////////////////////////////////////////////////////////////
NON-STANDARD 4626 OPTIONS
//////////////////////////////////////////////////////////////*/
function withdraw(
uint256 assets,
address receiver,
address owner,
uint256 maxLoss
) external returns (uint256);
function redeem(
uint256 shares,
address receiver,
address owner,
uint256 maxLoss
) external returns (uint256);
function maxWithdraw(
address owner,
uint256 /*maxLoss*/
) external view returns (uint256);
function maxRedeem(
address owner,
uint256 /*maxLoss*/
) external view returns (uint256);
/*//////////////////////////////////////////////////////////////
MODIFIER HELPERS
//////////////////////////////////////////////////////////////*/
function requireManagement(address _sender) external view;
function requireKeeperOrManagement(address _sender) external view;
function requireEmergencyAuthorized(address _sender) external view;
/*//////////////////////////////////////////////////////////////
KEEPERS FUNCTIONS
//////////////////////////////////////////////////////////////*/
function tend() external;
function report() external returns (uint256 _profit, uint256 _loss);
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
function MAX_FEE() external view returns (uint16);
function FACTORY() external view returns (address);
/*//////////////////////////////////////////////////////////////
GETTERS
//////////////////////////////////////////////////////////////*/
function apiVersion() external view returns (string memory);
function pricePerShare() external view returns (uint256);
function management() external view returns (address);
function pendingManagement() external view returns (address);
function keeper() external view returns (address);
function emergencyAdmin() external view returns (address);
function performanceFee() external view returns (uint16);
function performanceFeeRecipient() external view returns (address);
function fullProfitUnlockDate() external view returns (uint256);
function profitUnlockingRate() external view returns (uint256);
function profitMaxUnlockTime() external view returns (uint256);
function lastReport() external view returns (uint256);
function isShutdown() external view returns (bool);
function unlockedShares() external view returns (uint256);
/*//////////////////////////////////////////////////////////////
SETTERS
//////////////////////////////////////////////////////////////*/
function setPendingManagement(address) external;
function acceptManagement() external;
function setKeeper(address _keeper) external;
function setEmergencyAdmin(address _emergencyAdmin) external;
function setPerformanceFee(uint16 _performanceFee) external;
function setPerformanceFeeRecipient(
address _performanceFeeRecipient
) external;
function setProfitMaxUnlockTime(uint256 _profitMaxUnlockTime) external;
function setName(string calldata _newName) external;
function shutdownStrategy() external;
function emergencyWithdraw(uint256 _amount) external;
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
interface IPPrincipalToken is IERC20Metadata {
function burnByYT(address user, uint256 amount) external;
function mintByYT(address user, uint256 amount) external;
function initialize(address _YT) external;
function SY() external view returns (address);
function YT() external view returns (address);
function factory() external view returns (address);
function expiry() external view returns (uint256);
function isExpired() external view returns (bool);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import "./IRewardManager.sol";
import "./IPInterestManagerYT.sol";
interface IPYieldToken is IERC20Metadata, IRewardManager, IPInterestManagerYT {
event NewInterestIndex(uint256 indexed newIndex);
event Mint(
address indexed caller,
address indexed receiverPT,
address indexed receiverYT,
uint256 amountSyToMint,
uint256 amountPYOut
);
event Burn(address indexed caller, address indexed receiver, uint256 amountPYToRedeem, uint256 amountSyOut);
event RedeemRewards(address indexed user, uint256[] amountRewardsOut);
event RedeemInterest(address indexed user, uint256 interestOut);
event CollectRewardFee(address indexed rewardToken, uint256 amountRewardFee);
function mintPY(address receiverPT, address receiverYT) external returns (uint256 amountPYOut);
function redeemPY(address receiver) external returns (uint256 amountSyOut);
function redeemPYMulti(
address[] calldata receivers,
uint256[] calldata amountPYToRedeems
) external returns (uint256[] memory amountSyOuts);
function redeemDueInterestAndRewards(
address user,
bool redeemInterest,
bool redeemRewards
) external returns (uint256 interestOut, uint256[] memory rewardsOut);
function rewardIndexesCurrent() external returns (uint256[] memory);
function pyIndexCurrent() external returns (uint256);
function pyIndexStored() external view returns (uint256);
function getRewardTokens() external view returns (address[] memory);
function SY() external view returns (address);
function PT() external view returns (address);
function factory() external view returns (address);
function expiry() external view returns (uint256);
function isExpired() external view returns (bool);
function doCacheIndexSameBlock() external view returns (bool);
function pyIndexLastUpdatedBlock() external view returns (uint128);
}// SPDX-License-Identifier: GPL-3.0-or-later
/*
* MIT License
* ===========
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
*/
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
interface IStandardizedYield is IERC20Metadata {
/// @dev Emitted when any base tokens is deposited to mint shares
event Deposit(
address indexed caller,
address indexed receiver,
address indexed tokenIn,
uint256 amountDeposited,
uint256 amountSyOut
);
/// @dev Emitted when any shares are redeemed for base tokens
event Redeem(
address indexed caller,
address indexed receiver,
address indexed tokenOut,
uint256 amountSyToRedeem,
uint256 amountTokenOut
);
/// @dev check `assetInfo()` for more information
enum AssetType {
TOKEN,
LIQUIDITY
}
/// @dev Emitted when (`user`) claims their rewards
event ClaimRewards(address indexed user, address[] rewardTokens, uint256[] rewardAmounts);
/**
* @notice mints an amount of shares by depositing a base token.
* @param receiver shares recipient address
* @param tokenIn address of the base tokens to mint shares
* @param amountTokenToDeposit amount of base tokens to be transferred from (`msg.sender`)
* @param minSharesOut reverts if amount of shares minted is lower than this
* @return amountSharesOut amount of shares minted
* @dev Emits a {Deposit} event
*
* Requirements:
* - (`tokenIn`) must be a valid base token.
*/
function deposit(
address receiver,
address tokenIn,
uint256 amountTokenToDeposit,
uint256 minSharesOut
) external payable returns (uint256 amountSharesOut);
/**
* @notice redeems an amount of base tokens by burning some shares
* @param receiver recipient address
* @param amountSharesToRedeem amount of shares to be burned
* @param tokenOut address of the base token to be redeemed
* @param minTokenOut reverts if amount of base token redeemed is lower than this
* @param burnFromInternalBalance if true, burns from balance of `address(this)`, otherwise burns from `msg.sender`
* @return amountTokenOut amount of base tokens redeemed
* @dev Emits a {Redeem} event
*
* Requirements:
* - (`tokenOut`) must be a valid base token.
*/
function redeem(
address receiver,
uint256 amountSharesToRedeem,
address tokenOut,
uint256 minTokenOut,
bool burnFromInternalBalance
) external returns (uint256 amountTokenOut);
/**
* @notice exchangeRate * syBalance / 1e18 must return the asset balance of the account
* @notice vice-versa, if a user uses some amount of tokens equivalent to X asset, the amount of sy
he can mint must be X * exchangeRate / 1e18
* @dev SYUtils's assetToSy & syToAsset should be used instead of raw multiplication
& division
*/
function exchangeRate() external view returns (uint256 res);
/**
* @notice claims reward for (`user`)
* @param user the user receiving their rewards
* @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`
* @dev
* Emits a `ClaimRewards` event
* See {getRewardTokens} for list of reward tokens
*/
function claimRewards(address user) external returns (uint256[] memory rewardAmounts);
/**
* @notice get the amount of unclaimed rewards for (`user`)
* @param user the user to check for
* @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens`
*/
function accruedRewards(address user) external view returns (uint256[] memory rewardAmounts);
function rewardIndexesCurrent() external returns (uint256[] memory indexes);
function rewardIndexesStored() external view returns (uint256[] memory indexes);
/**
* @notice returns the list of reward token addresses
*/
function getRewardTokens() external view returns (address[] memory);
/**
* @notice returns the address of the underlying yield token
*/
function yieldToken() external view returns (address);
/**
* @notice returns all tokens that can mint this SY
*/
function getTokensIn() external view returns (address[] memory res);
/**
* @notice returns all tokens that can be redeemed by this SY
*/
function getTokensOut() external view returns (address[] memory res);
function isValidTokenIn(address token) external view returns (bool);
function isValidTokenOut(address token) external view returns (bool);
function previewDeposit(
address tokenIn,
uint256 amountTokenToDeposit
) external view returns (uint256 amountSharesOut);
function previewRedeem(
address tokenOut,
uint256 amountSharesToRedeem
) external view returns (uint256 amountTokenOut);
/**
* @notice This function contains information to interpret what the asset is
* @return assetType the type of the asset (0 for ERC20 tokens, 1 for AMM liquidity tokens,
2 for bridged yield bearing tokens like wstETH, rETH on Arbi whose the underlying asset doesn't exist on the chain)
* @return assetAddress the address of the asset
* @return assetDecimals the decimals of the asset
*/
function assetInfo() external view returns (AssetType assetType, address assetAddress, uint8 assetDecimals);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface IPGauge {
function totalActiveSupply() external view returns (uint256);
function activeBalance(address user) external view returns (uint256);
// only available for newer factories. please check the verified contracts
event RedeemRewards(address indexed user, uint256[] rewardsOut);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "../libraries/math/PMath.sol";
import "../libraries/math/LogExpMath.sol";
import "../StandardizedYield/PYIndex.sol";
import "../libraries/MiniHelpers.sol";
import "../libraries/Errors.sol";
struct MarketState {
int256 totalPt;
int256 totalSy;
int256 totalLp;
address treasury;
/// immutable variables ///
int256 scalarRoot;
uint256 expiry;
/// fee data ///
uint256 lnFeeRateRoot;
uint256 reserveFeePercent; // base 100
/// last trade data ///
uint256 lastLnImpliedRate;
}
// params that are expensive to compute, therefore we pre-compute them
struct MarketPreCompute {
int256 rateScalar;
int256 totalAsset;
int256 rateAnchor;
int256 feeRate;
}
// solhint-disable ordering
library MarketMathCore {
using PMath for uint256;
using PMath for int256;
using LogExpMath for int256;
using PYIndexLib for PYIndex;
int256 internal constant MINIMUM_LIQUIDITY = 10 ** 3;
int256 internal constant PERCENTAGE_DECIMALS = 100;
uint256 internal constant DAY = 86400;
uint256 internal constant IMPLIED_RATE_TIME = 365 * DAY;
int256 internal constant MAX_MARKET_PROPORTION = (1e18 * 96) / 100;
using PMath for uint256;
using PMath for int256;
/*///////////////////////////////////////////////////////////////
UINT FUNCTIONS TO PROXY TO CORE FUNCTIONS
//////////////////////////////////////////////////////////////*/
function addLiquidity(
MarketState memory market,
uint256 syDesired,
uint256 ptDesired,
uint256 blockTime
) internal pure returns (uint256 lpToReserve, uint256 lpToAccount, uint256 syUsed, uint256 ptUsed) {
(int256 _lpToReserve, int256 _lpToAccount, int256 _syUsed, int256 _ptUsed) = addLiquidityCore(
market,
syDesired.Int(),
ptDesired.Int(),
blockTime
);
lpToReserve = _lpToReserve.Uint();
lpToAccount = _lpToAccount.Uint();
syUsed = _syUsed.Uint();
ptUsed = _ptUsed.Uint();
}
function removeLiquidity(
MarketState memory market,
uint256 lpToRemove
) internal pure returns (uint256 netSyToAccount, uint256 netPtToAccount) {
(int256 _syToAccount, int256 _ptToAccount) = removeLiquidityCore(market, lpToRemove.Int());
netSyToAccount = _syToAccount.Uint();
netPtToAccount = _ptToAccount.Uint();
}
function swapExactPtForSy(
MarketState memory market,
PYIndex index,
uint256 exactPtToMarket,
uint256 blockTime
) internal pure returns (uint256 netSyToAccount, uint256 netSyFee, uint256 netSyToReserve) {
(int256 _netSyToAccount, int256 _netSyFee, int256 _netSyToReserve) = executeTradeCore(
market,
index,
exactPtToMarket.neg(),
blockTime
);
netSyToAccount = _netSyToAccount.Uint();
netSyFee = _netSyFee.Uint();
netSyToReserve = _netSyToReserve.Uint();
}
function swapSyForExactPt(
MarketState memory market,
PYIndex index,
uint256 exactPtToAccount,
uint256 blockTime
) internal pure returns (uint256 netSyToMarket, uint256 netSyFee, uint256 netSyToReserve) {
(int256 _netSyToAccount, int256 _netSyFee, int256 _netSyToReserve) = executeTradeCore(
market,
index,
exactPtToAccount.Int(),
blockTime
);
netSyToMarket = _netSyToAccount.neg().Uint();
netSyFee = _netSyFee.Uint();
netSyToReserve = _netSyToReserve.Uint();
}
/*///////////////////////////////////////////////////////////////
CORE FUNCTIONS
//////////////////////////////////////////////////////////////*/
function addLiquidityCore(
MarketState memory market,
int256 syDesired,
int256 ptDesired,
uint256 blockTime
) internal pure returns (int256 lpToReserve, int256 lpToAccount, int256 syUsed, int256 ptUsed) {
/// ------------------------------------------------------------
/// CHECKS
/// ------------------------------------------------------------
if (syDesired == 0 || ptDesired == 0) revert Errors.MarketZeroAmountsInput();
if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();
/// ------------------------------------------------------------
/// MATH
/// ------------------------------------------------------------
if (market.totalLp == 0) {
lpToAccount = PMath.sqrt((syDesired * ptDesired).Uint()).Int() - MINIMUM_LIQUIDITY;
lpToReserve = MINIMUM_LIQUIDITY;
syUsed = syDesired;
ptUsed = ptDesired;
} else {
int256 netLpByPt = (ptDesired * market.totalLp) / market.totalPt;
int256 netLpBySy = (syDesired * market.totalLp) / market.totalSy;
if (netLpByPt < netLpBySy) {
lpToAccount = netLpByPt;
ptUsed = ptDesired;
syUsed = (market.totalSy * lpToAccount).rawDivUp(market.totalLp);
} else {
lpToAccount = netLpBySy;
syUsed = syDesired;
ptUsed = (market.totalPt * lpToAccount).rawDivUp(market.totalLp);
}
}
if (lpToAccount <= 0 || syUsed <= 0 || ptUsed <= 0) revert Errors.MarketZeroAmountsOutput();
/// ------------------------------------------------------------
/// WRITE
/// ------------------------------------------------------------
market.totalSy += syUsed;
market.totalPt += ptUsed;
market.totalLp += lpToAccount + lpToReserve;
}
function removeLiquidityCore(
MarketState memory market,
int256 lpToRemove
) internal pure returns (int256 netSyToAccount, int256 netPtToAccount) {
/// ------------------------------------------------------------
/// CHECKS
/// ------------------------------------------------------------
if (lpToRemove == 0) revert Errors.MarketZeroAmountsInput();
/// ------------------------------------------------------------
/// MATH
/// ------------------------------------------------------------
netSyToAccount = (lpToRemove * market.totalSy) / market.totalLp;
netPtToAccount = (lpToRemove * market.totalPt) / market.totalLp;
if (netSyToAccount == 0 && netPtToAccount == 0) revert Errors.MarketZeroAmountsOutput();
/// ------------------------------------------------------------
/// WRITE
/// ------------------------------------------------------------
market.totalLp = market.totalLp.subNoNeg(lpToRemove);
market.totalPt = market.totalPt.subNoNeg(netPtToAccount);
market.totalSy = market.totalSy.subNoNeg(netSyToAccount);
}
function executeTradeCore(
MarketState memory market,
PYIndex index,
int256 netPtToAccount,
uint256 blockTime
) internal pure returns (int256 netSyToAccount, int256 netSyFee, int256 netSyToReserve) {
/// ------------------------------------------------------------
/// CHECKS
/// ------------------------------------------------------------
if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();
if (market.totalPt <= netPtToAccount)
revert Errors.MarketInsufficientPtForTrade(market.totalPt, netPtToAccount);
/// ------------------------------------------------------------
/// MATH
/// ------------------------------------------------------------
MarketPreCompute memory comp = getMarketPreCompute(market, index, blockTime);
(netSyToAccount, netSyFee, netSyToReserve) = calcTrade(market, comp, index, netPtToAccount);
/// ------------------------------------------------------------
/// WRITE
/// ------------------------------------------------------------
_setNewMarketStateTrade(market, comp, index, netPtToAccount, netSyToAccount, netSyToReserve, blockTime);
}
function getMarketPreCompute(
MarketState memory market,
PYIndex index,
uint256 blockTime
) internal pure returns (MarketPreCompute memory res) {
if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();
uint256 timeToExpiry = market.expiry - blockTime;
res.rateScalar = _getRateScalar(market, timeToExpiry);
res.totalAsset = index.syToAsset(market.totalSy);
if (market.totalPt == 0 || res.totalAsset == 0)
revert Errors.MarketZeroTotalPtOrTotalAsset(market.totalPt, res.totalAsset);
res.rateAnchor = _getRateAnchor(
market.totalPt,
market.lastLnImpliedRate,
res.totalAsset,
res.rateScalar,
timeToExpiry
);
res.feeRate = _getExchangeRateFromImpliedRate(market.lnFeeRateRoot, timeToExpiry);
}
function calcTrade(
MarketState memory market,
MarketPreCompute memory comp,
PYIndex index,
int256 netPtToAccount
) internal pure returns (int256 netSyToAccount, int256 netSyFee, int256 netSyToReserve) {
int256 preFeeExchangeRate = _getExchangeRate(
market.totalPt,
comp.totalAsset,
comp.rateScalar,
comp.rateAnchor,
netPtToAccount
);
int256 preFeeAssetToAccount = netPtToAccount.divDown(preFeeExchangeRate).neg();
int256 fee = comp.feeRate;
if (netPtToAccount > 0) {
int256 postFeeExchangeRate = preFeeExchangeRate.divDown(fee);
if (postFeeExchangeRate < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(postFeeExchangeRate);
fee = preFeeAssetToAccount.mulDown(PMath.IONE - fee);
} else {
fee = ((preFeeAssetToAccount * (PMath.IONE - fee)) / fee).neg();
}
int256 netAssetToReserve = (fee * market.reserveFeePercent.Int()) / PERCENTAGE_DECIMALS;
int256 netAssetToAccount = preFeeAssetToAccount - fee;
netSyToAccount = netAssetToAccount < 0
? index.assetToSyUp(netAssetToAccount)
: index.assetToSy(netAssetToAccount);
netSyFee = index.assetToSy(fee);
netSyToReserve = index.assetToSy(netAssetToReserve);
}
function _setNewMarketStateTrade(
MarketState memory market,
MarketPreCompute memory comp,
PYIndex index,
int256 netPtToAccount,
int256 netSyToAccount,
int256 netSyToReserve,
uint256 blockTime
) internal pure {
uint256 timeToExpiry = market.expiry - blockTime;
market.totalPt = market.totalPt.subNoNeg(netPtToAccount);
market.totalSy = market.totalSy.subNoNeg(netSyToAccount + netSyToReserve);
market.lastLnImpliedRate = _getLnImpliedRate(
market.totalPt,
index.syToAsset(market.totalSy),
comp.rateScalar,
comp.rateAnchor,
timeToExpiry
);
if (market.lastLnImpliedRate == 0) revert Errors.MarketZeroLnImpliedRate();
}
function _getRateAnchor(
int256 totalPt,
uint256 lastLnImpliedRate,
int256 totalAsset,
int256 rateScalar,
uint256 timeToExpiry
) internal pure returns (int256 rateAnchor) {
int256 newExchangeRate = _getExchangeRateFromImpliedRate(lastLnImpliedRate, timeToExpiry);
if (newExchangeRate < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(newExchangeRate);
{
int256 proportion = totalPt.divDown(totalPt + totalAsset);
int256 lnProportion = _logProportion(proportion);
rateAnchor = newExchangeRate - lnProportion.divDown(rateScalar);
}
}
/// @notice Calculates the current market implied rate.
/// @return lnImpliedRate the implied rate
function _getLnImpliedRate(
int256 totalPt,
int256 totalAsset,
int256 rateScalar,
int256 rateAnchor,
uint256 timeToExpiry
) internal pure returns (uint256 lnImpliedRate) {
// This will check for exchange rates < PMath.IONE
int256 exchangeRate = _getExchangeRate(totalPt, totalAsset, rateScalar, rateAnchor, 0);
// exchangeRate >= 1 so its ln >= 0
uint256 lnRate = exchangeRate.ln().Uint();
lnImpliedRate = (lnRate * IMPLIED_RATE_TIME) / timeToExpiry;
}
/// @notice Converts an implied rate to an exchange rate given a time to expiry. The
/// formula is E = e^rt
function _getExchangeRateFromImpliedRate(
uint256 lnImpliedRate,
uint256 timeToExpiry
) internal pure returns (int256 exchangeRate) {
uint256 rt = (lnImpliedRate * timeToExpiry) / IMPLIED_RATE_TIME;
exchangeRate = LogExpMath.exp(rt.Int());
}
function _getExchangeRate(
int256 totalPt,
int256 totalAsset,
int256 rateScalar,
int256 rateAnchor,
int256 netPtToAccount
) internal pure returns (int256 exchangeRate) {
int256 numerator = totalPt.subNoNeg(netPtToAccount);
int256 proportion = (numerator.divDown(totalPt + totalAsset));
if (proportion > MAX_MARKET_PROPORTION)
revert Errors.MarketProportionTooHigh(proportion, MAX_MARKET_PROPORTION);
int256 lnProportion = _logProportion(proportion);
exchangeRate = lnProportion.divDown(rateScalar) + rateAnchor;
if (exchangeRate < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(exchangeRate);
}
function _logProportion(int256 proportion) internal pure returns (int256 res) {
if (proportion == PMath.IONE) revert Errors.MarketProportionMustNotEqualOne();
int256 logitP = proportion.divDown(PMath.IONE - proportion);
res = logitP.ln();
}
function _getRateScalar(MarketState memory market, uint256 timeToExpiry) internal pure returns (int256 rateScalar) {
rateScalar = (market.scalarRoot * IMPLIED_RATE_TIME.Int()) / timeToExpiry.Int();
if (rateScalar <= 0) revert Errors.MarketRateScalarBelowZero(rateScalar);
}
function setInitialLnImpliedRate(
MarketState memory market,
PYIndex index,
int256 initialAnchor,
uint256 blockTime
) internal pure {
/// ------------------------------------------------------------
/// CHECKS
/// ------------------------------------------------------------
if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired();
/// ------------------------------------------------------------
/// MATH
/// ------------------------------------------------------------
int256 totalAsset = index.syToAsset(market.totalSy);
uint256 timeToExpiry = market.expiry - blockTime;
int256 rateScalar = _getRateScalar(market, timeToExpiry);
/// ------------------------------------------------------------
/// WRITE
/// ------------------------------------------------------------
market.lastLnImpliedRate = _getLnImpliedRate(
market.totalPt,
totalAsset,
rateScalar,
initialAnchor,
timeToExpiry
);
}
}// SPDX-License-Identifier: GPL-3.0-or-later
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
pragma solidity ^0.8.0;
/* solhint-disable private-vars-leading-underscore, reason-string */
library PMath {
uint256 internal constant ONE = 1e18; // 18 decimal places
int256 internal constant IONE = 1e18; // 18 decimal places
function subMax0(uint256 a, uint256 b) internal pure returns (uint256) {
unchecked {
return (a >= b ? a - b : 0);
}
}
function subNoNeg(int256 a, int256 b) internal pure returns (int256) {
require(a >= b, "negative");
return a - b; // no unchecked since if b is very negative, a - b might overflow
}
function mulDown(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 product = a * b;
unchecked {
return product / ONE;
}
}
function mulDown(int256 a, int256 b) internal pure returns (int256) {
int256 product = a * b;
unchecked {
return product / IONE;
}
}
function divDown(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 aInflated = a * ONE;
unchecked {
return aInflated / b;
}
}
function divDown(int256 a, int256 b) internal pure returns (int256) {
int256 aInflated = a * IONE;
unchecked {
return aInflated / b;
}
}
function rawDivUp(uint256 a, uint256 b) internal pure returns (uint256) {
return (a + b - 1) / b;
}
function rawDivUp(int256 a, int256 b) internal pure returns (int256) {
return (a + b - 1) / b;
}
function tweakUp(uint256 a, uint256 factor) internal pure returns (uint256) {
return mulDown(a, ONE + factor);
}
function tweakDown(uint256 a, uint256 factor) internal pure returns (uint256) {
return mulDown(a, ONE - factor);
}
/// @return res = min(a + b, bound)
/// @dev This function should handle arithmetic operation and bound check without overflow/underflow
function addWithUpperBound(uint256 a, uint256 b, uint256 bound) internal pure returns (uint256 res) {
unchecked {
if (type(uint256).max - b < a) res = bound;
else res = min(bound, a + b);
}
}
/// @return res = max(a - b, bound)
/// @dev This function should handle arithmetic operation and bound check without overflow/underflow
function subWithLowerBound(uint256 a, uint256 b, uint256 bound) internal pure returns (uint256 res) {
unchecked {
if (b > a) res = bound;
else res = max(a - b, bound);
}
}
function clamp(uint256 x, uint256 lower, uint256 upper) internal pure returns (uint256 res) {
res = x;
if (x < lower) res = lower;
else if (x > upper) res = upper;
}
// @author Uniswap
function sqrt(uint256 y) internal pure returns (uint256 z) {
if (y > 3) {
z = y;
uint256 x = y / 2 + 1;
while (x < z) {
z = x;
x = (y / x + x) / 2;
}
} else if (y != 0) {
z = 1;
}
}
function square(uint256 x) internal pure returns (uint256) {
return x * x;
}
function squareDown(uint256 x) internal pure returns (uint256) {
return mulDown(x, x);
}
function abs(int256 x) internal pure returns (uint256) {
return uint256(x > 0 ? x : -x);
}
function neg(int256 x) internal pure returns (int256) {
return x * (-1);
}
function neg(uint256 x) internal pure returns (int256) {
return Int(x) * (-1);
}
function max(uint256 x, uint256 y) internal pure returns (uint256) {
return (x > y ? x : y);
}
function max(int256 x, int256 y) internal pure returns (int256) {
return (x > y ? x : y);
}
function min(uint256 x, uint256 y) internal pure returns (uint256) {
return (x < y ? x : y);
}
function min(int256 x, int256 y) internal pure returns (int256) {
return (x < y ? x : y);
}
/*///////////////////////////////////////////////////////////////
SIGNED CASTS
//////////////////////////////////////////////////////////////*/
function Int(uint256 x) internal pure returns (int256) {
require(x <= uint256(type(int256).max));
return int256(x);
}
function Int128(int256 x) internal pure returns (int128) {
require(type(int128).min <= x && x <= type(int128).max);
return int128(x);
}
function Int128(uint256 x) internal pure returns (int128) {
return Int128(Int(x));
}
/*///////////////////////////////////////////////////////////////
UNSIGNED CASTS
//////////////////////////////////////////////////////////////*/
function Uint(int256 x) internal pure returns (uint256) {
require(x >= 0);
return uint256(x);
}
function Uint32(uint256 x) internal pure returns (uint32) {
require(x <= type(uint32).max);
return uint32(x);
}
function Uint64(uint256 x) internal pure returns (uint64) {
require(x <= type(uint64).max);
return uint64(x);
}
function Uint112(uint256 x) internal pure returns (uint112) {
require(x <= type(uint112).max);
return uint112(x);
}
function Uint96(uint256 x) internal pure returns (uint96) {
require(x <= type(uint96).max);
return uint96(x);
}
function Uint128(uint256 x) internal pure returns (uint128) {
require(x <= type(uint128).max);
return uint128(x);
}
function Uint192(uint256 x) internal pure returns (uint192) {
require(x <= type(uint192).max);
return uint192(x);
}
function Uint80(uint256 x) internal pure returns (uint80) {
require(x <= type(uint80).max);
return uint80(x);
}
function isAApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
return mulDown(b, ONE - eps) <= a && a <= mulDown(b, ONE + eps);
}
function isAGreaterApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
return a >= b && a <= mulDown(b, ONE + eps);
}
function isASmallerApproxB(uint256 a, uint256 b, uint256 eps) internal pure returns (bool) {
return a <= b && a >= mulDown(b, ONE - eps);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface of the ERC-4626 "Tokenized Vault Standard", as defined in
* https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
*/
interface IERC4626 is IERC20, IERC20Metadata {
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed sender,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
/**
* @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
*
* - MUST be an ERC-20 token contract.
* - MUST NOT revert.
*/
function asset() external view returns (address assetTokenAddress);
/**
* @dev Returns the total amount of the underlying asset that is “managed” by Vault.
*
* - SHOULD include any compounding that occurs from yield.
* - MUST be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT revert.
*/
function totalAssets() external view returns (uint256 totalManagedAssets);
/**
* @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
* through a deposit call.
*
* - MUST return a limited value if receiver is subject to some deposit limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
* - MUST NOT revert.
*/
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
* call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
* in the same transaction.
* - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
* deposit would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/**
* @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* deposit execution, and are accounted for during deposit.
* - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
* - MUST return a limited value if receiver is subject to some mint limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
* - MUST NOT revert.
*/
function maxMint(address receiver) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
* in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
* same transaction.
* - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
* would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by minting.
*/
function previewMint(uint256 shares) external view returns (uint256 assets);
/**
* @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
* execution, and are accounted for during mint.
* - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function mint(uint256 shares, address receiver) external returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
* call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
* called
* in the same transaction.
* - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
* the withdrawal would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/**
* @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* withdraw execution, and are accounted for during withdraw.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
* through a redeem call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxRedeem(address owner) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
* in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
* same transaction.
* - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
* redemption would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by redeeming.
*/
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/**
* @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* redeem execution, and are accounted for during redeem.
* - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
import {IERC3156FlashBorrower} from "./IERC3156FlashBorrower.sol";
/// @notice https://eips.ethereum.org/EIPS/eip-3156
interface IERC3156FlashLender {
/// @notice Protected deposits are not available for a flash loan.
/// During the execution of the flashloan, Silo methods are not taking into consideration the fact,
/// that some (or all) tokens were transferred as flashloan, therefore some methods can return invalid state
/// eg. maxWithdraw can return amount that are not available to withdraw during flashlon.
/// @dev Initiate a flash loan.
/// @param _receiver The receiver of the tokens in the loan, and the receiver of the callback.
/// @param _token The loan currency.
/// @param _amount The amount of tokens lent.
/// @param _data Arbitrary data structure, intended to contain user-defined parameters.
function flashLoan(IERC3156FlashBorrower _receiver, address _token, uint256 _amount, bytes calldata _data)
external
returns (bool);
/// @dev The amount of currency available to be lent.
/// @param _token The loan currency.
/// @return The amount of `token` that can be borrowed.
function maxFlashLoan(address _token) external view returns (uint256);
/// @dev The fee to be charged for a given loan.
/// @param _token The loan currency.
/// @param _amount The amount of tokens lent.
/// @return The amount of `token` to be charged for the loan, on top of the returned principal.
function flashFee(address _token, uint256 _amount) external view returns (uint256);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
import {IERC721} from "openzeppelin5/interfaces/IERC721.sol";
import {ISiloConfig} from "./ISiloConfig.sol";
interface ISiloFactory is IERC721 {
struct Range {
uint128 min;
uint128 max;
}
/// @notice Emitted on the creation of a Silo.
/// @param implementation Address of the Silo implementation.
/// @param token0 Address of the first Silo token.
/// @param token1 Address of the second Silo token.
/// @param silo0 Address of the first Silo.
/// @param silo1 Address of the second Silo.
/// @param siloConfig Address of the SiloConfig.
event NewSilo(
address indexed implementation,
address indexed token0,
address indexed token1,
address silo0,
address silo1,
address siloConfig
);
event BaseURI(string newBaseURI);
/// @notice Emitted on the update of DAO fee.
/// @param minDaoFee Value of the new minimal DAO fee.
/// @param maxDaoFee Value of the new maximal DAO fee.
event DaoFeeChanged(uint128 minDaoFee, uint128 maxDaoFee);
/// @notice Emitted on the update of max deployer fee.
/// @param maxDeployerFee Value of the new max deployer fee.
event MaxDeployerFeeChanged(uint256 maxDeployerFee);
/// @notice Emitted on the update of max flashloan fee.
/// @param maxFlashloanFee Value of the new max flashloan fee.
event MaxFlashloanFeeChanged(uint256 maxFlashloanFee);
/// @notice Emitted on the update of max liquidation fee.
/// @param maxLiquidationFee Value of the new max liquidation fee.
event MaxLiquidationFeeChanged(uint256 maxLiquidationFee);
/// @notice Emitted on the change of DAO fee receiver.
/// @param daoFeeReceiver Address of the new DAO fee receiver.
event DaoFeeReceiverChanged(address daoFeeReceiver);
/// @notice Emitted on the change of DAO fee receiver for particular silo
/// @param silo Address for which new DAO fee receiver is set.
/// @param daoFeeReceiver Address of the new DAO fee receiver.
event DaoFeeReceiverChangedForSilo(address silo, address daoFeeReceiver);
/// @notice Emitted on the change of DAO fee receiver for particular asset
/// @param asset Address for which new DAO fee receiver is set.
/// @param daoFeeReceiver Address of the new DAO fee receiver.
event DaoFeeReceiverChangedForAsset(address asset, address daoFeeReceiver);
error MissingHookReceiver();
error ZeroAddress();
error DaoFeeReceiverZeroAddress();
error SameDaoFeeReceiver();
error EmptyToken0();
error EmptyToken1();
error MaxFeeExceeded();
error InvalidFeeRange();
error SameAsset();
error SameRange();
error InvalidIrm();
error InvalidMaxLtv();
error InvalidLt();
error InvalidDeployer();
error DaoMinRangeExceeded();
error DaoMaxRangeExceeded();
error MaxDeployerFeeExceeded();
error MaxFlashloanFeeExceeded();
error MaxLiquidationFeeExceeded();
error InvalidCallBeforeQuote();
error OracleMisconfiguration();
error InvalidQuoteToken();
error HookIsZeroAddress();
error LiquidationTargetLtvTooHigh();
error NotYourSilo();
error ConfigMismatchSilo();
error ConfigMismatchShareProtectedToken();
error ConfigMismatchShareDebtToken();
error ConfigMismatchShareCollateralToken();
/// @notice Create a new Silo.
/// @param _siloConfig Silo configuration.
/// @param _siloImpl Address of the `Silo` implementation.
/// @param _shareProtectedCollateralTokenImpl Address of the `ShareProtectedCollateralToken` implementation.
/// @param _shareDebtTokenImpl Address of the `ShareDebtToken` implementation.
/// @param _deployer Address of the deployer.
/// @param _creator Address of the creator.
function createSilo(
ISiloConfig _siloConfig,
address _siloImpl,
address _shareProtectedCollateralTokenImpl,
address _shareDebtTokenImpl,
address _deployer,
address _creator
)
external;
/// @notice NFT ownership represents the deployer fee receiver for the each Silo ID. After burning,
/// the deployer fee is sent to the DAO. Burning doesn't affect Silo's behavior. It is only about fee distribution.
/// @param _siloIdToBurn silo ID to burn.
function burn(uint256 _siloIdToBurn) external;
/// @notice Update the value of DAO fee. Updated value will be used only for a new Silos.
/// Previously deployed SiloConfigs are immutable.
/// @param _minFee Value of the new DAO minimal fee.
/// @param _maxFee Value of the new DAO maximal fee.
function setDaoFee(uint128 _minFee, uint128 _maxFee) external;
/// @notice Set the default DAO fee receiver.
/// @param _newDaoFeeReceiver Address of the new DAO fee receiver.
function setDaoFeeReceiver(address _newDaoFeeReceiver) external;
/// @notice Set the new DAO fee receiver for asset, this setup will be used when fee receiver for silo is empty.
/// @param _asset Address for which new DAO fee receiver is set.
/// @param _newDaoFeeReceiver Address of the new DAO fee receiver.
function setDaoFeeReceiverForAsset(address _asset, address _newDaoFeeReceiver) external;
/// @notice Set the new DAO fee receiver for silo. This setup has highest priority.
/// @param _silo Address for which new DAO fee receiver is set.
/// @param _newDaoFeeReceiver Address of the new DAO fee receiver.
function setDaoFeeReceiverForSilo(address _silo, address _newDaoFeeReceiver) external;
/// @notice Update the value of max deployer fee. Updated value will be used only for a new Silos max deployer
/// fee validation. Previously deployed SiloConfigs are immutable.
/// @param _newMaxDeployerFee Value of the new max deployer fee.
function setMaxDeployerFee(uint256 _newMaxDeployerFee) external;
/// @notice Update the value of max flashloan fee. Updated value will be used only for a new Silos max flashloan
/// fee validation. Previously deployed SiloConfigs are immutable.
/// @param _newMaxFlashloanFee Value of the new max flashloan fee.
function setMaxFlashloanFee(uint256 _newMaxFlashloanFee) external;
/// @notice Update the value of max liquidation fee. Updated value will be used only for a new Silos max
/// liquidation fee validation. Previously deployed SiloConfigs are immutable.
/// @param _newMaxLiquidationFee Value of the new max liquidation fee.
function setMaxLiquidationFee(uint256 _newMaxLiquidationFee) external;
/// @notice Update the base URI.
/// @param _newBaseURI Value of the new base URI.
function setBaseURI(string calldata _newBaseURI) external;
/// @notice Acceptable DAO fee range for new Silos. Denominated in 18 decimals points. 1e18 == 100%.
function daoFeeRange() external view returns (Range memory);
/// @notice Max deployer fee for a new Silos. Denominated in 18 decimals points. 1e18 == 100%.
function maxDeployerFee() external view returns (uint256);
/// @notice Max flashloan fee for a new Silos. Denominated in 18 decimals points. 1e18 == 100%.
function maxFlashloanFee() external view returns (uint256);
/// @notice Max liquidation fee for a new Silos. Denominated in 18 decimals points. 1e18 == 100%.
function maxLiquidationFee() external view returns (uint256);
/// @notice The recipient of DAO fees.
function daoFeeReceiver() external view returns (address);
/// @notice Get SiloConfig address by Silo id.
function idToSiloConfig(uint256 _id) external view returns (address);
/// @notice Get the counter of silos created by the wallet.
function creatorSiloCounter(address _creator) external view returns (uint256);
/// @notice Do not use this method to check if silo is secure. Anyone can deploy silo with any configuration
/// and implementation. Most critical part of verification would be to check who deployed it.
/// @dev True if the address was deployed using SiloFactory.
function isSilo(address _silo) external view returns (bool);
/// @notice Id of a next Silo to be deployed. This is an ID of non-existing Silo outside of createSilo
/// function call. ID of a first Silo is 1.
function getNextSiloId() external view returns (uint256);
/// @notice Get the DAO and deployer fee receivers for a particular Silo address.
/// @param _silo Silo address.
/// @return dao DAO fee receiver.
/// @return deployer Deployer fee receiver.
function getFeeReceivers(address _silo) external view returns (address dao, address deployer);
/// @notice Validate InitData for a new Silo. Config will be checked for the fee limits, missing parameters.
/// @param _initData Silo init data.
function validateSiloInitData(ISiloConfig.InitData memory _initData) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
import {ISiloConfig} from "./ISiloConfig.sol";
interface IHookReceiver {
struct HookConfig {
uint24 hooksBefore;
uint24 hooksAfter;
}
event HookConfigured(address silo, uint24 hooksBefore, uint24 hooksAfter);
/// @dev Revert if provided silo configuration during initialization is empty
error EmptySiloConfig();
/// @dev Revert if the hook receiver is already configured/initialized
error AlreadyConfigured();
/// @notice Initialize a hook receiver
/// @param _siloConfig Silo configuration with all the details about the silo
/// @param _data Data to initialize the hook receiver (if needed)
function initialize(ISiloConfig _siloConfig, bytes calldata _data) external;
/// @notice state of Silo before action, can be also without interest, if you need them, call silo.accrueInterest()
function beforeAction(address _silo, uint256 _action, bytes calldata _input) external;
function afterAction(address _silo, uint256 _action, bytes calldata _inputAndOutput) external;
/// @notice return hooksBefore and hooksAfter configuration
function hookReceiverConfig(address _silo) external view returns (uint24 hooksBefore, uint24 hooksAfter);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
interface ICrossReentrancyGuard {
error CrossReentrantCall();
error CrossReentrancyNotActive();
/// @notice only silo method for cross Silo reentrancy
function turnOnReentrancyProtection() external;
/// @notice only silo method for cross Silo reentrancy
function turnOffReentrancyProtection() external;
/// @notice view method for checking cross Silo reentrancy flag
/// @return entered true if the reentrancy guard is currently set to "entered", which indicates there is a
/// `nonReentrant` function in the call stack.
function reentrancyGuardEntered() external view returns (bool entered);
}// 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
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 standard as defined in the ERC.
*/
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
pragma solidity >=0.6.2 <0.9.0;
library Utils {
// https://docs.soliditylang.org/en/latest/assembly.html#example
function getCodeAt(address _addr) internal view returns (bytes memory oCode) {
assembly {
// retrieve the size of the code, this needs assembly
let size := extcodesize(_addr)
// allocate output byte array - this could also be done without assembly
// by using o_code = new bytes(size)
oCode := mload(0x40)
// new "memory end" including padding
mstore(0x40, add(oCode, and(add(add(size, 0x20), 0x1f), not(0x1f))))
// store length in memory
mstore(oCode, size)
// actually retrieve the code, this needs assembly
extcodecopy(_addr, add(oCode, 0x20), 0, size)
}
}
// https://ethereum.stackexchange.com/questions/10932/how-to-convert-string-to-int
function stringToUint(string memory s) internal pure returns (uint256) {
bytes memory b = bytes(s);
uint256 result = 0;
for (uint256 i = 0; i < b.length; i++) {
if (uint8(b[i]) >= 48 && uint8(b[i]) <= 57) {
result = result * 10 + (uint256(uint8(b[i])) - 48);
}
}
return result;
}
function encodeGetAddressCall(bytes4 _fnSig, uint256 _chainId, string memory _key)
internal
pure
returns (bytes memory fnPayload)
{
assembly {
function allocate(length) -> pos {
pos := mload(0x40)
mstore(0x40, add(pos, length))
}
let keyLength := mload(_key)
fnPayload := mload(0x40)
let p := allocate(0x20)
let keySlots := mul(add(div(keyLength, 0x20), 0x01), 0x20)
mstore(p, add(0x64, keySlots))
p := allocate(0x04)
mstore(p, _fnSig)
p := allocate(0x20)
mstore(p, _chainId)
p := allocate(0x20)
mstore(p, 0x40)
p := allocate(0x20)
mstore(p, keyLength)
for { let i := 0 } lt(i, keyLength) { i := add(i, 0x20) } {
p := allocate(0x20)
mstore(p, mload(add(add(_key, 0x20), i)))
}
}
}
function encodeSingleSelectorCall(bytes4 _fnSig) internal pure returns (bytes memory fnPayload) {
assembly {
function allocate(length) -> pos {
pos := mload(0x40)
mstore(0x40, add(pos, length))
}
fnPayload := mload(0x40)
let p := allocate(0x20)
mstore(fnPayload, 0x04)
p := allocate(0x04)
mstore(p, _fnSig)
}
}
// https://ethereum.stackexchange.com/questions/15350/how-to-convert-an-bytes-to-address-in-solidity
function asciiBytesToAddress(bytes memory b) internal pure returns (address) {
uint256 result = 0;
for (uint256 i = 0; i < b.length; i++) {
uint256 c = uint256(uint8(b[i]));
if (c >= 48 && c <= 57) {
result = result * 16 + (c - 48);
}
if (c >= 65 && c <= 90) {
result = result * 16 + (c - 55);
}
if (c >= 97 && c <= 122) {
result = result * 16 + (c - 87);
}
}
return address(uint160(result));
}
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
import {ISiloConfig} from "./ISiloConfig.sol";
import {ISilo} from "./ISilo.sol";
import {IInterestRateModel} from "./IInterestRateModel.sol";
import {IPartialLiquidation} from "./IPartialLiquidation.sol";
interface ISiloLens {
struct Borrower {
ISilo silo;
address wallet;
}
struct BorrowerHealth {
uint256 lt;
uint256 ltv;
}
struct APR {
uint256 depositAPR;
uint256 borrowAPR;
}
error InvalidAsset();
/// @dev calculates solvency
/// @notice this is backwards compatible method, you can use `_silo.isSolvent(_borrower)` directly.
/// @param _silo Silo address from which to read data
/// @param _borrower wallet address
/// @return true if solvent, false otherwise
function isSolvent(ISilo _silo, address _borrower) external view returns (bool);
/// @dev Amount of token that is available for borrowing.
/// @notice this is backwards compatible method, you can use `_silo.getLiquidity()`
/// @param _silo Silo address from which to read data
/// @return Silo liquidity
function liquidity(ISilo _silo) external view returns (uint256);
/// @return liquidity based on contract state (without interest, fees)
function getRawLiquidity(ISilo _silo) external view returns (uint256 liquidity);
/// @notice Retrieves the maximum loan-to-value (LTV) ratio
/// @param _silo Address of the silo
/// @return maxLtv The maximum LTV ratio configured for the silo in 18 decimals points
function getMaxLtv(ISilo _silo) external view returns (uint256 maxLtv);
/// @notice Retrieves the LT value
/// @param _silo Address of the silo
/// @return lt The LT value in 18 decimals points
function getLt(ISilo _silo) external view returns (uint256 lt);
/// @notice Retrieves the LT for a specific borrower
/// @param _silo Address of the silo
/// @param _borrower Address of the borrower
/// @return userLT The LT for the borrower in 18 decimals points, returns 0 if no debt
function getUserLT(ISilo _silo, address _borrower) external view returns (uint256 userLT);
/// @notice Retrieves the LT for a specific borrowers
/// @param _borrowers list of borrowers with corresponding silo addresses
/// @return usersLTs The LTs for the borrowers in 18 decimals points, returns 0 for users with no debt
function getUsersLT(Borrower[] calldata _borrowers) external view returns (uint256[] memory usersLTs);
/// @notice Retrieves the LT and LTV for a specific borrowers
/// @param _borrowers list of borrowers with corresponding silo addresses
/// @return healths The LTs and LTVs for the borrowers in 18 decimals points, returns 0 for users with no debt
function getUsersHealth(Borrower[] calldata _borrowers) external view returns (BorrowerHealth[] memory healths);
/// @notice Retrieves the loan-to-value (LTV) for a specific borrower
/// @param _silo Address of the silo
/// @param _borrower Address of the borrower
/// @return userLTV The LTV for the borrower in 18 decimals points
function getUserLTV(ISilo _silo, address _borrower) external view returns (uint256 userLTV);
/// @notice Retrieves the loan-to-value (LTV) for a specific borrower
/// @param _silo Address of the silo
/// @param _borrower Address of the borrower
/// @return ltv The LTV for the borrower in 18 decimals points
function getLtv(ISilo _silo, address _borrower) external view returns (uint256 ltv);
/// @notice Check if user has position (collateral, protected or debt)
/// in any asset in a market (both silos are checked)
/// @param _siloConfig Market address (silo config address)
/// @param _borrower wallet address for which to read data
/// @return TRUE if user has position in any asset
function hasPosition(ISiloConfig _siloConfig, address _borrower) external view returns (bool);
/// @notice Check if user is in debt
/// @param _siloConfig Market address (silo config address)
/// @param _borrower wallet address for which to read data
/// @return TRUE if user borrowed any amount of any asset, otherwise FALSE
function inDebt(ISiloConfig _siloConfig, address _borrower) external view returns (bool);
/// @notice Retrieves the fee details in 18 decimals points and the addresses of the DAO and deployer fee receivers
/// @param _silo Address of the silo
/// @return daoFeeReceiver The address of the DAO fee receiver
/// @return deployerFeeReceiver The address of the deployer fee receiver
/// @return daoFee The total fee for the DAO in 18 decimals points
/// @return deployerFee The total fee for the deployer in 18 decimals points
function getFeesAndFeeReceivers(ISilo _silo)
external
view
returns (address daoFeeReceiver, address deployerFeeReceiver, uint256 daoFee, uint256 deployerFee);
/// @notice Get underlying balance of all deposits of silo asset of given user including "protected"
/// deposits, with interest
/// @param _silo Address of the silo
/// @param _borrower wallet address for which to read data
/// @return balance of underlying tokens for the given `_borrower`
function collateralBalanceOfUnderlying(ISilo _silo, address _borrower)
external
view
returns (uint256);
/// @notice Get amount of debt of underlying token for given user
/// @param _silo Silo address from which to read data
/// @param _borrower wallet address for which to read data
/// @return balance of underlying token owed
function debtBalanceOfUnderlying(ISilo _silo, address _borrower) external view returns (uint256);
/// @param _silo silo where borrower has debt
/// @param _hook hook for silo with debt
/// @param _borrower borrower address
/// @return collateralToLiquidate underestimated amount of collateral liquidator will get
/// @return debtToRepay debt amount needed to be repay to get `collateralToLiquidate`
/// @return sTokenRequired TRUE, when liquidation with underlying asset is not possible because of not enough
/// liquidity
/// @return fullLiquidation TRUE if position has to be fully liquidated
function maxLiquidation(ISilo _silo, IPartialLiquidation _hook, address _borrower)
external
view
returns (uint256 collateralToLiquidate, uint256 debtToRepay, bool sTokenRequired, bool fullLiquidation);
/// @notice Get amount of underlying asset that has been deposited to Silo
/// @dev It reads directly from storage so interest generated between last update and now is not
/// taken for account
/// @param _silo Silo address from which to read data
/// @return totalDeposits amount of all deposits made for given asset
function totalDeposits(ISilo _silo) external view returns (uint256 totalDeposits);
/// @notice returns total deposits with interest dynamically calculated at current block timestamp
/// @return total deposits amount with interest
function totalDepositsWithInterest(ISilo _silo) external view returns (uint256);
/// @notice returns total borrow amount with interest dynamically calculated at current block timestamp
/// @return _totalBorrowAmount total deposits amount with interest
function totalBorrowAmountWithInterest(ISilo _silo)
external
view
returns (uint256 _totalBorrowAmount);
/// @notice Get amount of protected asset token that has been deposited to Silo
/// @param _silo Silo address from which to read data
/// @return amount of all "protected" deposits
function collateralOnlyDeposits(ISilo _silo) external view returns (uint256);
/// @notice Calculates current deposit (with interest) for user
/// without protected deposits
/// @param _silo Silo address from which to read data
/// @param _borrower account for which calculation are done
/// @return borrowerDeposits amount of asset _borrower posses
function getDepositAmount(ISilo _silo, address _borrower)
external
view
returns (uint256 borrowerDeposits);
/// @notice Get amount of asset that has been borrowed
/// @dev It reads directly from storage so interest generated between last update and now is not
/// taken for account
/// @param _silo Silo address from which to read data
/// @return amount of asset that has been borrowed
function totalBorrowAmount(ISilo _silo) external view returns (uint256);
/// @notice Get totalSupply of debt token
/// @dev Debt token represents a share in total debt of given asset
/// @param _silo Silo address from which to read data
/// @return totalSupply of debt token
function totalBorrowShare(ISilo _silo) external view returns (uint256);
/// @notice Calculates current borrow amount for user with interest
/// @param _silo Silo address from which to read data
/// @param _borrower account for which calculation are done
/// @return total amount of asset user needs to repay at provided timestamp
function getBorrowAmount(ISilo _silo, address _borrower)
external
view
returns (uint256);
/// @notice Get debt token balance of a user
/// @dev Debt token represents a share in total debt of given asset.
/// This method calls balanceOf(_borrower) on that token.
/// @param _silo Silo address from which to read data
/// @param _borrower wallet address for which to read data
/// @return balance of debt token of given user
function borrowShare(ISilo _silo, address _borrower) external view returns (uint256);
/// @notice Get amount of fees earned by protocol to date
/// @dev It reads directly from storage so interest generated between last update and now is not
/// taken for account. In SiloLens v1 this was total (ever growing) amount, in this one is since last withdraw.
/// @param _silo Silo address from which to read data
/// @return amount of fees earned by protocol to date since last withdraw
function protocolFees(ISilo _silo) external view returns (uint256);
/// @notice Calculate value of collateral asset for user
/// @dev It dynamically adds interest earned. Takes for account protected deposits as well.
/// In v1 result is always in 18 decimals, here it depends on oracle setup.
/// @param _siloConfig Market address (silo config address)
/// @param _borrower account for which calculation are done
/// @return value of collateral denominated in quote token, decimal depends on oracle setup.
function calculateCollateralValue(ISiloConfig _siloConfig, address _borrower) external view returns (uint256);
/// @notice Calculate value of borrowed asset by user
/// @dev It dynamically adds interest earned to borrowed amount
/// In v1 result is always in 18 decimals, here it depends on oracle setup.
/// @param _siloConfig Market address (silo config address)
/// @param _borrower account for which calculation are done
/// @return value of debt denominated in quote token, decimal depends on oracle setup.
function calculateBorrowValue(ISiloConfig _siloConfig, address _borrower) external view returns (uint256);
/// @notice Calculates fraction between borrowed amount and the current liquidity of tokens for given asset
/// denominated in percentage
/// @dev [v1 NOT compatible] Utilization is calculated current values in storage so it does not take for account
/// earned interest and ever-increasing total borrow amount. It assumes `Model.DP()` = 100%.
/// @param _silo Silo address from which to read data
/// @return utilization value
function getUtilization(ISilo _silo) external view returns (uint256);
/// @notice Retrieves the interest rate model
/// @param _silo Address of the silo
/// @return irm InterestRateModel contract address
function getInterestRateModel(ISilo _silo) external view returns (address irm);
/// @notice Calculates current borrow interest rate
/// @param _silo Address of the silo
/// @return borrowAPR The interest rate value in 18 decimals points. 10**18 is equal to 100% per year
function getBorrowAPR(ISilo _silo) external view returns (uint256 borrowAPR);
/// @notice Calculates current deposit interest rate.
/// @param _silo Address of the silo
/// @return depositAPR The interest rate value in 18 decimals points. 10**18 is equal to 100% per year.
function getDepositAPR(ISilo _silo) external view returns (uint256 depositAPR);
/// @notice Calculates current deposit and borrow interest rates (bulk method).
/// @param _silos Addresses of the silos
/// @return aprs The interest rate values in 18 decimals points. 10**18 is equal to 100% per year.
function getAPRs(ISilo[] calldata _silos) external view returns (APR[] memory aprs);
/// @dev gets interest rates model object
/// @param _silo Silo address from which to read data
/// @return IInterestRateModel interest rates model object
function getModel(ISilo _silo) external view returns (IInterestRateModel);
/// @notice Get names of all programs in Silo incentives controller
/// @param _siloIncentivesController Address of the Silo incentives controller
/// @return programsNames Names of all programs in Silo incentives controller
function getSiloIncentivesControllerProgramsNames(
address _siloIncentivesController
) external view returns (string[] memory programsNames);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
import {IERC20Metadata} from "openzeppelin5/token/ERC20/extensions/IERC20Metadata.sol";
import {ISiloConfig} from "./ISiloConfig.sol";
import {ISilo} from "./ISilo.sol";
interface IShareToken is IERC20Metadata {
struct HookSetup {
/// @param this is the same as in siloConfig
address hookReceiver;
/// @param hooks bitmap
uint24 hooksBefore;
/// @param hooks bitmap
uint24 hooksAfter;
/// @param tokenType must be one of this hooks values: COLLATERAL_TOKEN, PROTECTED_TOKEN, DEBT_TOKEN
uint24 tokenType;
}
struct ShareTokenStorage {
/// @notice Silo address for which tokens was deployed
ISilo silo;
/// @dev cached silo config address
ISiloConfig siloConfig;
/// @notice Copy of hooks setup from SiloConfig for optimisation purposes
HookSetup hookSetup;
bool transferWithChecks;
}
/// @notice Emitted every time receiver is notified about token transfer
/// @param notificationReceiver receiver address
/// @param success false if TX reverted on `notificationReceiver` side, otherwise true
event NotificationSent(address indexed notificationReceiver, bool success);
error OnlySilo();
error OnlySiloConfig();
error OwnerIsZero();
error RecipientIsZero();
error AmountExceedsAllowance();
error RecipientNotSolventAfterTransfer();
error SenderNotSolventAfterTransfer();
error ZeroTransfer();
/// @notice method for SiloConfig to synchronize hooks
/// @param _hooksBefore hooks bitmap to trigger hooks BEFORE action
/// @param _hooksAfter hooks bitmap to trigger hooks AFTER action
function synchronizeHooks(uint24 _hooksBefore, uint24 _hooksAfter) external;
/// @notice Mint method for Silo to create debt
/// @param _owner wallet for which to mint token
/// @param _spender wallet that asks for mint
/// @param _amount amount of token to be minted
function mint(address _owner, address _spender, uint256 _amount) external;
/// @notice Burn method for Silo to close debt
/// @param _owner wallet for which to burn token
/// @param _spender wallet that asks for burn
/// @param _amount amount of token to be burned
function burn(address _owner, address _spender, uint256 _amount) external;
/// @notice TransferFrom method for liquidation
/// @param _from wallet from which we transferring tokens
/// @param _to wallet that will get tokens
/// @param _amount amount of token to transfer
function forwardTransferFromNoChecks(address _from, address _to, uint256 _amount) external;
/// @dev Returns the amount of tokens owned by `account`.
/// @param _account address for which to return data
/// @return balance of the _account
/// @return totalSupply total supply of the token
function balanceOfAndTotalSupply(address _account) external view returns (uint256 balance, uint256 totalSupply);
/// @notice Returns silo address for which token was deployed
/// @return silo address
function silo() external view returns (ISilo silo);
function siloConfig() external view returns (ISiloConfig silo);
/// @notice Returns hook setup
function hookSetup() external view returns (HookSetup memory);
/// @notice Returns hook receiver address
function hookReceiver() external view returns (address);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
interface IPartialLiquidation {
struct HookSetup {
/// @param this is the same as in siloConfig
address hookReceiver;
/// @param hooks bitmap
uint24 hooksBefore;
/// @param hooks bitmap
uint24 hooksAfter;
}
/// @dev Emitted when a borrower is liquidated.
/// @param liquidator The address of the liquidator
/// @param silo The address of the silo on which position was liquidated
/// @param borrower The address of the borrower
/// @param repayDebtAssets Repay amount
/// @param withdrawCollateral Total (collateral + protected) withdraw amount, in case `receiveSToken` is TRUE
/// then this is estimated withdraw, and representation of this amount in sToken was transferred
/// @param receiveSToken True if the liquidators wants to receive the collateral sTokens, `false` if he wants
/// to receive the underlying collateral asset directly
event LiquidationCall(
address indexed liquidator,
address indexed silo,
address indexed borrower,
uint256 repayDebtAssets,
uint256 withdrawCollateral,
bool receiveSToken
);
error UnexpectedCollateralToken();
error UnexpectedDebtToken();
error NoDebtToCover();
error FullLiquidationRequired();
error UserIsSolvent();
error UnknownRatio();
error NoRepayAssets();
/// @notice Function to liquidate insolvent position
/// - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives
/// an equivalent amount in `collateralAsset` plus a liquidation fee to cover market risk
/// @dev this method reverts when:
/// - `_maxDebtToCover` is zero
/// - `_collateralAsset` is not `_user` collateral token (note, that user can have both tokens in Silo, but only one
/// is for backing debt
/// - `_debtAsset` is not a token that `_user` borrow
/// - `_user` is solvent and there is no debt to cover
/// - `_maxDebtToCover` is set to cover only part of the debt but full liquidation is required
/// - when not enough liquidity to transfer from `_user` collateral to liquidator
/// (use `_receiveSToken == true` in that case)
/// @param _collateralAsset The address of the underlying asset used as collateral, to receive as result
/// @param _debtAsset The address of the underlying borrowed asset to be repaid with the liquidation
/// @param _user The address of the borrower getting liquidated
/// @param _maxDebtToCover The maximum debt amount of borrowed `asset` the liquidator wants to cover,
/// in case this amount is too big, it will be reduced to maximum allowed liquidation amount
/// @param _receiveSToken True if the liquidators wants to receive the collateral sTokens, `false` if he wants
/// to receive the underlying collateral asset directly
/// @return withdrawCollateral collateral that was send to `msg.sender`, in case of `_receiveSToken` is TRUE,
/// `withdrawCollateral` will be estimated, on redeem one can expect this value to be rounded down
/// @return repayDebtAssets actual debt value that was repaid by `msg.sender`
function liquidationCall(
address _collateralAsset,
address _debtAsset,
address _user,
uint256 _maxDebtToCover,
bool _receiveSToken
)
external
returns (uint256 withdrawCollateral, uint256 repayDebtAssets);
/// @dev debt is keep growing over time, so when dApp use this view to calculate max, tx should never revert
/// because actual max can be only higher
/// @return collateralToLiquidate underestimated amount of collateral liquidator will get
/// @return debtToRepay debt amount needed to be repay to get `collateralToLiquidate`
/// @return sTokenRequired TRUE, when liquidation with underlying asset is not possible because of not enough
/// liquidity
function maxLiquidation(address _borrower)
external
view
returns (uint256 collateralToLiquidate, uint256 debtToRepay, bool sTokenRequired);
}// SPDX-License-Identifier: MIT
pragma solidity >=0.5.0;
interface IInterestRateModel {
event InterestRateModelError();
/// @dev Sets config address for all Silos that will use this model
/// @param _irmConfig address of IRM config contract
function initialize(address _irmConfig) external;
/// @dev get compound interest rate and update model storage for current block.timestamp
/// @param _collateralAssets total silo collateral assets
/// @param _debtAssets total silo debt assets
/// @param _interestRateTimestamp last IRM timestamp
/// @return rcomp compounded interest rate from last update until now (1e18 == 100%)
function getCompoundInterestRateAndUpdate(
uint256 _collateralAssets,
uint256 _debtAssets,
uint256 _interestRateTimestamp
)
external
returns (uint256 rcomp);
/// @dev get compound interest rate
/// @param _silo address of Silo for which interest rate should be calculated
/// @param _blockTimestamp current block timestamp
/// @return rcomp compounded interest rate from last update until now (1e18 == 100%)
function getCompoundInterestRate(address _silo, uint256 _blockTimestamp)
external
view
returns (uint256 rcomp);
/// @dev get current annual interest rate
/// @param _silo address of Silo for which interest rate should be calculated
/// @param _blockTimestamp current block timestamp
/// @return rcur current annual interest rate (1e18 == 100%)
function getCurrentInterestRate(address _silo, uint256 _blockTimestamp)
external
view
returns (uint256 rcur);
/// @dev returns decimal points used by model
function decimals() external view returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {ISilo} from "../interfaces/ISilo.sol";
import {IShareToken} from "../interfaces/IShareToken.sol";
import {ISiloConfig} from "../interfaces/ISiloConfig.sol";
import {IInterestRateModel} from "../interfaces/IInterestRateModel.sol";
import {SiloSolvencyLib} from "./SiloSolvencyLib.sol";
import {SiloMathLib} from "./SiloMathLib.sol";
library SiloLensLib {
uint256 internal constant _PRECISION_DECIMALS = 1e18;
function getRawLiquidity(ISilo _silo) internal view returns (uint256 liquidity) {
return SiloMathLib.liquidity(
_silo.getTotalAssetsStorage(ISilo.AssetType.Collateral),
_silo.getTotalAssetsStorage(ISilo.AssetType.Debt)
);
}
function getMaxLtv(ISilo _silo) internal view returns (uint256 maxLtv) {
maxLtv = _silo.config().getConfig(address(_silo)).maxLtv;
}
function getLt(ISilo _silo) internal view returns (uint256 lt) {
lt = _silo.config().getConfig(address(_silo)).lt;
}
function getInterestRateModel(ISilo _silo) internal view returns (address irm) {
irm = _silo.config().getConfig(address(_silo)).interestRateModel;
}
function getBorrowAPR(ISilo _silo) internal view returns (uint256 borrowAPR) {
IInterestRateModel model = IInterestRateModel(_silo.config().getConfig((address(_silo))).interestRateModel);
borrowAPR = model.getCurrentInterestRate(address(_silo), block.timestamp);
}
function getDepositAPR(ISilo _silo) internal view returns (uint256 depositAPR) {
uint256 collateralAssets = _silo.getCollateralAssets();
if (collateralAssets == 0) {
return 0;
}
ISiloConfig.ConfigData memory cfg = _silo.config().getConfig((address(_silo)));
depositAPR = getBorrowAPR(_silo) * _silo.getDebtAssets() / collateralAssets;
depositAPR = depositAPR * (_PRECISION_DECIMALS - cfg.daoFee - cfg.deployerFee) / _PRECISION_DECIMALS;
}
function getLtv(ISilo _silo, address _borrower) internal view returns (uint256 ltv) {
(
ISiloConfig.ConfigData memory collateralConfig,
ISiloConfig.ConfigData memory debtConfig
) = _silo.config().getConfigsForSolvency(_borrower);
if (debtConfig.silo != address(0)) {
ltv = SiloSolvencyLib.getLtv(
collateralConfig,
debtConfig,
_borrower,
ISilo.OracleType.Solvency,
ISilo.AccrueInterestInMemory.Yes,
IShareToken(debtConfig.debtShareToken).balanceOf(_borrower)
);
}
}
function getUserLt(ISilo _silo, address _borrower) internal view returns (uint256 lt) {
(
ISiloConfig.ConfigData memory collateralConfig,
ISiloConfig.ConfigData memory debtConfig
) = _silo.config().getConfigsForSolvency(_borrower);
if (debtConfig.silo != address(0)) lt = collateralConfig.lt;
}
function getLtvAndLt(ISilo _silo, address _borrower) internal view returns (uint256 ltv, uint256 lt) {
(
ISiloConfig.ConfigData memory collateralConfig,
ISiloConfig.ConfigData memory debtConfig
) = _silo.config().getConfigsForSolvency(_borrower);
if (debtConfig.silo != address(0)) {
ltv = SiloSolvencyLib.getLtv(
collateralConfig,
debtConfig,
_borrower,
ISilo.OracleType.Solvency,
ISilo.AccrueInterestInMemory.Yes,
IShareToken(debtConfig.debtShareToken).balanceOf(_borrower)
);
lt = collateralConfig.lt;
}
}
function hasPosition(ISiloConfig _siloConfig, address _borrower) internal view returns (bool has) {
(address silo0, address silo1) = _siloConfig.getSilos();
ISiloConfig.ConfigData memory cfg0 = _siloConfig.getConfig(silo0);
ISiloConfig.ConfigData memory cfg1 = _siloConfig.getConfig(silo1);
if (IShareToken(cfg0.collateralShareToken).balanceOf(_borrower) != 0) return true;
if (IShareToken(cfg0.protectedShareToken).balanceOf(_borrower) != 0) return true;
if (IShareToken(cfg1.collateralShareToken).balanceOf(_borrower) != 0) return true;
if (IShareToken(cfg1.protectedShareToken).balanceOf(_borrower) != 0) return true;
if (IShareToken(cfg0.debtShareToken).balanceOf(_borrower) != 0) return true;
if (IShareToken(cfg1.debtShareToken).balanceOf(_borrower) != 0) return true;
return false;
}
function inDebt(ISiloConfig _siloConfig, address _borrower) internal view returns (bool has) {
(, ISiloConfig.ConfigData memory debtConfig) = _siloConfig.getConfigsForSolvency(_borrower);
has = debtConfig.debtShareToken != address(0)
&& IShareToken(debtConfig.debtShareToken).balanceOf(_borrower) != 0;
}
function collateralBalanceOfUnderlying(ISilo _silo, address _borrower)
internal
view
returns (uint256 borrowerCollateral)
{
(
address protectedShareToken, address collateralShareToken,
) = _silo.config().getShareTokens(address(_silo));
uint256 protectedShareBalance = IShareToken(protectedShareToken).balanceOf(_borrower);
uint256 collateralShareBalance = IShareToken(collateralShareToken).balanceOf(_borrower);
if (protectedShareBalance != 0) {
borrowerCollateral = _silo.previewRedeem(protectedShareBalance, ISilo.CollateralType.Protected);
}
if (collateralShareBalance != 0) {
borrowerCollateral += _silo.previewRedeem(collateralShareBalance, ISilo.CollateralType.Collateral);
}
}
function totalBorrowShare(ISilo _silo) internal view returns (uint256) {
(,, address debtShareToken) = _silo.config().getShareTokens(address(_silo));
return IShareToken(debtShareToken).totalSupply();
}
function borrowShare(ISilo _silo, address _borrower) external view returns (uint256) {
(,, address debtShareToken) = _silo.config().getShareTokens(address(_silo));
return IShareToken(debtShareToken).balanceOf(_borrower);
}
function calculateValues(ISiloConfig _siloConfig, address _borrower)
internal
view
returns (uint256 sumOfBorrowerCollateralValue, uint256 totalBorrowerDebtValue)
{
(
ISiloConfig.ConfigData memory collateralConfig,
ISiloConfig.ConfigData memory debtConfig
) = _siloConfig.getConfigsForSolvency(_borrower);
// if no debt collateralConfig and debtConfig are empty
if (collateralConfig.token == address(0)) return (0, 0);
SiloSolvencyLib.LtvData memory ltvData = SiloSolvencyLib.getAssetsDataForLtvCalculations(
collateralConfig,
debtConfig,
_borrower,
ISilo.OracleType.Solvency,
ISilo.AccrueInterestInMemory.Yes,
IShareToken(debtConfig.debtShareToken).balanceOf(_borrower)
);
(
sumOfBorrowerCollateralValue, totalBorrowerDebtValue,
) = SiloSolvencyLib.calculateLtv(ltvData, collateralConfig.token, debtConfig.token);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {SafeERC20} from "openzeppelin5/token/ERC20/utils/SafeERC20.sol";
import {IERC20} from "openzeppelin5/token/ERC20/IERC20.sol";
import {ISiloConfig} from "../interfaces/ISiloConfig.sol";
import {ISilo} from "../interfaces/ISilo.sol";
import {IInterestRateModel} from "../interfaces/IInterestRateModel.sol";
import {IShareToken} from "../interfaces/IShareToken.sol";
import {SiloMathLib} from "./SiloMathLib.sol";
library SiloStdLib {
using SafeERC20 for IERC20;
uint256 internal constant _PRECISION_DECIMALS = 1e18;
/// @notice Returns flash fee amount
/// @param _config address of config contract for Silo
/// @param _token for which fee is calculated
/// @param _amount for which fee is calculated
/// @return fee flash fee amount
function flashFee(ISiloConfig _config, address _token, uint256 _amount) internal view returns (uint256 fee) {
if (_amount == 0) return 0;
// all user set fees are in 18 decimals points
(,, uint256 flashloanFee, address asset) = _config.getFeesWithAsset(address(this));
require(_token == asset, ISilo.UnsupportedFlashloanToken());
if (flashloanFee == 0) return 0;
require(type(uint256).max / _amount >= flashloanFee, ISilo.FlashloanAmountTooBig());
fee = _amount * flashloanFee / _PRECISION_DECIMALS;
// round up
if (fee == 0) return 1;
}
/// @notice Returns totalAssets and totalShares for conversion math (convertToAssets and convertToShares)
/// @dev This is useful for view functions that do not accrue interest before doing calculations. To work on
/// updated numbers, interest should be added on the fly.
/// @param _configData for a single token for which to do calculations
/// @param _assetType used to read proper storage data
/// @return totalAssets total assets in Silo with interest for given asset type
/// @return totalShares total shares in Silo for given asset type
function getTotalAssetsAndTotalSharesWithInterest(
ISiloConfig.ConfigData memory _configData,
ISilo.AssetType _assetType
)
internal
view
returns (uint256 totalAssets, uint256 totalShares)
{
if (_assetType == ISilo.AssetType.Protected) {
totalAssets = ISilo(_configData.silo).getTotalAssetsStorage(ISilo.AssetType.Protected);
totalShares = IShareToken(_configData.protectedShareToken).totalSupply();
} else if (_assetType == ISilo.AssetType.Collateral) {
totalAssets = getTotalCollateralAssetsWithInterest(
_configData.silo,
_configData.interestRateModel,
_configData.daoFee,
_configData.deployerFee
);
totalShares = IShareToken(_configData.collateralShareToken).totalSupply();
} else { // ISilo.AssetType.Debt
totalAssets = getTotalDebtAssetsWithInterest(_configData.silo, _configData.interestRateModel);
totalShares = IShareToken(_configData.debtShareToken).totalSupply();
}
}
/// @notice Retrieves fee amounts in 18 decimals points and their respective receivers along with the asset
/// @param _silo Silo address
/// @return daoFeeReceiver Address of the DAO fee receiver
/// @return deployerFeeReceiver Address of the deployer fee receiver
/// @return daoFee DAO fee amount in 18 decimals points
/// @return deployerFee Deployer fee amount in 18 decimals points
/// @return asset Address of the associated asset
function getFeesAndFeeReceiversWithAsset(ISilo _silo)
internal
view
returns (
address daoFeeReceiver,
address deployerFeeReceiver,
uint256 daoFee,
uint256 deployerFee,
address asset
)
{
(daoFee, deployerFee,, asset) = _silo.config().getFeesWithAsset(address(_silo));
(daoFeeReceiver, deployerFeeReceiver) = _silo.factory().getFeeReceivers(address(_silo));
}
/// @notice Calculates the total collateral assets with accrued interest
/// @dev Do not use this method when accrueInterest were executed already, in that case total does not change
/// @param _silo Address of the silo contract
/// @param _interestRateModel Interest rate model to fetch compound interest rates
/// @param _daoFee DAO fee in 18 decimals points
/// @param _deployerFee Deployer fee in 18 decimals points
/// @return totalCollateralAssetsWithInterest Accumulated collateral amount with interest
function getTotalCollateralAssetsWithInterest(
address _silo,
address _interestRateModel,
uint256 _daoFee,
uint256 _deployerFee
) internal view returns (uint256 totalCollateralAssetsWithInterest) {
uint256 rcomp;
try IInterestRateModel(_interestRateModel).getCompoundInterestRate(_silo, block.timestamp) returns (uint256 r) {
rcomp = r;
} catch {
// do not lock silo
}
(uint256 collateralAssets, uint256 debtAssets) = ISilo(_silo).getCollateralAndDebtTotalsStorage();
(totalCollateralAssetsWithInterest,,,) = SiloMathLib.getCollateralAmountsWithInterest({
_collateralAssets: collateralAssets,
_debtAssets: debtAssets,
_rcomp: rcomp,
_daoFee: _daoFee,
_deployerFee: _deployerFee
});
}
/// @param _balanceCached if balance of `_owner` is unknown beforehand, then pass `0`
function getSharesAndTotalSupply(address _shareToken, address _owner, uint256 _balanceCached)
internal
view
returns (uint256 shares, uint256 totalSupply)
{
if (_balanceCached == 0) {
(shares, totalSupply) = IShareToken(_shareToken).balanceOfAndTotalSupply(_owner);
} else {
shares = _balanceCached;
totalSupply = IShareToken(_shareToken).totalSupply();
}
}
/// @notice Calculates the total debt assets with accrued interest
/// @param _silo Address of the silo contract
/// @param _interestRateModel Interest rate model to fetch compound interest rates
/// @return totalDebtAssetsWithInterest Accumulated debt amount with interest
function getTotalDebtAssetsWithInterest(address _silo, address _interestRateModel)
internal
view
returns (uint256 totalDebtAssetsWithInterest)
{
uint256 rcomp;
try IInterestRateModel(_interestRateModel).getCompoundInterestRate(_silo, block.timestamp) returns (uint256 r) {
rcomp = r;
} catch {
// do not lock silo
}
(
totalDebtAssetsWithInterest,
) = SiloMathLib.getDebtAmountsWithInterest(ISilo(_silo).getTotalAssetsStorage(ISilo.AssetType.Debt), rcomp);
}
}// SPDX-License-Identifier: agpl-3.0
pragma solidity 0.8.28;
import {DistributionTypes} from "../lib/DistributionTypes.sol";
interface IDistributionManager {
struct IncentivesProgram {
uint256 index;
address rewardToken; // can't be updated after creation
uint104 emissionPerSecond; // configured by owner
uint40 lastUpdateTimestamp;
uint40 distributionEnd; // configured by owner
mapping(address user => uint256 userIndex) users;
}
struct IncentiveProgramDetails {
uint256 index;
address rewardToken;
uint104 emissionPerSecond;
uint40 lastUpdateTimestamp;
uint40 distributionEnd;
}
struct AccruedRewards {
uint256 amount;
bytes32 programId;
address rewardToken;
}
event AssetConfigUpdated(address indexed asset, uint256 emission);
event AssetIndexUpdated(address indexed asset, uint256 index);
event DistributionEndUpdated(string incentivesProgram, uint256 newDistributionEnd);
event IncentivesProgramIndexUpdated(string incentivesProgram, uint256 newIndex);
event UserIndexUpdated(address indexed user, string incentivesProgram, uint256 newIndex);
error OnlyNotifier();
error TooLongProgramName();
error InvalidIncentivesProgramName();
error OnlyNotifierOrOwner();
error ZeroAddress();
/**
* @dev Sets the end date for the distribution
* @param _incentivesProgram The incentives program name
* @param _distributionEnd The end date timestamp
*/
function setDistributionEnd(string calldata _incentivesProgram, uint40 _distributionEnd) external;
/**
* @dev Gets the end date for the distribution
* @param _incentivesProgram The incentives program name
* @return The end of the distribution
*/
function getDistributionEnd(string calldata _incentivesProgram) external view returns (uint256);
/**
* @dev Returns the data of an user on a distribution
* @param _user Address of the user
* @param _incentivesProgram The incentives program name
* @return The new index
*/
function getUserData(address _user, string calldata _incentivesProgram) external view returns (uint256);
/**
* @dev Returns the configuration of the distribution for a certain incentives program
* @param _incentivesProgram The incentives program name
* @return details The configuration of the incentives program
*/
function incentivesProgram(string calldata _incentivesProgram)
external
view
returns (IncentiveProgramDetails memory details);
/**
* @dev Returns the program id for the given program name.
* This method TRUNCATES the program name to 32 bytes.
* If provided strings only differ after the 32nd byte they would result in the same ProgramId.
* Ensure to use inputs that will result in 32 bytes or less.
* @param _programName The incentives program name
* @return programId
*/
function getProgramId(string calldata _programName) external pure returns (bytes32 programId);
/**
* @dev returns the names of all the incentives programs
* @return programsNames the names of all the incentives programs
*/
function getAllProgramsNames() external view returns (string[] memory programsNames);
/**
* @dev returns the name of an incentives program
* @param _programName the name (bytes32) of the incentives program
* @return programName the name (string) of the incentives program
*/
function getProgramName(bytes32 _programName) external view returns (string memory programName);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)
pragma solidity ^0.8.20;
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract Context {
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
function _contextSuffixLength() internal view virtual returns (uint256) {
return 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (interfaces/draft-IERC6093.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard ERC-20 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-20 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 ERC-721 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-721 tokens.
*/
interface IERC721Errors {
/**
* @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in ERC-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 ERC-1155 Errors
* Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC-1155 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.3.0) (interfaces/IERC4626.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface of the ERC-4626 "Tokenized Vault Standard", as defined in
* https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
*/
interface IERC4626 is IERC20, IERC20Metadata {
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed sender,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
/**
* @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
*
* - MUST be an ERC-20 token contract.
* - MUST NOT revert.
*/
function asset() external view returns (address assetTokenAddress);
/**
* @dev Returns the total amount of the underlying asset that is “managed” by Vault.
*
* - SHOULD include any compounding that occurs from yield.
* - MUST be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT revert.
*/
function totalAssets() external view returns (uint256 totalManagedAssets);
/**
* @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
* through a deposit call.
*
* - MUST return a limited value if receiver is subject to some deposit limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
* - MUST NOT revert.
*/
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
* call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
* in the same transaction.
* - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
* deposit would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/**
* @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* deposit execution, and are accounted for during deposit.
* - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
* - MUST return a limited value if receiver is subject to some mint limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
* - MUST NOT revert.
*/
function maxMint(address receiver) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
* in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
* same transaction.
* - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
* would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by minting.
*/
function previewMint(uint256 shares) external view returns (uint256 assets);
/**
* @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
* execution, and are accounted for during mint.
* - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function mint(uint256 shares, address receiver) external returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
* call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
* called
* in the same transaction.
* - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
* the withdrawal would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/**
* @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* withdraw execution, and are accounted for during withdraw.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
* through a redeem call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxRedeem(address owner) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their redemption at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
* in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
* same transaction.
* - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
* redemption would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by redeeming.
*/
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/**
* @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* redeem execution, and are accounted for during redeem.
* - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC-20 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: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface IRewardManager {
function userReward(address token, address user) external view returns (uint128 index, uint128 accrued);
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
interface IPInterestManagerYT {
event CollectInterestFee(uint256 amountInterestFee);
function userInterest(address user) external view returns (uint128 lastPYIndex, uint128 accruedInterest);
}// SPDX-License-Identifier: GPL-3.0-or-later
// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
// documentation files (the “Software”), to deal in the Software without restriction, including without limitation the
// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to the following conditions:
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
// Software.
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
// WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
// COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
pragma solidity ^0.8.0;
/* solhint-disable */
/**
* @dev Exponentiation and logarithm functions for 18 decimal fixed point numbers (both base and exponent/argument).
*
* Exponentiation and logarithm with arbitrary bases (x^y and log_x(y)) are implemented by conversion to natural
* exponentiation and logarithm (where the base is Euler's number).
*
* @author Fernando Martinelli - @fernandomartinelli
* @author Sergio Yuhjtman - @sergioyuhjtman
* @author Daniel Fernandez - @dmf7z
*/
library LogExpMath {
// All fixed point multiplications and divisions are inlined. This means we need to divide by ONE when multiplying
// two numbers, and multiply by ONE when dividing them.
// All arguments and return values are 18 decimal fixed point numbers.
int256 constant ONE_18 = 1e18;
// Internally, intermediate values are computed with higher precision as 20 decimal fixed point numbers, and in the
// case of ln36, 36 decimals.
int256 constant ONE_20 = 1e20;
int256 constant ONE_36 = 1e36;
// The domain of natural exponentiation is bound by the word size and number of decimals used.
//
// Because internally the result will be stored using 20 decimals, the largest possible result is
// (2^255 - 1) / 10^20, which makes the largest exponent ln((2^255 - 1) / 10^20) = 130.700829182905140221.
// The smallest possible result is 10^(-18), which makes largest negative argument
// ln(10^(-18)) = -41.446531673892822312.
// We use 130.0 and -41.0 to have some safety margin.
int256 constant MAX_NATURAL_EXPONENT = 130e18;
int256 constant MIN_NATURAL_EXPONENT = -41e18;
// Bounds for ln_36's argument. Both ln(0.9) and ln(1.1) can be represented with 36 decimal places in a fixed point
// 256 bit integer.
int256 constant LN_36_LOWER_BOUND = ONE_18 - 1e17;
int256 constant LN_36_UPPER_BOUND = ONE_18 + 1e17;
uint256 constant MILD_EXPONENT_BOUND = 2 ** 254 / uint256(ONE_20);
// 18 decimal constants
int256 constant x0 = 128000000000000000000; // 2ˆ7
int256 constant a0 = 38877084059945950922200000000000000000000000000000000000; // eˆ(x0) (no decimals)
int256 constant x1 = 64000000000000000000; // 2ˆ6
int256 constant a1 = 6235149080811616882910000000; // eˆ(x1) (no decimals)
// 20 decimal constants
int256 constant x2 = 3200000000000000000000; // 2ˆ5
int256 constant a2 = 7896296018268069516100000000000000; // eˆ(x2)
int256 constant x3 = 1600000000000000000000; // 2ˆ4
int256 constant a3 = 888611052050787263676000000; // eˆ(x3)
int256 constant x4 = 800000000000000000000; // 2ˆ3
int256 constant a4 = 298095798704172827474000; // eˆ(x4)
int256 constant x5 = 400000000000000000000; // 2ˆ2
int256 constant a5 = 5459815003314423907810; // eˆ(x5)
int256 constant x6 = 200000000000000000000; // 2ˆ1
int256 constant a6 = 738905609893065022723; // eˆ(x6)
int256 constant x7 = 100000000000000000000; // 2ˆ0
int256 constant a7 = 271828182845904523536; // eˆ(x7)
int256 constant x8 = 50000000000000000000; // 2ˆ-1
int256 constant a8 = 164872127070012814685; // eˆ(x8)
int256 constant x9 = 25000000000000000000; // 2ˆ-2
int256 constant a9 = 128402541668774148407; // eˆ(x9)
int256 constant x10 = 12500000000000000000; // 2ˆ-3
int256 constant a10 = 113314845306682631683; // eˆ(x10)
int256 constant x11 = 6250000000000000000; // 2ˆ-4
int256 constant a11 = 106449445891785942956; // eˆ(x11)
/**
* @dev Natural exponentiation (e^x) with signed 18 decimal fixed point exponent.
*
* Reverts if `x` is smaller than MIN_NATURAL_EXPONENT, or larger than `MAX_NATURAL_EXPONENT`.
*/
function exp(int256 x) internal pure returns (int256) {
unchecked {
require(x >= MIN_NATURAL_EXPONENT && x <= MAX_NATURAL_EXPONENT, "Invalid exponent");
if (x < 0) {
// We only handle positive exponents: e^(-x) is computed as 1 / e^x. We can safely make x positive since it
// fits in the signed 256 bit range (as it is larger than MIN_NATURAL_EXPONENT).
// Fixed point division requires multiplying by ONE_18.
return ((ONE_18 * ONE_18) / exp(-x));
}
// First, we use the fact that e^(x+y) = e^x * e^y to decompose x into a sum of powers of two, which we call x_n,
// where x_n == 2^(7 - n), and e^x_n = a_n has been precomputed. We choose the first x_n, x0, to equal 2^7
// because all larger powers are larger than MAX_NATURAL_EXPONENT, and therefore not present in the
// decomposition.
// At the end of this process we will have the product of all e^x_n = a_n that apply, and the remainder of this
// decomposition, which will be lower than the smallest x_n.
// exp(x) = k_0 * a_0 * k_1 * a_1 * ... + k_n * a_n * exp(remainder), where each k_n equals either 0 or 1.
// We mutate x by subtracting x_n, making it the remainder of the decomposition.
// The first two a_n (e^(2^7) and e^(2^6)) are too large if stored as 18 decimal numbers, and could cause
// intermediate overflows. Instead we store them as plain integers, with 0 decimals.
// Additionally, x0 + x1 is larger than MAX_NATURAL_EXPONENT, which means they will not both be present in the
// decomposition.
// For each x_n, we test if that term is present in the decomposition (if x is larger than it), and if so deduct
// it and compute the accumulated product.
int256 firstAN;
if (x >= x0) {
x -= x0;
firstAN = a0;
} else if (x >= x1) {
x -= x1;
firstAN = a1;
} else {
firstAN = 1; // One with no decimal places
}
// We now transform x into a 20 decimal fixed point number, to have enhanced precision when computing the
// smaller terms.
x *= 100;
// `product` is the accumulated product of all a_n (except a0 and a1), which starts at 20 decimal fixed point
// one. Recall that fixed point multiplication requires dividing by ONE_20.
int256 product = ONE_20;
if (x >= x2) {
x -= x2;
product = (product * a2) / ONE_20;
}
if (x >= x3) {
x -= x3;
product = (product * a3) / ONE_20;
}
if (x >= x4) {
x -= x4;
product = (product * a4) / ONE_20;
}
if (x >= x5) {
x -= x5;
product = (product * a5) / ONE_20;
}
if (x >= x6) {
x -= x6;
product = (product * a6) / ONE_20;
}
if (x >= x7) {
x -= x7;
product = (product * a7) / ONE_20;
}
if (x >= x8) {
x -= x8;
product = (product * a8) / ONE_20;
}
if (x >= x9) {
x -= x9;
product = (product * a9) / ONE_20;
}
// x10 and x11 are unnecessary here since we have high enough precision already.
// Now we need to compute e^x, where x is small (in particular, it is smaller than x9). We use the Taylor series
// expansion for e^x: 1 + x + (x^2 / 2!) + (x^3 / 3!) + ... + (x^n / n!).
int256 seriesSum = ONE_20; // The initial one in the sum, with 20 decimal places.
int256 term; // Each term in the sum, where the nth term is (x^n / n!).
// The first term is simply x.
term = x;
seriesSum += term;
// Each term (x^n / n!) equals the previous one times x, divided by n. Since x is a fixed point number,
// multiplying by it requires dividing by ONE_20, but dividing by the non-fixed point n values does not.
term = ((term * x) / ONE_20) / 2;
seriesSum += term;
term = ((term * x) / ONE_20) / 3;
seriesSum += term;
term = ((term * x) / ONE_20) / 4;
seriesSum += term;
term = ((term * x) / ONE_20) / 5;
seriesSum += term;
term = ((term * x) / ONE_20) / 6;
seriesSum += term;
term = ((term * x) / ONE_20) / 7;
seriesSum += term;
term = ((term * x) / ONE_20) / 8;
seriesSum += term;
term = ((term * x) / ONE_20) / 9;
seriesSum += term;
term = ((term * x) / ONE_20) / 10;
seriesSum += term;
term = ((term * x) / ONE_20) / 11;
seriesSum += term;
term = ((term * x) / ONE_20) / 12;
seriesSum += term;
// 12 Taylor terms are sufficient for 18 decimal precision.
// We now have the first a_n (with no decimals), and the product of all other a_n present, and the Taylor
// approximation of the exponentiation of the remainder (both with 20 decimals). All that remains is to multiply
// all three (one 20 decimal fixed point multiplication, dividing by ONE_20, and one integer multiplication),
// and then drop two digits to return an 18 decimal value.
return (((product * seriesSum) / ONE_20) * firstAN) / 100;
}
}
/**
* @dev Natural logarithm (ln(a)) with signed 18 decimal fixed point argument.
*/
function ln(int256 a) internal pure returns (int256) {
unchecked {
// The real natural logarithm is not defined for negative numbers or zero.
require(a > 0, "out of bounds");
if (LN_36_LOWER_BOUND < a && a < LN_36_UPPER_BOUND) {
return _ln_36(a) / ONE_18;
} else {
return _ln(a);
}
}
}
/**
* @dev Exponentiation (x^y) with unsigned 18 decimal fixed point base and exponent.
*
* Reverts if ln(x) * y is smaller than `MIN_NATURAL_EXPONENT`, or larger than `MAX_NATURAL_EXPONENT`.
*/
function pow(uint256 x, uint256 y) internal pure returns (uint256) {
unchecked {
if (y == 0) {
// We solve the 0^0 indetermination by making it equal one.
return uint256(ONE_18);
}
if (x == 0) {
return 0;
}
// Instead of computing x^y directly, we instead rely on the properties of logarithms and exponentiation to
// arrive at that r`esult. In particular, exp(ln(x)) = x, and ln(x^y) = y * ln(x). This means
// x^y = exp(y * ln(x)).
// The ln function takes a signed value, so we need to make sure x fits in the signed 256 bit range.
require(x < 2 ** 255, "x out of bounds");
int256 x_int256 = int256(x);
// We will compute y * ln(x) in a single step. Depending on the value of x, we can either use ln or ln_36. In
// both cases, we leave the division by ONE_18 (due to fixed point multiplication) to the end.
// This prevents y * ln(x) from overflowing, and at the same time guarantees y fits in the signed 256 bit range.
require(y < MILD_EXPONENT_BOUND, "y out of bounds");
int256 y_int256 = int256(y);
int256 logx_times_y;
if (LN_36_LOWER_BOUND < x_int256 && x_int256 < LN_36_UPPER_BOUND) {
int256 ln_36_x = _ln_36(x_int256);
// ln_36_x has 36 decimal places, so multiplying by y_int256 isn't as straightforward, since we can't just
// bring y_int256 to 36 decimal places, as it might overflow. Instead, we perform two 18 decimal
// multiplications and add the results: one with the first 18 decimals of ln_36_x, and one with the
// (downscaled) last 18 decimals.
logx_times_y = ((ln_36_x / ONE_18) * y_int256 + ((ln_36_x % ONE_18) * y_int256) / ONE_18);
} else {
logx_times_y = _ln(x_int256) * y_int256;
}
logx_times_y /= ONE_18;
// Finally, we compute exp(y * ln(x)) to arrive at x^y
require(
MIN_NATURAL_EXPONENT <= logx_times_y && logx_times_y <= MAX_NATURAL_EXPONENT,
"product out of bounds"
);
return uint256(exp(logx_times_y));
}
}
/**
* @dev Internal natural logarithm (ln(a)) with signed 18 decimal fixed point argument.
*/
function _ln(int256 a) private pure returns (int256) {
unchecked {
if (a < ONE_18) {
// Since ln(a^k) = k * ln(a), we can compute ln(a) as ln(a) = ln((1/a)^(-1)) = - ln((1/a)). If a is less
// than one, 1/a will be greater than one, and this if statement will not be entered in the recursive call.
// Fixed point division requires multiplying by ONE_18.
return (-_ln((ONE_18 * ONE_18) / a));
}
// First, we use the fact that ln^(a * b) = ln(a) + ln(b) to decompose ln(a) into a sum of powers of two, which
// we call x_n, where x_n == 2^(7 - n), which are the natural logarithm of precomputed quantities a_n (that is,
// ln(a_n) = x_n). We choose the first x_n, x0, to equal 2^7 because the exponential of all larger powers cannot
// be represented as 18 fixed point decimal numbers in 256 bits, and are therefore larger than a.
// At the end of this process we will have the sum of all x_n = ln(a_n) that apply, and the remainder of this
// decomposition, which will be lower than the smallest a_n.
// ln(a) = k_0 * x_0 + k_1 * x_1 + ... + k_n * x_n + ln(remainder), where each k_n equals either 0 or 1.
// We mutate a by subtracting a_n, making it the remainder of the decomposition.
// For reasons related to how `exp` works, the first two a_n (e^(2^7) and e^(2^6)) are not stored as fixed point
// numbers with 18 decimals, but instead as plain integers with 0 decimals, so we need to multiply them by
// ONE_18 to convert them to fixed point.
// For each a_n, we test if that term is present in the decomposition (if a is larger than it), and if so divide
// by it and compute the accumulated sum.
int256 sum = 0;
if (a >= a0 * ONE_18) {
a /= a0; // Integer, not fixed point division
sum += x0;
}
if (a >= a1 * ONE_18) {
a /= a1; // Integer, not fixed point division
sum += x1;
}
// All other a_n and x_n are stored as 20 digit fixed point numbers, so we convert the sum and a to this format.
sum *= 100;
a *= 100;
// Because further a_n are 20 digit fixed point numbers, we multiply by ONE_20 when dividing by them.
if (a >= a2) {
a = (a * ONE_20) / a2;
sum += x2;
}
if (a >= a3) {
a = (a * ONE_20) / a3;
sum += x3;
}
if (a >= a4) {
a = (a * ONE_20) / a4;
sum += x4;
}
if (a >= a5) {
a = (a * ONE_20) / a5;
sum += x5;
}
if (a >= a6) {
a = (a * ONE_20) / a6;
sum += x6;
}
if (a >= a7) {
a = (a * ONE_20) / a7;
sum += x7;
}
if (a >= a8) {
a = (a * ONE_20) / a8;
sum += x8;
}
if (a >= a9) {
a = (a * ONE_20) / a9;
sum += x9;
}
if (a >= a10) {
a = (a * ONE_20) / a10;
sum += x10;
}
if (a >= a11) {
a = (a * ONE_20) / a11;
sum += x11;
}
// a is now a small number (smaller than a_11, which roughly equals 1.06). This means we can use a Taylor series
// that converges rapidly for values of `a` close to one - the same one used in ln_36.
// Let z = (a - 1) / (a + 1).
// ln(a) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))
// Recall that 20 digit fixed point division requires multiplying by ONE_20, and multiplication requires
// division by ONE_20.
int256 z = ((a - ONE_20) * ONE_20) / (a + ONE_20);
int256 z_squared = (z * z) / ONE_20;
// num is the numerator of the series: the z^(2 * n + 1) term
int256 num = z;
// seriesSum holds the accumulated sum of each term in the series, starting with the initial z
int256 seriesSum = num;
// In each step, the numerator is multiplied by z^2
num = (num * z_squared) / ONE_20;
seriesSum += num / 3;
num = (num * z_squared) / ONE_20;
seriesSum += num / 5;
num = (num * z_squared) / ONE_20;
seriesSum += num / 7;
num = (num * z_squared) / ONE_20;
seriesSum += num / 9;
num = (num * z_squared) / ONE_20;
seriesSum += num / 11;
// 6 Taylor terms are sufficient for 36 decimal precision.
// Finally, we multiply by 2 (non fixed point) to compute ln(remainder)
seriesSum *= 2;
// We now have the sum of all x_n present, and the Taylor approximation of the logarithm of the remainder (both
// with 20 decimals). All that remains is to sum these two, and then drop two digits to return a 18 decimal
// value.
return (sum + seriesSum) / 100;
}
}
/**
* @dev Intrnal high precision (36 decimal places) natural logarithm (ln(x)) with signed 18 decimal fixed point argument,
* for x close to one.
*
* Should only be used if x is between LN_36_LOWER_BOUND and LN_36_UPPER_BOUND.
*/
function _ln_36(int256 x) private pure returns (int256) {
unchecked {
// Since ln(1) = 0, a value of x close to one will yield a very small result, which makes using 36 digits
// worthwhile.
// First, we transform x to a 36 digit fixed point value.
x *= ONE_18;
// We will use the following Taylor expansion, which converges very rapidly. Let z = (x - 1) / (x + 1).
// ln(x) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))
// Recall that 36 digit fixed point division requires multiplying by ONE_36, and multiplication requires
// division by ONE_36.
int256 z = ((x - ONE_36) * ONE_36) / (x + ONE_36);
int256 z_squared = (z * z) / ONE_36;
// num is the numerator of the series: the z^(2 * n + 1) term
int256 num = z;
// seriesSum holds the accumulated sum of each term in the series, starting with the initial z
int256 seriesSum = num;
// In each step, the numerator is multiplied by z^2
num = (num * z_squared) / ONE_36;
seriesSum += num / 3;
num = (num * z_squared) / ONE_36;
seriesSum += num / 5;
num = (num * z_squared) / ONE_36;
seriesSum += num / 7;
num = (num * z_squared) / ONE_36;
seriesSum += num / 9;
num = (num * z_squared) / ONE_36;
seriesSum += num / 11;
num = (num * z_squared) / ONE_36;
seriesSum += num / 13;
num = (num * z_squared) / ONE_36;
seriesSum += num / 15;
// 8 Taylor terms are sufficient for 36 decimal precision.
// All that remains is multiplying by 2 (non fixed point).
return seriesSum * 2;
}
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
import "../../interfaces/IPYieldToken.sol";
import "../../interfaces/IPPrincipalToken.sol";
import "./SYUtils.sol";
import "../libraries/math/PMath.sol";
type PYIndex is uint256;
library PYIndexLib {
using PMath for uint256;
using PMath for int256;
function newIndex(IPYieldToken YT) internal returns (PYIndex) {
return PYIndex.wrap(YT.pyIndexCurrent());
}
function syToAsset(PYIndex index, uint256 syAmount) internal pure returns (uint256) {
return SYUtils.syToAsset(PYIndex.unwrap(index), syAmount);
}
function assetToSy(PYIndex index, uint256 assetAmount) internal pure returns (uint256) {
return SYUtils.assetToSy(PYIndex.unwrap(index), assetAmount);
}
function assetToSyUp(PYIndex index, uint256 assetAmount) internal pure returns (uint256) {
return SYUtils.assetToSyUp(PYIndex.unwrap(index), assetAmount);
}
function syToAssetUp(PYIndex index, uint256 syAmount) internal pure returns (uint256) {
uint256 _index = PYIndex.unwrap(index);
return SYUtils.syToAssetUp(_index, syAmount);
}
function syToAsset(PYIndex index, int256 syAmount) internal pure returns (int256) {
int256 sign = syAmount < 0 ? int256(-1) : int256(1);
return sign * (SYUtils.syToAsset(PYIndex.unwrap(index), syAmount.abs())).Int();
}
function assetToSy(PYIndex index, int256 assetAmount) internal pure returns (int256) {
int256 sign = assetAmount < 0 ? int256(-1) : int256(1);
return sign * (SYUtils.assetToSy(PYIndex.unwrap(index), assetAmount.abs())).Int();
}
function assetToSyUp(PYIndex index, int256 assetAmount) internal pure returns (int256) {
int256 sign = assetAmount < 0 ? int256(-1) : int256(1);
return sign * (SYUtils.assetToSyUp(PYIndex.unwrap(index), assetAmount.abs())).Int();
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
library MiniHelpers {
function isCurrentlyExpired(uint256 expiry) internal view returns (bool) {
return (expiry <= block.timestamp);
}
function isExpired(uint256 expiry, uint256 blockTime) internal pure returns (bool) {
return (expiry <= blockTime);
}
function isTimeInThePast(uint256 timestamp) internal view returns (bool) {
return (timestamp <= block.timestamp); // same definition as isCurrentlyExpired
}
}// SPDX-License-Identifier: GPL-3.0-or-later
pragma solidity ^0.8.0;
library Errors {
// BulkSeller
error BulkInsufficientSyForTrade(uint256 currentAmount, uint256 requiredAmount);
error BulkInsufficientTokenForTrade(uint256 currentAmount, uint256 requiredAmount);
error BulkInSufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut);
error BulkInSufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
error BulkInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance);
error BulkNotMaintainer();
error BulkNotAdmin();
error BulkSellerAlreadyExisted(address token, address SY, address bulk);
error BulkSellerInvalidToken(address token, address SY);
error BulkBadRateTokenToSy(uint256 actualRate, uint256 currentRate, uint256 eps);
error BulkBadRateSyToToken(uint256 actualRate, uint256 currentRate, uint256 eps);
// APPROX
error ApproxFail();
error ApproxParamsInvalid(uint256 guessMin, uint256 guessMax, uint256 eps);
error ApproxBinarySearchInputInvalid(
uint256 approxGuessMin,
uint256 approxGuessMax,
uint256 minGuessMin,
uint256 maxGuessMax
);
// MARKET + MARKET MATH CORE
error MarketExpired();
error MarketZeroAmountsInput();
error MarketZeroAmountsOutput();
error MarketZeroLnImpliedRate();
error MarketInsufficientPtForTrade(int256 currentAmount, int256 requiredAmount);
error MarketInsufficientPtReceived(uint256 actualBalance, uint256 requiredBalance);
error MarketInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance);
error MarketZeroTotalPtOrTotalAsset(int256 totalPt, int256 totalAsset);
error MarketExchangeRateBelowOne(int256 exchangeRate);
error MarketProportionMustNotEqualOne();
error MarketRateScalarBelowZero(int256 rateScalar);
error MarketScalarRootBelowZero(int256 scalarRoot);
error MarketProportionTooHigh(int256 proportion, int256 maxProportion);
error OracleUninitialized();
error OracleTargetTooOld(uint32 target, uint32 oldest);
error OracleZeroCardinality();
error MarketFactoryExpiredPt();
error MarketFactoryInvalidPt();
error MarketFactoryMarketExists();
error MarketFactoryLnFeeRateRootTooHigh(uint80 lnFeeRateRoot, uint256 maxLnFeeRateRoot);
error MarketFactoryOverriddenFeeTooHigh(uint80 overriddenFee, uint256 marketLnFeeRateRoot);
error MarketFactoryReserveFeePercentTooHigh(uint8 reserveFeePercent, uint8 maxReserveFeePercent);
error MarketFactoryZeroTreasury();
error MarketFactoryInitialAnchorTooLow(int256 initialAnchor, int256 minInitialAnchor);
error MFNotPendleMarket(address addr);
// ROUTER
error RouterInsufficientLpOut(uint256 actualLpOut, uint256 requiredLpOut);
error RouterInsufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut);
error RouterInsufficientPtOut(uint256 actualPtOut, uint256 requiredPtOut);
error RouterInsufficientYtOut(uint256 actualYtOut, uint256 requiredYtOut);
error RouterInsufficientPYOut(uint256 actualPYOut, uint256 requiredPYOut);
error RouterInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
error RouterInsufficientSyRepay(uint256 actualSyRepay, uint256 requiredSyRepay);
error RouterInsufficientPtRepay(uint256 actualPtRepay, uint256 requiredPtRepay);
error RouterNotAllSyUsed(uint256 netSyDesired, uint256 netSyUsed);
error RouterTimeRangeZero();
error RouterCallbackNotPendleMarket(address caller);
error RouterInvalidAction(bytes4 selector);
error RouterInvalidFacet(address facet);
error RouterKyberSwapDataZero();
error SimulationResults(bool success, bytes res);
// YIELD CONTRACT
error YCExpired();
error YCNotExpired();
error YieldContractInsufficientSy(uint256 actualSy, uint256 requiredSy);
error YCNothingToRedeem();
error YCPostExpiryDataNotSet();
error YCNoFloatingSy();
// YieldFactory
error YCFactoryInvalidExpiry();
error YCFactoryYieldContractExisted();
error YCFactoryZeroExpiryDivisor();
error YCFactoryZeroTreasury();
error YCFactoryInterestFeeRateTooHigh(uint256 interestFeeRate, uint256 maxInterestFeeRate);
error YCFactoryRewardFeeRateTooHigh(uint256 newRewardFeeRate, uint256 maxRewardFeeRate);
// SY
error SYInvalidTokenIn(address token);
error SYInvalidTokenOut(address token);
error SYZeroDeposit();
error SYZeroRedeem();
error SYInsufficientSharesOut(uint256 actualSharesOut, uint256 requiredSharesOut);
error SYInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut);
// SY-specific
error SYQiTokenMintFailed(uint256 errCode);
error SYQiTokenRedeemFailed(uint256 errCode);
error SYQiTokenRedeemRewardsFailed(uint256 rewardAccruedType0, uint256 rewardAccruedType1);
error SYQiTokenBorrowRateTooHigh(uint256 borrowRate, uint256 borrowRateMax);
error SYCurveInvalidPid();
error SYCurve3crvPoolNotFound();
error SYApeDepositAmountTooSmall(uint256 amountDeposited);
error SYBalancerInvalidPid();
error SYInvalidRewardToken(address token);
error SYStargateRedeemCapExceeded(uint256 amountLpDesired, uint256 amountLpRedeemable);
error SYBalancerReentrancy();
error NotFromTrustedRemote(uint16 srcChainId, bytes path);
error ApxETHNotEnoughBuffer();
// Liquidity Mining
error VCInactivePool(address pool);
error VCPoolAlreadyActive(address pool);
error VCZeroVePendle(address user);
error VCExceededMaxWeight(uint256 totalWeight, uint256 maxWeight);
error VCEpochNotFinalized(uint256 wTime);
error VCPoolAlreadyAddAndRemoved(address pool);
error VEInvalidNewExpiry(uint256 newExpiry);
error VEExceededMaxLockTime();
error VEInsufficientLockTime();
error VENotAllowedReduceExpiry();
error VEZeroAmountLocked();
error VEPositionNotExpired();
error VEZeroPosition();
error VEZeroSlope(uint128 bias, uint128 slope);
error VEReceiveOldSupply(uint256 msgTime);
error GCNotPendleMarket(address caller);
error GCNotVotingController(address caller);
error InvalidWTime(uint256 wTime);
error ExpiryInThePast(uint256 expiry);
error ChainNotSupported(uint256 chainId);
error FDTotalAmountFundedNotMatch(uint256 actualTotalAmount, uint256 expectedTotalAmount);
error FDEpochLengthMismatch();
error FDInvalidPool(address pool);
error FDPoolAlreadyExists(address pool);
error FDInvalidNewFinishedEpoch(uint256 oldFinishedEpoch, uint256 newFinishedEpoch);
error FDInvalidStartEpoch(uint256 startEpoch);
error FDInvalidWTimeFund(uint256 lastFunded, uint256 wTime);
error FDFutureFunding(uint256 lastFunded, uint256 currentWTime);
error BDInvalidEpoch(uint256 epoch, uint256 startTime);
// Cross-Chain
error MsgNotFromSendEndpoint(uint16 srcChainId, bytes path);
error MsgNotFromReceiveEndpoint(address sender);
error InsufficientFeeToSendMsg(uint256 currentFee, uint256 requiredFee);
error ApproxDstExecutionGasNotSet();
error InvalidRetryData();
// GENERIC MSG
error ArrayLengthMismatch();
error ArrayEmpty();
error ArrayOutOfBounds();
error ZeroAddress();
error FailedToSendEther();
error InvalidMerkleProof();
error OnlyLayerZeroEndpoint();
error OnlyYT();
error OnlyYCFactory();
error OnlyWhitelisted();
// Swap Aggregator
error SAInsufficientTokenIn(address tokenIn, uint256 amountExpected, uint256 amountActual);
error UnsupportedSelector(uint256 aggregatorType, bytes4 selector);
}// 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 ERC-20 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) (interfaces/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC721} from "../token/ERC721/IERC721.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
import {Panic} from "../Panic.sol";
import {SafeCast} from "./SafeCast.sol";
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
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 success flag (no overflow).
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an success flag (no overflow).
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an success flag (no overflow).
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
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 success flag (no division by zero).
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a success flag (no division by zero).
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool success, uint256 result) {
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.
Panic.panic(Panic.DIVISION_BY_ZERO);
}
// The following calculation ensures accurate ceiling division without overflow.
// Since a is non-zero, (a - 1) / b will not overflow.
// The largest possible result occurs when (a - 1) / b is type(uint256).max,
// but the largest value we can obtain is type(uint256).max - 1, which happens
// when a = type(uint256).max and b = 1.
unchecked {
return a == 0 ? 0 : (a - 1) / b + 1;
}
}
/**
* @dev Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
*
* 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²⁵⁶ and mod 2²⁵⁶ - 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²⁵⁶ + 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²⁵⁶. Also prevents denominator == 0.
if (denominator <= prod1) {
Panic.panic(denominator == 0 ? Panic.DIVISION_BY_ZERO : Panic.UNDER_OVERFLOW);
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
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²⁵⁶ / 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²⁵⁶. Now that denominator is an odd number, it has an inverse modulo 2²⁵⁶ such
// that denominator * inv ≡ 1 mod 2²⁵⁶. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv ≡ 1 mod 2⁴.
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⁸
inverse *= 2 - denominator * inverse; // inverse mod 2¹⁶
inverse *= 2 - denominator * inverse; // inverse mod 2³²
inverse *= 2 - denominator * inverse; // inverse mod 2⁶⁴
inverse *= 2 - denominator * inverse; // inverse mod 2¹²⁸
inverse *= 2 - denominator * inverse; // inverse mod 2²⁵⁶
// 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²⁵⁶. Since the preconditions guarantee that the outcome is
// less than 2²⁵⁶, 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;
}
}
/**
* @dev 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) {
return mulDiv(x, y, denominator) + SafeCast.toUint(unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0);
}
/**
* @dev Calculate the modular multiplicative inverse of a number in Z/nZ.
*
* If n is a prime, then Z/nZ is a field. In that case all elements are inversible, expect 0.
* If n is not a prime, then Z/nZ is not a field, and some elements might not be inversible.
*
* If the input value is not inversible, 0 is returned.
*
* NOTE: If you know for sure that n is (big) a prime, it may be cheaper to use Ferma's little theorem and get the
* inverse using `Math.modExp(a, n - 2, n)`.
*/
function invMod(uint256 a, uint256 n) internal pure returns (uint256) {
unchecked {
if (n == 0) return 0;
// The inverse modulo is calculated using the Extended Euclidean Algorithm (iterative version)
// Used to compute integers x and y such that: ax + ny = gcd(a, n).
// When the gcd is 1, then the inverse of a modulo n exists and it's x.
// ax + ny = 1
// ax = 1 + (-y)n
// ax ≡ 1 (mod n) # x is the inverse of a modulo n
// If the remainder is 0 the gcd is n right away.
uint256 remainder = a % n;
uint256 gcd = n;
// Therefore the initial coefficients are:
// ax + ny = gcd(a, n) = n
// 0a + 1n = n
int256 x = 0;
int256 y = 1;
while (remainder != 0) {
uint256 quotient = gcd / remainder;
(gcd, remainder) = (
// The old remainder is the next gcd to try.
remainder,
// Compute the next remainder.
// Can't overflow given that (a % gcd) * (gcd // (a % gcd)) <= gcd
// where gcd is at most n (capped to type(uint256).max)
gcd - remainder * quotient
);
(x, y) = (
// Increment the coefficient of a.
y,
// Decrement the coefficient of n.
// Can overflow, but the result is casted to uint256 so that the
// next value of y is "wrapped around" to a value between 0 and n - 1.
x - y * int256(quotient)
);
}
if (gcd != 1) return 0; // No inverse exists.
return x < 0 ? (n - uint256(-x)) : uint256(x); // Wrap the result if it's negative.
}
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m)
*
* Requirements:
* - modulus can't be zero
* - underlying staticcall to precompile must succeed
*
* IMPORTANT: The result is only valid if the underlying call succeeds. When using this function, make
* sure the chain you're using it on supports the precompiled contract for modular exponentiation
* at address 0x05 as specified in https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise,
* the underlying function will succeed given the lack of a revert, but the result may be incorrectly
* interpreted as 0.
*/
function modExp(uint256 b, uint256 e, uint256 m) internal view returns (uint256) {
(bool success, uint256 result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Returns the modular exponentiation of the specified base, exponent and modulus (b ** e % m).
* It includes a success flag indicating if the operation succeeded. Operation will be marked has failed if trying
* to operate modulo 0 or if the underlying precompile reverted.
*
* IMPORTANT: The result is only valid if the success flag is true. When using this function, make sure the chain
* you're using it on supports the precompiled contract for modular exponentiation at address 0x05 as specified in
* https://eips.ethereum.org/EIPS/eip-198[EIP-198]. Otherwise, the underlying function will succeed given the lack
* of a revert, but the result may be incorrectly interpreted as 0.
*/
function tryModExp(uint256 b, uint256 e, uint256 m) internal view returns (bool success, uint256 result) {
if (m == 0) return (false, 0);
/// @solidity memory-safe-assembly
assembly {
let ptr := mload(0x40)
// | Offset | Content | Content (Hex) |
// |-----------|------------|--------------------------------------------------------------------|
// | 0x00:0x1f | size of b | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x20:0x3f | size of e | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x40:0x5f | size of m | 0x0000000000000000000000000000000000000000000000000000000000000020 |
// | 0x60:0x7f | value of b | 0x<.............................................................b> |
// | 0x80:0x9f | value of e | 0x<.............................................................e> |
// | 0xa0:0xbf | value of m | 0x<.............................................................m> |
mstore(ptr, 0x20)
mstore(add(ptr, 0x20), 0x20)
mstore(add(ptr, 0x40), 0x20)
mstore(add(ptr, 0x60), b)
mstore(add(ptr, 0x80), e)
mstore(add(ptr, 0xa0), m)
// Given the result < m, it's guaranteed to fit in 32 bytes,
// so we can use the memory scratch space located at offset 0.
success := staticcall(gas(), 0x05, ptr, 0xc0, 0x00, 0x20)
result := mload(0x00)
}
}
/**
* @dev Variant of {modExp} that supports inputs of arbitrary length.
*/
function modExp(bytes memory b, bytes memory e, bytes memory m) internal view returns (bytes memory) {
(bool success, bytes memory result) = tryModExp(b, e, m);
if (!success) {
Panic.panic(Panic.DIVISION_BY_ZERO);
}
return result;
}
/**
* @dev Variant of {tryModExp} that supports inputs of arbitrary length.
*/
function tryModExp(
bytes memory b,
bytes memory e,
bytes memory m
) internal view returns (bool success, bytes memory result) {
if (_zeroBytes(m)) return (false, new bytes(0));
uint256 mLen = m.length;
// Encode call args in result and move the free memory pointer
result = abi.encodePacked(b.length, e.length, mLen, b, e, m);
/// @solidity memory-safe-assembly
assembly {
let dataPtr := add(result, 0x20)
// Write result on top of args to avoid allocating extra memory.
success := staticcall(gas(), 0x05, dataPtr, mload(result), dataPtr, mLen)
// Overwrite the length.
// result.length > returndatasize() is guaranteed because returndatasize() == m.length
mstore(result, mLen)
// Set the memory pointer after the returned data.
mstore(0x40, add(dataPtr, mLen))
}
}
/**
* @dev Returns whether the provided byte array is zero.
*/
function _zeroBytes(bytes memory byteArray) private pure returns (bool) {
for (uint256 i = 0; i < byteArray.length; ++i) {
if (byteArray[i] != 0) {
return false;
}
}
return true;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* This method is based on Newton's method for computing square roots; the algorithm is restricted to only
* using integer operations.
*/
function sqrt(uint256 a) internal pure returns (uint256) {
unchecked {
// Take care of easy edge cases when a == 0 or a == 1
if (a <= 1) {
return a;
}
// In this function, we use Newton's method to get a root of `f(x) := x² - a`. It involves building a
// sequence x_n that converges toward sqrt(a). For each iteration x_n, we also define the error between
// the current value as `ε_n = | x_n - sqrt(a) |`.
//
// For our first estimation, we consider `e` the smallest power of 2 which is bigger than the square root
// of the target. (i.e. `2**(e-1) ≤ sqrt(a) < 2**e`). We know that `e ≤ 128` because `(2¹²⁸)² = 2²⁵⁶` is
// bigger than any uint256.
//
// By noticing that
// `2**(e-1) ≤ sqrt(a) < 2**e → (2**(e-1))² ≤ a < (2**e)² → 2**(2*e-2) ≤ a < 2**(2*e)`
// we can deduce that `e - 1` is `log2(a) / 2`. We can thus compute `x_n = 2**(e-1)` using a method similar
// to the msb function.
uint256 aa = a;
uint256 xn = 1;
if (aa >= (1 << 128)) {
aa >>= 128;
xn <<= 64;
}
if (aa >= (1 << 64)) {
aa >>= 64;
xn <<= 32;
}
if (aa >= (1 << 32)) {
aa >>= 32;
xn <<= 16;
}
if (aa >= (1 << 16)) {
aa >>= 16;
xn <<= 8;
}
if (aa >= (1 << 8)) {
aa >>= 8;
xn <<= 4;
}
if (aa >= (1 << 4)) {
aa >>= 4;
xn <<= 2;
}
if (aa >= (1 << 2)) {
xn <<= 1;
}
// We now have x_n such that `x_n = 2**(e-1) ≤ sqrt(a) < 2**e = 2 * x_n`. This implies ε_n ≤ 2**(e-1).
//
// We can refine our estimation by noticing that the middle of that interval minimizes the error.
// If we move x_n to equal 2**(e-1) + 2**(e-2), then we reduce the error to ε_n ≤ 2**(e-2).
// This is going to be our x_0 (and ε_0)
xn = (3 * xn) >> 1; // ε_0 := | x_0 - sqrt(a) | ≤ 2**(e-2)
// From here, Newton's method give us:
// x_{n+1} = (x_n + a / x_n) / 2
//
// One should note that:
// x_{n+1}² - a = ((x_n + a / x_n) / 2)² - a
// = ((x_n² + a) / (2 * x_n))² - a
// = (x_n⁴ + 2 * a * x_n² + a²) / (4 * x_n²) - a
// = (x_n⁴ + 2 * a * x_n² + a² - 4 * a * x_n²) / (4 * x_n²)
// = (x_n⁴ - 2 * a * x_n² + a²) / (4 * x_n²)
// = (x_n² - a)² / (2 * x_n)²
// = ((x_n² - a) / (2 * x_n))²
// ≥ 0
// Which proves that for all n ≥ 1, sqrt(a) ≤ x_n
//
// This gives us the proof of quadratic convergence of the sequence:
// ε_{n+1} = | x_{n+1} - sqrt(a) |
// = | (x_n + a / x_n) / 2 - sqrt(a) |
// = | (x_n² + a - 2*x_n*sqrt(a)) / (2 * x_n) |
// = | (x_n - sqrt(a))² / (2 * x_n) |
// = | ε_n² / (2 * x_n) |
// = ε_n² / | (2 * x_n) |
//
// For the first iteration, we have a special case where x_0 is known:
// ε_1 = ε_0² / | (2 * x_0) |
// ≤ (2**(e-2))² / (2 * (2**(e-1) + 2**(e-2)))
// ≤ 2**(2*e-4) / (3 * 2**(e-1))
// ≤ 2**(e-3) / 3
// ≤ 2**(e-3-log2(3))
// ≤ 2**(e-4.5)
//
// For the following iterations, we use the fact that, 2**(e-1) ≤ sqrt(a) ≤ x_n:
// ε_{n+1} = ε_n² / | (2 * x_n) |
// ≤ (2**(e-k))² / (2 * 2**(e-1))
// ≤ 2**(2*e-2*k) / 2**e
// ≤ 2**(e-2*k)
xn = (xn + a / xn) >> 1; // ε_1 := | x_1 - sqrt(a) | ≤ 2**(e-4.5) -- special case, see above
xn = (xn + a / xn) >> 1; // ε_2 := | x_2 - sqrt(a) | ≤ 2**(e-9) -- general case with k = 4.5
xn = (xn + a / xn) >> 1; // ε_3 := | x_3 - sqrt(a) | ≤ 2**(e-18) -- general case with k = 9
xn = (xn + a / xn) >> 1; // ε_4 := | x_4 - sqrt(a) | ≤ 2**(e-36) -- general case with k = 18
xn = (xn + a / xn) >> 1; // ε_5 := | x_5 - sqrt(a) | ≤ 2**(e-72) -- general case with k = 36
xn = (xn + a / xn) >> 1; // ε_6 := | x_6 - sqrt(a) | ≤ 2**(e-144) -- general case with k = 72
// Because e ≤ 128 (as discussed during the first estimation phase), we know have reached a precision
// ε_6 ≤ 2**(e-144) < 1. Given we're operating on integers, then we can ensure that xn is now either
// sqrt(a) or sqrt(a) + 1.
return xn - SafeCast.toUint(xn > a / xn);
}
}
/**
* @dev 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && result * result < a);
}
}
/**
* @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;
uint256 exp;
unchecked {
exp = 128 * SafeCast.toUint(value > (1 << 128) - 1);
value >>= exp;
result += exp;
exp = 64 * SafeCast.toUint(value > (1 << 64) - 1);
value >>= exp;
result += exp;
exp = 32 * SafeCast.toUint(value > (1 << 32) - 1);
value >>= exp;
result += exp;
exp = 16 * SafeCast.toUint(value > (1 << 16) - 1);
value >>= exp;
result += exp;
exp = 8 * SafeCast.toUint(value > (1 << 8) - 1);
value >>= exp;
result += exp;
exp = 4 * SafeCast.toUint(value > (1 << 4) - 1);
value >>= exp;
result += exp;
exp = 2 * SafeCast.toUint(value > (1 << 2) - 1);
value >>= exp;
result += exp;
result += SafeCast.toUint(value > 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << result < value);
}
}
/**
* @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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 10 ** result < value);
}
}
/**
* @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;
uint256 isGt;
unchecked {
isGt = SafeCast.toUint(value > (1 << 128) - 1);
value >>= isGt * 128;
result += isGt * 16;
isGt = SafeCast.toUint(value > (1 << 64) - 1);
value >>= isGt * 64;
result += isGt * 8;
isGt = SafeCast.toUint(value > (1 << 32) - 1);
value >>= isGt * 32;
result += isGt * 4;
isGt = SafeCast.toUint(value > (1 << 16) - 1);
value >>= isGt * 16;
result += isGt * 2;
result += SafeCast.toUint(value > (1 << 8) - 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 + SafeCast.toUint(unsignedRoundsUp(rounding) && 1 << (result << 3) < value);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (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 {
// Formula from the "Bit Twiddling Hacks" by Sean Eron Anderson.
// Since `n` is a signed integer, the generated bytecode will use the SAR opcode to perform the right shift,
// taking advantage of the most significant (or "sign" bit) in two's complement representation.
// This opcode adds new most significant bits set to the value of the previous most significant bit. As a result,
// the mask will either be `bytes(0)` (if n is positive) or `~bytes32(0)` (if n is negative).
int256 mask = n >> 255;
// A `bytes(0)` mask leaves the input unchanged, while a `~bytes32(0)` mask complements it.
return uint256((n + mask) ^ mask);
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {Math} from "openzeppelin5/utils/math/Math.sol";
import {ISiloOracle} from "../interfaces/ISiloOracle.sol";
import {SiloStdLib, ISiloConfig, IShareToken, ISilo} from "./SiloStdLib.sol";
import {SiloMathLib} from "./SiloMathLib.sol";
import {Rounding} from "./Rounding.sol";
library SiloSolvencyLib {
using Math for uint256;
struct LtvData {
ISiloOracle collateralOracle;
ISiloOracle debtOracle;
uint256 borrowerProtectedAssets;
uint256 borrowerCollateralAssets;
uint256 borrowerDebtAssets;
}
uint256 internal constant _PRECISION_DECIMALS = 1e18;
uint256 internal constant _INFINITY = type(uint256).max;
/// @notice Determines if a borrower is solvent based on the Loan-to-Value (LTV) ratio
/// @param _collateralConfig Configuration data for the collateral
/// @param _debtConfig Configuration data for the debt
/// @param _borrower Address of the borrower to check solvency for
/// @param _accrueInMemory Determines whether or not to consider un-accrued interest in calculations
/// @return True if the borrower is solvent, false otherwise
function isSolvent(
ISiloConfig.ConfigData memory _collateralConfig,
ISiloConfig.ConfigData memory _debtConfig,
address _borrower,
ISilo.AccrueInterestInMemory _accrueInMemory
) internal view returns (bool) {
if (_debtConfig.silo == address(0)) return true; // no debt, so solvent
uint256 ltv = getLtv(
_collateralConfig,
_debtConfig,
_borrower,
ISilo.OracleType.Solvency,
_accrueInMemory,
IShareToken(_debtConfig.debtShareToken).balanceOf(_borrower)
);
return ltv <= _collateralConfig.lt;
}
/// @notice Determines if a borrower's Loan-to-Value (LTV) ratio is below the maximum allowed LTV
/// @param _collateralConfig Configuration data for the collateral
/// @param _debtConfig Configuration data for the debt
/// @param _borrower Address of the borrower to check against max LTV
/// @param _accrueInMemory Determines whether or not to consider un-accrued interest in calculations
/// @return True if the borrower's LTV is below the maximum, false otherwise
function isBelowMaxLtv(
ISiloConfig.ConfigData memory _collateralConfig,
ISiloConfig.ConfigData memory _debtConfig,
address _borrower,
ISilo.AccrueInterestInMemory _accrueInMemory
) internal view returns (bool) {
uint256 debtShareBalance = IShareToken(_debtConfig.debtShareToken).balanceOf(_borrower);
if (debtShareBalance == 0) return true;
uint256 ltv = getLtv(
_collateralConfig,
_debtConfig,
_borrower,
ISilo.OracleType.MaxLtv,
_accrueInMemory,
debtShareBalance
);
return ltv <= _collateralConfig.maxLtv;
}
/// @notice Retrieves assets data required for LTV calculations
/// @param _collateralConfig Configuration data for the collateral
/// @param _debtConfig Configuration data for the debt
/// @param _borrower Address of the borrower whose LTV data is to be calculated
/// @param _oracleType Specifies whether to use the MaxLTV or Solvency oracle type for calculations
/// @param _accrueInMemory Determines whether or not to consider un-accrued interest in calculations
/// @param _debtShareBalanceCached Cached value of debt share balance for the borrower. If debt shares of
/// `_borrower` is unknown, simply pass `0`.
/// @return ltvData Data structure containing necessary data to compute LTV
function getAssetsDataForLtvCalculations( // solhint-disable-line function-max-lines
ISiloConfig.ConfigData memory _collateralConfig,
ISiloConfig.ConfigData memory _debtConfig,
address _borrower,
ISilo.OracleType _oracleType,
ISilo.AccrueInterestInMemory _accrueInMemory,
uint256 _debtShareBalanceCached
) internal view returns (LtvData memory ltvData) {
if (_collateralConfig.token != _debtConfig.token) {
// When calculating maxLtv, use maxLtv oracle.
(ltvData.collateralOracle, ltvData.debtOracle) = _oracleType == ISilo.OracleType.MaxLtv
? (ISiloOracle(_collateralConfig.maxLtvOracle), ISiloOracle(_debtConfig.maxLtvOracle))
: (ISiloOracle(_collateralConfig.solvencyOracle), ISiloOracle(_debtConfig.solvencyOracle));
}
uint256 totalShares;
uint256 shares;
(shares, totalShares) = SiloStdLib.getSharesAndTotalSupply(
_collateralConfig.protectedShareToken, _borrower, 0 /* no cache */
);
(
uint256 totalCollateralAssets, uint256 totalProtectedAssets
) = ISilo(_collateralConfig.silo).getCollateralAndProtectedTotalsStorage();
ltvData.borrowerProtectedAssets = SiloMathLib.convertToAssets(
shares, totalProtectedAssets, totalShares, Rounding.COLLATERAL_TO_ASSETS, ISilo.AssetType.Protected
);
(shares, totalShares) = SiloStdLib.getSharesAndTotalSupply(
_collateralConfig.collateralShareToken, _borrower, 0 /* no cache */
);
totalCollateralAssets = _accrueInMemory == ISilo.AccrueInterestInMemory.Yes
? SiloStdLib.getTotalCollateralAssetsWithInterest(
_collateralConfig.silo,
_collateralConfig.interestRateModel,
_collateralConfig.daoFee,
_collateralConfig.deployerFee
)
: totalCollateralAssets;
ltvData.borrowerCollateralAssets = SiloMathLib.convertToAssets(
shares, totalCollateralAssets, totalShares, Rounding.COLLATERAL_TO_ASSETS, ISilo.AssetType.Collateral
);
(shares, totalShares) = SiloStdLib.getSharesAndTotalSupply(
_debtConfig.debtShareToken, _borrower, _debtShareBalanceCached
);
uint256 totalDebtAssets = _accrueInMemory == ISilo.AccrueInterestInMemory.Yes
? SiloStdLib.getTotalDebtAssetsWithInterest(_debtConfig.silo, _debtConfig.interestRateModel)
: ISilo(_debtConfig.silo).getTotalAssetsStorage(ISilo.AssetType.Debt);
// BORROW value -> to assets -> UP
ltvData.borrowerDebtAssets = SiloMathLib.convertToAssets(
shares, totalDebtAssets, totalShares, Rounding.DEBT_TO_ASSETS, ISilo.AssetType.Debt
);
}
/// @notice Calculates the Loan-To-Value (LTV) ratio for a given borrower
/// @param _collateralConfig Configuration data related to the collateral asset
/// @param _debtConfig Configuration data related to the debt asset
/// @param _borrower Address of the borrower whose LTV is to be computed
/// @param _oracleType Oracle type to use for fetching the asset prices
/// @param _accrueInMemory Determines whether or not to consider un-accrued interest in calculations
/// @return ltvInDp The computed LTV ratio in 18 decimals precision
function getLtv(
ISiloConfig.ConfigData memory _collateralConfig,
ISiloConfig.ConfigData memory _debtConfig,
address _borrower,
ISilo.OracleType _oracleType,
ISilo.AccrueInterestInMemory _accrueInMemory,
uint256 _debtShareBalance
) internal view returns (uint256 ltvInDp) {
if (_debtShareBalance == 0) return 0;
LtvData memory ltvData = getAssetsDataForLtvCalculations(
_collateralConfig, _debtConfig, _borrower, _oracleType, _accrueInMemory, _debtShareBalance
);
if (ltvData.borrowerDebtAssets == 0) return 0;
(,, ltvInDp) = calculateLtv(ltvData, _collateralConfig.token, _debtConfig.token);
}
/// @notice Calculates the Loan-to-Value (LTV) ratio based on provided collateral and debt data
/// @dev calculation never reverts, if there is revert, then it is because of oracle
/// @param _ltvData Data structure containing relevant information to calculate LTV
/// @param _collateralToken Address of the collateral token
/// @param _debtAsset Address of the debt token
/// @return sumOfBorrowerCollateralValue Total value of borrower's collateral
/// @return totalBorrowerDebtValue Total debt value for the borrower
/// @return ltvInDp Calculated LTV in 18 decimal precision
function calculateLtv(
SiloSolvencyLib.LtvData memory _ltvData, address _collateralToken, address _debtAsset)
internal
view
returns (uint256 sumOfBorrowerCollateralValue, uint256 totalBorrowerDebtValue, uint256 ltvInDp)
{
(
sumOfBorrowerCollateralValue, totalBorrowerDebtValue
) = getPositionValues(_ltvData, _collateralToken, _debtAsset);
if (sumOfBorrowerCollateralValue == 0 && totalBorrowerDebtValue == 0) {
return (0, 0, 0);
} else if (sumOfBorrowerCollateralValue == 0) {
ltvInDp = _INFINITY;
} else {
ltvInDp = ltvMath(totalBorrowerDebtValue, sumOfBorrowerCollateralValue);
}
}
/// @notice Computes the value of collateral and debt based on given LTV data and asset addresses
/// @param _ltvData Data structure containing the assets data required for LTV calculations
/// @param _collateralAsset Address of the collateral asset
/// @param _debtAsset Address of the debt asset
/// @return sumOfCollateralValue Total value of collateral assets considering both protected and regular collateral
/// assets
/// @return debtValue Total value of debt assets
function getPositionValues(LtvData memory _ltvData, address _collateralAsset, address _debtAsset)
internal
view
returns (uint256 sumOfCollateralValue, uint256 debtValue)
{
uint256 sumOfCollateralAssets;
sumOfCollateralAssets = _ltvData.borrowerProtectedAssets + _ltvData.borrowerCollateralAssets;
if (sumOfCollateralAssets != 0) {
// if no oracle is set, assume price 1, we should also not set oracle for quote token
sumOfCollateralValue = address(_ltvData.collateralOracle) != address(0)
? _ltvData.collateralOracle.quote(sumOfCollateralAssets, _collateralAsset)
: sumOfCollateralAssets;
}
if (_ltvData.borrowerDebtAssets != 0) {
// if no oracle is set, assume price 1, we should also not set oracle for quote token
debtValue = address(_ltvData.debtOracle) != address(0)
? _ltvData.debtOracle.quote(_ltvData.borrowerDebtAssets, _debtAsset)
: _ltvData.borrowerDebtAssets;
}
}
function ltvMath(uint256 _totalBorrowerDebtValue, uint256 _sumOfBorrowerCollateralValue)
internal
pure
returns (uint256 ltvInDp)
{
ltvInDp = _totalBorrowerDebtValue.mulDiv(_PRECISION_DECIMALS, _sumOfBorrowerCollateralValue, Rounding.LTV);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.28;
import {Math} from "openzeppelin5/utils/math/Math.sol";
import {Rounding} from "../lib/Rounding.sol";
import {ISilo} from "../interfaces/ISilo.sol";
library SiloMathLib {
using Math for uint256;
uint256 internal constant _PRECISION_DECIMALS = 1e18;
uint256 internal constant _DECIMALS_OFFSET = 3;
/// @dev this is constant version of openzeppelin5/contracts/token/ERC20/extensions/ERC4626._decimalsOffset
uint256 internal constant _DECIMALS_OFFSET_POW = 10 ** _DECIMALS_OFFSET;
/// @notice Returns available liquidity to be borrowed
/// @dev Accrued interest is entirely added to `debtAssets` but only part of it is added to `collateralAssets`. The
/// difference is DAO's and deployer's cut. That means DAO's and deployer's cut is not considered a borrowable
/// liquidity.
function liquidity(uint256 _collateralAssets, uint256 _debtAssets) internal pure returns (uint256 liquidAssets) {
unchecked {
// we checked the underflow
liquidAssets = _debtAssets > _collateralAssets ? 0 : _collateralAssets - _debtAssets;
}
}
/// @notice Calculate collateral assets with accrued interest and associated fees
/// @param _collateralAssets The total amount of collateral assets
/// @param _debtAssets The total amount of debt assets
/// @param _rcomp Compound interest rate for debt
/// @param _daoFee The fee (in 18 decimals points) to be taken for the DAO
/// @param _deployerFee The fee (in 18 decimals points) to be taken for the deployer
/// @return collateralAssetsWithInterest The total collateral assets including the accrued interest
/// @return debtAssetsWithInterest The debt assets with accrued interest
/// @return daoAndDeployerRevenue Total fees amount to be split between DAO and deployer
/// @return accruedInterest The total accrued interest
function getCollateralAmountsWithInterest(
uint256 _collateralAssets,
uint256 _debtAssets,
uint256 _rcomp,
uint256 _daoFee,
uint256 _deployerFee
)
internal
pure
returns (
uint256 collateralAssetsWithInterest,
uint256 debtAssetsWithInterest,
uint256 daoAndDeployerRevenue,
uint256 accruedInterest
)
{
(debtAssetsWithInterest, accruedInterest) = getDebtAmountsWithInterest(_debtAssets, _rcomp);
uint256 fees;
// _daoFee and _deployerFee are expected to be less than 1e18, so we will not overflow
unchecked { fees = _daoFee + _deployerFee; }
daoAndDeployerRevenue = mulDivOverflow(accruedInterest, fees, _PRECISION_DECIMALS);
// we will not underflow because daoAndDeployerRevenue is chunk of accruedInterest
uint256 collateralInterest = accruedInterest - daoAndDeployerRevenue;
uint256 cap;
// save to uncheck because variable can not be more than max
unchecked { cap = type(uint256).max - _collateralAssets; }
if (cap < collateralInterest) {
// avoid overflow on interest
collateralInterest = cap;
}
// safe to uncheck because of cap
unchecked { collateralAssetsWithInterest = _collateralAssets + collateralInterest; }
}
/// @notice Calculate the debt assets with accrued interest, it should never revert with over/under flow
/// @param _totalDebtAssets The total amount of debt assets before accrued interest
/// @param _rcomp Compound interest rate for the debt in 18 decimal precision
/// @return debtAssetsWithInterest The debt assets including the accrued interest
/// @return accruedInterest The total amount of interest accrued on the debt assets
function getDebtAmountsWithInterest(uint256 _totalDebtAssets, uint256 _rcomp)
internal
pure
returns (uint256 debtAssetsWithInterest, uint256 accruedInterest)
{
if (_totalDebtAssets == 0 || _rcomp == 0) {
return (_totalDebtAssets, 0);
}
accruedInterest = mulDivOverflow(_totalDebtAssets, _rcomp, _PRECISION_DECIMALS);
unchecked {
// We intentionally allow overflow here, to prevent transaction revert due to interest calculation.
debtAssetsWithInterest = _totalDebtAssets + accruedInterest;
// If overflow occurs, we skip accruing interest.
if (debtAssetsWithInterest < _totalDebtAssets) {
debtAssetsWithInterest = _totalDebtAssets;
accruedInterest = 0;
}
}
}
/// @notice Calculates fraction between borrowed and deposited amount of tokens denominated in percentage
/// @dev It assumes `_dp` = 100%.
/// @param _dp decimal points used by model
/// @param _collateralAssets current total deposits for assets
/// @param _debtAssets current total borrows for assets
/// @return utilization value, capped to 100%
/// Limiting utilization ratio by 100% max will allows us to perform better interest rate computations
/// and should not affect any other part of protocol. It is possible to go over 100% only when bad debt.
function calculateUtilization(uint256 _dp, uint256 _collateralAssets, uint256 _debtAssets)
internal
pure
returns (uint256 utilization)
{
if (_collateralAssets == 0 || _debtAssets == 0 || _dp == 0) return 0;
/*
how to prevent overflow on: _debtAssets.mulDiv(_dp, _collateralAssets, Rounding.ACCRUED_INTEREST):
1. max > _debtAssets * _dp / _collateralAssets
2. max / _dp > _debtAssets / _collateralAssets
*/
if (type(uint256).max / _dp > _debtAssets / _collateralAssets) {
utilization = _debtAssets.mulDiv(_dp, _collateralAssets, Rounding.ACCRUED_INTEREST);
// cap at 100%
if (utilization > _dp) utilization = _dp;
} else {
// we have overflow
utilization = _dp;
}
}
function convertToAssetsOrToShares(
uint256 _assets,
uint256 _shares,
uint256 _totalAssets,
uint256 _totalShares,
Math.Rounding _roundingToAssets,
Math.Rounding _roundingToShares,
ISilo.AssetType _assetType
) internal pure returns (uint256 assets, uint256 shares) {
if (_assets == 0) {
require(_shares != 0, ISilo.InputZeroShares());
shares = _shares;
assets = convertToAssets(_shares, _totalAssets, _totalShares, _roundingToAssets, _assetType);
require(assets != 0, ISilo.ReturnZeroAssets());
} else if (_shares == 0) {
shares = convertToShares(_assets, _totalAssets, _totalShares, _roundingToShares, _assetType);
assets = _assets;
require(shares != 0, ISilo.ReturnZeroShares());
} else {
revert ISilo.InputCanBeAssetsOrShares();
}
}
/// @dev Math for collateral is exact copy of
/// openzeppelin5/contracts/token/ERC20/extensions/ERC4626._convertToShares
function convertToShares(
uint256 _assets,
uint256 _totalAssets,
uint256 _totalShares,
Math.Rounding _rounding,
ISilo.AssetType _assetType
) internal pure returns (uint256 shares) {
(uint256 totalShares, uint256 totalAssets) = _commonConvertTo(_totalAssets, _totalShares, _assetType);
// initially, in case of debt, if silo is empty we return shares==assets
// for collateral, this will never be the case, because we are adding `+1` and offset in `_commonConvertTo`
if (totalShares == 0) return _assets;
shares = _assets.mulDiv(totalShares, totalAssets, _rounding);
}
/// @dev Math for collateral is exact copy of
/// openzeppelin5/contracts/token/ERC20/extensions/ERC4626._convertToAssets
function convertToAssets(
uint256 _shares,
uint256 _totalAssets,
uint256 _totalShares,
Math.Rounding _rounding,
ISilo.AssetType _assetType
) internal pure returns (uint256 assets) {
(uint256 totalShares, uint256 totalAssets) = _commonConvertTo(_totalAssets, _totalShares, _assetType);
// initially, in case of debt, if silo is empty we return shares==assets
// for collateral, this will never be the case, because of `+1` in line above
if (totalShares == 0) return _shares;
assets = _shares.mulDiv(totalAssets, totalShares, _rounding);
}
/// @param _collateralMaxLtv maxLTV in 18 decimals that is set for debt asset
/// @param _sumOfBorrowerCollateralValue borrower total collateral value (including protected)
/// @param _borrowerDebtValue total value of borrower debt
/// @return maxBorrowValue max borrow value yet available for borrower
function calculateMaxBorrowValue(
uint256 _collateralMaxLtv,
uint256 _sumOfBorrowerCollateralValue,
uint256 _borrowerDebtValue
) internal pure returns (uint256 maxBorrowValue) {
if (_sumOfBorrowerCollateralValue == 0) {
return 0;
}
uint256 maxDebtValue = _sumOfBorrowerCollateralValue.mulDiv(
_collateralMaxLtv, _PRECISION_DECIMALS, Rounding.MAX_BORROW_VALUE
);
unchecked {
// we will not underflow because we checking `maxDebtValue > _borrowerDebtValue`
maxBorrowValue = maxDebtValue > _borrowerDebtValue ? maxDebtValue - _borrowerDebtValue : 0;
}
}
/// @notice Calculate the maximum assets a borrower can withdraw without breaching the liquidation threshold
/// @param _sumOfCollateralsValue The combined value of collateral and protected assets of the borrower
/// @param _debtValue The total debt value of the borrower
/// @param _lt The liquidation threshold in 18 decimal points
/// @param _borrowerCollateralAssets The borrower's collateral assets before the withdrawal
/// @param _borrowerProtectedAssets The borrower's protected assets before the withdrawal
/// @return maxAssets The maximum assets the borrower can safely withdraw
function calculateMaxAssetsToWithdraw(
uint256 _sumOfCollateralsValue,
uint256 _debtValue,
uint256 _lt,
uint256 _borrowerCollateralAssets,
uint256 _borrowerProtectedAssets
) internal pure returns (uint256 maxAssets) {
if (_sumOfCollateralsValue == 0) return 0;
if (_debtValue == 0) return _sumOfCollateralsValue;
if (_lt == 0) return 0;
// using Rounding.LT (up) to have highest collateralValue that we have to leave for user to stay solvent
uint256 minimumCollateralValue = _debtValue.mulDiv(_PRECISION_DECIMALS, _lt, Rounding.LTV);
// if we over LT, we can not withdraw
if (_sumOfCollateralsValue <= minimumCollateralValue) {
return 0;
}
uint256 spareCollateralValue;
// safe because we checked `if (_sumOfCollateralsValue <= minimumCollateralValue)`
unchecked { spareCollateralValue = _sumOfCollateralsValue - minimumCollateralValue; }
maxAssets = (_borrowerProtectedAssets + _borrowerCollateralAssets)
.mulDiv(spareCollateralValue, _sumOfCollateralsValue, Rounding.MAX_WITHDRAW_TO_ASSETS);
}
/// @notice Determines the maximum number of assets and corresponding shares a borrower can safely withdraw
/// @param _maxAssets The calculated limit on how many assets can be withdrawn without breaching the liquidation
/// threshold
/// @param _borrowerCollateralAssets Amount of collateral assets currently held by the borrower
/// @param _borrowerProtectedAssets Amount of protected assets currently held by the borrower
/// @param _collateralType Specifies whether the asset is of type Collateral or Protected
/// @param _totalAssets The entire quantity of assets available in the system for withdrawal
/// @param _assetTypeShareTokenTotalSupply Total supply of share tokens for the specified asset type
/// @param _liquidity Current liquidity in the system for the asset type
/// @return assets Maximum assets the borrower can withdraw
/// @return shares Corresponding number of shares for the derived `assets` amount
function maxWithdrawToAssetsAndShares(
uint256 _maxAssets,
uint256 _borrowerCollateralAssets,
uint256 _borrowerProtectedAssets,
ISilo.CollateralType _collateralType,
uint256 _totalAssets,
uint256 _assetTypeShareTokenTotalSupply,
uint256 _liquidity
) internal pure returns (uint256 assets, uint256 shares) {
if (_maxAssets == 0) return (0, 0);
if (_assetTypeShareTokenTotalSupply == 0) return (0, 0);
if (_collateralType == ISilo.CollateralType.Collateral) {
assets = _maxAssets > _borrowerCollateralAssets ? _borrowerCollateralAssets : _maxAssets;
if (assets > _liquidity) {
assets = _liquidity;
}
} else {
assets = _maxAssets > _borrowerProtectedAssets ? _borrowerProtectedAssets : _maxAssets;
}
shares = SiloMathLib.convertToShares(
assets,
_totalAssets,
_assetTypeShareTokenTotalSupply,
Rounding.MAX_WITHDRAW_TO_SHARES,
ISilo.AssetType(uint256(_collateralType))
);
}
/// @dev executed `_a * _b / _c`, reverts on _c == 0
/// @return mulDivResult on overflow returns 0
function mulDivOverflow(uint256 _a, uint256 _b, uint256 _c)
internal
pure
returns (uint256 mulDivResult)
{
if (_a == 0) return (0);
unchecked {
// we have to uncheck to detect overflow
mulDivResult = _a * _b;
if (mulDivResult / _a != _b) return 0;
mulDivResult /= _c;
}
}
/// @dev Debt calculations should not lower the result. Debt is a liability so protocol should not take any for
/// itself. It should return actual result and round it up.
function _commonConvertTo(
uint256 _totalAssets,
uint256 _totalShares,
ISilo.AssetType _assetType
) private pure returns (uint256 totalShares, uint256 totalAssets) {
if (_totalShares == 0) {
// silo is empty and we have dust to redistribute: this can only happen when everyone exits silo
// this case can happen only for collateral, because for collateral we rounding in favorite of protocol
// by resetting totalAssets, the dust that we have will go to first depositor and we starts from clean state
_totalAssets = 0;
}
(totalShares, totalAssets) = _assetType == ISilo.AssetType.Debt
? (_totalShares, _totalAssets)
: (_totalShares + _DECIMALS_OFFSET_POW, _totalAssets + 1);
}
/// @dev Calculates the fraction of a given total and percentage
/// @param _total The total value to calculate the fraction from
/// @param _percent The percentage to calculate the fraction from
/// @param _currentFraction The current fraction to add to the result
/// @return integral The integral part of the fraction
/// @return fraction The fractional part of the fraction
function calculateFraction(
uint256 _total,
uint256 _percent,
uint64 _currentFraction
) internal pure returns (uint256 integral, uint64 fraction) {
if (_total == 0) {
return (0, _currentFraction);
}
unchecked {
// safe to unchecked because: _currentFraction if never more than max uint256, div is safe
if (type(uint256).max / _total < _percent) {
// when overflow, reset `_currentFraction ` to zero as part of circuit breaker
return (0, 0);
}
// `_total * _percent` safe to unchecked because we checked for overflow in above `if`
// `% _PRECISION_DECIMALS` safe, because max value after modulo will be 1e18 - 1 (_PRECISION_DECIMALS - 1)
// and this is less than 2 ** 64
// calculate remainder for current interest
uint256 remainder = (_total * _percent) % _PRECISION_DECIMALS;
// integral is amount above 1e18 after adding _currentFraction and remainder
integral = (_currentFraction + remainder) / _PRECISION_DECIMALS;
// fraction is what we get below 1e18
fraction = uint64((_currentFraction + remainder) % _PRECISION_DECIMALS);
}
}
}// 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 {IERC1363} from "../../../interfaces/IERC1363.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC-20 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 ERC-20 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 Performs an {ERC1363} transferAndCall, with a fallback to the simple {ERC20} transfer if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
safeTransfer(token, to, value);
} else if (!token.transferAndCall(to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} transferFromAndCall, with a fallback to the simple {ERC20} transferFrom if the target
* has no code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* Reverts if the returned value is other than `true`.
*/
function transferFromAndCallRelaxed(
IERC1363 token,
address from,
address to,
uint256 value,
bytes memory data
) internal {
if (to.code.length == 0) {
safeTransferFrom(token, from, to, value);
} else if (!token.transferFromAndCall(from, to, value, data)) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Performs an {ERC1363} approveAndCall, with a fallback to the simple {ERC20} approve if the target has no
* code. This can be used to implement an {ERC721}-like safe transfer that rely on {ERC1363} checks when
* targeting contracts.
*
* NOTE: When the recipient address (`to`) has no code (i.e. is an EOA), this function behaves as {forceApprove}.
* Opposedly, when the recipient address (`to`) has code, this function only attempts to call {ERC1363-approveAndCall}
* once without retrying, and relies on the returned value to be true.
*
* Reverts if the returned value is other than `true`.
*/
function approveAndCallRelaxed(IERC1363 token, address to, uint256 value, bytes memory data) internal {
if (to.code.length == 0) {
forceApprove(token, to, value);
} else if (!token.approveAndCall(to, value, data)) {
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).
*/
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: agpl-3.0
pragma solidity 0.8.28;
library DistributionTypes {
struct IncentivesProgramCreationInput {
string name;
address rewardToken;
uint104 emissionPerSecond;
uint40 distributionEnd;
}
struct AssetConfigInput {
uint104 emissionPerSecond;
uint256 totalStaked;
address underlyingAsset;
}
struct UserStakeInput {
address underlyingAsset;
uint256 stakedByUser;
uint256 totalStaked;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* 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[ERC 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: GPL-3.0-or-later
pragma solidity ^0.8.0;
library SYUtils {
uint256 internal constant ONE = 1e18;
function syToAsset(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) {
return (syAmount * exchangeRate) / ONE;
}
function syToAssetUp(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) {
return (syAmount * exchangeRate + ONE - 1) / ONE;
}
function assetToSy(uint256 exchangeRate, uint256 assetAmount) internal pure returns (uint256) {
return (assetAmount * ONE) / exchangeRate;
}
function assetToSyUp(uint256 exchangeRate, uint256 assetAmount) internal pure returns (uint256) {
return (assetAmount * ONE + exchangeRate - 1) / exchangeRate;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../../utils/introspection/IERC165.sol";
/**
* @dev Required interface of an ERC-721 compliant contract.
*/
interface IERC721 is IERC165 {
/**
* @dev Emitted when `tokenId` token is transferred from `from` to `to`.
*/
event Transfer(address indexed from, address indexed to, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables `approved` to manage the `tokenId` token.
*/
event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId);
/**
* @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets.
*/
event ApprovalForAll(address indexed owner, address indexed operator, bool approved);
/**
* @dev Returns the number of tokens in ``owner``'s account.
*/
function balanceOf(address owner) external view returns (uint256 balance);
/**
* @dev Returns the owner of the `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function ownerOf(uint256 tokenId) external view returns (address owner);
/**
* @dev Safely transfers `tokenId` token from `from` to `to`.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
/**
* @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients
* are aware of the ERC-721 protocol to prevent tokens from being forever locked.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must exist and be owned by `from`.
* - If the caller is not `from`, it must have been allowed to move this token by either {approve} or
* {setApprovalForAll}.
* - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon
* a safe transfer.
*
* Emits a {Transfer} event.
*/
function safeTransferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Transfers `tokenId` token from `from` to `to`.
*
* WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC-721
* or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must
* understand this adds an external call which potentially creates a reentrancy vulnerability.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `tokenId` token must be owned by `from`.
* - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 tokenId) external;
/**
* @dev Gives permission to `to` to transfer `tokenId` token to another account.
* The approval is cleared when the token is transferred.
*
* Only a single account can be approved at a time, so approving the zero address clears previous approvals.
*
* Requirements:
*
* - The caller must own the token or be an approved operator.
* - `tokenId` must exist.
*
* Emits an {Approval} event.
*/
function approve(address to, uint256 tokenId) external;
/**
* @dev Approve or remove `operator` as an operator for the caller.
* Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller.
*
* Requirements:
*
* - The `operator` cannot be the address zero.
*
* Emits an {ApprovalForAll} event.
*/
function setApprovalForAll(address operator, bool approved) external;
/**
* @dev Returns the account approved for `tokenId` token.
*
* Requirements:
*
* - `tokenId` must exist.
*/
function getApproved(uint256 tokenId) external view returns (address operator);
/**
* @dev Returns if the `operator` is allowed to manage all of the assets of `owner`.
*
* See {setApprovalForAll}
*/
function isApprovedForAll(address owner, address operator) external view returns (bool);
}// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev Helper library for emitting standardized panic codes.
*
* ```solidity
* contract Example {
* using Panic for uint256;
*
* // Use any of the declared internal constants
* function foo() { Panic.GENERIC.panic(); }
*
* // Alternatively
* function foo() { Panic.panic(Panic.GENERIC); }
* }
* ```
*
* Follows the list from https://github.com/ethereum/solidity/blob/v0.8.24/libsolutil/ErrorCodes.h[libsolutil].
*/
// slither-disable-next-line unused-state
library Panic {
/// @dev generic / unspecified error
uint256 internal constant GENERIC = 0x00;
/// @dev used by the assert() builtin
uint256 internal constant ASSERT = 0x01;
/// @dev arithmetic underflow or overflow
uint256 internal constant UNDER_OVERFLOW = 0x11;
/// @dev division or modulo by zero
uint256 internal constant DIVISION_BY_ZERO = 0x12;
/// @dev enum conversion error
uint256 internal constant ENUM_CONVERSION_ERROR = 0x21;
/// @dev invalid encoding in storage
uint256 internal constant STORAGE_ENCODING_ERROR = 0x22;
/// @dev empty array pop
uint256 internal constant EMPTY_ARRAY_POP = 0x31;
/// @dev array out of bounds access
uint256 internal constant ARRAY_OUT_OF_BOUNDS = 0x32;
/// @dev resource error (too large allocation or too large array)
uint256 internal constant RESOURCE_ERROR = 0x41;
/// @dev calling invalid internal function
uint256 internal constant INVALID_INTERNAL_FUNCTION = 0x51;
/// @dev Reverts with a panic code. Recommended to use with
/// the internal constants with predefined codes.
function panic(uint256 code) internal pure {
/// @solidity memory-safe-assembly
assembly {
mstore(0x00, 0x4e487b71)
mstore(0x20, code)
revert(0x1c, 0x24)
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX/bool casting operators with added overflow
* checks.
*
* Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
* easily result in undesired exploitation or bugs, since developers usually
* assume that overflows raise errors. `SafeCast` restores this intuition by
* reverting the transaction when such an operation overflows.
*
* Using this library instead of the unchecked operations eliminates an entire
* class of bugs, so it's recommended to use it always.
*/
library SafeCast {
/**
* @dev Value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value);
/**
* @dev An int value doesn't fit in an uint of `bits` size.
*/
error SafeCastOverflowedIntToUint(int256 value);
/**
* @dev Value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedIntDowncast(uint8 bits, int256 value);
/**
* @dev An uint value doesn't fit in an int of `bits` size.
*/
error SafeCastOverflowedUintToInt(uint256 value);
/**
* @dev Returns the downcasted uint248 from uint256, reverting on
* overflow (when the input is greater than largest uint248).
*
* Counterpart to Solidity's `uint248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toUint248(uint256 value) internal pure returns (uint248) {
if (value > type(uint248).max) {
revert SafeCastOverflowedUintDowncast(248, value);
}
return uint248(value);
}
/**
* @dev Returns the downcasted uint240 from uint256, reverting on
* overflow (when the input is greater than largest uint240).
*
* Counterpart to Solidity's `uint240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toUint240(uint256 value) internal pure returns (uint240) {
if (value > type(uint240).max) {
revert SafeCastOverflowedUintDowncast(240, value);
}
return uint240(value);
}
/**
* @dev Returns the downcasted uint232 from uint256, reverting on
* overflow (when the input is greater than largest uint232).
*
* Counterpart to Solidity's `uint232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toUint232(uint256 value) internal pure returns (uint232) {
if (value > type(uint232).max) {
revert SafeCastOverflowedUintDowncast(232, value);
}
return uint232(value);
}
/**
* @dev Returns the downcasted uint224 from uint256, reverting on
* overflow (when the input is greater than largest uint224).
*
* Counterpart to Solidity's `uint224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toUint224(uint256 value) internal pure returns (uint224) {
if (value > type(uint224).max) {
revert SafeCastOverflowedUintDowncast(224, value);
}
return uint224(value);
}
/**
* @dev Returns the downcasted uint216 from uint256, reverting on
* overflow (when the input is greater than largest uint216).
*
* Counterpart to Solidity's `uint216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toUint216(uint256 value) internal pure returns (uint216) {
if (value > type(uint216).max) {
revert SafeCastOverflowedUintDowncast(216, value);
}
return uint216(value);
}
/**
* @dev Returns the downcasted uint208 from uint256, reverting on
* overflow (when the input is greater than largest uint208).
*
* Counterpart to Solidity's `uint208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toUint208(uint256 value) internal pure returns (uint208) {
if (value > type(uint208).max) {
revert SafeCastOverflowedUintDowncast(208, value);
}
return uint208(value);
}
/**
* @dev Returns the downcasted uint200 from uint256, reverting on
* overflow (when the input is greater than largest uint200).
*
* Counterpart to Solidity's `uint200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toUint200(uint256 value) internal pure returns (uint200) {
if (value > type(uint200).max) {
revert SafeCastOverflowedUintDowncast(200, value);
}
return uint200(value);
}
/**
* @dev Returns the downcasted uint192 from uint256, reverting on
* overflow (when the input is greater than largest uint192).
*
* Counterpart to Solidity's `uint192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toUint192(uint256 value) internal pure returns (uint192) {
if (value > type(uint192).max) {
revert SafeCastOverflowedUintDowncast(192, value);
}
return uint192(value);
}
/**
* @dev Returns the downcasted uint184 from uint256, reverting on
* overflow (when the input is greater than largest uint184).
*
* Counterpart to Solidity's `uint184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toUint184(uint256 value) internal pure returns (uint184) {
if (value > type(uint184).max) {
revert SafeCastOverflowedUintDowncast(184, value);
}
return uint184(value);
}
/**
* @dev Returns the downcasted uint176 from uint256, reverting on
* overflow (when the input is greater than largest uint176).
*
* Counterpart to Solidity's `uint176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toUint176(uint256 value) internal pure returns (uint176) {
if (value > type(uint176).max) {
revert SafeCastOverflowedUintDowncast(176, value);
}
return uint176(value);
}
/**
* @dev Returns the downcasted uint168 from uint256, reverting on
* overflow (when the input is greater than largest uint168).
*
* Counterpart to Solidity's `uint168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toUint168(uint256 value) internal pure returns (uint168) {
if (value > type(uint168).max) {
revert SafeCastOverflowedUintDowncast(168, value);
}
return uint168(value);
}
/**
* @dev Returns the downcasted uint160 from uint256, reverting on
* overflow (when the input is greater than largest uint160).
*
* Counterpart to Solidity's `uint160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toUint160(uint256 value) internal pure returns (uint160) {
if (value > type(uint160).max) {
revert SafeCastOverflowedUintDowncast(160, value);
}
return uint160(value);
}
/**
* @dev Returns the downcasted uint152 from uint256, reverting on
* overflow (when the input is greater than largest uint152).
*
* Counterpart to Solidity's `uint152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toUint152(uint256 value) internal pure returns (uint152) {
if (value > type(uint152).max) {
revert SafeCastOverflowedUintDowncast(152, value);
}
return uint152(value);
}
/**
* @dev Returns the downcasted uint144 from uint256, reverting on
* overflow (when the input is greater than largest uint144).
*
* Counterpart to Solidity's `uint144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toUint144(uint256 value) internal pure returns (uint144) {
if (value > type(uint144).max) {
revert SafeCastOverflowedUintDowncast(144, value);
}
return uint144(value);
}
/**
* @dev Returns the downcasted uint136 from uint256, reverting on
* overflow (when the input is greater than largest uint136).
*
* Counterpart to Solidity's `uint136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toUint136(uint256 value) internal pure returns (uint136) {
if (value > type(uint136).max) {
revert SafeCastOverflowedUintDowncast(136, value);
}
return uint136(value);
}
/**
* @dev Returns the downcasted uint128 from uint256, reverting on
* overflow (when the input is greater than largest uint128).
*
* Counterpart to Solidity's `uint128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toUint128(uint256 value) internal pure returns (uint128) {
if (value > type(uint128).max) {
revert SafeCastOverflowedUintDowncast(128, value);
}
return uint128(value);
}
/**
* @dev Returns the downcasted uint120 from uint256, reverting on
* overflow (when the input is greater than largest uint120).
*
* Counterpart to Solidity's `uint120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toUint120(uint256 value) internal pure returns (uint120) {
if (value > type(uint120).max) {
revert SafeCastOverflowedUintDowncast(120, value);
}
return uint120(value);
}
/**
* @dev Returns the downcasted uint112 from uint256, reverting on
* overflow (when the input is greater than largest uint112).
*
* Counterpart to Solidity's `uint112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toUint112(uint256 value) internal pure returns (uint112) {
if (value > type(uint112).max) {
revert SafeCastOverflowedUintDowncast(112, value);
}
return uint112(value);
}
/**
* @dev Returns the downcasted uint104 from uint256, reverting on
* overflow (when the input is greater than largest uint104).
*
* Counterpart to Solidity's `uint104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toUint104(uint256 value) internal pure returns (uint104) {
if (value > type(uint104).max) {
revert SafeCastOverflowedUintDowncast(104, value);
}
return uint104(value);
}
/**
* @dev Returns the downcasted uint96 from uint256, reverting on
* overflow (when the input is greater than largest uint96).
*
* Counterpart to Solidity's `uint96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toUint96(uint256 value) internal pure returns (uint96) {
if (value > type(uint96).max) {
revert SafeCastOverflowedUintDowncast(96, value);
}
return uint96(value);
}
/**
* @dev Returns the downcasted uint88 from uint256, reverting on
* overflow (when the input is greater than largest uint88).
*
* Counterpart to Solidity's `uint88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toUint88(uint256 value) internal pure returns (uint88) {
if (value > type(uint88).max) {
revert SafeCastOverflowedUintDowncast(88, value);
}
return uint88(value);
}
/**
* @dev Returns the downcasted uint80 from uint256, reverting on
* overflow (when the input is greater than largest uint80).
*
* Counterpart to Solidity's `uint80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toUint80(uint256 value) internal pure returns (uint80) {
if (value > type(uint80).max) {
revert SafeCastOverflowedUintDowncast(80, value);
}
return uint80(value);
}
/**
* @dev Returns the downcasted uint72 from uint256, reverting on
* overflow (when the input is greater than largest uint72).
*
* Counterpart to Solidity's `uint72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toUint72(uint256 value) internal pure returns (uint72) {
if (value > type(uint72).max) {
revert SafeCastOverflowedUintDowncast(72, value);
}
return uint72(value);
}
/**
* @dev Returns the downcasted uint64 from uint256, reverting on
* overflow (when the input is greater than largest uint64).
*
* Counterpart to Solidity's `uint64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toUint64(uint256 value) internal pure returns (uint64) {
if (value > type(uint64).max) {
revert SafeCastOverflowedUintDowncast(64, value);
}
return uint64(value);
}
/**
* @dev Returns the downcasted uint56 from uint256, reverting on
* overflow (when the input is greater than largest uint56).
*
* Counterpart to Solidity's `uint56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toUint56(uint256 value) internal pure returns (uint56) {
if (value > type(uint56).max) {
revert SafeCastOverflowedUintDowncast(56, value);
}
return uint56(value);
}
/**
* @dev Returns the downcasted uint48 from uint256, reverting on
* overflow (when the input is greater than largest uint48).
*
* Counterpart to Solidity's `uint48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toUint48(uint256 value) internal pure returns (uint48) {
if (value > type(uint48).max) {
revert SafeCastOverflowedUintDowncast(48, value);
}
return uint48(value);
}
/**
* @dev Returns the downcasted uint40 from uint256, reverting on
* overflow (when the input is greater than largest uint40).
*
* Counterpart to Solidity's `uint40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toUint40(uint256 value) internal pure returns (uint40) {
if (value > type(uint40).max) {
revert SafeCastOverflowedUintDowncast(40, value);
}
return uint40(value);
}
/**
* @dev Returns the downcasted uint32 from uint256, reverting on
* overflow (when the input is greater than largest uint32).
*
* Counterpart to Solidity's `uint32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toUint32(uint256 value) internal pure returns (uint32) {
if (value > type(uint32).max) {
revert SafeCastOverflowedUintDowncast(32, value);
}
return uint32(value);
}
/**
* @dev Returns the downcasted uint24 from uint256, reverting on
* overflow (when the input is greater than largest uint24).
*
* Counterpart to Solidity's `uint24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toUint24(uint256 value) internal pure returns (uint24) {
if (value > type(uint24).max) {
revert SafeCastOverflowedUintDowncast(24, value);
}
return uint24(value);
}
/**
* @dev Returns the downcasted uint16 from uint256, reverting on
* overflow (when the input is greater than largest uint16).
*
* Counterpart to Solidity's `uint16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toUint16(uint256 value) internal pure returns (uint16) {
if (value > type(uint16).max) {
revert SafeCastOverflowedUintDowncast(16, value);
}
return uint16(value);
}
/**
* @dev Returns the downcasted uint8 from uint256, reverting on
* overflow (when the input is greater than largest uint8).
*
* Counterpart to Solidity's `uint8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toUint8(uint256 value) internal pure returns (uint8) {
if (value > type(uint8).max) {
revert SafeCastOverflowedUintDowncast(8, value);
}
return uint8(value);
}
/**
* @dev Converts a signed int256 into an unsigned uint256.
*
* Requirements:
*
* - input must be greater than or equal to 0.
*/
function toUint256(int256 value) internal pure returns (uint256) {
if (value < 0) {
revert SafeCastOverflowedIntToUint(value);
}
return uint256(value);
}
/**
* @dev Returns the downcasted int248 from int256, reverting on
* overflow (when the input is less than smallest int248 or
* greater than largest int248).
*
* Counterpart to Solidity's `int248` operator.
*
* Requirements:
*
* - input must fit into 248 bits
*/
function toInt248(int256 value) internal pure returns (int248 downcasted) {
downcasted = int248(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(248, value);
}
}
/**
* @dev Returns the downcasted int240 from int256, reverting on
* overflow (when the input is less than smallest int240 or
* greater than largest int240).
*
* Counterpart to Solidity's `int240` operator.
*
* Requirements:
*
* - input must fit into 240 bits
*/
function toInt240(int256 value) internal pure returns (int240 downcasted) {
downcasted = int240(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(240, value);
}
}
/**
* @dev Returns the downcasted int232 from int256, reverting on
* overflow (when the input is less than smallest int232 or
* greater than largest int232).
*
* Counterpart to Solidity's `int232` operator.
*
* Requirements:
*
* - input must fit into 232 bits
*/
function toInt232(int256 value) internal pure returns (int232 downcasted) {
downcasted = int232(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(232, value);
}
}
/**
* @dev Returns the downcasted int224 from int256, reverting on
* overflow (when the input is less than smallest int224 or
* greater than largest int224).
*
* Counterpart to Solidity's `int224` operator.
*
* Requirements:
*
* - input must fit into 224 bits
*/
function toInt224(int256 value) internal pure returns (int224 downcasted) {
downcasted = int224(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(224, value);
}
}
/**
* @dev Returns the downcasted int216 from int256, reverting on
* overflow (when the input is less than smallest int216 or
* greater than largest int216).
*
* Counterpart to Solidity's `int216` operator.
*
* Requirements:
*
* - input must fit into 216 bits
*/
function toInt216(int256 value) internal pure returns (int216 downcasted) {
downcasted = int216(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(216, value);
}
}
/**
* @dev Returns the downcasted int208 from int256, reverting on
* overflow (when the input is less than smallest int208 or
* greater than largest int208).
*
* Counterpart to Solidity's `int208` operator.
*
* Requirements:
*
* - input must fit into 208 bits
*/
function toInt208(int256 value) internal pure returns (int208 downcasted) {
downcasted = int208(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(208, value);
}
}
/**
* @dev Returns the downcasted int200 from int256, reverting on
* overflow (when the input is less than smallest int200 or
* greater than largest int200).
*
* Counterpart to Solidity's `int200` operator.
*
* Requirements:
*
* - input must fit into 200 bits
*/
function toInt200(int256 value) internal pure returns (int200 downcasted) {
downcasted = int200(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(200, value);
}
}
/**
* @dev Returns the downcasted int192 from int256, reverting on
* overflow (when the input is less than smallest int192 or
* greater than largest int192).
*
* Counterpart to Solidity's `int192` operator.
*
* Requirements:
*
* - input must fit into 192 bits
*/
function toInt192(int256 value) internal pure returns (int192 downcasted) {
downcasted = int192(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(192, value);
}
}
/**
* @dev Returns the downcasted int184 from int256, reverting on
* overflow (when the input is less than smallest int184 or
* greater than largest int184).
*
* Counterpart to Solidity's `int184` operator.
*
* Requirements:
*
* - input must fit into 184 bits
*/
function toInt184(int256 value) internal pure returns (int184 downcasted) {
downcasted = int184(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(184, value);
}
}
/**
* @dev Returns the downcasted int176 from int256, reverting on
* overflow (when the input is less than smallest int176 or
* greater than largest int176).
*
* Counterpart to Solidity's `int176` operator.
*
* Requirements:
*
* - input must fit into 176 bits
*/
function toInt176(int256 value) internal pure returns (int176 downcasted) {
downcasted = int176(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(176, value);
}
}
/**
* @dev Returns the downcasted int168 from int256, reverting on
* overflow (when the input is less than smallest int168 or
* greater than largest int168).
*
* Counterpart to Solidity's `int168` operator.
*
* Requirements:
*
* - input must fit into 168 bits
*/
function toInt168(int256 value) internal pure returns (int168 downcasted) {
downcasted = int168(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(168, value);
}
}
/**
* @dev Returns the downcasted int160 from int256, reverting on
* overflow (when the input is less than smallest int160 or
* greater than largest int160).
*
* Counterpart to Solidity's `int160` operator.
*
* Requirements:
*
* - input must fit into 160 bits
*/
function toInt160(int256 value) internal pure returns (int160 downcasted) {
downcasted = int160(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(160, value);
}
}
/**
* @dev Returns the downcasted int152 from int256, reverting on
* overflow (when the input is less than smallest int152 or
* greater than largest int152).
*
* Counterpart to Solidity's `int152` operator.
*
* Requirements:
*
* - input must fit into 152 bits
*/
function toInt152(int256 value) internal pure returns (int152 downcasted) {
downcasted = int152(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(152, value);
}
}
/**
* @dev Returns the downcasted int144 from int256, reverting on
* overflow (when the input is less than smallest int144 or
* greater than largest int144).
*
* Counterpart to Solidity's `int144` operator.
*
* Requirements:
*
* - input must fit into 144 bits
*/
function toInt144(int256 value) internal pure returns (int144 downcasted) {
downcasted = int144(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(144, value);
}
}
/**
* @dev Returns the downcasted int136 from int256, reverting on
* overflow (when the input is less than smallest int136 or
* greater than largest int136).
*
* Counterpart to Solidity's `int136` operator.
*
* Requirements:
*
* - input must fit into 136 bits
*/
function toInt136(int256 value) internal pure returns (int136 downcasted) {
downcasted = int136(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(136, value);
}
}
/**
* @dev Returns the downcasted int128 from int256, reverting on
* overflow (when the input is less than smallest int128 or
* greater than largest int128).
*
* Counterpart to Solidity's `int128` operator.
*
* Requirements:
*
* - input must fit into 128 bits
*/
function toInt128(int256 value) internal pure returns (int128 downcasted) {
downcasted = int128(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(128, value);
}
}
/**
* @dev Returns the downcasted int120 from int256, reverting on
* overflow (when the input is less than smallest int120 or
* greater than largest int120).
*
* Counterpart to Solidity's `int120` operator.
*
* Requirements:
*
* - input must fit into 120 bits
*/
function toInt120(int256 value) internal pure returns (int120 downcasted) {
downcasted = int120(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(120, value);
}
}
/**
* @dev Returns the downcasted int112 from int256, reverting on
* overflow (when the input is less than smallest int112 or
* greater than largest int112).
*
* Counterpart to Solidity's `int112` operator.
*
* Requirements:
*
* - input must fit into 112 bits
*/
function toInt112(int256 value) internal pure returns (int112 downcasted) {
downcasted = int112(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(112, value);
}
}
/**
* @dev Returns the downcasted int104 from int256, reverting on
* overflow (when the input is less than smallest int104 or
* greater than largest int104).
*
* Counterpart to Solidity's `int104` operator.
*
* Requirements:
*
* - input must fit into 104 bits
*/
function toInt104(int256 value) internal pure returns (int104 downcasted) {
downcasted = int104(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(104, value);
}
}
/**
* @dev Returns the downcasted int96 from int256, reverting on
* overflow (when the input is less than smallest int96 or
* greater than largest int96).
*
* Counterpart to Solidity's `int96` operator.
*
* Requirements:
*
* - input must fit into 96 bits
*/
function toInt96(int256 value) internal pure returns (int96 downcasted) {
downcasted = int96(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(96, value);
}
}
/**
* @dev Returns the downcasted int88 from int256, reverting on
* overflow (when the input is less than smallest int88 or
* greater than largest int88).
*
* Counterpart to Solidity's `int88` operator.
*
* Requirements:
*
* - input must fit into 88 bits
*/
function toInt88(int256 value) internal pure returns (int88 downcasted) {
downcasted = int88(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(88, value);
}
}
/**
* @dev Returns the downcasted int80 from int256, reverting on
* overflow (when the input is less than smallest int80 or
* greater than largest int80).
*
* Counterpart to Solidity's `int80` operator.
*
* Requirements:
*
* - input must fit into 80 bits
*/
function toInt80(int256 value) internal pure returns (int80 downcasted) {
downcasted = int80(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(80, value);
}
}
/**
* @dev Returns the downcasted int72 from int256, reverting on
* overflow (when the input is less than smallest int72 or
* greater than largest int72).
*
* Counterpart to Solidity's `int72` operator.
*
* Requirements:
*
* - input must fit into 72 bits
*/
function toInt72(int256 value) internal pure returns (int72 downcasted) {
downcasted = int72(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(72, value);
}
}
/**
* @dev Returns the downcasted int64 from int256, reverting on
* overflow (when the input is less than smallest int64 or
* greater than largest int64).
*
* Counterpart to Solidity's `int64` operator.
*
* Requirements:
*
* - input must fit into 64 bits
*/
function toInt64(int256 value) internal pure returns (int64 downcasted) {
downcasted = int64(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(64, value);
}
}
/**
* @dev Returns the downcasted int56 from int256, reverting on
* overflow (when the input is less than smallest int56 or
* greater than largest int56).
*
* Counterpart to Solidity's `int56` operator.
*
* Requirements:
*
* - input must fit into 56 bits
*/
function toInt56(int256 value) internal pure returns (int56 downcasted) {
downcasted = int56(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(56, value);
}
}
/**
* @dev Returns the downcasted int48 from int256, reverting on
* overflow (when the input is less than smallest int48 or
* greater than largest int48).
*
* Counterpart to Solidity's `int48` operator.
*
* Requirements:
*
* - input must fit into 48 bits
*/
function toInt48(int256 value) internal pure returns (int48 downcasted) {
downcasted = int48(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(48, value);
}
}
/**
* @dev Returns the downcasted int40 from int256, reverting on
* overflow (when the input is less than smallest int40 or
* greater than largest int40).
*
* Counterpart to Solidity's `int40` operator.
*
* Requirements:
*
* - input must fit into 40 bits
*/
function toInt40(int256 value) internal pure returns (int40 downcasted) {
downcasted = int40(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(40, value);
}
}
/**
* @dev Returns the downcasted int32 from int256, reverting on
* overflow (when the input is less than smallest int32 or
* greater than largest int32).
*
* Counterpart to Solidity's `int32` operator.
*
* Requirements:
*
* - input must fit into 32 bits
*/
function toInt32(int256 value) internal pure returns (int32 downcasted) {
downcasted = int32(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(32, value);
}
}
/**
* @dev Returns the downcasted int24 from int256, reverting on
* overflow (when the input is less than smallest int24 or
* greater than largest int24).
*
* Counterpart to Solidity's `int24` operator.
*
* Requirements:
*
* - input must fit into 24 bits
*/
function toInt24(int256 value) internal pure returns (int24 downcasted) {
downcasted = int24(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(24, value);
}
}
/**
* @dev Returns the downcasted int16 from int256, reverting on
* overflow (when the input is less than smallest int16 or
* greater than largest int16).
*
* Counterpart to Solidity's `int16` operator.
*
* Requirements:
*
* - input must fit into 16 bits
*/
function toInt16(int256 value) internal pure returns (int16 downcasted) {
downcasted = int16(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(16, value);
}
}
/**
* @dev Returns the downcasted int8 from int256, reverting on
* overflow (when the input is less than smallest int8 or
* greater than largest int8).
*
* Counterpart to Solidity's `int8` operator.
*
* Requirements:
*
* - input must fit into 8 bits
*/
function toInt8(int256 value) internal pure returns (int8 downcasted) {
downcasted = int8(value);
if (downcasted != value) {
revert SafeCastOverflowedIntDowncast(8, value);
}
}
/**
* @dev Converts an unsigned uint256 into a signed int256.
*
* Requirements:
*
* - input must be less than or equal to maxInt256.
*/
function toInt256(uint256 value) internal pure returns (int256) {
// Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
if (value > uint256(type(int256).max)) {
revert SafeCastOverflowedUintToInt(value);
}
return int256(value);
}
/**
* @dev Cast a boolean (false or true) to a uint256 (0 or 1) with no jump.
*/
function toUint(bool b) internal pure returns (uint256 u) {
/// @solidity memory-safe-assembly
assembly {
u := iszero(iszero(b))
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.28;
import {Math} from "openzeppelin5/utils/math/Math.sol";
// solhint-disable private-vars-leading-underscore
library Rounding {
Math.Rounding internal constant UP = Math.Rounding.Ceil;
Math.Rounding internal constant DOWN = Math.Rounding.Floor;
Math.Rounding internal constant DEBT_TO_ASSETS = Math.Rounding.Ceil;
// COLLATERAL_TO_ASSETS is used to calculate borrower collateral (so we want to round down)
Math.Rounding internal constant COLLATERAL_TO_ASSETS = Math.Rounding.Floor;
// why DEPOSIT_TO_ASSETS is Up if COLLATERAL_TO_ASSETS is Down?
// DEPOSIT_TO_ASSETS is used for preview deposit and deposit, based on provided shares we want to pull "more" tokens
// so we rounding up, "token flow" is in different direction than for COLLATERAL_TO_ASSETS, that's why
// different rounding policy
Math.Rounding internal constant DEPOSIT_TO_ASSETS = Math.Rounding.Ceil;
Math.Rounding internal constant DEPOSIT_TO_SHARES = Math.Rounding.Floor;
Math.Rounding internal constant BORROW_TO_ASSETS = Math.Rounding.Floor;
Math.Rounding internal constant BORROW_TO_SHARES = Math.Rounding.Ceil;
Math.Rounding internal constant MAX_BORROW_TO_ASSETS = Math.Rounding.Floor;
Math.Rounding internal constant MAX_BORROW_TO_SHARES = Math.Rounding.Floor;
Math.Rounding internal constant MAX_BORROW_VALUE = Math.Rounding.Floor;
Math.Rounding internal constant REPAY_TO_ASSETS = Math.Rounding.Ceil;
Math.Rounding internal constant REPAY_TO_SHARES = Math.Rounding.Floor;
Math.Rounding internal constant MAX_REPAY_TO_ASSETS = Math.Rounding.Ceil;
Math.Rounding internal constant WITHDRAW_TO_ASSETS = Math.Rounding.Floor;
Math.Rounding internal constant WITHDRAW_TO_SHARES = Math.Rounding.Ceil;
Math.Rounding internal constant MAX_WITHDRAW_TO_ASSETS = Math.Rounding.Floor;
Math.Rounding internal constant MAX_WITHDRAW_TO_SHARES = Math.Rounding.Floor;
Math.Rounding internal constant LIQUIDATE_TO_SHARES = Math.Rounding.Floor;
Math.Rounding internal constant LTV = Math.Rounding.Ceil;
Math.Rounding internal constant ACCRUED_INTEREST = Math.Rounding.Floor;
Math.Rounding internal constant DAO_REVENUE = Math.Rounding.Ceil;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC1363.sol)
pragma solidity ^0.8.20;
import {IERC20} from "./IERC20.sol";
import {IERC165} from "./IERC165.sol";
/**
* @title IERC1363
* @dev Interface of the ERC-1363 standard as defined in the https://eips.ethereum.org/EIPS/eip-1363[ERC-1363].
*
* Defines an extension interface for ERC-20 tokens that supports executing code on a recipient contract
* after `transfer` or `transferFrom`, or code on a spender contract after `approve`, in a single transaction.
*/
interface IERC1363 is IERC20, IERC165 {
/*
* Note: the ERC-165 identifier for this interface is 0xb0202a11.
* 0xb0202a11 ===
* bytes4(keccak256('transferAndCall(address,uint256)')) ^
* bytes4(keccak256('transferAndCall(address,uint256,bytes)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256)')) ^
* bytes4(keccak256('transferFromAndCall(address,address,uint256,bytes)')) ^
* bytes4(keccak256('approveAndCall(address,uint256)')) ^
* bytes4(keccak256('approveAndCall(address,uint256,bytes)'))
*/
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferAndCall(address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the allowance mechanism
* and then calls {IERC1363Receiver-onTransferReceived} on `to`.
* @param from The address which you want to send tokens from.
* @param to The address which you want to transfer to.
* @param value The amount of tokens to be transferred.
* @param data Additional data with no specified format, sent in call to `to`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function transferFromAndCall(address from, address to, uint256 value, bytes calldata data) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value) external returns (bool);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens and then calls {IERC1363Spender-onApprovalReceived} on `spender`.
* @param spender The address which will spend the funds.
* @param value The amount of tokens to be spent.
* @param data Additional data with no specified format, sent in call to `spender`.
* @return A boolean value indicating whether the operation succeeded unless throwing.
*/
function approveAndCall(address spender, uint256 value, bytes calldata data) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
import {Errors} from "./Errors.sol";
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @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 Errors.InsufficientBalance(address(this).balance, amount);
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert Errors.FailedCall();
}
}
/**
* @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
* {Errors.FailedCall} 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 Errors.InsufficientBalance(address(this).balance, value);
}
(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 {Errors.FailedCall}) 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 {Errors.FailedCall} 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 {Errors.FailedCall}.
*/
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 Errors.FailedCall();
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC-165 standard, as defined in the
* https://eips.ethereum.org/EIPS/eip-165[ERC].
*
* 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[ERC 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
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC165.sol)
pragma solidity ^0.8.20;
import {IERC165} from "../utils/introspection/IERC165.sol";// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;
/**
* @dev Collection of common custom errors used in multiple contracts
*
* IMPORTANT: Backwards compatibility is not guaranteed in future versions of the library.
* It is recommended to avoid relying on the error API for critical functionality.
*/
library Errors {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error InsufficientBalance(uint256 balance, uint256 needed);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedCall();
/**
* @dev The deployment failed.
*/
error FailedDeployment();
}{
"remappings": [
"forge-std/=lib/forge-std/src/",
"@openzeppelin/contracts-upgradeable/=node_modules/@openzeppelin/contracts-upgradeable/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"@tokenized-strategy/=lib/tokenized-strategy/src/",
"@periphery/=lib/tokenized-strategy-periphery/src/",
"@pendle/core-v2/=node_modules/@pendle/core-v2/",
"@balancer-v3-monorepo/interfaces/=lib/balancer-v3-monorepo/pkg/interfaces/contracts/",
"@balancer-v3-monorepo/vault/=lib/balancer-v3-monorepo/pkg/vault/contracts/",
"@permit2/=lib/permit2/src/",
"silo-core/=lib/silo-contracts-v2/silo-core/",
"@uniswap/v3-core/=lib/silo-contracts-v2/gitmodules/uniswap/v3-core/",
"@yearn-vaults/=lib/tokenized-strategy-periphery/lib/yearn-vaults-v3/contracts/",
"ERC4626/=lib/silo-contracts-v2/gitmodules/crytic/properties/lib/ERC4626/contracts/",
"a16z-erc4626-tests/=lib/silo-contracts-v2/gitmodules/a16z-erc4626-tests/",
"balancer-labs/v2-interfaces/=lib/silo-contracts-v2/external/balancer-v2-monorepo/pkg/interfaces/contracts/",
"balancer-labs/v2-liquidity-mining/=lib/silo-contracts-v2/external/balancer-v2-monorepo/pkg/liquidity-mining/contracts/",
"balancer-labs/v2-solidity-utils/=lib/silo-contracts-v2/external/balancer-v2-monorepo/pkg/solidity-utils/contracts/",
"balancer-v3-monorepo/=lib/balancer-v3-monorepo/",
"chainlink-ccip/=lib/silo-contracts-v2/gitmodules/chainlink-ccip/contracts/src/",
"chainlink/=lib/silo-contracts-v2/gitmodules/chainlink/contracts/src/",
"createx/=lib/silo-contracts-v2/gitmodules/pyth-sdk-solidity/lazer/contracts/evm/lib/createx/src/",
"crytic/=lib/silo-contracts-v2/gitmodules/crytic/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/tokenized-strategy/lib/erc4626-tests/",
"forge-gas-snapshot/=lib/permit2/lib/forge-gas-snapshot/src/",
"halmos-cheatcodes/=lib/openzeppelin-contracts/lib/halmos-cheatcodes/src/",
"morpho-blue/=lib/silo-contracts-v2/gitmodules/morpho-blue/src/",
"openzeppelin-contracts-5/=lib/silo-contracts-v2/gitmodules/openzeppelin-contracts-5/",
"openzeppelin-contracts-upgradeable-5/=lib/silo-contracts-v2/gitmodules/openzeppelin-contracts-upgradeable-5/",
"openzeppelin-contracts-upgradeable/=lib/silo-contracts-v2/gitmodules/pyth-sdk-solidity/lazer/contracts/evm/lib/openzeppelin-contracts-upgradeable/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/",
"openzeppelin/=lib/tokenized-strategy/lib/openzeppelin-contracts/contracts/",
"openzeppelin5-upgradeable/=lib/silo-contracts-v2/gitmodules/openzeppelin-contracts-upgradeable-5/contracts/",
"openzeppelin5/=lib/silo-contracts-v2/gitmodules/openzeppelin-contracts-5/contracts/",
"permit2/=lib/permit2/",
"properties/=lib/silo-contracts-v2/gitmodules/crytic/properties/contracts/",
"pyth-sdk-solidity/=lib/silo-contracts-v2/gitmodules/pyth-sdk-solidity/target_chains/ethereum/sdk/solidity/",
"silo-contracts-v2/=lib/silo-contracts-v2/",
"silo-foundry-utils/=lib/silo-contracts-v2/gitmodules/silo-foundry-utils/contracts/",
"silo-oracles/=lib/silo-contracts-v2/silo-oracles/",
"silo-vaults/=lib/silo-contracts-v2/silo-vaults/",
"solady/=lib/silo-contracts-v2/gitmodules/pyth-sdk-solidity/lazer/contracts/evm/lib/createx/lib/solady/",
"solmate/=lib/permit2/lib/solmate/",
"tokenized-strategy-periphery/=lib/tokenized-strategy-periphery/",
"tokenized-strategy/=lib/tokenized-strategy/",
"uniswap/=lib/silo-contracts-v2/gitmodules/uniswap/",
"ve-silo/=lib/silo-contracts-v2/ve-silo/",
"yearn-vaults-v3/=lib/tokenized-strategy-periphery/lib/yearn-vaults-v3/"
],
"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
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"string","name":"_name","type":"string"},{"internalType":"address","name":"_ceresSwapper","type":"address"},{"internalType":"address","name":"_pendleMarket","type":"address"},{"internalType":"address","name":"_siloMarketConfig","type":"address"},{"internalType":"bool","name":"isCollateralProtected","type":"bool"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"uint256","name":"maxValue","type":"uint256"}],"name":"AboveMaxValue","type":"error"},{"inputs":[],"name":"InvalidAddress","type":"error"},{"inputs":[],"name":"InvalidAsset","type":"error"},{"inputs":[],"name":"InvalidFlashLoanAmount","type":"error"},{"inputs":[],"name":"InvalidFlashLoanCaller","type":"error"},{"inputs":[],"name":"InvalidFlashLoanInitiator","type":"error"},{"inputs":[],"name":"InvalidFlashLoanToken","type":"error"},{"inputs":[],"name":"InvalidPendleMarket","type":"error"},{"inputs":[],"name":"InvalidPrice","type":"error"},{"inputs":[],"name":"InvalidSiloMarket","type":"error"},{"inputs":[],"name":"InvalidToken","type":"error"},{"inputs":[],"name":"InvalidValue","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"SlippageLimitExceeded","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"assetAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"ptAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"borrowTokenBalance","type":"uint256"}],"name":"DepositIdleAssets","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"DepositLimitSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amountRepaid","type":"uint256"}],"name":"FlashLoan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"lossAmount","type":"uint256"}],"name":"LossOnWithdrawal","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"slippage","type":"uint256"}],"name":"MaxSlippageSet","type":"event"},{"anonymous":false,"inputs":[],"name":"StrategyFundsDeallocated","type":"event"},{"anonymous":false,"inputs":[],"name":"StrategyRebalance","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"updatedSwapper","type":"address"}],"name":"SwapperSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"targetLtv","type":"uint256"}],"name":"TargetLtvSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"TokensRecovered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint32","name":"","type":"uint32"}],"name":"TwapDurationSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"","type":"uint256"}],"name":"WithdrawLimitSet","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"BORROW_SILO","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"BORROW_TOKEN_DECIMALS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DEPOSIT_SILO","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PENDLE_MARKET","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PT","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PT_TOKEN_DECIMALS","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SILO_BORROW_TOKEN","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SILO_COLLATERAL_TYPE","outputs":[{"internalType":"enum ISilo.CollateralType","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"SILO_CONFIG","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"availableDepositLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"availableWithdrawLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ltvBuffer","type":"uint256"}],"name":"calculateFlashLoanAmount","outputs":[{"internalType":"uint256","name":"flashLoanAmount","type":"uint256"},{"internalType":"bool","name":"isLeverageUp","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"ceresSwapper","outputs":[{"internalType":"contract ICeresSwapper","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"configMaxSlippageBps","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"configTargetLtv","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"configTwapDuration","outputs":[{"internalType":"uint32","name":"","type":"uint32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetAmount","type":"uint256"}],"name":"convertAssetToBorrowToken","outputs":[{"internalType":"uint256","name":"borrowTokenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assetAmount","type":"uint256"}],"name":"convertAssetToPt","outputs":[{"internalType":"uint256","name":"ptAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"tokenAmount","type":"uint256"}],"name":"convertBorrowTokenToAsset","outputs":[{"internalType":"uint256","name":"assetAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"borrowTokenAmount","type":"uint256"}],"name":"convertBorrowTokenToPt","outputs":[{"internalType":"uint256","name":"ptAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ptAmount","type":"uint256"}],"name":"convertPtToAsset","outputs":[{"internalType":"uint256","name":"assetAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"ptAmount","type":"uint256"}],"name":"convertPtToBorrowToken","outputs":[{"internalType":"uint256","name":"borrowTokenAmount","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deployFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositIdleStrategyAssets","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"depositLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"freeFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getMaxLtvSilo","outputs":[{"internalType":"uint256","name":"maxLtv","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPositionCollateral","outputs":[{"internalType":"uint256","name":"collateralBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPositionDebt","outputs":[{"internalType":"uint256","name":"debtBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPositionLtv","outputs":[{"internalType":"uint256","name":"positionLtv","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRealAssetBalance","outputs":[{"internalType":"uint256","name":"totalAssets","type":"uint256"},{"internalType":"uint256","name":"totalCollateral","type":"uint256"},{"internalType":"uint256","name":"totalDebt","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getSiloCollateralTokenPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvestAndReport","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"initiator","type":"address"},{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"fee","type":"uint256"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"onFlashLoan","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"flashLoanAmount","type":"uint256"},{"internalType":"bool","name":"isLeverageUp","type":"bool"},{"internalType":"bool","name":"shouldDepositIdleAssets","type":"bool"},{"internalType":"bytes","name":"swapData","type":"bytes"}],"name":"rebalanceStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"ltvBuffer","type":"uint256"},{"internalType":"bool","name":"shouldDepositIdleAssets","type":"bool"}],"name":"rebalanceStrategy","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"rescueTokens","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"revokeSwapperApprovals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_depositLimit","type":"uint256"}],"name":"setDepositLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxSlippageBps","type":"uint256"}],"name":"setMaxSlippageBps","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_swapper","type":"address"}],"name":"setSwapper","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"setSwapperApprovals","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_targetLtv","type":"uint256"}],"name":"setTargetLtv","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint32","name":"_twapDuration","type":"uint32"}],"name":"setTwapDuration","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_withdrawLimit","type":"uint256"}],"name":"setWithdrawLimit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"shutdownWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_totalIdle","type":"uint256"}],"name":"tendThis","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"tendTrigger","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"tokenizedStrategyAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"withdrawLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"}]Contract Creation Code
6101e06040526706f05b59d3b2000060015560646002556003805463ffffffff19166107081790555f19600481905560055534801561003c575f5ffd5b5060405161577e38038061577e83398101604081905261005b91610945565b6001600160a01b0386166080523060a052604051869086906100c09061008d9084908490339081908190602401610a48565b60408051601f198184030181529190526020810180516001600160e01b03908116634b839d7360e11b1790915261040616565b505073d377919fa87120584b21279a491f82d5265a139c7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc55506001600160a01b038416158061011757506001600160a01b038316155b8061012957506001600160a01b038216155b156101475760405163e6c4247b60e01b815260040160405180910390fd5b61015083610490565b6001600160a01b03831660c081905260408051630b2339af60e21b815290515f9291632c8ce6bc9160048083019260609291908290030181865afa15801561019a573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906101be9190610aac565b506001600160a01b03811660e05291506101d9905083610591565b6001600160a01b0390811661014081905291811661012052841661010052604080516338d52e0f60e01b815290516338d52e0f916004808201926020929091908290030181865afa158015610230573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102549190610af6565b6001600160a01b03166101608190526040805163313ce56760e01b8152905163313ce567916004808201926020929091908290030181865afa15801561029c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906102c09190610b11565b60ff166101808160ff168152505060e0516001600160a01b031663313ce5676040518163ffffffff1660e01b8152600401602060405180830381865afa15801561030c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103309190610b11565b60ff166101a08190526040805163313ce56760e01b815290516001600160a01b038a169163313ce5679160048083019260209291908290030181865afa15801561037c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103a09190610b11565b60ff16146103c15760405163a1ea7a6f60e01b815260040160405180910390fd5b81156103d1575f6101c0526103d8565b60016101c0525b5f80546001600160a01b0319166001600160a01b0387161790556103fa610722565b50505050505050610b7f565b60605f5f73d377919fa87120584b21279a491f82d5265a139c6001600160a01b0316846040516104369190610b31565b5f60405180830381855af49150503d805f811461046e576040519150601f19603f3d011682016040523d82523d5f602084013e610473565b606091505b509150915081610489576040513d805f833e8082fd5b9392505050565b5f816001600160a01b0316632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa1580156104cd573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104f19190610aac565b505090505f816001600160a01b03166376d5de856040518163ffffffff1660e01b8152600401602060405180830381865afa158015610532573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105569190610af6565b9050806001600160a01b03166080516001600160a01b03161461058c57604051636448d6e960e11b815260040160405180910390fd5b505050565b5f5f5f5f846001600160a01b031663aecc90cb6040518163ffffffff1660e01b81526004016040805180830381865afa1580156105d0573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906105f49190610b47565b91509150816001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610634573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106589190610af6565b6001600160a01b031660e0516001600160a01b03160361067d5781935080925061071b565b806001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156106b9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906106dd9190610af6565b6001600160a01b031660e0516001600160a01b0316036107025780935081925061071b565b6040516336d336ef60e01b815260040160405180910390fd5b5050915091565b5f546001600160a01b0316806107355750565b60805161074d906001600160a01b0316825f19610781565b60e051610765906001600160a01b0316825f19610781565b6101605161077e906001600160a01b0316825f19610781565b50565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b179091526107d9908590839061084516565b61083f57604080516001600160a01b03851660248201525f6044808301919091528251808303909101815260649091019091526020810180516001600160e01b0390811663095ea7b360e01b1790915261083591869161088e16565b61083f848261088e565b50505050565b5f5f5f5f60205f8651602088015f8a5af192503d91505f519050828015610884575081156108765780600114610884565b5f866001600160a01b03163b115b9695505050505050565b5f5f60205f8451602086015f885af1806108ad576040513d5f823e3d81fd5b50505f513d915081156108c45780600114156108d1565b6001600160a01b0384163b155b1561083f57604051635274afe760e01b81526001600160a01b038516600482015260240160405180910390fd5b6001600160a01b038116811461077e575f5ffd5b805161091d816108fe565b919050565b634e487b7160e01b5f52604160045260245ffd5b8051801515811461091d575f5ffd5b5f5f5f5f5f5f60c0878903121561095a575f5ffd5b8651610965816108fe565b60208801519096506001600160401b03811115610980575f5ffd5b8701601f81018913610990575f5ffd5b80516001600160401b038111156109a9576109a9610922565b604051601f8201601f19908116603f011681016001600160401b03811182821017156109d7576109d7610922565b6040528181528282016020018b10156109ee575f5ffd5b8160208401602083015e5f60208383010152809750505050610a1260408801610912565b9350610a2060608801610912565b9250610a2e60808801610912565b9150610a3c60a08801610936565b90509295509295509295565b60018060a01b038616815260a060208201525f85518060a0840152806020880160c085015e5f60c08285018101919091526001600160a01b0396871660408501529486166060840152929094166080820152601f909101601f191601019392505050565b5f5f5f60608486031215610abe575f5ffd5b8351610ac9816108fe565b6020850151909350610ada816108fe565b6040850151909250610aeb816108fe565b809150509250925092565b5f60208284031215610b06575f5ffd5b8151610489816108fe565b5f60208284031215610b21575f5ffd5b815160ff81168114610489575f5ffd5b5f82518060208501845e5f920191825250919050565b5f5f60408385031215610b58575f5ffd5b8251610b63816108fe565b6020840151909250610b74816108fe565b809150509250929050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516101c051614933610e4b5f395f818161054e01528181611fc90152818161241d01528181612ed801526131cc01525f8181610450015281816121200152818161283d0152612a0801525f8181610708015281816120e2015281816127fc0152612a4501525f818161058a0152818161086201528181610ad701528181610bca015281816118550152818161198d01528181611b0c01528181611bcc01528181611f11015281816121f401528181612373015281816124e1015281816125b8015281816129ba01528181612cb2015261308c01525f818161032001528181610a6e01528181610bec01528181610e2401528181611ee201528181612589015281816126e501528181612f8701528181612fd5015281816130ae01526130ef01525f81816103870152818161174501528181611f9a015281816123ee0152818161274d01528181612b4201528181612e6b01528181612ea7015261319901525f818161072f015261277901525f81816106e101528181610826015281816118cf01528181611de301528181612051015281816121b30152818161233e015281816124a4015281816128890152818161298601528181612de401528181612e49015261324a01525f81816103ae0152818161266e01528181612aaf01528181612d8b015261321301525f818161078b015281816109e601528181610c5601528181610d7a01528181610ee301528181610faf0152818161100f01528181611176015281816112a0015281816113af01528181611434015281816114dc0152818161160f0152818161166f01526132ef01525f81816107eb01528181611892015281816119af01528181611aea01528181611baa01528181612245015281816122a7015281816123090152818161295201528181612cd401528181612dc2015261326c01526149335ff3fe608060405234801561000f575f5ffd5b50600436106102d3575f3560e01c8063836e5c7c11610180578063c73730e9116100e7578063d34fde93116100a0578063e54f6c661161007a578063e54f6c661461072a578063ecf7085814610751578063f848d5411461075a578063fde813a814610763576102d3565b8063d34fde93146106d4578063d94073d4146106dc578063dab6f76514610703576102d3565b8063c73730e91461066f578063c7ba912714610678578063c96184ca1461068b578063ccb603d814610693578063cf2a546d146106a6578063d19a3bb8146106b9576102d3565b80639c82f2a4116101395780639c82f2a4146106085780639d7fb70c1461061b578063ac5804541461062e578063bb94ea4b14610636578063bdc8144b14610649578063c433c80a1461065c576102d3565b8063836e5c7c146105855780638c2c679e146105ac578063908ee91c146105b4578063995113be146105c75780639a26ee56146105d05780639c635a46146105e3576102d3565b8063419a7ce31161023f57806350743cbf116101f85780635d265d3f116101d25780635d265d3f146105205780636579f2e514610536578063771f6b1f1461054957806381400a3e1461057d576102d3565b806350743cbf146104e25780635159dd68146105055780635322f9b81461050d576102d3565b8063419a7ce31461048457806346aa2f121461049757806349317f1d146104ac5780634abdf2e0146104b45780634c00ed9b146104c7578063503160d9146104cf576102d3565b8063146135a311610291578063146135a3146103d857806323e30c8b146103ea57806325fc1b3d146103fd5780633722cb4b146104105780633d6cb575146104385780633fc928cd1461044b576102d3565b8062ae3bf81461030857806302f141c11461031b57806304bd46291461035f5780630a2cc185146103825780630f61ac82146103a95780630fbe8b3c146103d0575b73d377919fa87120584b21279a491f82d5265a139c365f80375f5f365f845af43d5f5f3e808015610302573d5ff35b3d5ffd5b005b610306610316366004613eac565b610776565b6103427f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b61037461036d366004613eac565b5060055490565b604051908152602001610356565b6103427f000000000000000000000000000000000000000000000000000000000000000081565b6103427f000000000000000000000000000000000000000000000000000000000000000081565b610306610979565b5f54610342906001600160a01b031681565b6103746103f8366004613f0c565b610a62565b61030661040b366004613f83565b610c41565b61042361041e366004613f83565b610d17565b60408051928352901515602083015201610356565b610306610446366004613f83565b610d2b565b6104727f000000000000000000000000000000000000000000000000000000000000000081565b60405160ff9091168152602001610356565b610374610492366004613f83565b610d3f565b6103746104a5366004613eac565b5060045490565b610374610d4f565b6103066104c2366004613f83565b610d65565b610374610e0d565b6103066104dd366004613f83565b610eb0565b6104ea610eb8565b60408051938452602084019290925290820152606001610356565b610306610ece565b61030661051b366004613fa7565b610f4b565b610528611108565b604051610356929190614043565b610374610544366004613f83565b611145565b6105707f000000000000000000000000000000000000000000000000000000000000000081565b6040516103569190614091565b61037461114f565b6103427f000000000000000000000000000000000000000000000000000000000000000081565b610374611158565b6103066105c2366004613f83565b611161565b61037460025481565b6103746105de366004613f83565b611281565b6003546105f39063ffffffff1681565b60405163ffffffff9091168152602001610356565b610306610616366004613eac565b61128b565b610306610629366004613f83565b611389565b61030661139a565b610374610644366004613f83565b611415565b610306610657366004613f83565b61141f565b61030661066a36600461409f565b6114c7565b61037460015481565b6103066106863660046140c2565b6115ab565b61037461172e565b6103746106a1366004613f83565b611790565b6103746106b4366004613f83565b61179a565b61034273d377919fa87120584b21279a491f82d5265a139c81565b6103746117a4565b6103427f000000000000000000000000000000000000000000000000000000000000000081565b6104727f000000000000000000000000000000000000000000000000000000000000000081565b6103427f000000000000000000000000000000000000000000000000000000000000000081565b61037460045481565b61037460055481565b610306610771366004613f83565b6117ad565b6040516320b8029160e21b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906382e00a44906024015f6040518083038186803b1580156107d3575f5ffd5b505afa1580156107e5573d5f5f3e3d5ffd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b0316148061085a57507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b0316145b8061089657507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316816001600160a01b0316145b156108b45760405163c1ab6dc160e01b815260040160405180910390fd5b6040516370a0823160e01b81523060048201525f906001600160a01b038316906370a0823190602401602060405180830381865afa1580156108f8573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061091c91906140f0565b90506109326001600160a01b03831633836117be565b816001600160a01b03167f46d2e6e71fc567877b817ff3d940571f989d4ee4d40f2b70806d36e738feef6f8260405161096d91815260200190565b60405180910390a25050565b5f610982611822565b600c810154909150600160a01b900460ff166001146109bc5760405162461bcd60e51b81526004016109b390614107565b60405180910390fd5b600c8101805460ff60a01b1916600160a11b17905560405163d43fdcf760e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063d43fdcf7906024015f6040518083038186803b158015610a2e575f5ffd5b505afa158015610a40573d5f5f3e3d5ffd5b50505050610a4c61184f565b600c01805460ff60a01b1916600160a01b179055565b5f336001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614610aac5760405163e17c49b760e01b815260040160405180910390fd5b6001600160a01b0387163014610ad5576040516377be635b60e11b815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316866001600160a01b031614610b2757604051633dc3c03d60e01b815260040160405180910390fd5b5f8080610b36858701876141ad565b925092509250878314610b5c5760405163dc25cb1560e01b815260040160405180910390fd5b5f610b67888a614270565b90508215610b7f57610b7a89828461194a565b610b8a565b610b8a898284611a58565b6040518181527f97bf554031869ec4edcf72b0dcdc2234dd406afe091f3631be088f348e1795749060200160405180910390a1610c116001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083611c44565b507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd99a9950505050505050505050565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610c9e575f5ffd5b505afa158015610cb0573d5f5f3e3d5ffd5b505050506101f4811115610cdb57604051634f70461160e01b81526101f460048201526024016109b3565b60028190556040518181527f655eeddda94c0a9de22c1474e6b5aa4f18d3e8048dc9eff185437c7fe3bfb505906020015b60405180910390a150565b5f5f610d2283611cd9565b91509150915091565b610d33611d9a565b610d3c81611dd1565b50565b5f610d49826120cb565b92915050565b5f610d58611d9a565b610d6061216d565b905090565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610dc2575f5ffd5b505afa158015610dd4573d5f5f3e3d5ffd5b5050506005829055506040518181527fbc8c1565f6722bdddca67f542dddabedb4e66481264dafda5d475b7327b5b94c90602001610d0c565b60405163f5125d3f60e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201523060248201525f9073925d5466d4d5b01995e20e1245924ada6415126a9063f5125d3f906044015b602060405180830381865afa158015610e8c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d6091906140f0565b610d3c611d9a565b5f5f5f610ec36121a3565b925092509250909192565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015610f2b575f5ffd5b505afa158015610f3d573d5f5f3e3d5ffd5b50505050610f496122e9565b565b5f610f54611822565b600c810154909150600160a01b900460ff16600114610f855760405162461bcd60e51b81526004016109b390614107565b600c8101805460ff60a01b1916600160a11b17905560405163d43fdcf760e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063d43fdcf7906024015f6040518083038186803b158015610ff7575f5ffd5b505afa158015611009573d5f5f3e3d5ffd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bf86d6906040518163ffffffff1660e01b8152600401602060405180830381865afa158015611069573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061108d919061428e565b1561109f5761109a61239b565b6110ed565b83156110ad576110ad61184f565b6110ed868685858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061254692505050565b600c01805460ff60a01b1916600160a01b1790555050505050565b5f6060611113612653565b6040805160048152602481019091526020810180516001600160e01b031663440368a360e01b17905290939092509050565b5f610d498261265c565b5f610d606126ce565b5f610d60612736565b60405163d43fdcf760e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063d43fdcf7906024015f6040518083038186803b1580156111be575f5ffd5b505afa1580156111d0573d5f5f3e3d5ffd5b50505050670de0b6b3a7640000811061120657604051634f70461160e01b8152670de0b6b3a764000060048201526024016109b3565b5f61120f61172e565b90508061122366b1a2bc2ec5000084614270565b111561124557604051634f70461160e01b8152600481018290526024016109b3565b60018290556040518281527fc008cb9fa00383ebb3760e80ded049adfa410a42627f3dfb6fcc143f919814619060200160405180910390a15050565b5f610d4982612916565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156112e8575f5ffd5b505afa1580156112fa573d5f5f3e3d5ffd5b5050506001600160a01b03821690506113265760405163e6c4247b60e01b815260040160405180910390fd5b61132e612932565b5f80546001600160a01b0319166001600160a01b0383161790556113506122e9565b6040516001600160a01b03821681527f673779832598d6a388768ee342f8de96fdd5c39a468a6955377cf1405b9652b990602001610d0c565b611391611d9a565b610d3c816129e1565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b1580156113f7575f5ffd5b505afa158015611409573d5f5f3e3d5ffd5b50505050610f49612932565b5f610d49826129f1565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b15801561147c575f5ffd5b505afa15801561148e573d5f5f3e3d5ffd5b5050506004829055506040518181527f5d2e73196f8ba1b44e887e2bcc9bcd1f3e657add341d4a0a632ffff00d6903f290602001610d0c565b6040516348e4a64960e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906348e4a649906024015f6040518083038186803b158015611524575f5ffd5b505afa158015611536573d5f5f3e3d5ffd5b5050505061012c8163ffffffff16101561156357604051632a9ffab760e21b815260040160405180910390fd5b6003805463ffffffff191663ffffffff83169081179091556040519081527fae45eae27fdd572bcc5daa11e5155fef7d0b5081d88a374f0580bf91bfdc29b990602001610d0c565b5f6115b4611822565b600c810154909150600160a01b900460ff166001146115e55760405162461bcd60e51b81526004016109b390614107565b600c8101805460ff60a01b1916600160a11b17905560405163d43fdcf760e01b81523360048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063d43fdcf7906024015f6040518083038186803b158015611657575f5ffd5b505afa158015611669573d5f5f3e3d5ffd5b505050507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663bf86d6906040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116c9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116ed919061428e565b156116ff576116fa61239b565b611716565b811561170d5761170d61184f565b61171683612a75565b600c01805460ff60a01b1916600160a01b1790555050565b604051630323f06160e51b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201525f9073925d5466d4d5b01995e20e1245924ada6415126a9063647e0c2090602401610e71565b5f610d4982612a9d565b5f610d4982612b0f565b5f610d60612b2b565b6117b5611d9a565b610d3c81612b93565b6040516001600160a01b0383811660248301526044820183905261181d91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612ba3565b505050565b5f80610d4960017fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b16142a9565b5f6118797f0000000000000000000000000000000000000000000000000000000000000000612c0f565b9050801561188c5761188a81612c77565b505b5f6118b67f0000000000000000000000000000000000000000000000000000000000000000612c0f565b905080156118c9576118c781612d6c565b505b5f6118f37f0000000000000000000000000000000000000000000000000000000000000000612c0f565b905080156119045761190481612e3c565b60408051838152602081018390529081018490527f72c4c0049880524690614a131b2c97912dedae3fe43e6fcfb9083f1d113f8e2b9060600160405180910390a1505050565b80515f9015611a27575f61196561196086612916565b612f44565b5f546040516356ddd23d60e01b81529192506001600160a01b0316906356ddd23d906119df907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000908a90879030908b906004016142bc565b6020604051808303815f875af11580156119fb573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a1f91906140f0565b915050611a33565b611a3084612c77565b90505b5f611a3d82612d6c565b9050611a4881612e3c565b611a5184612f64565b5050505050565b611a6183612fbe565b505f612710600254612710611a769190614270565b611a7f856120cb565b611a899190614309565b611a939190614334565b90505f611a9f8261316a565b90505f611aab826131f4565b845190915015611b83575f611ac261196083612b0f565b5f546040516356ddd23d60e01b81529192506001600160a01b0316906356ddd23d90611b3c907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000908790879030908d906004016142bc565b6020604051808303815f875af1158015611b58573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b7c91906140f0565b5050611c3c565b5f5460405163efe6bda160e01b81526001600160a01b039091169063efe6bda190611bfa907f0000000000000000000000000000000000000000000000000000000000000000907f0000000000000000000000000000000000000000000000000000000000000000908a9087903090600401614347565b6020604051808303815f875af1158015611c16573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c3a91906140f0565b505b505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611c958482613298565b611cd3576040516001600160a01b0384811660248301525f6044830152611cc991869182169063095ea7b3906064016117eb565b611cd38482612ba3565b50505050565b5f5f5f5f611ce56121a3565b92505091505f611cf483612b0f565b90505f600154670de0b6b3a7640000611d0d91906142a9565b600154611d1a9084614309565b611d249190614334565b90505f611d2f610e0d565b600154909150611d3f8983614270565b1015611d5e57611d4f84836142a9565b98600198509650505050505050565b87600154611d6c9190614270565b811115611d8b57611d7d82856142a9565b985f98509650505050505050565b505f9788975095505050505050565b333014610f495760405162461bcd60e51b815260206004820152600560248201526410b9b2b63360d91b60448201526064016109b3565b5f611ddb82612a9d565b90505f611e077f0000000000000000000000000000000000000000000000000000000000000000612c0f565b9050818110611e1957611cd3826131f4565b611e2381836142a9565b91505f5f611e2f6121a3565b925050915081851115611e40578194505b5f600154670de0b6b3a7640000611e5791906142a9565b600154611e6c611e6789876142a9565b612b0f565b611e769190614309565b611e809190614334565b905081811015611f81575f611e9582846142a9565b60408051602081018390525f918101829052606080820152608081018290529192509060a00160408051601f1981840301815290829052632e7ff4ef60e11b825291506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690635cffe9de90611f3d9030907f00000000000000000000000000000000000000000000000000000000000000009087908790600401614385565b6020604051808303815f875af1158015611f59573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f7d919061428e565b5050505b6040516304b5315d60e21b81525f906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906312d4c57490611ff19030907f0000000000000000000000000000000000000000000000000000000000000000906004016143b7565b602060405180830381865afa15801561200c573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061203091906140f0565b90508086111561203e578095505b6120478661316a565b505f61207a6120757f0000000000000000000000000000000000000000000000000000000000000000612c0f565b6131f4565b9050878110156120c1577f88f5063ca4c0af21d7235eb8fb972b73ec8c3dfc382456cdba6c695035e1b9736120af828a6142a9565b60405190815260200160405180910390a15b5050505050505050565b5f8115612168575f6120db612736565b90506121087f0000000000000000000000000000000000000000000000000000000000000000600a6144b7565b6121129082614309565b670de0b6b3a76400006121467f0000000000000000000000000000000000000000000000000000000000000000600a6144b7565b6121509086614309565b61215a9190614309565b6121649190614334565b9150505b919050565b5f6121766132e1565b156121935761218361184f565b61219366b1a2bc2ec50000612a75565b61219b6121a3565b509092915050565b5f5f5f6121ae612b2b565b6121d77f0000000000000000000000000000000000000000000000000000000000000000612c0f565b6121e19190614270565b91505f6121ec6126ce565b90505f6122187f0000000000000000000000000000000000000000000000000000000000000000612c0f565b9050808211156122845761222c81836142a9565b925061223783612916565b6122408561265c565b6122697f0000000000000000000000000000000000000000000000000000000000000000612c0f565b6122739190614270565b61227d91906142a9565b94506122e2565b5f925061229961229483836142a9565b612916565b6122a28561265c565b6122cb7f0000000000000000000000000000000000000000000000000000000000000000612c0f565b6122d59190614270565b6122df9190614270565b94505b5050909192565b5f546001600160a01b0316806122fc5750565b6123316001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016825f19611c44565b6123666001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016825f19611c44565b610d3c6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016825f19611c44565b5f60018190556123a96126ce565b90506123c4815f60405180602001604052805f815250612546565b5f6123cd612b2b565b9050801561249e576040516304b5315d60e21b81525f906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906312d4c574906124459030907f0000000000000000000000000000000000000000000000000000000000000000906004016143b7565b602060405180830381865afa158015612460573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061248491906140f0565b905080821115612492578091505b61249b8261316a565b50505b5f6124c87f0000000000000000000000000000000000000000000000000000000000000000612c0f565b905080156124db576124d9816131f4565b505b5f6125057f0000000000000000000000000000000000000000000000000000000000000000612c0f565b905080156125185761251681612c77565b505b6040517f8d522e0e5c0b3f31d932913d4f534af13f714a3e10cca18fd09ddc9cff2055b3905f90a150505050565b821561181d575f838383604051602001612562939291906144c5565b60408051601f1981840301815290829052632e7ff4ef60e11b825291506001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001690635cffe9de906125e49030907f00000000000000000000000000000000000000000000000000000000000000009089908790600401614385565b6020604051808303815f875af1158015612600573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612624919061428e565b506040517f430ab3f3198f60d411e429a537683d330a9296d687fa5da362a742676ef4512f905f90a150505050565b5f610d606132e1565b5f8115612168576003545f90612699907f00000000000000000000000000000000000000000000000000000000000000009063ffffffff166133bd565b9050805f036126ba5760405162bfc92160e01b815260040160405180910390fd5b6121648382670de0b6b3a76400005f6133ff565b604051636cde875160e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201523060248201525f9073925d5466d4d5b01995e20e1245924ada6415126a9063d9bd0ea290604401610e71565b60405163e48a5f7b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000811660048301525f9182917f0000000000000000000000000000000000000000000000000000000000000000169063e48a5f7b9060240161022060405180830381865afa1580156127bf573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127e391906144f0565b60e0015190506001600160a01b038116612829576128227f0000000000000000000000000000000000000000000000000000000000000000600a6144b7565b91506128f3565b6001600160a01b0381166313b0be336128637f0000000000000000000000000000000000000000000000000000000000000000600a6144b7565b6040516001600160e01b031960e084901b16815260048101919091526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166024820152604401602060405180830381865afa1580156128cc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128f091906140f0565b91505b815f036129125760405162bfc92160e01b815260040160405180910390fd5b5090565b5f8115612168575f612927836120cb565b90506121648161265c565b5f546001600160a01b0316806129455750565b6129796001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016825f611c44565b6129ad6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016825f611c44565b610d3c6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016825f611c44565b610d3c66b1a2bc2ec50000612a75565b5f8115612168575f612a01612736565b9050612a2e7f0000000000000000000000000000000000000000000000000000000000000000600a6144b7565b612a4090670de0b6b3a7640000614309565b612a6b7f0000000000000000000000000000000000000000000000000000000000000000600a6144b7565b6121508386614309565b5f5f612a8083611cd9565b9150915061181d828260405180602001604052805f815250612546565b5f8115612168576003545f90612ada907f00000000000000000000000000000000000000000000000000000000000000009063ffffffff166133bd565b9050805f03612afb5760405162bfc92160e01b815260040160405180910390fd5b61216483670de0b6b3a7640000835f6133ff565b5f8115612168575f612b2083612a9d565b9050612164816129f1565b60405163360ae4dd60e11b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201523060248201525f9073925d5466d4d5b01995e20e1245924ada6415126a90636c15c9ba90604401610e71565b5f198103610d3357610d3c61239b565b5f5f60205f8451602086015f885af180612bc2576040513d5f823e3d81fd5b50505f513d91508115612bd9578060011415612be6565b6001600160a01b0384163b155b15611cd357604051635274afe760e01b81526001600160a01b03851660048201526024016109b3565b6040516370a0823160e01b81523060048201525f906001600160a01b038316906370a0823190602401602060405180830381865afa158015612c53573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d4991906140f0565b5f5f612c8561196084612916565b5f805460405163fd52722760e01b815292935090916001600160a01b039091169063fd52722790612d02907f0000000000000000000000000000000000000000000000000000000000000000907f000000000000000000000000000000000000000000000000000000000000000090899088903090600401614347565b6020604051808303815f875af1158015612d1e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d4291906140f0565b905081811015612d65576040516322cb457760e11b815260040160405180910390fd5b9392505050565b5f5f612d7a61196084612a9d565b5f8054604080516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116602083015294955092939091169163fd527227917f0000000000000000000000000000000000000000000000000000000000000000917f000000000000000000000000000000000000000000000000000000000000000091899188913091015b6040516020818303038152906040526040518763ffffffff1660e01b8152600401612d02969594939291906142bc565b612e906001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000083611c44565b60405163b7ec8d4b60e01b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b7ec8d4b90612f0090849030907f000000000000000000000000000000000000000000000000000000000000000090600401614609565b6020604051808303815f875af1158015612f1c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f4091906140f0565b5050565b5f610d49600254612710612f5891906142a9565b839061271060016133ff565b604051633545906160e21b815260048101829052306024820181905260448201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063d516418490606401612f00565b6040516338bad5e560e01b81526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001660048201523060248201525f90819073925d5466d4d5b01995e20e1245924ada6415126a906338bad5e590604401602060405180830381865afa15801561303e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061306291906140f0565b905080831115613070578092505b825f0361307f57505f92915050565b6130d36001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f000000000000000000000000000000000000000000000000000000000000000085611c44565b60405163acb7081560e01b8152600481018490523060248201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063acb70815906044015b6020604051808303815f875af115801561313e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061316291906140f0565b509192915050565b5f5f613174612b2b565b905080831115613182578092505b604051635c19be1560e11b81526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063b8337c2a90613122908690309081907f00000000000000000000000000000000000000000000000000000000000000009060040161462c565b5f5f6132026119608461265c565b5f8054604080516001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116602083015294955092939091169163fd527227917f0000000000000000000000000000000000000000000000000000000000000000917f00000000000000000000000000000000000000000000000000000000000000009189918891309101612e0c565b5f5f5f5f60205f8651602088015f8a5af192503d91505f5190508280156132d7575081156132c957806001146132d7565b5f866001600160a01b03163b115b9695505050505050565b5f5f6132eb610e0d565b90507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015613349573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061336d91906140f0565b158015906133a9575060015461338a66b1a2bc2ec5000083614270565b10806133a9575066b1a2bc2ec500006001546133a69190614270565b81115b156133b657600191505090565b5f91505090565b5f5f5f6133c98561344c565b915091508082106133f1576133e8826133e2878761366f565b90613737565b92505050610d49565b6133e8816133e2878761366f565b5f61342c61340c83613765565b801561342757505f848061342257613422614320565b868809115b151590565b613437868686613791565b6134419190614270565b90505b949350505050565b5f5f5f5f846001600160a01b0316632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa15801561348c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134b09190614660565b9250509150816001600160a01b0316633ba0b9a96040518163ffffffff1660e01b8152600401602060405180830381865afa1580156134f1573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061351591906140f0565b93505f816001600160a01b031663d2a3584e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613554573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061357891906140f0565b9050816001600160a01b031663516399df6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156135b6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135da919061428e565b801561364d575043826001600160a01b03166360e0a9e16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561361e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061364291906146aa565b6001600160801b0316145b1561365a57809350613667565b6136648582613841565b93505b505050915091565b5f5f836001600160a01b031663e184c9be6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156136ad573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136d191906140f0565b90504281116136eb57670de0b6b3a7640000915050610d49565b5f6136f68585613856565b90505f61370342846142a9565b90505f61371861371384846139fe565b613a36565b905061372c670de0b6b3a764000082613737565b945050505050610d49565b5f8061374b670de0b6b3a764000085614309565b905082818161375c5761375c614320565b04949350505050565b5f600282600381111561377a5761377a61405d565b61378491906146d0565b60ff166001149050919050565b5f5f5f61379e8686613a43565b91509150815f036137c2578381816137b8576137b8614320565b0492505050612d65565b8184116137d9576137d96003851502601118613a5f565b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010185841190960395909502919093039390930492909217029150509392505050565b5f81831161384f5781612d65565b5090919050565b5f8163ffffffff165f036138e3575f836001600160a01b031663c3fb90d66040518163ffffffff1660e01b815260040160c060405180830381865afa1580156138a1573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906138c59190614713565b50505092505050806bffffffffffffffffffffffff16915050610d49565b6040805160028082526060820183525f9260208301908036833701905050905082815f8151811061391657613916614795565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81525f906001600160a01b0386169063883bdbfd906139599085906004016147a9565b5f60405180830381865afa158015613973573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261399a91908101906147f1565b90508363ffffffff16815f815181106139b5576139b5614795565b6020026020010151826001815181106139d0576139d0614795565b60200260200101516139e291906148b0565b6139ec91906148cf565b6001600160d81b031695945050505050565b5f80613a0f6201518061016d614309565b613a198486614309565b613a239190614334565b9050613444613a3182613a70565b613a84565b5f5f821215612912575f5ffd5b5f805f1983850993909202808410938190039390930393915050565b634e487b715f52806020526024601cfd5b5f6001600160ff1b03821115612912575f5ffd5b5f680238fd42c5cf03ffff198212158015613aa8575068070c1cc73b00c800008213155b613ae75760405162461bcd60e51b815260206004820152601060248201526f125b9d985b1a5908195e1c1bdb995b9d60821b60448201526064016109b3565b5f821215613b1e57613afa825f03613a84565b6ec097ce7bc90715b34b9f100000000081613b1757613b17614320565b0592915050565b5f6806f05b59d3b20000008312613b5d57506806f05b59d3b1ffffff1990910190770195e54c5dd42177f53a27172fa9ec630262827000000000613b93565b6803782dace9d90000008312613b8f57506803782dace9d8ffffff19909101906b1425982cf597cd205cef7380613b93565b5060015b6064929092029168056bc75e2d6310000068ad78ebc5ac620000008412613be35768ad78ebc5ac61ffffff199093019268056bc75e2d631000006e01855144814a7ff805980ff008400082020590505b6856bc75e2d6310000008412613c1f576856bc75e2d630ffffff199093019268056bc75e2d631000006b02df0ab5a80a22c61ab5a70082020590505b682b5e3af16b188000008412613c5957682b5e3af16b187fffff199093019268056bc75e2d63100000693f1fce3da636ea5cf85082020590505b6815af1d78b58c4000008412613c93576815af1d78b58c3fffff199093019268056bc75e2d63100000690127fa27722cc06cc5e282020590505b680ad78ebc5ac62000008412613ccc57680ad78ebc5ac61fffff199093019268056bc75e2d6310000068280e60114edb805d0382020590505b68056bc75e2d631000008412613d055768056bc75e2d630fffff199093019268056bc75e2d63100000680ebc5fb4174612111082020590505b6802b5e3af16b18800008412613d3e576802b5e3af16b187ffff199093019268056bc75e2d631000006808f00f760a4b2db55d82020590505b68015af1d78b58c400008412613d775768015af1d78b58c3ffff199093019268056bc75e2d631000006806f5f177578893793782020590505b68056bc75e2d631000008481019085906002908280020505918201919050600368056bc75e2d631000008783020505918201919050600468056bc75e2d631000008783020505918201919050600568056bc75e2d631000008783020505918201919050600668056bc75e2d631000008783020505918201919050600768056bc75e2d631000008783020505918201919050600868056bc75e2d631000008783020505918201919050600968056bc75e2d631000008783020505918201919050600a68056bc75e2d631000008783020505918201919050600b68056bc75e2d631000008783020505918201919050600c68056bc75e2d631000008783020505918201919050606468056bc75e2d63100000848402058502059695505050505050565b6001600160a01b0381168114610d3c575f5ffd5b5f60208284031215613ebc575f5ffd5b8135612d6581613e98565b5f5f83601f840112613ed7575f5ffd5b50813567ffffffffffffffff811115613eee575f5ffd5b602083019150836020828501011115613f05575f5ffd5b9250929050565b5f5f5f5f5f5f60a08789031215613f21575f5ffd5b8635613f2c81613e98565b95506020870135613f3c81613e98565b94506040870135935060608701359250608087013567ffffffffffffffff811115613f65575f5ffd5b613f7189828a01613ec7565b979a9699509497509295939492505050565b5f60208284031215613f93575f5ffd5b5035919050565b8015158114610d3c575f5ffd5b5f5f5f5f5f60808688031215613fbb575f5ffd5b853594506020860135613fcd81613f9a565b93506040860135613fdd81613f9a565b9250606086013567ffffffffffffffff811115613ff8575f5ffd5b61400488828901613ec7565b969995985093965092949392505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8215158152604060208201525f6134446040830184614015565b634e487b7160e01b5f52602160045260245ffd5b6002811061408d57634e487b7160e01b5f52602160045260245ffd5b9052565b60208101610d498284614071565b5f602082840312156140af575f5ffd5b813563ffffffff81168114612d65575f5ffd5b5f5f604083850312156140d3575f5ffd5b8235915060208301356140e581613f9a565b809150509250929050565b5f60208284031215614100575f5ffd5b5051919050565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b634e487b7160e01b5f52604160045260245ffd5b604051610220810167ffffffffffffffff811182821017156141765761417661413e565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156141a5576141a561413e565b604052919050565b5f5f5f606084860312156141bf575f5ffd5b8335925060208401356141d181613f9a565b9150604084013567ffffffffffffffff8111156141ec575f5ffd5b8401601f810186136141fc575f5ffd5b803567ffffffffffffffff8111156142165761421661413e565b614229601f8201601f191660200161417c565b81815287602083850101111561423d575f5ffd5b816020840160208301375f602083830101528093505050509250925092565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610d4957610d4961425c565b805161216881613f9a565b5f6020828403121561429e575f5ffd5b8151612d6581613f9a565b81810381811115610d4957610d4961425c565b6001600160a01b038781168252868116602083015260408201869052606082018590528316608082015260c060a082018190525f906142fd90830184614015565b98975050505050505050565b8082028115828204841417610d4957610d4961425c565b634e487b7160e01b5f52601260045260245ffd5b5f8261434257614342614320565b500490565b6001600160a01b039586168152938516602085015260408401929092526060830152909116608082015260c060a082018190525f9082015260e00190565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f906132d790830184614015565b6001600160a01b038316815260408101612d656020830184614071565b6001815b600184111561440f578085048111156143f3576143f361425c565b600184161561440157908102905b60019390931c9280026143d8565b935093915050565b5f8261442557506001610d49565b8161443157505f610d49565b816001811461444757600281146144515761446d565b6001915050610d49565b60ff8411156144625761446261425c565b50506001821b610d49565b5060208310610133831016604e8410600b8410161715614490575081810a610d49565b61449c5f1984846143d4565b805f19048211156144af576144af61425c565b029392505050565b5f612d6560ff841683614417565b8381528215156020820152606060408201525f6134416060830184614015565b805161216881613e98565b5f610220828403128015614502575f5ffd5b5061450b614152565b8251815260208084015190820152614525604084016144e5565b6040820152614536606084016144e5565b6060820152614547608084016144e5565b608082015261455860a084016144e5565b60a082015261456960c084016144e5565b60c082015261457a60e084016144e5565b60e082015261458c61010084016144e5565b61010082015261459f61012084016144e5565b6101208201526101408381015190820152610160808401519082015261018080840151908201526101a080840151908201526101c080840151908201526145e96101e084016144e5565b6101e08201526145fc6102008401614283565b6102008201529392505050565b8381526001600160a01b0383166020820152606081016134446040830184614071565b8481526001600160a01b03848116602083015283166040820152608081016146576060830184614071565b95945050505050565b5f5f5f60608486031215614672575f5ffd5b835161467d81613e98565b602085015190935061468e81613e98565b604085015190925061469f81613e98565b809150509250925092565b5f602082840312156146ba575f5ffd5b81516001600160801b0381168114612d65575f5ffd5b5f60ff8316806146e2576146e2614320565b8060ff84160691505092915050565b8051600f81900b8114612168575f5ffd5b805161ffff81168114612168575f5ffd5b5f5f5f5f5f5f60c08789031215614728575f5ffd5b614731876146f1565b955061473f602088016146f1565b945060408701516bffffffffffffffffffffffff8116811461475f575f5ffd5b935061476d60608801614702565b925061477b60808801614702565b915061478960a08801614702565b90509295509295509295565b634e487b7160e01b5f52603260045260245ffd5b602080825282518282018190525f918401906040840190835b818110156147e657835163ffffffff168352602093840193909201916001016147c2565b509095945050505050565b5f60208284031215614801575f5ffd5b815167ffffffffffffffff811115614817575f5ffd5b8201601f81018413614827575f5ffd5b805167ffffffffffffffff8111156148415761484161413e565b8060051b6148516020820161417c565b9182526020818401810192908101908784111561486c575f5ffd5b6020850194505b838510156148a557845192506001600160d81b0383168314614893575f5ffd5b82825260209485019490910190614873565b979650505050505050565b6001600160d81b038281168282160390811115610d4957610d4961425c565b5f6001600160d81b038316806148e7576148e7614320565b6001600160d81b0392909216919091049291505056fea2646970667358221220593b94554e7acf962df35070b744d131bc864d9d2c27c9f9f4cc5e7ca3ad6f2764736f6c634300081c0033000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c2900000000000000000000000000000000000000000000000000000000000000c00000000000000000000000002c13bb26cbf37381a0b816f30de33bd70fd7c4300000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc000000000000000000000000cb2dcdc5e4016d0be706df5e312fb3e7c23414970000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001f436572657320736d73555344204c657665726167656420537472617465677900
Deployed Bytecode
0x608060405234801561000f575f5ffd5b50600436106102d3575f3560e01c8063836e5c7c11610180578063c73730e9116100e7578063d34fde93116100a0578063e54f6c661161007a578063e54f6c661461072a578063ecf7085814610751578063f848d5411461075a578063fde813a814610763576102d3565b8063d34fde93146106d4578063d94073d4146106dc578063dab6f76514610703576102d3565b8063c73730e91461066f578063c7ba912714610678578063c96184ca1461068b578063ccb603d814610693578063cf2a546d146106a6578063d19a3bb8146106b9576102d3565b80639c82f2a4116101395780639c82f2a4146106085780639d7fb70c1461061b578063ac5804541461062e578063bb94ea4b14610636578063bdc8144b14610649578063c433c80a1461065c576102d3565b8063836e5c7c146105855780638c2c679e146105ac578063908ee91c146105b4578063995113be146105c75780639a26ee56146105d05780639c635a46146105e3576102d3565b8063419a7ce31161023f57806350743cbf116101f85780635d265d3f116101d25780635d265d3f146105205780636579f2e514610536578063771f6b1f1461054957806381400a3e1461057d576102d3565b806350743cbf146104e25780635159dd68146105055780635322f9b81461050d576102d3565b8063419a7ce31461048457806346aa2f121461049757806349317f1d146104ac5780634abdf2e0146104b45780634c00ed9b146104c7578063503160d9146104cf576102d3565b8063146135a311610291578063146135a3146103d857806323e30c8b146103ea57806325fc1b3d146103fd5780633722cb4b146104105780633d6cb575146104385780633fc928cd1461044b576102d3565b8062ae3bf81461030857806302f141c11461031b57806304bd46291461035f5780630a2cc185146103825780630f61ac82146103a95780630fbe8b3c146103d0575b73d377919fa87120584b21279a491f82d5265a139c365f80375f5f365f845af43d5f5f3e808015610302573d5ff35b3d5ffd5b005b610306610316366004613eac565b610776565b6103427f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef86794181565b6040516001600160a01b0390911681526020015b60405180910390f35b61037461036d366004613eac565b5060055490565b604051908152602001610356565b6103427f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e81565b6103427f0000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc81565b610306610979565b5f54610342906001600160a01b031681565b6103746103f8366004613f0c565b610a62565b61030661040b366004613f83565b610c41565b61042361041e366004613f83565b610d17565b60408051928352901515602083015201610356565b610306610446366004613f83565b610d2b565b6104727f000000000000000000000000000000000000000000000000000000000000001281565b60405160ff9091168152602001610356565b610374610492366004613f83565b610d3f565b6103746104a5366004613eac565b5060045490565b610374610d4f565b6103066104c2366004613f83565b610d65565b610374610e0d565b6103066104dd366004613f83565b610eb0565b6104ea610eb8565b60408051938452602084019290925290820152606001610356565b610306610ece565b61030661051b366004613fa7565b610f4b565b610528611108565b604051610356929190614043565b610374610544366004613f83565b611145565b6105707f000000000000000000000000000000000000000000000000000000000000000081565b6040516103569190614091565b61037461114f565b6103427f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d403889481565b610374611158565b6103066105c2366004613f83565b611161565b61037460025481565b6103746105de366004613f83565b611281565b6003546105f39063ffffffff1681565b60405163ffffffff9091168152602001610356565b610306610616366004613eac565b61128b565b610306610629366004613f83565b611389565b61030661139a565b610374610644366004613f83565b611415565b610306610657366004613f83565b61141f565b61030661066a36600461409f565b6114c7565b61037460015481565b6103066106863660046140c2565b6115ab565b61037461172e565b6103746106a1366004613f83565b611790565b6103746106b4366004613f83565b61179a565b61034273d377919fa87120584b21279a491f82d5265a139c81565b6103746117a4565b6103427f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba65781565b6104727f000000000000000000000000000000000000000000000000000000000000000681565b6103427f000000000000000000000000cb2dcdc5e4016d0be706df5e312fb3e7c234149781565b61037460045481565b61037460055481565b610306610771366004613f83565b6117ad565b6040516320b8029160e21b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906382e00a44906024015f6040518083038186803b1580156107d3575f5ffd5b505afa1580156107e5573d5f5f3e3d5ffd5b505050507f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c296001600160a01b0316816001600160a01b0316148061085a57507f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba6576001600160a01b0316816001600160a01b0316145b8061089657507f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b0316816001600160a01b0316145b156108b45760405163c1ab6dc160e01b815260040160405180910390fd5b6040516370a0823160e01b81523060048201525f906001600160a01b038316906370a0823190602401602060405180830381865afa1580156108f8573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061091c91906140f0565b90506109326001600160a01b03831633836117be565b816001600160a01b03167f46d2e6e71fc567877b817ff3d940571f989d4ee4d40f2b70806d36e738feef6f8260405161096d91815260200190565b60405180910390a25050565b5f610982611822565b600c810154909150600160a01b900460ff166001146109bc5760405162461bcd60e51b81526004016109b390614107565b60405180910390fd5b600c8101805460ff60a01b1916600160a11b17905560405163d43fdcf760e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b03169063d43fdcf7906024015f6040518083038186803b158015610a2e575f5ffd5b505afa158015610a40573d5f5f3e3d5ffd5b50505050610a4c61184f565b600c01805460ff60a01b1916600160a01b179055565b5f336001600160a01b037f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679411614610aac5760405163e17c49b760e01b815260040160405180910390fd5b6001600160a01b0387163014610ad5576040516377be635b60e11b815260040160405180910390fd5b7f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388946001600160a01b0316866001600160a01b031614610b2757604051633dc3c03d60e01b815260040160405180910390fd5b5f8080610b36858701876141ad565b925092509250878314610b5c5760405163dc25cb1560e01b815260040160405180910390fd5b5f610b67888a614270565b90508215610b7f57610b7a89828461194a565b610b8a565b610b8a898284611a58565b6040518181527f97bf554031869ec4edcf72b0dcdc2234dd406afe091f3631be088f348e1795749060200160405180910390a1610c116001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894167f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef86794183611c44565b507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd99a9950505050505050505050565b6040516348e4a64960e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906348e4a649906024015f6040518083038186803b158015610c9e575f5ffd5b505afa158015610cb0573d5f5f3e3d5ffd5b505050506101f4811115610cdb57604051634f70461160e01b81526101f460048201526024016109b3565b60028190556040518181527f655eeddda94c0a9de22c1474e6b5aa4f18d3e8048dc9eff185437c7fe3bfb505906020015b60405180910390a150565b5f5f610d2283611cd9565b91509150915091565b610d33611d9a565b610d3c81611dd1565b50565b5f610d49826120cb565b92915050565b5f610d58611d9a565b610d6061216d565b905090565b6040516348e4a64960e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906348e4a649906024015f6040518083038186803b158015610dc2575f5ffd5b505afa158015610dd4573d5f5f3e3d5ffd5b5050506005829055506040518181527fbc8c1565f6722bdddca67f542dddabedb4e66481264dafda5d475b7327b5b94c90602001610d0c565b60405163f5125d3f60e01b81526001600160a01b037f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679411660048201523060248201525f9073925d5466d4d5b01995e20e1245924ada6415126a9063f5125d3f906044015b602060405180830381865afa158015610e8c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d6091906140f0565b610d3c611d9a565b5f5f5f610ec36121a3565b925092509250909192565b6040516348e4a64960e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906348e4a649906024015f6040518083038186803b158015610f2b575f5ffd5b505afa158015610f3d573d5f5f3e3d5ffd5b50505050610f496122e9565b565b5f610f54611822565b600c810154909150600160a01b900460ff16600114610f855760405162461bcd60e51b81526004016109b390614107565b600c8101805460ff60a01b1916600160a11b17905560405163d43fdcf760e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b03169063d43fdcf7906024015f6040518083038186803b158015610ff7575f5ffd5b505afa158015611009573d5f5f3e3d5ffd5b505050507f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b031663bf86d6906040518163ffffffff1660e01b8152600401602060405180830381865afa158015611069573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061108d919061428e565b1561109f5761109a61239b565b6110ed565b83156110ad576110ad61184f565b6110ed868685858080601f0160208091040260200160405190810160405280939291908181526020018383808284375f9201919091525061254692505050565b600c01805460ff60a01b1916600160a01b1790555050505050565b5f6060611113612653565b6040805160048152602481019091526020810180516001600160e01b031663440368a360e01b17905290939092509050565b5f610d498261265c565b5f610d606126ce565b5f610d60612736565b60405163d43fdcf760e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b03169063d43fdcf7906024015f6040518083038186803b1580156111be575f5ffd5b505afa1580156111d0573d5f5f3e3d5ffd5b50505050670de0b6b3a7640000811061120657604051634f70461160e01b8152670de0b6b3a764000060048201526024016109b3565b5f61120f61172e565b90508061122366b1a2bc2ec5000084614270565b111561124557604051634f70461160e01b8152600481018290526024016109b3565b60018290556040518281527fc008cb9fa00383ebb3760e80ded049adfa410a42627f3dfb6fcc143f919814619060200160405180910390a15050565b5f610d4982612916565b6040516348e4a64960e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906348e4a649906024015f6040518083038186803b1580156112e8575f5ffd5b505afa1580156112fa573d5f5f3e3d5ffd5b5050506001600160a01b03821690506113265760405163e6c4247b60e01b815260040160405180910390fd5b61132e612932565b5f80546001600160a01b0319166001600160a01b0383161790556113506122e9565b6040516001600160a01b03821681527f673779832598d6a388768ee342f8de96fdd5c39a468a6955377cf1405b9652b990602001610d0c565b611391611d9a565b610d3c816129e1565b6040516348e4a64960e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906348e4a649906024015f6040518083038186803b1580156113f7575f5ffd5b505afa158015611409573d5f5f3e3d5ffd5b50505050610f49612932565b5f610d49826129f1565b6040516348e4a64960e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906348e4a649906024015f6040518083038186803b15801561147c575f5ffd5b505afa15801561148e573d5f5f3e3d5ffd5b5050506004829055506040518181527f5d2e73196f8ba1b44e887e2bcc9bcd1f3e657add341d4a0a632ffff00d6903f290602001610d0c565b6040516348e4a64960e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b0316906348e4a649906024015f6040518083038186803b158015611524575f5ffd5b505afa158015611536573d5f5f3e3d5ffd5b5050505061012c8163ffffffff16101561156357604051632a9ffab760e21b815260040160405180910390fd5b6003805463ffffffff191663ffffffff83169081179091556040519081527fae45eae27fdd572bcc5daa11e5155fef7d0b5081d88a374f0580bf91bfdc29b990602001610d0c565b5f6115b4611822565b600c810154909150600160a01b900460ff166001146115e55760405162461bcd60e51b81526004016109b390614107565b600c8101805460ff60a01b1916600160a11b17905560405163d43fdcf760e01b81523360048201527f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b03169063d43fdcf7906024015f6040518083038186803b158015611657575f5ffd5b505afa158015611669573d5f5f3e3d5ffd5b505050507f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b031663bf86d6906040518163ffffffff1660e01b8152600401602060405180830381865afa1580156116c9573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906116ed919061428e565b156116ff576116fa61239b565b611716565b811561170d5761170d61184f565b61171683612a75565b600c01805460ff60a01b1916600160a01b1790555050565b604051630323f06160e51b81526001600160a01b037f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e1660048201525f9073925d5466d4d5b01995e20e1245924ada6415126a9063647e0c2090602401610e71565b5f610d4982612a9d565b5f610d4982612b0f565b5f610d60612b2b565b6117b5611d9a565b610d3c81612b93565b6040516001600160a01b0383811660248301526044820183905261181d91859182169063a9059cbb906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b038381831617835250505050612ba3565b505050565b5f80610d4960017fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b16142a9565b5f6118797f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894612c0f565b9050801561188c5761188a81612c77565b505b5f6118b67f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29612c0f565b905080156118c9576118c781612d6c565b505b5f6118f37f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657612c0f565b905080156119045761190481612e3c565b60408051838152602081018390529081018490527f72c4c0049880524690614a131b2c97912dedae3fe43e6fcfb9083f1d113f8e2b9060600160405180910390a1505050565b80515f9015611a27575f61196561196086612916565b612f44565b5f546040516356ddd23d60e01b81529192506001600160a01b0316906356ddd23d906119df907f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894907f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29908a90879030908b906004016142bc565b6020604051808303815f875af11580156119fb573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a1f91906140f0565b915050611a33565b611a3084612c77565b90505b5f611a3d82612d6c565b9050611a4881612e3c565b611a5184612f64565b5050505050565b611a6183612fbe565b505f612710600254612710611a769190614270565b611a7f856120cb565b611a899190614309565b611a939190614334565b90505f611a9f8261316a565b90505f611aab826131f4565b845190915015611b83575f611ac261196083612b0f565b5f546040516356ddd23d60e01b81529192506001600160a01b0316906356ddd23d90611b3c907f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29907f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894908790879030908d906004016142bc565b6020604051808303815f875af1158015611b58573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b7c91906140f0565b5050611c3c565b5f5460405163efe6bda160e01b81526001600160a01b039091169063efe6bda190611bfa907f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29907f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894908a9087903090600401614347565b6020604051808303815f875af1158015611c16573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c3a91906140f0565b505b505050505050565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052611c958482613298565b611cd3576040516001600160a01b0384811660248301525f6044830152611cc991869182169063095ea7b3906064016117eb565b611cd38482612ba3565b50505050565b5f5f5f5f611ce56121a3565b92505091505f611cf483612b0f565b90505f600154670de0b6b3a7640000611d0d91906142a9565b600154611d1a9084614309565b611d249190614334565b90505f611d2f610e0d565b600154909150611d3f8983614270565b1015611d5e57611d4f84836142a9565b98600198509650505050505050565b87600154611d6c9190614270565b811115611d8b57611d7d82856142a9565b985f98509650505050505050565b505f9788975095505050505050565b333014610f495760405162461bcd60e51b815260206004820152600560248201526410b9b2b63360d91b60448201526064016109b3565b5f611ddb82612a9d565b90505f611e077f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657612c0f565b9050818110611e1957611cd3826131f4565b611e2381836142a9565b91505f5f611e2f6121a3565b925050915081851115611e40578194505b5f600154670de0b6b3a7640000611e5791906142a9565b600154611e6c611e6789876142a9565b612b0f565b611e769190614309565b611e809190614334565b905081811015611f81575f611e9582846142a9565b60408051602081018390525f918101829052606080820152608081018290529192509060a00160408051601f1981840301815290829052632e7ff4ef60e11b825291506001600160a01b037f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679411690635cffe9de90611f3d9030907f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388949087908790600401614385565b6020604051808303815f875af1158015611f59573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f7d919061428e565b5050505b6040516304b5315d60e21b81525f906001600160a01b037f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e16906312d4c57490611ff19030907f0000000000000000000000000000000000000000000000000000000000000000906004016143b7565b602060405180830381865afa15801561200c573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061203091906140f0565b90508086111561203e578095505b6120478661316a565b505f61207a6120757f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657612c0f565b6131f4565b9050878110156120c1577f88f5063ca4c0af21d7235eb8fb972b73ec8c3dfc382456cdba6c695035e1b9736120af828a6142a9565b60405190815260200160405180910390a15b5050505050505050565b5f8115612168575f6120db612736565b90506121087f0000000000000000000000000000000000000000000000000000000000000006600a6144b7565b6121129082614309565b670de0b6b3a76400006121467f0000000000000000000000000000000000000000000000000000000000000012600a6144b7565b6121509086614309565b61215a9190614309565b6121649190614334565b9150505b919050565b5f6121766132e1565b156121935761218361184f565b61219366b1a2bc2ec50000612a75565b61219b6121a3565b509092915050565b5f5f5f6121ae612b2b565b6121d77f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657612c0f565b6121e19190614270565b91505f6121ec6126ce565b90505f6122187f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894612c0f565b9050808211156122845761222c81836142a9565b925061223783612916565b6122408561265c565b6122697f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29612c0f565b6122739190614270565b61227d91906142a9565b94506122e2565b5f925061229961229483836142a9565b612916565b6122a28561265c565b6122cb7f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29612c0f565b6122d59190614270565b6122df9190614270565b94505b5050909192565b5f546001600160a01b0316806122fc5750565b6123316001600160a01b037f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c2916825f19611c44565b6123666001600160a01b037f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba65716825f19611c44565b610d3c6001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d403889416825f19611c44565b5f60018190556123a96126ce565b90506123c4815f60405180602001604052805f815250612546565b5f6123cd612b2b565b9050801561249e576040516304b5315d60e21b81525f906001600160a01b037f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e16906312d4c574906124459030907f0000000000000000000000000000000000000000000000000000000000000000906004016143b7565b602060405180830381865afa158015612460573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061248491906140f0565b905080821115612492578091505b61249b8261316a565b50505b5f6124c87f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657612c0f565b905080156124db576124d9816131f4565b505b5f6125057f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894612c0f565b905080156125185761251681612c77565b505b6040517f8d522e0e5c0b3f31d932913d4f534af13f714a3e10cca18fd09ddc9cff2055b3905f90a150505050565b821561181d575f838383604051602001612562939291906144c5565b60408051601f1981840301815290829052632e7ff4ef60e11b825291506001600160a01b037f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679411690635cffe9de906125e49030907f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d40388949089908790600401614385565b6020604051808303815f875af1158015612600573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612624919061428e565b506040517f430ab3f3198f60d411e429a537683d330a9296d687fa5da362a742676ef4512f905f90a150505050565b5f610d606132e1565b5f8115612168576003545f90612699907f0000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc9063ffffffff166133bd565b9050805f036126ba5760405162bfc92160e01b815260040160405180910390fd5b6121648382670de0b6b3a76400005f6133ff565b604051636cde875160e11b81526001600160a01b037f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679411660048201523060248201525f9073925d5466d4d5b01995e20e1245924ada6415126a9063d9bd0ea290604401610e71565b60405163e48a5f7b60e01b81526001600160a01b037f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e811660048301525f9182917f000000000000000000000000cb2dcdc5e4016d0be706df5e312fb3e7c2341497169063e48a5f7b9060240161022060405180830381865afa1580156127bf573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906127e391906144f0565b60e0015190506001600160a01b038116612829576128227f0000000000000000000000000000000000000000000000000000000000000006600a6144b7565b91506128f3565b6001600160a01b0381166313b0be336128637f0000000000000000000000000000000000000000000000000000000000000012600a6144b7565b6040516001600160e01b031960e084901b16815260048101919091526001600160a01b037f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657166024820152604401602060405180830381865afa1580156128cc573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906128f091906140f0565b91505b815f036129125760405162bfc92160e01b815260040160405180910390fd5b5090565b5f8115612168575f612927836120cb565b90506121648161265c565b5f546001600160a01b0316806129455750565b6129796001600160a01b037f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c2916825f611c44565b6129ad6001600160a01b037f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba65716825f611c44565b610d3c6001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d403889416825f611c44565b610d3c66b1a2bc2ec50000612a75565b5f8115612168575f612a01612736565b9050612a2e7f0000000000000000000000000000000000000000000000000000000000000012600a6144b7565b612a4090670de0b6b3a7640000614309565b612a6b7f0000000000000000000000000000000000000000000000000000000000000006600a6144b7565b6121508386614309565b5f5f612a8083611cd9565b9150915061181d828260405180602001604052805f815250612546565b5f8115612168576003545f90612ada907f0000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc9063ffffffff166133bd565b9050805f03612afb5760405162bfc92160e01b815260040160405180910390fd5b61216483670de0b6b3a7640000835f6133ff565b5f8115612168575f612b2083612a9d565b9050612164816129f1565b60405163360ae4dd60e11b81526001600160a01b037f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e1660048201523060248201525f9073925d5466d4d5b01995e20e1245924ada6415126a90636c15c9ba90604401610e71565b5f198103610d3357610d3c61239b565b5f5f60205f8451602086015f885af180612bc2576040513d5f823e3d81fd5b50505f513d91508115612bd9578060011415612be6565b6001600160a01b0384163b155b15611cd357604051635274afe760e01b81526001600160a01b03851660048201526024016109b3565b6040516370a0823160e01b81523060048201525f906001600160a01b038316906370a0823190602401602060405180830381865afa158015612c53573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d4991906140f0565b5f5f612c8561196084612916565b5f805460405163fd52722760e01b815292935090916001600160a01b039091169063fd52722790612d02907f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894907f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c2990899088903090600401614347565b6020604051808303815f875af1158015612d1e573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d4291906140f0565b905081811015612d65576040516322cb457760e11b815260040160405180910390fd5b9392505050565b5f5f612d7a61196084612a9d565b5f8054604080516001600160a01b037f0000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc8116602083015294955092939091169163fd527227917f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29917f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba65791899188913091015b6040516020818303038152906040526040518763ffffffff1660e01b8152600401612d02969594939291906142bc565b612e906001600160a01b037f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657167f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e83611c44565b60405163b7ec8d4b60e01b81526001600160a01b037f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e169063b7ec8d4b90612f0090849030907f000000000000000000000000000000000000000000000000000000000000000090600401614609565b6020604051808303815f875af1158015612f1c573d5f5f3e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612f4091906140f0565b5050565b5f610d49600254612710612f5891906142a9565b839061271060016133ff565b604051633545906160e21b815260048101829052306024820181905260448201527f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679416001600160a01b03169063d516418490606401612f00565b6040516338bad5e560e01b81526001600160a01b037f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679411660048201523060248201525f90819073925d5466d4d5b01995e20e1245924ada6415126a906338bad5e590604401602060405180830381865afa15801561303e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061306291906140f0565b905080831115613070578092505b825f0361307f57505f92915050565b6130d36001600160a01b037f00000000000000000000000029219dd400f2bf60e5a23d13be72b486d4038894167f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef86794185611c44565b60405163acb7081560e01b8152600481018490523060248201527f0000000000000000000000007184bea7743ccfbe390f9cd830095a13ef8679416001600160a01b03169063acb70815906044015b6020604051808303815f875af115801561313e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061316291906140f0565b509192915050565b5f5f613174612b2b565b905080831115613182578092505b604051635c19be1560e11b81526001600160a01b037f00000000000000000000000094b4bdd55bac19da3b0a31edc0fc899ae918699e169063b8337c2a90613122908690309081907f00000000000000000000000000000000000000000000000000000000000000009060040161462c565b5f5f6132026119608461265c565b5f8054604080516001600160a01b037f0000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc8116602083015294955092939091169163fd527227917f0000000000000000000000005ee17fd12ede62b508f9615db384ce7b834ba657917f000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c299189918891309101612e0c565b5f5f5f5f60205f8651602088015f8a5af192503d91505f5190508280156132d7575081156132c957806001146132d7565b5f866001600160a01b03163b115b9695505050505050565b5f5f6132eb610e0d565b90507f00000000000000000000000085bba6f69055e1cd712745bd8566807fd875d5436001600160a01b03166301e1d1146040518163ffffffff1660e01b8152600401602060405180830381865afa158015613349573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061336d91906140f0565b158015906133a9575060015461338a66b1a2bc2ec5000083614270565b10806133a9575066b1a2bc2ec500006001546133a69190614270565b81115b156133b657600191505090565b5f91505090565b5f5f5f6133c98561344c565b915091508082106133f1576133e8826133e2878761366f565b90613737565b92505050610d49565b6133e8816133e2878761366f565b5f61342c61340c83613765565b801561342757505f848061342257613422614320565b868809115b151590565b613437868686613791565b6134419190614270565b90505b949350505050565b5f5f5f5f846001600160a01b0316632c8ce6bc6040518163ffffffff1660e01b8152600401606060405180830381865afa15801561348c573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906134b09190614660565b9250509150816001600160a01b0316633ba0b9a96040518163ffffffff1660e01b8152600401602060405180830381865afa1580156134f1573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061351591906140f0565b93505f816001600160a01b031663d2a3584e6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613554573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061357891906140f0565b9050816001600160a01b031663516399df6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156135b6573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906135da919061428e565b801561364d575043826001600160a01b03166360e0a9e16040518163ffffffff1660e01b8152600401602060405180830381865afa15801561361e573d5f5f3e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061364291906146aa565b6001600160801b0316145b1561365a57809350613667565b6136648582613841565b93505b505050915091565b5f5f836001600160a01b031663e184c9be6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156136ad573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136d191906140f0565b90504281116136eb57670de0b6b3a7640000915050610d49565b5f6136f68585613856565b90505f61370342846142a9565b90505f61371861371384846139fe565b613a36565b905061372c670de0b6b3a764000082613737565b945050505050610d49565b5f8061374b670de0b6b3a764000085614309565b905082818161375c5761375c614320565b04949350505050565b5f600282600381111561377a5761377a61405d565b61378491906146d0565b60ff166001149050919050565b5f5f5f61379e8686613a43565b91509150815f036137c2578381816137b8576137b8614320565b0492505050612d65565b8184116137d9576137d96003851502601118613a5f565b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010185841190960395909502919093039390930492909217029150509392505050565b5f81831161384f5781612d65565b5090919050565b5f8163ffffffff165f036138e3575f836001600160a01b031663c3fb90d66040518163ffffffff1660e01b815260040160c060405180830381865afa1580156138a1573d5f5f3e3d5ffd5b505050506040513d601f19601f820116820180604052508101906138c59190614713565b50505092505050806bffffffffffffffffffffffff16915050610d49565b6040805160028082526060820183525f9260208301908036833701905050905082815f8151811061391657613916614795565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81525f906001600160a01b0386169063883bdbfd906139599085906004016147a9565b5f60405180830381865afa158015613973573d5f5f3e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261399a91908101906147f1565b90508363ffffffff16815f815181106139b5576139b5614795565b6020026020010151826001815181106139d0576139d0614795565b60200260200101516139e291906148b0565b6139ec91906148cf565b6001600160d81b031695945050505050565b5f80613a0f6201518061016d614309565b613a198486614309565b613a239190614334565b9050613444613a3182613a70565b613a84565b5f5f821215612912575f5ffd5b5f805f1983850993909202808410938190039390930393915050565b634e487b715f52806020526024601cfd5b5f6001600160ff1b03821115612912575f5ffd5b5f680238fd42c5cf03ffff198212158015613aa8575068070c1cc73b00c800008213155b613ae75760405162461bcd60e51b815260206004820152601060248201526f125b9d985b1a5908195e1c1bdb995b9d60821b60448201526064016109b3565b5f821215613b1e57613afa825f03613a84565b6ec097ce7bc90715b34b9f100000000081613b1757613b17614320565b0592915050565b5f6806f05b59d3b20000008312613b5d57506806f05b59d3b1ffffff1990910190770195e54c5dd42177f53a27172fa9ec630262827000000000613b93565b6803782dace9d90000008312613b8f57506803782dace9d8ffffff19909101906b1425982cf597cd205cef7380613b93565b5060015b6064929092029168056bc75e2d6310000068ad78ebc5ac620000008412613be35768ad78ebc5ac61ffffff199093019268056bc75e2d631000006e01855144814a7ff805980ff008400082020590505b6856bc75e2d6310000008412613c1f576856bc75e2d630ffffff199093019268056bc75e2d631000006b02df0ab5a80a22c61ab5a70082020590505b682b5e3af16b188000008412613c5957682b5e3af16b187fffff199093019268056bc75e2d63100000693f1fce3da636ea5cf85082020590505b6815af1d78b58c4000008412613c93576815af1d78b58c3fffff199093019268056bc75e2d63100000690127fa27722cc06cc5e282020590505b680ad78ebc5ac62000008412613ccc57680ad78ebc5ac61fffff199093019268056bc75e2d6310000068280e60114edb805d0382020590505b68056bc75e2d631000008412613d055768056bc75e2d630fffff199093019268056bc75e2d63100000680ebc5fb4174612111082020590505b6802b5e3af16b18800008412613d3e576802b5e3af16b187ffff199093019268056bc75e2d631000006808f00f760a4b2db55d82020590505b68015af1d78b58c400008412613d775768015af1d78b58c3ffff199093019268056bc75e2d631000006806f5f177578893793782020590505b68056bc75e2d631000008481019085906002908280020505918201919050600368056bc75e2d631000008783020505918201919050600468056bc75e2d631000008783020505918201919050600568056bc75e2d631000008783020505918201919050600668056bc75e2d631000008783020505918201919050600768056bc75e2d631000008783020505918201919050600868056bc75e2d631000008783020505918201919050600968056bc75e2d631000008783020505918201919050600a68056bc75e2d631000008783020505918201919050600b68056bc75e2d631000008783020505918201919050600c68056bc75e2d631000008783020505918201919050606468056bc75e2d63100000848402058502059695505050505050565b6001600160a01b0381168114610d3c575f5ffd5b5f60208284031215613ebc575f5ffd5b8135612d6581613e98565b5f5f83601f840112613ed7575f5ffd5b50813567ffffffffffffffff811115613eee575f5ffd5b602083019150836020828501011115613f05575f5ffd5b9250929050565b5f5f5f5f5f5f60a08789031215613f21575f5ffd5b8635613f2c81613e98565b95506020870135613f3c81613e98565b94506040870135935060608701359250608087013567ffffffffffffffff811115613f65575f5ffd5b613f7189828a01613ec7565b979a9699509497509295939492505050565b5f60208284031215613f93575f5ffd5b5035919050565b8015158114610d3c575f5ffd5b5f5f5f5f5f60808688031215613fbb575f5ffd5b853594506020860135613fcd81613f9a565b93506040860135613fdd81613f9a565b9250606086013567ffffffffffffffff811115613ff8575f5ffd5b61400488828901613ec7565b969995985093965092949392505050565b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b8215158152604060208201525f6134446040830184614015565b634e487b7160e01b5f52602160045260245ffd5b6002811061408d57634e487b7160e01b5f52602160045260245ffd5b9052565b60208101610d498284614071565b5f602082840312156140af575f5ffd5b813563ffffffff81168114612d65575f5ffd5b5f5f604083850312156140d3575f5ffd5b8235915060208301356140e581613f9a565b809150509250929050565b5f60208284031215614100575f5ffd5b5051919050565b6020808252601f908201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c00604082015260600190565b634e487b7160e01b5f52604160045260245ffd5b604051610220810167ffffffffffffffff811182821017156141765761417661413e565b60405290565b604051601f8201601f1916810167ffffffffffffffff811182821017156141a5576141a561413e565b604052919050565b5f5f5f606084860312156141bf575f5ffd5b8335925060208401356141d181613f9a565b9150604084013567ffffffffffffffff8111156141ec575f5ffd5b8401601f810186136141fc575f5ffd5b803567ffffffffffffffff8111156142165761421661413e565b614229601f8201601f191660200161417c565b81815287602083850101111561423d575f5ffd5b816020840160208301375f602083830101528093505050509250925092565b634e487b7160e01b5f52601160045260245ffd5b80820180821115610d4957610d4961425c565b805161216881613f9a565b5f6020828403121561429e575f5ffd5b8151612d6581613f9a565b81810381811115610d4957610d4961425c565b6001600160a01b038781168252868116602083015260408201869052606082018590528316608082015260c060a082018190525f906142fd90830184614015565b98975050505050505050565b8082028115828204841417610d4957610d4961425c565b634e487b7160e01b5f52601260045260245ffd5b5f8261434257614342614320565b500490565b6001600160a01b039586168152938516602085015260408401929092526060830152909116608082015260c060a082018190525f9082015260e00190565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f906132d790830184614015565b6001600160a01b038316815260408101612d656020830184614071565b6001815b600184111561440f578085048111156143f3576143f361425c565b600184161561440157908102905b60019390931c9280026143d8565b935093915050565b5f8261442557506001610d49565b8161443157505f610d49565b816001811461444757600281146144515761446d565b6001915050610d49565b60ff8411156144625761446261425c565b50506001821b610d49565b5060208310610133831016604e8410600b8410161715614490575081810a610d49565b61449c5f1984846143d4565b805f19048211156144af576144af61425c565b029392505050565b5f612d6560ff841683614417565b8381528215156020820152606060408201525f6134416060830184614015565b805161216881613e98565b5f610220828403128015614502575f5ffd5b5061450b614152565b8251815260208084015190820152614525604084016144e5565b6040820152614536606084016144e5565b6060820152614547608084016144e5565b608082015261455860a084016144e5565b60a082015261456960c084016144e5565b60c082015261457a60e084016144e5565b60e082015261458c61010084016144e5565b61010082015261459f61012084016144e5565b6101208201526101408381015190820152610160808401519082015261018080840151908201526101a080840151908201526101c080840151908201526145e96101e084016144e5565b6101e08201526145fc6102008401614283565b6102008201529392505050565b8381526001600160a01b0383166020820152606081016134446040830184614071565b8481526001600160a01b03848116602083015283166040820152608081016146576060830184614071565b95945050505050565b5f5f5f60608486031215614672575f5ffd5b835161467d81613e98565b602085015190935061468e81613e98565b604085015190925061469f81613e98565b809150509250925092565b5f602082840312156146ba575f5ffd5b81516001600160801b0381168114612d65575f5ffd5b5f60ff8316806146e2576146e2614320565b8060ff84160691505092915050565b8051600f81900b8114612168575f5ffd5b805161ffff81168114612168575f5ffd5b5f5f5f5f5f5f60c08789031215614728575f5ffd5b614731876146f1565b955061473f602088016146f1565b945060408701516bffffffffffffffffffffffff8116811461475f575f5ffd5b935061476d60608801614702565b925061477b60808801614702565b915061478960a08801614702565b90509295509295509295565b634e487b7160e01b5f52603260045260245ffd5b602080825282518282018190525f918401906040840190835b818110156147e657835163ffffffff168352602093840193909201916001016147c2565b509095945050505050565b5f60208284031215614801575f5ffd5b815167ffffffffffffffff811115614817575f5ffd5b8201601f81018413614827575f5ffd5b805167ffffffffffffffff8111156148415761484161413e565b8060051b6148516020820161417c565b9182526020818401810192908101908784111561486c575f5ffd5b6020850194505b838510156148a557845192506001600160d81b0383168314614893575f5ffd5b82825260209485019490910190614873565b979650505050505050565b6001600160d81b038281168282160390811115610d4957610d4961425c565b5f6001600160d81b038316806148e7576148e7614320565b6001600160d81b0392909216919091049291505056fea2646970667358221220593b94554e7acf962df35070b744d131bc864d9d2c27c9f9f4cc5e7ca3ad6f2764736f6c634300081c0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c2900000000000000000000000000000000000000000000000000000000000000c00000000000000000000000002c13bb26cbf37381a0b816f30de33bd70fd7c4300000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc000000000000000000000000cb2dcdc5e4016d0be706df5e312fb3e7c23414970000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000001f436572657320736d73555344204c657665726167656420537472617465677900
-----Decoded View---------------
Arg [0] : _asset (address): 0xc7990369DA608C2F4903715E3bD22f2970536C29
Arg [1] : _name (string): Ceres smsUSD Leveraged Strategy
Arg [2] : _ceresSwapper (address): 0x2C13bB26CbF37381A0B816F30de33bd70fD7C430
Arg [3] : _pendleMarket (address): 0x4aEe8d5e242c2485bd124299203C7Fa5108C25Fc
Arg [4] : _siloMarketConfig (address): 0xCB2dcdC5E4016d0BE706df5e312Fb3E7c2341497
Arg [5] : isCollateralProtected (bool): True
-----Encoded View---------------
8 Constructor Arguments found :
Arg [0] : 000000000000000000000000c7990369da608c2f4903715e3bd22f2970536c29
Arg [1] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [2] : 0000000000000000000000002c13bb26cbf37381a0b816f30de33bd70fd7c430
Arg [3] : 0000000000000000000000004aee8d5e242c2485bd124299203c7fa5108c25fc
Arg [4] : 000000000000000000000000cb2dcdc5e4016d0be706df5e312fb3e7c2341497
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [6] : 000000000000000000000000000000000000000000000000000000000000001f
Arg [7] : 436572657320736d73555344204c657665726167656420537472617465677900
Loading...
Loading
Loading...
Loading
Loading...
Loading
Net Worth in USD
$1.00
Net Worth in S
Token Allocations
USDC
100.00%
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|---|---|---|---|---|
| SONIC | 100.00% | $0.999608 | 0.9969 | $0.9965 |
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export ]
[ 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.