Feature Tip: Add private address tag to any address under My Name Tag !
Overview
ETH Balance
0 ETH
Eth Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Cross-Chain Transactions
Loading...
Loading
Contract Name:
USD3
Compiler Version
v0.8.22+commit.4fc1097e
Optimization Enabled:
Yes with 999999 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: AGPL-3.0
pragma solidity 0.8.22;
import {BaseHooksUpgradeable} from "./base/BaseHooksUpgradeable.sol";
import {IERC20, SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import {Math} from "../../lib/openzeppelin/contracts/utils/math/Math.sol";
import {IMorpho, IMorphoCredit, MarketParams, Id} from "../interfaces/IMorpho.sol";
import {MorphoLib} from "../libraries/periphery/MorphoLib.sol";
import {MorphoBalancesLib} from "../libraries/periphery/MorphoBalancesLib.sol";
import {SharesMathLib} from "../libraries/SharesMathLib.sol";
import {IERC4626} from "../../lib/openzeppelin/contracts/interfaces/IERC4626.sol";
import {Pausable} from "../../lib/openzeppelin/contracts/utils/Pausable.sol";
import {TokenizedStrategyStorageLib, ERC20} from "@periphery/libraries/TokenizedStrategyStorageLib.sol";
import {IProtocolConfig} from "../interfaces/IProtocolConfig.sol";
import {ProtocolConfigLib} from "../libraries/ProtocolConfigLib.sol";
/**
* @title USD3
* @author 3Jane Protocol
* @notice Senior tranche strategy for USDC-based lending on 3Jane's credit markets
* @dev Implements Yearn V3 tokenized strategy pattern for unsecured lending via MorphoCredit.
* Deploys USDC capital to 3Jane's modified Morpho Blue markets that use credit-based
* underwriting instead of collateral. Features first-loss protection through sUSD3
* subordinate tranche absorption.
*
* Key features:
* - Senior tranche with first-loss protection from sUSD3 holders
* - Configurable deployment ratio to credit markets (maxOnCredit)
* - Automatic yield distribution to sUSD3 via performance fees
* - Loss absorption through direct share burning of sUSD3 holdings
* - Commitment period enforcement for deposits
* - Optional whitelist for controlled access
* - Dynamic fee adjustment via ProtocolConfig integration
*
* Yield Distribution Mechanism:
* - Tranche share distributed to sUSD3 holders via TokenizedStrategy's performance fee
* - Performance fee can be set from 0-100% through syncTrancheShare()
* - Direct storage manipulation bypasses TokenizedStrategy's 50% fee limit
* - Keeper-controlled updates ensure protocol-wide consistency
*
* Loss Absorption Mechanism:
* - When losses occur, sUSD3 shares are burned first (subordination)
* - Direct storage manipulation used to burn shares without asset transfers
* - USD3 holders protected up to total sUSD3 holdings
* - Losses exceeding sUSD3 balance shared proportionally among USD3 holders
*/
contract USD3 is BaseHooksUpgradeable {
using SafeERC20 for IERC20;
using MorphoLib for IMorpho;
using MorphoBalancesLib for IMorpho;
using SharesMathLib for uint256;
using Math for uint256;
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
IERC4626 public constant WAUSDC = IERC4626(0xD4fa2D31b7968E448877f69A96DE69f5de8cD23E);
/*//////////////////////////////////////////////////////////////
STORAGE - MORPHO PARAMETERS
//////////////////////////////////////////////////////////////*/
/// @notice MorphoCredit contract for lending operations
IMorpho public morphoCredit;
/// @notice Market ID for the lending market this strategy uses
Id public marketId;
/// @notice Market parameters for the lending market
MarketParams internal _marketParams;
/*//////////////////////////////////////////////////////////////
UPGRADEABLE STORAGE
//////////////////////////////////////////////////////////////*/
/// @notice Address of the subordinate sUSD3 strategy
/// @dev Used for loss absorption and yield distribution
address public sUSD3;
/// @notice Whether whitelist is enforced for deposits
bool public whitelistEnabled;
/// @notice Whitelist status for addresses
mapping(address => bool) public whitelist;
/// @notice Whitelist of depositors allowed to 3rd party deposit
mapping(address => bool) public depositorWhitelist;
/// @notice Minimum deposit amount required
uint256 public minDeposit;
/// @notice Timestamp of last deposit for each user
/// @dev Used to enforce commitment periods
mapping(address => uint256) public depositTimestamp;
/*//////////////////////////////////////////////////////////////
EVENTS
//////////////////////////////////////////////////////////////*/
event SUSD3StrategyUpdated(address oldStrategy, address newStrategy);
event WhitelistUpdated(address indexed user, bool allowed);
event DepositorWhitelistUpdated(address indexed depositor, bool allowed);
event MinDepositUpdated(uint256 newMinDeposit);
event TrancheShareSynced(uint256 trancheShare);
/// @custom:oz-upgrades-unsafe-allow constructor
constructor() {
_disableInitializers();
}
/**
* @notice Initialize the USD3 strategy
* @param _morphoCredit Address of the MorphoCredit lending contract
* @param _marketId Market ID for the lending market
* @param _management Management address for the strategy
* @param _keeper Keeper address for automated operations
*/
function initialize(address _morphoCredit, Id _marketId, address _management, address _keeper)
external
initializer
{
require(_morphoCredit != address(0), "!morpho");
morphoCredit = IMorpho(_morphoCredit);
marketId = _marketId;
// Get and cache market params
MarketParams memory params = morphoCredit.idToMarketParams(_marketId);
require(params.loanToken != address(0), "Invalid market");
_marketParams = params;
// Initialize BaseStrategy with management as temporary performanceFeeRecipient
// It will be updated to sUSD3 address after sUSD3 is deployed
__BaseStrategy_init(params.loanToken, "USD3", _management, _management, _keeper);
// Approve Morpho
IERC20(asset).forceApprove(address(morphoCredit), type(uint256).max);
}
/**
* @notice Reinitialize the USD3 strategy to switch asset from waUSDC to USDC
* @dev This function is called during the upgrade from the previous USD3 implementation.
* The upgrade process MUST follow this sequence to prevent user losses:
* 1. Set performance fee to 0 (via setPerformanceFee)
* 2. Set profit unlock time to 0 (via setProfitMaxUnlockTime)
* 3. Call report() on OLD implementation to finalize state before upgrade
* 4. Upgrade proxy to new implementation
* 5. Call reinitialize() to switch the underlying asset
* 6. Call report() on NEW implementation to update totalAssets with new asset
* 7. Call syncTrancheShare() to restore performance fees
* 8. Restore profit unlock time to previous value
* This ensures totalAssets reflects the true USDC value before users can withdraw.
* Without both report() calls, users would lose value as totalAssets would not
* account for waUSDC appreciation or the asset switch.
*/
function reinitialize() external reinitializer(2) {
address usdc = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48;
asset = ERC20(usdc);
TokenizedStrategyStorageLib.StrategyData storage strategyData = TokenizedStrategyStorageLib.getStrategyStorage();
strategyData.asset = ERC20(usdc);
IERC20(usdc).forceApprove(address(WAUSDC), type(uint256).max);
}
/*//////////////////////////////////////////////////////////////
EXTERNAL VIEW FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Get the symbol for the USD3 token
* @return Symbol string "USD3"
*/
function symbol() external pure returns (string memory) {
return "USD3";
}
/**
* @notice Get the market parameters for this strategy
* @return MarketParams struct containing lending market configuration
*/
function marketParams() external view returns (MarketParams memory) {
return _marketParams;
}
/*//////////////////////////////////////////////////////////////
INTERNAL VIEW FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @dev Get current market liquidity information
* @return totalSupplyAssets Total assets supplied to the market
* @return totalShares Total supply shares in the market
* @return totalBorrowAssets Total assets borrowed from the market
* @return waUSDCLiquidity Available liquidity in the market
*/
function getMarketLiquidity()
public
view
returns (uint256 totalSupplyAssets, uint256 totalShares, uint256 totalBorrowAssets, uint256 waUSDCLiquidity)
{
(totalSupplyAssets, totalShares, totalBorrowAssets,) = morphoCredit.expectedMarketBalances(_marketParams);
waUSDCLiquidity = totalSupplyAssets > totalBorrowAssets ? totalSupplyAssets - totalBorrowAssets : 0;
}
/**
* @dev Get strategy's position in the market
* @return shares Number of supply shares held
* @return waUSDCMax Maximum waUSDC that can be withdrawn
* @return waUSDCLiquidity Available market liquidity in waUSDC
*/
function getPosition() internal view returns (uint256 shares, uint256 waUSDCMax, uint256 waUSDCLiquidity) {
shares = morphoCredit.position(marketId, address(this)).supplyShares;
uint256 totalSupplyAssets;
uint256 totalShares;
(totalSupplyAssets, totalShares,, waUSDCLiquidity) = getMarketLiquidity();
waUSDCMax = shares.toAssetsDown(totalSupplyAssets, totalShares);
}
/*//////////////////////////////////////////////////////////////
INTERNAL STRATEGY FUNCTIONS
//////////////////////////////////////////////////////////////*/
/// @dev Deploy funds to MorphoCredit market respecting maxOnCredit ratio
/// @param _amount Amount of asset to deploy
function _deployFunds(uint256 _amount) internal override {
if (_amount == 0) return;
// Wrap USDC to waUSDC
_amount = WAUSDC.deposit(_amount, address(this));
uint256 maxOnCreditRatio = maxOnCredit();
if (maxOnCreditRatio == 0) {
// Don't deploy anything when set to 0%, keep all waUSDC local
return;
}
// Calculate total waUSDC (deployed + local)
uint256 deployedWaUSDC = suppliedWaUSDC();
uint256 localWaUSDC = balanceOfWaUSDC();
uint256 totalWaUSDC = deployedWaUSDC + localWaUSDC;
// Calculate max that should be deployed to MorphoCredit
uint256 maxDeployableWaUSDC = (totalWaUSDC * maxOnCreditRatio) / 10_000;
if (maxDeployableWaUSDC <= deployedWaUSDC) {
// Already at or above max, keep all new waUSDC local
return;
}
// Deploy only the amount needed to reach max
uint256 waUSDCToSupply = Math.min(localWaUSDC, maxDeployableWaUSDC - deployedWaUSDC);
_supplyToMorpho(waUSDCToSupply);
}
/// @dev Withdraw funds from MorphoCredit market
/// @param _amount Amount of asset to free up
function _freeFunds(uint256 _amount) internal override {
if (_amount == 0) {
return;
}
// Calculate how much waUSDC we need
uint256 waUSDCNeeded = WAUSDC.previewWithdraw(_amount);
// Check local waUSDC balance first
uint256 localWaUSDC = balanceOfWaUSDC();
if (localWaUSDC < waUSDCNeeded) {
// Need to withdraw from MorphoCredit
uint256 waUSDCToWithdraw = waUSDCNeeded - localWaUSDC;
uint256 withdrawn = _withdrawFromMorpho(waUSDCToWithdraw);
if (withdrawn > 0) {
localWaUSDC = balanceOfWaUSDC();
}
}
uint256 waUSDCToUnwrap = Math.min(localWaUSDC, waUSDCNeeded);
if (waUSDCToUnwrap > 0) {
WAUSDC.redeem(waUSDCToUnwrap, address(this), address(this));
}
}
/// @dev Emergency withdraw function to free funds from MorphoCredit
/// @param amount The amount to withdraw (use type(uint256).max for all)
function _emergencyWithdraw(uint256 amount) internal override {
// This is called during shutdown to free funds from Morpho
// Use _freeFunds which already handles the withdrawal logic
_freeFunds(amount);
}
/// @dev Harvest interest from MorphoCredit and report total assets
/// @return Total assets held by the strategy
function _harvestAndReport() internal override returns (uint256) {
MarketParams memory params = _marketParams;
morphoCredit.accrueInterest(params);
_tend(asset.balanceOf(address(this)));
uint256 totalWaUSDC = suppliedWaUSDC() + balanceOfWaUSDC();
return WAUSDC.convertToAssets(totalWaUSDC) + asset.balanceOf(address(this));
}
/// @dev Rebalances between idle and deployed funds to maintain maxOnCredit ratio
/// @param _totalIdle Current idle funds available
function _tend(uint256 _totalIdle) internal virtual override {
// First wrap any idle USDC to waUSDC
if (_totalIdle > 0) {
WAUSDC.deposit(_totalIdle, address(this));
}
// Calculate based on waUSDC amounts
uint256 deployedWaUSDC = suppliedWaUSDC();
uint256 localWaUSDC = balanceOfWaUSDC();
uint256 totalWaUSDC = deployedWaUSDC + localWaUSDC;
uint256 targetDeployedWaUSDC = (totalWaUSDC * maxOnCredit()) / 10_000;
if (deployedWaUSDC > targetDeployedWaUSDC) {
// Withdraw excess from MorphoCredit
uint256 waUSDCToWithdraw = deployedWaUSDC - targetDeployedWaUSDC;
_withdrawFromMorpho(waUSDCToWithdraw);
} else if (targetDeployedWaUSDC > deployedWaUSDC && localWaUSDC > 0) {
// Deploy more if we have local waUSDC
uint256 waUSDCToDeploy = Math.min(localWaUSDC, targetDeployedWaUSDC - deployedWaUSDC);
_supplyToMorpho(waUSDCToDeploy);
}
}
/// @dev Helper function to supply waUSDC to MorphoCredit
/// @param amount Amount of waUSDC to supply
/// @return supplied Actual amount supplied (for consistency with withdraw helper)
function _supplyToMorpho(uint256 amount) internal returns (uint256 supplied) {
if (amount == 0) return 0;
morphoCredit.supply(_marketParams, amount, 0, address(this), "");
return amount;
}
/// @dev Helper function to withdraw waUSDC from MorphoCredit
/// @param amountRequested Amount of waUSDC to withdraw
/// @return amountWithdrawn Actual amount withdrawn (may be less than requested)
function _withdrawFromMorpho(uint256 amountRequested) internal returns (uint256 amountWithdrawn) {
if (amountRequested == 0) return 0;
morphoCredit.accrueInterest(_marketParams);
(uint256 shares, uint256 waUSDCMax, uint256 waUSDCLiquidity) = getPosition();
uint256 availableWaUSDC = Math.min(waUSDCMax, waUSDCLiquidity);
if (availableWaUSDC == 0) {
return 0;
}
amountWithdrawn = Math.min(amountRequested, availableWaUSDC);
if (amountWithdrawn > 0) {
if (amountWithdrawn >= waUSDCMax) {
morphoCredit.withdraw(_marketParams, 0, shares, address(this), address(this));
} else {
morphoCredit.withdraw(_marketParams, amountWithdrawn, 0, address(this), address(this));
}
}
return amountWithdrawn;
}
/*//////////////////////////////////////////////////////////////
PUBLIC VIEW FUNCTIONS (OVERRIDES)
//////////////////////////////////////////////////////////////*/
/// @dev Returns available withdraw limit, enforcing commitment time
/// @param _owner Address to check limit for
/// @return Maximum amount that can be withdrawn
function availableWithdrawLimit(address _owner) public view override returns (uint256) {
// Get available liquidity first
uint256 idleAsset = asset.balanceOf(address(this));
(, uint256 waUSDCMax, uint256 waUSDCLiquidity) = getPosition();
uint256 availableWaUSDC;
if (Pausable(address(WAUSDC)).paused()) {
availableWaUSDC = 0;
} else {
uint256 localWaUSDC = Math.min(balanceOfWaUSDC(), WAUSDC.maxRedeem(address(this)));
uint256 morphoWaUSDC = Math.min(waUSDCMax, waUSDCLiquidity);
morphoWaUSDC = Math.min(morphoWaUSDC, WAUSDC.maxRedeem(address(morphoCredit)));
availableWaUSDC = localWaUSDC + morphoWaUSDC;
}
uint256 availableLiquidity = idleAsset + WAUSDC.convertToAssets(availableWaUSDC);
// During shutdown, bypass all checks
if (TokenizedStrategy.isShutdown()) {
return availableLiquidity;
}
// Check commitment time
uint256 commitTime = minCommitmentTime();
if (commitTime > 0) {
uint256 depositTime = depositTimestamp[_owner];
if (depositTime > 0 && block.timestamp < depositTime + commitTime) {
return 0; // Commitment period not met
}
}
return availableLiquidity;
}
/// @dev Returns available deposit limit, enforcing whitelist and supply cap
/// @param _owner Address to check limit for
/// @return Maximum amount that can be deposited
function availableDepositLimit(address _owner) public view override returns (uint256) {
// Check whitelist if enabled
if (whitelistEnabled && !whitelist[_owner]) {
return 0;
}
uint256 maxDeposit = WAUSDC.maxDeposit(address(this));
if (Pausable(address(WAUSDC)).paused() || maxDeposit == 0) {
return 0;
}
// Block deposits from borrowers
if (morphoCredit.borrowShares(marketId, _owner) > 0) {
return 0;
}
uint256 cap = supplyCap();
if (cap == 0 || cap == type(uint256).max) {
return type(uint256).max;
}
uint256 currentTotalAssets = TokenizedStrategy.totalAssets();
if (cap <= currentTotalAssets) {
return 0;
}
return Math.min(cap - currentTotalAssets, maxDeposit);
}
/*//////////////////////////////////////////////////////////////
HOOKS IMPLEMENTATION
//////////////////////////////////////////////////////////////*/
/// @dev Pre-deposit hook to enforce minimum deposit and track commitment time
function _preDepositHook(uint256 assets, uint256 shares, address receiver) internal override {
if (assets == 0 && shares > 0) {
assets = TokenizedStrategy.previewMint(shares);
}
// Handle type(uint256).max case - resolve to actual balance
if (assets == type(uint256).max) {
assets = asset.balanceOf(msg.sender);
}
// Enforce minimum deposit only for first-time depositors
uint256 currentBalance = TokenizedStrategy.balanceOf(receiver);
if (currentBalance == 0) {
require(assets >= minDeposit, "Below minimum deposit");
}
// Prevent commitment bypass and griefing attacks
if (minCommitmentTime() > 0) {
// Only allow self-deposits or whitelisted depositors
require(
msg.sender == receiver || depositorWhitelist[msg.sender],
"USD3: Only self or whitelisted deposits allowed"
);
// Always extend commitment for valid deposits
depositTimestamp[receiver] = block.timestamp;
}
}
/// @dev Post-withdraw hook to clear commitment on full exit
function _postWithdrawHook(uint256 assets, uint256 shares, address receiver, address owner, uint256 maxLoss)
internal
override
{
// Clear commitment timestamp if user fully exited
if (TokenizedStrategy.balanceOf(owner) == 0) {
delete depositTimestamp[owner];
}
}
/// @dev Post-report hook to handle loss absorption by burning sUSD3's shares
function _postReportHook(uint256 profit, uint256 loss) internal override {
if (loss > 0 && sUSD3 != address(0)) {
// Get sUSD3's current USD3 balance
uint256 susd3Balance = TokenizedStrategy.balanceOf(sUSD3);
if (susd3Balance > 0) {
// Calculate how many shares are needed to cover the loss
// IMPORTANT: We must use pre-report values to calculate the correct share amount
// The report has already reduced totalAssets, so we add the loss back
uint256 totalSupply = TokenizedStrategy.totalSupply();
uint256 totalAssets = TokenizedStrategy.totalAssets();
// Calculate shares to burn using pre-loss exchange rate
uint256 sharesToBurn = loss.mulDiv(totalSupply, totalAssets + loss, Math.Rounding.Floor);
// Cap at sUSD3's actual balance - they can't lose more than they have
if (sharesToBurn > susd3Balance) {
sharesToBurn = susd3Balance;
}
if (sharesToBurn > 0) {
_burnSharesFromSusd3(sharesToBurn);
}
}
}
}
/**
* @notice Prevent transfers during commitment period
* @dev Override from BaseHooksUpgradeable to enforce commitment
* @param from Address transferring shares
* @param to Address receiving shares
* @param amount Amount of shares being transferred
*/
function _preTransferHook(address from, address to, uint256 amount) internal override {
// Allow minting (from == 0) and burning (to == 0)
if (from == address(0) || to == address(0)) return;
// Allow transfers to/from sUSD3 (staking and withdrawals)
if (to == sUSD3 || from == sUSD3) return;
// Check commitment period
uint256 commitmentEnd = depositTimestamp[from] + minCommitmentTime();
require(
block.timestamp >= commitmentEnd || depositTimestamp[from] == 0,
"USD3: Cannot transfer during commitment period"
);
}
/*//////////////////////////////////////////////////////////////
INTERNAL HELPER FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @dev Directly burn shares from sUSD3's balance using storage manipulation
*
* IMPORTANT: Direct storage manipulation is necessary here because TokenizedStrategy
* does not expose a public burn function. The only ways to burn shares in
* TokenizedStrategy are through withdraw/redeem (which require asset transfers)
* or internal profit/loss accounting. Since we need to burn sUSD3's shares
* without triggering asset transfers, direct storage manipulation is the only
* viable approach.
*
* @param amount Number of shares to burn from sUSD3
*/
function _burnSharesFromSusd3(uint256 amount) internal {
// Calculate storage slots using the library
bytes32 totalSupplySlot = TokenizedStrategyStorageLib.totalSupplySlot();
bytes32 balanceSlot = TokenizedStrategyStorageLib.balancesSlot(sUSD3);
// Read current values
uint256 currentBalance;
uint256 currentTotalSupply;
assembly {
currentBalance := sload(balanceSlot)
currentTotalSupply := sload(totalSupplySlot)
}
// Ensure we don't burn more than available
uint256 actualBurn = amount;
if (actualBurn > currentBalance) {
actualBurn = currentBalance;
}
// Update storage
assembly {
sstore(balanceSlot, sub(currentBalance, actualBurn))
sstore(totalSupplySlot, sub(currentTotalSupply, actualBurn))
}
// Emit Transfer event to address(0) for transparency
emit IERC20.Transfer(sUSD3, address(0), actualBurn);
}
/*//////////////////////////////////////////////////////////////
PUBLIC VIEW FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Get the balance of waUSDC held locally (not deployed to MorphoCredit)
* @return Amount of waUSDC held in this contract
*/
function balanceOfWaUSDC() public view returns (uint256) {
return WAUSDC.balanceOf(address(this));
}
/**
* @notice Get the amount of waUSDC supplied to MorphoCredit
* @return Amount of waUSDC deployed to the lending market
*/
function suppliedWaUSDC() public view returns (uint256) {
return morphoCredit.expectedSupplyAssets(_marketParams, address(this));
}
/**
* @notice Get the maximum percentage of funds to deploy to credit markets from ProtocolConfig
* @return Maximum deployment ratio in basis points (10000 = 100%)
* @dev Returns the value from ProtocolConfig directly. If not configured in ProtocolConfig,
* it returns 0, effectively preventing deployment until explicitly configured.
*/
function maxOnCredit() public view returns (uint256) {
IProtocolConfig config = IProtocolConfig(IMorphoCredit(address(morphoCredit)).protocolConfig());
return config.getMaxOnCredit();
}
/**
* @notice Get the minimum commitment time from ProtocolConfig
* @return Minimum commitment time in seconds
*/
function minCommitmentTime() public view returns (uint256) {
IProtocolConfig config = IProtocolConfig(IMorphoCredit(address(morphoCredit)).protocolConfig());
return config.getUsd3CommitmentTime();
}
/**
* @notice Get the supply cap from ProtocolConfig
* @return Supply cap in asset units (0 means no cap)
*/
function supplyCap() public view returns (uint256) {
IProtocolConfig config = IProtocolConfig(IMorphoCredit(address(morphoCredit)).protocolConfig());
return config.config(ProtocolConfigLib.USD3_SUPPLY_CAP);
}
/*//////////////////////////////////////////////////////////////
MANAGEMENT FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Set the sUSD3 subordinate strategy address
* @param _sUSD3 Address of the sUSD3 strategy
* @dev Only callable by management. After calling, also set performance fee recipient.
*/
function setSUSD3(address _sUSD3) external onlyManagement {
require(sUSD3 == address(0), "sUSD3 already set");
require(_sUSD3 != address(0), "Invalid address");
sUSD3 = _sUSD3;
emit SUSD3StrategyUpdated(address(0), _sUSD3);
// NOTE: After calling this, management should also call:
// ITokenizedStrategy(usd3Address).setPerformanceFeeRecipient(_sUSD3)
// to ensure yield distribution goes to sUSD3
}
/**
* @notice Enable or disable whitelist requirement
* @param _enabled True to enable whitelist, false to disable
*/
function setWhitelistEnabled(bool _enabled) external onlyManagement {
whitelistEnabled = _enabled;
}
/**
* @notice Update whitelist status for an address
* @param _user Address to update
* @param _allowed True to whitelist, false to remove from whitelist
*/
function setWhitelist(address _user, bool _allowed) external onlyManagement {
whitelist[_user] = _allowed;
emit WhitelistUpdated(_user, _allowed);
}
/**
* @notice Update depositor whitelist status for an address
* @param _depositor Address to update
* @param _allowed True to allow extending commitments, false to disallow
*/
function setDepositorWhitelist(address _depositor, bool _allowed) external onlyManagement {
depositorWhitelist[_depositor] = _allowed;
emit DepositorWhitelistUpdated(_depositor, _allowed);
}
/**
* @notice Set minimum deposit amount
* @param _minDeposit Minimum amount required for deposits
*/
function setMinDeposit(uint256 _minDeposit) external onlyManagement {
minDeposit = _minDeposit;
emit MinDepositUpdated(_minDeposit);
}
/*//////////////////////////////////////////////////////////////
KEEPER FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Sync the tranche share (performance fee) from ProtocolConfig
* @dev Reads TRANCHE_SHARE_VARIANT from ProtocolConfig and updates local storage
*
* IMPORTANT: Direct storage manipulation is necessary here because TokenizedStrategy's
* setPerformanceFee() function has a hardcoded MAX_FEE limit of 5000 (50%). Since we
* need to support higher fee distributions to sUSD3 (potentially up to 100% for full
* subordination scenarios), we must bypass this restriction by directly modifying the
* storage slot.
*
* Storage layout in TokenizedStrategy (slot 9):
* - Bits 0-31: profitMaxUnlockTime (uint32)
* - Bits 32-47: performanceFee (uint16) <- We modify this
* - Bits 48-207: performanceFeeRecipient (address)
*
* @dev Only callable by keepers to ensure controlled updates
*/
function syncTrancheShare() external onlyKeepers {
// Get the protocol config through MorphoCredit
IProtocolConfig config = IProtocolConfig(IMorphoCredit(address(morphoCredit)).protocolConfig());
// Read the tranche share variant (yield share to sUSD3 in basis points)
uint256 trancheShare = config.getTrancheShareVariant();
require(trancheShare <= 10_000, "Invalid tranche share");
// Get the storage slot for performanceFee using the library
bytes32 targetSlot = TokenizedStrategyStorageLib.profitConfigSlot();
// Read current slot value
uint256 currentSlotValue;
assembly {
currentSlotValue := sload(targetSlot)
}
// Clear the performanceFee bits (32-47) and set new value
uint256 mask = ~(uint256(0xFFFF) << 32);
uint256 newSlotValue = (currentSlotValue & mask) | (trancheShare << 32);
// Write back to storage
assembly {
sstore(targetSlot, newSlotValue)
}
emit TrancheShareSynced(trancheShare);
}
/*//////////////////////////////////////////////////////////////
STORAGE GAP
//////////////////////////////////////////////////////////////*/
/**
* @dev This empty reserved space is put in place to allow future versions to add new
* variables without shifting down storage in the inheritance chain.
* See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
*/
uint256[40] private __gap;
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;
import {BaseStrategyUpgradeable} from "./BaseStrategyUpgradeable.sol";
import {Hooks} from "@periphery/Bases/Hooks/Hooks.sol";
import {ITokenizedStrategy} from "@tokenized-strategy/interfaces/ITokenizedStrategy.sol";
/**
* @title BaseHooksUpgradeable
* @author Yearn's BaseHooks adapted for upgradeable strategies
* @notice This contract can be inherited by any strategy wishing to implement
* pre or post hooks for deposit, withdraw, transfer, or report functions.
*
* This version:
* - Inherits from BaseStrategyUpgradeable instead of BaseHealthCheck
* - Uses Yearn's Hooks contract for standardized hook interfaces
* - Is compatible with upgradeable proxy patterns
*/
abstract contract BaseHooksUpgradeable is BaseStrategyUpgradeable, Hooks {
/*//////////////////////////////////////////////////////////////
CONSTANTS
//////////////////////////////////////////////////////////////*/
uint256 internal constant MAX_BPS = 10_000;
/*//////////////////////////////////////////////////////////////
OVERRIDDEN FUNCTIONS
//////////////////////////////////////////////////////////////*/
/**
* @notice Deposit assets and receive shares
* @param assets Amount of assets to deposit
* @param receiver Address to receive the shares
* @return shares Amount of shares minted
*/
function deposit(uint256 assets, address receiver) external virtual returns (uint256 shares) {
_preDepositHook(assets, shares, receiver);
shares = abi.decode(
_delegateCall(abi.encodeCall(ITokenizedStrategy(address(this)).deposit, (assets, receiver))), (uint256)
);
_postDepositHook(assets, shares, receiver);
}
/**
* @notice Mint shares by depositing assets
* @param shares Amount of shares to mint
* @param receiver Address to receive the shares
* @return assets Amount of assets deposited
*/
function mint(uint256 shares, address receiver) external virtual returns (uint256 assets) {
_preDepositHook(assets, shares, receiver);
assets = abi.decode(
_delegateCall(abi.encodeCall(ITokenizedStrategy(address(this)).mint, (shares, receiver))), (uint256)
);
_postDepositHook(assets, shares, receiver);
}
/**
* @notice Withdraw assets by burning shares
* @param assets Amount of assets to withdraw
* @param receiver Address to receive the assets
* @param owner Address whose shares are burned
* @return shares Amount of shares burned
*/
function withdraw(uint256 assets, address receiver, address owner) external virtual returns (uint256 shares) {
return withdraw(assets, receiver, owner, 0);
}
/**
* @notice Withdraw assets with custom max loss
* @param assets Amount of assets to withdraw
* @param receiver Address to receive the assets
* @param owner Address whose shares are burned
* @param maxLoss Maximum acceptable loss in basis points
* @return shares Amount of shares burned
*/
function withdraw(uint256 assets, address receiver, address owner, uint256 maxLoss)
public
virtual
returns (uint256 shares)
{
_preWithdrawHook(assets, shares, receiver, owner, maxLoss);
shares = abi.decode(
_delegateCall(
abi.encodeWithSelector(ITokenizedStrategy.withdraw.selector, assets, receiver, owner, maxLoss)
),
(uint256)
);
_postWithdrawHook(assets, shares, receiver, owner, maxLoss);
}
/**
* @notice Redeem shares for assets
* @param shares Amount of shares to redeem
* @param receiver Address to receive the assets
* @param owner Address whose shares are burned
* @return assets Amount of assets withdrawn
*/
function redeem(uint256 shares, address receiver, address owner) external virtual returns (uint256 assets) {
return redeem(shares, receiver, owner, MAX_BPS);
}
/**
* @notice Redeem shares with custom max loss
* @param shares Amount of shares to redeem
* @param receiver Address to receive the assets
* @param owner Address whose shares are burned
* @param maxLoss Maximum acceptable loss in basis points
* @return assets Amount of assets withdrawn
*/
function redeem(uint256 shares, address receiver, address owner, uint256 maxLoss)
public
virtual
returns (uint256 assets)
{
_preWithdrawHook(assets, shares, receiver, owner, maxLoss);
assets = abi.decode(
_delegateCall(abi.encodeWithSelector(ITokenizedStrategy.redeem.selector, shares, receiver, owner, maxLoss)),
(uint256)
);
_postWithdrawHook(assets, shares, receiver, owner, maxLoss);
}
/**
* @notice Transfer shares to another address
* @param to Address to receive the shares
* @param amount Amount of shares to transfer
* @return success Whether the transfer succeeded
*/
function transfer(address to, uint256 amount) external virtual returns (bool) {
_preTransferHook(msg.sender, to, amount);
bool success =
abi.decode(_delegateCall(abi.encodeCall(ITokenizedStrategy(address(this)).transfer, (to, amount))), (bool));
_postTransferHook(msg.sender, to, amount, success);
return success;
}
/**
* @notice Transfer shares from one address to another
* @param from Address to transfer from
* @param to Address to transfer to
* @param amount Amount of shares to transfer
* @return success Whether the transfer succeeded
*/
function transferFrom(address from, address to, uint256 amount) external virtual returns (bool) {
_preTransferHook(from, to, amount);
bool success = abi.decode(
_delegateCall(abi.encodeCall(ITokenizedStrategy(address(this)).transferFrom, (from, to, amount))), (bool)
);
_postTransferHook(from, to, amount, success);
return success;
}
/**
* @notice Report profit and loss
* @return profit Amount of profit generated
* @return loss Amount of loss incurred
*/
function report() external virtual returns (uint256 profit, uint256 loss) {
_preReportHook();
(profit, loss) =
abi.decode(_delegateCall(abi.encodeCall(ITokenizedStrategy(address(this)).report, ())), (uint256, uint256));
_postReportHook(profit, loss);
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @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.encodeWithSelector(token.transfer.selector, 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.encodeWithSelector(token.transferFrom.selector, from, to, value));
}
/**
* @dev Deprecated. This function has issues similar to the ones found in
* {IERC20-approve}, and its usage is discouraged.
*
* Whenever possible, use {safeIncreaseAllowance} and
* {safeDecreaseAllowance} instead.
*/
function safeApprove(IERC20 token, address spender, uint256 value) internal {
// safeApprove should only be called when setting an initial allowance,
// or when resetting it to zero. To increase and decrease it, use
// 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
require(
(value == 0) || (token.allowance(address(this), spender) == 0),
"SafeERC20: approve from non-zero to non-zero allowance"
);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
}
/**
* @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);
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
unchecked {
uint256 oldAllowance = token.allowance(address(this), spender);
require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
}
}
/**
* @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.encodeWithSelector(token.approve.selector, spender, value);
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
* Revert on invalid signature.
*/
function safePermit(
IERC20Permit token,
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) internal {
uint256 nonceBefore = token.nonces(owner);
token.permit(owner, spender, value, deadline, v, r, s);
uint256 nonceAfter = token.nonces(owner);
require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* 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.isContract(address(token));
}
}// 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: GPL-2.0-or-later
pragma solidity ^0.8.18;
type Id is bytes32;
struct MarketParams {
address loanToken;
address collateralToken;
address oracle;
address irm;
uint256 lltv;
address creditLine;
}
/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
/// accrual.
struct Position {
uint256 supplyShares;
uint128 borrowShares;
uint128 collateral;
}
/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the additional shares accrued by `feeRecipient` since the last
/// interest accrual.
/// @dev Warning: `totalMarkdownAmount` may be stale as markdowns are only updated when borrowers are touched.
struct Market {
uint128 totalSupplyAssets;
uint128 totalSupplyShares;
uint128 totalBorrowAssets;
uint128 totalBorrowShares;
uint128 lastUpdate;
uint128 fee;
uint128 totalMarkdownAmount; // Running tally of all borrower markdowns
}
/// @notice Per-borrower premium tracking
/// @param lastAccrualTime Timestamp of the last premium accrual for this borrower
/// @param rate Current risk premium rate per second (scaled by WAD)
/// @param borrowAssetsAtLastAccrual Snapshot of borrow position at last premium accrual
struct BorrowerPremium {
uint128 lastAccrualTime;
uint128 rate;
uint128 borrowAssetsAtLastAccrual;
}
/// @notice Repayment tracking structures
enum RepaymentStatus {
Current,
GracePeriod,
Delinquent,
Default
}
struct PaymentCycle {
uint256 endDate;
}
struct RepaymentObligation {
uint128 paymentCycleId;
uint128 amountDue;
uint128 endingBalance;
}
/// @notice Markdown state for tracking defaulted debt value reduction
/// @param lastCalculatedMarkdown Last calculated markdown amount
struct MarkdownState {
uint128 lastCalculatedMarkdown;
}
struct Authorization {
address authorizer;
address authorized;
bool isAuthorized;
uint256 nonce;
uint256 deadline;
}
struct Signature {
uint8 v;
bytes32 r;
bytes32 s;
}
/// @dev This interface is used for factorizing IMorphoStaticTyping and IMorpho.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoBase {
/// @notice The EIP-712 domain separator.
/// @dev Warning: Every EIP-712 signed message based on this domain separator can be reused on chains sharing the
/// same chain id and on forks because the domain separator would be the same.
function DOMAIN_SEPARATOR() external view returns (bytes32);
/// @notice The owner of the contract.
/// @dev It has the power to change the owner.
/// @dev It has the power to set fees on markets and set the fee recipient.
/// @dev It has the power to enable but not disable IRMs and LLTVs.
function owner() external view returns (address);
/// @notice The fee recipient of all markets.
/// @dev The recipient receives the fees of a given market through a supply position on that market.
function feeRecipient() external view returns (address);
/// @notice Whether the `irm` is enabled.
function isIrmEnabled(address irm) external view returns (bool);
/// @notice Whether the `lltv` is enabled.
function isLltvEnabled(uint256 lltv) external view returns (bool);
/// @notice The `authorizer`'s current nonce. Used to prevent replay attacks with EIP-712 signatures.
function nonce(address authorizer) external view returns (uint256);
/// @notice Sets `newOwner` as `owner` of the contract.
/// @dev Warning: No two-step transfer ownership.
/// @dev Warning: The owner can be set to the zero address.
function setOwner(address newOwner) external;
/// @notice Enables `irm` as a possible IRM for market creation.
/// @dev Warning: It is not possible to disable an IRM.
function enableIrm(address irm) external;
/// @notice Enables `lltv` as a possible LLTV for market creation.
/// @dev Warning: It is not possible to disable a LLTV.
function enableLltv(uint256 lltv) external;
/// @notice Sets the `newFee` for the given market `marketParams`.
/// @param newFee The new fee, scaled by WAD.
/// @dev Warning: The recipient can be the zero address.
function setFee(MarketParams memory marketParams, uint256 newFee) external;
/// @notice Sets `newFeeRecipient` as `feeRecipient` of the fee.
/// @dev Warning: If the fee recipient is set to the zero address, fees will accrue there and will be lost.
/// @dev Modifying the fee recipient will allow the new recipient to claim any pending fees not yet accrued. To
/// ensure that the current recipient receives all due fees, accrue interest manually prior to making any changes.
function setFeeRecipient(address newFeeRecipient) external;
/// @notice Creates the market `marketParams`.
/// @dev Here is the list of assumptions on the market's dependencies (tokens, IRM and oracle) that guarantees
/// Morpho behaves as expected:
/// - The token should be ERC-20 compliant, except that it can omit return values on `transfer` and `transferFrom`.
/// - The token balance of Morpho should only decrease on `transfer` and `transferFrom`. In particular, tokens with
/// burn functions are not supported.
/// - The token should not re-enter Morpho on `transfer` nor `transferFrom`.
/// - The token balance of the sender (resp. receiver) should decrease (resp. increase) by exactly the given amount
/// on `transfer` and `transferFrom`. In particular, tokens with fees on transfer are not supported.
/// - The IRM should not re-enter Morpho.
/// - The oracle should return a price with the correct scaling.
/// @dev Here is a list of assumptions on the market's dependencies which, if broken, could break Morpho's liveness
/// properties (funds could get stuck):
/// - The token should not revert on `transfer` and `transferFrom` if balances and approvals are right.
/// - The amount of assets supplied and borrowed should not go above ~1e35 (otherwise the computation of
/// `toSharesUp` and `toSharesDown` can overflow).
/// - The IRM should not revert on `borrowRate`.
/// - The IRM should not return a very high borrow rate (otherwise the computation of `interest` in
/// `_accrueInterest` can overflow).
/// - The oracle should not revert `price`.
/// - The oracle should not return a very high price (otherwise the computation of `maxBorrow` in `_isHealthy` or of
/// `assetsRepaid` in `liquidate` can overflow).
/// @dev The borrow share price of a market with less than 1e4 assets borrowed can be decreased by manipulations, to
/// the point where `totalBorrowShares` is very large and borrowing overflows.
function createMarket(MarketParams memory marketParams) external;
/// @notice Supplies `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
/// `onMorphoSupply` function with the given `data`.
/// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
/// caller is guaranteed to have `assets` tokens pulled from their balance, but the possibility to mint a specific
/// amount of shares is given for full compatibility and precision.
/// @dev Supplying a large amount can revert for overflow.
/// @dev Supplying an amount of shares may lead to supply more or fewer assets than expected due to slippage.
/// Consider using the `assets` parameter to avoid this.
/// @param marketParams The market to supply assets to.
/// @param assets The amount of assets to supply.
/// @param shares The amount of shares to mint.
/// @param onBehalf The address that will own the increased supply position.
/// @param data Arbitrary data to pass to the `onMorphoSupply` callback. Pass empty data if not needed.
/// @return assetsSupplied The amount of assets supplied.
/// @return sharesSupplied The amount of shares minted.
function supply(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
bytes memory data
) external returns (uint256 assetsSupplied, uint256 sharesSupplied);
/// @notice Withdraws `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
/// @dev Either `assets` or `shares` should be zero. To withdraw max, pass the `shares`'s balance of `onBehalf`.
/// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
/// @dev Withdrawing an amount corresponding to more shares than supplied will revert for underflow.
/// @dev It is advised to use the `shares` input when withdrawing the full position to avoid reverts due to
/// conversion roundings between shares and assets.
/// @param marketParams The market to withdraw assets from.
/// @param assets The amount of assets to withdraw.
/// @param shares The amount of shares to burn.
/// @param onBehalf The address of the owner of the supply position.
/// @param receiver The address that will receive the withdrawn assets.
/// @return assetsWithdrawn The amount of assets withdrawn.
/// @return sharesWithdrawn The amount of shares burned.
function withdraw(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
address receiver
) external returns (uint256 assetsWithdrawn, uint256 sharesWithdrawn);
/// @notice Borrows `assets` or `shares` on behalf of `onBehalf` and sends the assets to `receiver`.
/// @dev Either `assets` or `shares` should be zero. Most use cases should rely on `assets` as an input so the
/// caller is guaranteed to borrow `assets` of tokens, but the possibility to mint a specific amount of shares is
/// given for full compatibility and precision.
/// @dev `msg.sender` must be authorized to manage `onBehalf`'s positions.
/// @dev Borrowing a large amount can revert for overflow.
/// @dev Borrowing an amount of shares may lead to borrow fewer assets than expected due to slippage.
/// Consider using the `assets` parameter to avoid this.
/// @param marketParams The market to borrow assets from.
/// @param assets The amount of assets to borrow.
/// @param shares The amount of shares to mint.
/// @param onBehalf The address that will own the increased borrow position.
/// @param receiver The address that will receive the borrowed assets.
/// @return assetsBorrowed The amount of assets borrowed.
/// @return sharesBorrowed The amount of shares minted.
function borrow(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
address receiver
) external returns (uint256 assetsBorrowed, uint256 sharesBorrowed);
/// @notice Repays `assets` or `shares` on behalf of `onBehalf`, optionally calling back the caller's
/// `onMorphoRepay` function with the given `data`.
/// @dev Either `assets` or `shares` should be zero. To repay max, pass the `shares`'s balance of `onBehalf`.
/// @dev Repaying an amount corresponding to more shares than borrowed will revert for underflow.
/// @dev It is advised to use the `shares` input when repaying the full position to avoid reverts due to conversion
/// roundings between shares and assets.
/// @dev An attacker can front-run a repay with a small repay making the transaction revert for underflow.
/// @param marketParams The market to repay assets to.
/// @param assets The amount of assets to repay.
/// @param shares The amount of shares to burn.
/// @param onBehalf The address of the owner of the debt position.
/// @param data Arbitrary data to pass to the `onMorphoRepay` callback. Pass empty data if not needed.
/// @return assetsRepaid The amount of assets repaid.
/// @return sharesRepaid The amount of shares burned.
function repay(
MarketParams memory marketParams,
uint256 assets,
uint256 shares,
address onBehalf,
bytes memory data
) external returns (uint256 assetsRepaid, uint256 sharesRepaid);
/// @notice Accrues interest for the given market `marketParams`.
function accrueInterest(MarketParams memory marketParams) external;
/// @notice Returns the data stored on the different `slots`.
function extSloads(bytes32[] memory slots) external view returns (bytes32[] memory);
}
/// @dev This interface is inherited by Morpho so that function signatures are checked by the compiler.
/// @dev Consider using the IMorpho interface instead of this one.
interface IMorphoStaticTyping is IMorphoBase {
/// @notice The state of the position of `user` on the market corresponding to `id`.
/// @dev Warning: For `feeRecipient`, `supplyShares` does not contain the accrued shares since the last interest
/// accrual.
function position(Id id, address user)
external
view
returns (uint256 supplyShares, uint128 borrowShares, uint128 collateral);
/// @notice The state of the market corresponding to `id`.
/// @dev Warning: `totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last interest
/// accrual.
function market(Id id)
external
view
returns (
uint128 totalSupplyAssets,
uint128 totalSupplyShares,
uint128 totalBorrowAssets,
uint128 totalBorrowShares,
uint128 lastUpdate,
uint128 fee,
uint128 totalMarkdownAmount
);
/// @notice The market params corresponding to `id`.
/// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
/// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
function idToMarketParams(Id id)
external
view
returns (
address loanToken,
address collateralToken,
address oracle,
address irm,
uint256 lltv,
address creditLine
);
}
/// @title IMorpho
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorpho is IMorphoBase {
/// @notice The state of the position of `user` on the market corresponding to `id`.
/// @dev Warning: For `feeRecipient`, `p.supplyShares` does not contain the accrued shares since the last interest
/// accrual.
function position(Id id, address user) external view returns (Position memory p);
/// @notice The state of the market corresponding to `id`.
/// @dev Warning: `m.totalSupplyAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `m.totalBorrowAssets` does not contain the accrued interest since the last interest accrual.
/// @dev Warning: `m.totalSupplyShares` does not contain the accrued shares by `feeRecipient` since the last
/// interest accrual.
function market(Id id) external view returns (Market memory m);
/// @notice The market params corresponding to `id`.
/// @dev This mapping is not used in Morpho. It is there to enable reducing the cost associated to calldata on layer
/// 2s by creating a wrapper contract with functions that take `id` as input instead of `marketParams`.
function idToMarketParams(Id id) external view returns (MarketParams memory);
}
/// @title IMorphoCredit
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @dev Use this interface for Morpho to have access to all the functions with the appropriate function signatures.
interface IMorphoCredit {
/// @notice The helper of the contract.
function helper() external view returns (address);
/// @notice The usd3 contract
function usd3() external view returns (address);
/// @notice The protocol config of the contract.
function protocolConfig() external view returns (address);
/// @notice Sets `helper` as `helper` of the contract.
/// @param newHelper The new helper address
function setHelper(address newHelper) external;
/// @notice Sets `usd3` as `usd3` of the contract.
/// @param newUsd3 The new usd3 address
function setUsd3(address newUsd3) external;
/// @notice Sets the credit line and premium rate for a borrower
/// @param id The market ID
/// @param borrower The borrower address
/// @param credit The credit line amount
/// @param drp The drp per second in WAD
function setCreditLine(Id id, address borrower, uint256 credit, uint128 drp) external;
/// @notice Returns the premium data for a specific borrower in a market
/// @param id The market ID
/// @param borrower The borrower address
/// @return lastAccrualTime Timestamp of the last premium accrual
/// @return rate Current risk premium rate per second (scaled by WAD)
/// @return borrowAssetsAtLastAccrual Snapshot of borrow position at last premium accrual
function borrowerPremium(Id id, address borrower)
external
view
returns (uint128 lastAccrualTime, uint128 rate, uint128 borrowAssetsAtLastAccrual);
/// @notice Batch accrue premiums for multiple borrowers
/// @param id Market ID
/// @param borrowers Array of borrower addresses
/// @dev Gas usage scales linearly with array size. Callers should manage batch sizes based on block gas limits.
function accruePremiumsForBorrowers(Id id, address[] calldata borrowers) external;
/// @notice Close a payment cycle and post obligations for multiple borrowers
/// @param id Market ID
/// @param endDate Cycle end date
/// @param borrowers Array of borrower addresses
/// @param repaymentBps Array of repayment basis points (e.g., 500 = 5%)
/// @param endingBalances Array of ending balances for penalty calculations
function closeCycleAndPostObligations(
Id id,
uint256 endDate,
address[] calldata borrowers,
uint256[] calldata repaymentBps,
uint256[] calldata endingBalances
) external;
/// @notice Add obligations to the latest payment cycle
/// @param id Market ID
/// @param borrowers Array of borrower addresses
/// @param repaymentBps Array of repayment basis points (e.g., 500 = 5%)
/// @param endingBalances Array of ending balances
function addObligationsToLatestCycle(
Id id,
address[] calldata borrowers,
uint256[] calldata repaymentBps,
uint256[] calldata endingBalances
) external;
/// @notice Get repayment obligation for a borrower
/// @param id Market ID
/// @param borrower Borrower address
/// @return cycleId The payment cycle ID
/// @return amountDue The amount due
/// @return endingBalance The ending balance for penalty calculations
function repaymentObligation(Id id, address borrower)
external
view
returns (uint128 cycleId, uint128 amountDue, uint128 endingBalance);
/// @notice Get payment cycle end date
/// @param id Market ID
/// @param cycleId Cycle ID
/// @return endDate The cycle end date
function paymentCycle(Id id, uint256 cycleId) external view returns (uint256 endDate);
/// @notice Settle a borrower's account by writing off all remaining debt
/// @dev Only callable by credit line contract
/// @dev Should be called after any partial repayments have been made
/// @param marketParams The market parameters
/// @param borrower The borrower whose account to settle
/// @return writtenOffAssets Amount of assets written off
/// @return writtenOffShares Amount of shares written off
function settleAccount(MarketParams memory marketParams, address borrower)
external
returns (uint256 writtenOffAssets, uint256 writtenOffShares);
/// @notice Get markdown state for a borrower
/// @param id Market ID
/// @param borrower Borrower address
/// @return lastCalculatedMarkdown Last calculated markdown amount
function markdownState(Id id, address borrower) external view returns (uint128 lastCalculatedMarkdown);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {IMorpho, Id} from "../../interfaces/IMorpho.sol";
import {MorphoStorageLib} from "./MorphoStorageLib.sol";
/// @title MorphoLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Helper library to access Morpho storage variables.
/// @dev Warning: Supply and borrow getters may return outdated values that do not include accrued interest.
library MorphoLib {
function supplyShares(IMorpho morpho, Id id, address user) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.positionSupplySharesSlot(id, user));
return uint256(morpho.extSloads(slot)[0]);
}
function borrowShares(IMorpho morpho, Id id, address user) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user));
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function collateral(IMorpho morpho, Id id, address user) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.positionBorrowSharesAndCollateralSlot(id, user));
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function totalSupplyAssets(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id));
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function totalSupplyShares(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalSupplyAssetsAndSharesSlot(id));
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function totalBorrowAssets(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id));
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function totalBorrowShares(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketTotalBorrowAssetsAndSharesSlot(id));
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function lastUpdate(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id));
return uint128(uint256(morpho.extSloads(slot)[0]));
}
function fee(IMorpho morpho, Id id) internal view returns (uint256) {
bytes32[] memory slot = _array(MorphoStorageLib.marketLastUpdateAndFeeSlot(id));
return uint256(morpho.extSloads(slot)[0] >> 128);
}
function _array(bytes32 x) private pure returns (bytes32[] memory) {
bytes32[] memory res = new bytes32[](1);
res[0] = x;
return res;
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Id, MarketParams, Market, IMorpho} from "../../interfaces/IMorpho.sol";
import {IIrm} from "../../interfaces/IIrm.sol";
import {MathLib} from "../MathLib.sol";
import {UtilsLib} from "../UtilsLib.sol";
import {MorphoLib} from "./MorphoLib.sol";
import {SharesMathLib} from "../SharesMathLib.sol";
import {MarketParamsLib} from "../MarketParamsLib.sol";
/// @title MorphoBalancesLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Helper library exposing getters with the expected value after interest accrual.
/// @dev This library is not used in Morpho itself and is intended to be used by integrators.
/// @dev The getter to retrieve the expected total borrow shares is not exposed because interest accrual does not apply
/// to it. The value can be queried directly on Morpho using `totalBorrowShares`.
library MorphoBalancesLib {
using MathLib for uint256;
using MathLib for uint128;
using UtilsLib for uint256;
using MorphoLib for IMorpho;
using SharesMathLib for uint256;
using MarketParamsLib for MarketParams;
/// @notice Returns the expected market balances of a market after having accrued interest.
/// @return The expected total supply assets.
/// @return The expected total supply shares.
/// @return The expected total borrow assets.
/// @return The expected total borrow shares.
function expectedMarketBalances(IMorpho morpho, MarketParams memory marketParams)
internal
view
returns (uint256, uint256, uint256, uint256)
{
Id id = marketParams.id();
Market memory market = morpho.market(id);
uint256 elapsed = block.timestamp - market.lastUpdate;
// Skipped if elapsed == 0 or totalBorrowAssets == 0 because interest would be null, or if irm == address(0).
if (elapsed != 0 && market.totalBorrowAssets != 0 && marketParams.irm != address(0)) {
uint256 borrowRate = IIrm(marketParams.irm).borrowRateView(marketParams, market);
uint256 interest = market.totalBorrowAssets.wMulDown(borrowRate.wTaylorCompounded(elapsed));
market.totalBorrowAssets += interest.toUint128();
market.totalSupplyAssets += interest.toUint128();
if (market.fee != 0) {
uint256 feeAmount = interest.wMulDown(market.fee);
// The fee amount is subtracted from the total supply in this calculation to compensate for the fact
// that total supply is already updated.
uint256 feeShares =
feeAmount.toSharesDown(market.totalSupplyAssets - feeAmount, market.totalSupplyShares);
market.totalSupplyShares += feeShares.toUint128();
}
}
return (market.totalSupplyAssets, market.totalSupplyShares, market.totalBorrowAssets, market.totalBorrowShares);
}
/// @notice Returns the expected total supply assets of a market after having accrued interest.
function expectedTotalSupplyAssets(IMorpho morpho, MarketParams memory marketParams)
internal
view
returns (uint256 totalSupplyAssets)
{
(totalSupplyAssets,,,) = expectedMarketBalances(morpho, marketParams);
}
/// @notice Returns the expected total borrow assets of a market after having accrued interest.
function expectedTotalBorrowAssets(IMorpho morpho, MarketParams memory marketParams)
internal
view
returns (uint256 totalBorrowAssets)
{
(,, totalBorrowAssets,) = expectedMarketBalances(morpho, marketParams);
}
/// @notice Returns the expected total supply shares of a market after having accrued interest.
function expectedTotalSupplyShares(IMorpho morpho, MarketParams memory marketParams)
internal
view
returns (uint256 totalSupplyShares)
{
(, totalSupplyShares,,) = expectedMarketBalances(morpho, marketParams);
}
/// @notice Returns the expected supply assets balance of `user` on a market after having accrued interest.
/// @dev Warning: Wrong for `feeRecipient` because their supply shares increase is not taken into account.
/// @dev Warning: Withdrawing using the expected supply assets can lead to a revert due to conversion roundings from
/// assets to shares.
function expectedSupplyAssets(IMorpho morpho, MarketParams memory marketParams, address user)
internal
view
returns (uint256)
{
Id id = marketParams.id();
uint256 supplyShares = morpho.supplyShares(id, user);
(uint256 totalSupplyAssets, uint256 totalSupplyShares,,) = expectedMarketBalances(morpho, marketParams);
return supplyShares.toAssetsDown(totalSupplyAssets, totalSupplyShares);
}
/// @notice Returns the expected borrow assets balance of `user` on a market after having accrued interest.
/// @dev Warning: The expected balance is rounded up, so it may be greater than the market's expected total borrow
/// assets.
function expectedBorrowAssets(IMorpho morpho, MarketParams memory marketParams, address user)
internal
view
returns (uint256)
{
Id id = marketParams.id();
uint256 borrowShares = morpho.borrowShares(id, user);
(,, uint256 totalBorrowAssets, uint256 totalBorrowShares) = expectedMarketBalances(morpho, marketParams);
return borrowShares.toAssetsUp(totalBorrowAssets, totalBorrowShares);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {MathLib} from "./MathLib.sol";
/// @title SharesMathLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Shares management library.
/// @dev This implementation mitigates share price manipulations, using OpenZeppelin's method of virtual shares:
/// https://docs.openzeppelin.com/contracts/4.x/erc4626#inflation-attack.
library SharesMathLib {
using MathLib for uint256;
/// @dev The number of virtual shares has been chosen low enough to prevent overflows, and high enough to ensure
/// high precision computations.
/// @dev Virtual shares can never be redeemed for the assets they are entitled to, but it is assumed the share price
/// stays low enough not to inflate these assets to a significant value.
/// @dev Warning: The assets to which virtual borrow shares are entitled behave like unrealizable bad debt.
uint256 internal constant VIRTUAL_SHARES = 1e6;
/// @dev A number of virtual assets of 1 enforces a conversion rate between shares and assets when a market is
/// empty.
uint256 internal constant VIRTUAL_ASSETS = 1;
/// @dev Calculates the value of `assets` quoted in shares, rounding down.
function toSharesDown(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return assets.mulDivDown(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
}
/// @dev Calculates the value of `shares` quoted in assets, rounding down.
function toAssetsDown(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return shares.mulDivDown(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
}
/// @dev Calculates the value of `assets` quoted in shares, rounding up.
function toSharesUp(uint256 assets, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return assets.mulDivUp(totalShares + VIRTUAL_SHARES, totalAssets + VIRTUAL_ASSETS);
}
/// @dev Calculates the value of `shares` quoted in assets, rounding up.
function toAssetsUp(uint256 shares, uint256 totalAssets, uint256 totalShares) internal pure returns (uint256) {
return shares.mulDivUp(totalAssets + VIRTUAL_ASSETS, totalShares + VIRTUAL_SHARES);
}
}// 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.3.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {Context} from "../utils/Context.sol";
/**
* @dev Contract module which allows children to implement an emergency stop
* mechanism that can be triggered by an authorized account.
*
* This module is used through inheritance. It will make available the
* modifiers `whenNotPaused` and `whenPaused`, which can be applied to
* the functions of your contract. Note that they will not be pausable by
* simply including this module, only once the modifiers are put in place.
*/
abstract contract Pausable is Context {
bool private _paused;
/**
* @dev Emitted when the pause is triggered by `account`.
*/
event Paused(address account);
/**
* @dev Emitted when the pause is lifted by `account`.
*/
event Unpaused(address account);
/**
* @dev The operation failed because the contract is paused.
*/
error EnforcedPause();
/**
* @dev The operation failed because the contract is not paused.
*/
error ExpectedPause();
/**
* @dev Modifier to make a function callable only when the contract is not paused.
*
* Requirements:
*
* - The contract must not be paused.
*/
modifier whenNotPaused() {
_requireNotPaused();
_;
}
/**
* @dev Modifier to make a function callable only when the contract is paused.
*
* Requirements:
*
* - The contract must be paused.
*/
modifier whenPaused() {
_requirePaused();
_;
}
/**
* @dev Returns true if the contract is paused, and false otherwise.
*/
function paused() public view virtual returns (bool) {
return _paused;
}
/**
* @dev Throws if the contract is paused.
*/
function _requireNotPaused() internal view virtual {
if (paused()) {
revert EnforcedPause();
}
}
/**
* @dev Throws if the contract is not paused.
*/
function _requirePaused() internal view virtual {
if (!paused()) {
revert ExpectedPause();
}
}
/**
* @dev Triggers stopped state.
*
* Requirements:
*
* - The contract must not be paused.
*/
function _pause() internal virtual whenNotPaused {
_paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
_paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;
import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
/**
* @title TokenizedStrategy Storage Library
* @author yearn.finance
* @notice Library for accessing storage slots of TokenizedStrategy contracts
* @dev This library provides helper functions to compute storage slot locations
* for TokenizedStrategy state variables. This is useful for off-chain tools,
* monitoring systems, and contracts that need direct storage access.
*
* Based on the pattern used by Morpho's MorphoStorageLib.
*/
library TokenizedStrategyStorageLib {
/**
* @dev The main storage slot for the StrategyData struct.
* This matches the BASE_STRATEGY_STORAGE constant in TokenizedStrategy.sol
*/
bytes32 internal constant BASE_STRATEGY_STORAGE =
bytes32(uint256(keccak256("yearn.base.strategy.storage")) - 1);
/**
* @dev The StrategyData struct that holds all storage for TokenizedStrategy v3.0.4.
* This must match the exact layout in TokenizedStrategy.sol to ensure compatibility.
*/
struct StrategyData {
// Slot 0: ERC20 asset (160 bits) + decimals (8 bits) + 88 bits unused
ERC20 asset;
uint8 decimals;
// Slot 1: string name (dynamic storage)
string name;
// Slot 2: uint256 totalSupply
uint256 totalSupply;
// Slot 3: mapping nonces
mapping(address => uint256) nonces;
// Slot 4: mapping balances
mapping(address => uint256) balances;
// Slot 5: mapping allowances
mapping(address => mapping(address => uint256)) allowances;
// Slot 6: uint256 totalAssets
uint256 totalAssets;
// Slot 7: uint256 profitUnlockingRate
uint256 profitUnlockingRate;
// Slot 8: uint96 fullProfitUnlockDate (96 bits) + address keeper (160 bits)
uint96 fullProfitUnlockDate;
address keeper;
// Slot 9: uint32 profitMaxUnlockTime + uint16 performanceFee + address performanceFeeRecipient (208 bits total)
uint32 profitMaxUnlockTime;
uint16 performanceFee;
address performanceFeeRecipient;
// Slot 10: uint96 lastReport + address management
uint96 lastReport;
address management;
// Slot 11: address pendingManagement
address pendingManagement;
// Slot 12: address emergencyAdmin
address emergencyAdmin;
// Slot 13: uint8 entered + bool shutdown
uint8 entered;
bool shutdown;
}
/**
* @notice Get the main storage slot for the StrategyData struct
* @return slot The storage slot where StrategyData is stored
*/
function strategyStorageSlot() internal pure returns (bytes32 slot) {
return BASE_STRATEGY_STORAGE;
}
/**
* @notice Get the storage slot for asset and decimals (packed)
* @return slot The storage slot containing asset (20 bytes) and decimals (1 byte)
*/
function assetSlot() internal pure returns (bytes32 slot) {
return BASE_STRATEGY_STORAGE;
}
/**
* @notice Get the storage slot for the strategy name
* @return slot The storage slot for the name string
*/
function nameSlot() internal pure returns (bytes32 slot) {
return bytes32(uint256(BASE_STRATEGY_STORAGE) + 1);
}
/**
* @notice Get the storage slot for totalSupply
* @return slot The storage slot for totalSupply
*/
function totalSupplySlot() internal pure returns (bytes32 slot) {
return bytes32(uint256(BASE_STRATEGY_STORAGE) + 2);
}
/**
* @notice Get the storage slot for totalAssets
* @return slot The storage slot for totalAssets
*/
function totalAssetsSlot() internal pure returns (bytes32 slot) {
return bytes32(uint256(BASE_STRATEGY_STORAGE) + 6);
}
/**
* @notice Get the storage slot for profitUnlockingRate
* @return slot The storage slot for profitUnlockingRate
*/
function profitUnlockingRateSlot() internal pure returns (bytes32 slot) {
return bytes32(uint256(BASE_STRATEGY_STORAGE) + 7);
}
/**
* @notice Get the storage slot for fullProfitUnlockDate and keeper (packed)
* @return slot The storage slot containing fullProfitUnlockDate (uint96) and keeper (address)
*/
function fullProfitUnlockDateAndKeeperSlot()
internal
pure
returns (bytes32 slot)
{
return bytes32(uint256(BASE_STRATEGY_STORAGE) + 8);
}
/**
* @notice Get the storage slot for profitMaxUnlockTime, performanceFee, and performanceFeeRecipient (packed)
* @return slot The storage slot containing profitMaxUnlockTime (uint32), performanceFee (uint16), and performanceFeeRecipient (address)
*/
function profitConfigSlot() internal pure returns (bytes32 slot) {
return bytes32(uint256(BASE_STRATEGY_STORAGE) + 9);
}
/**
* @notice Get the storage slot for lastReport and management (packed)
* @return slot The storage slot containing lastReport (uint96) and management (address)
*/
function lastReportAndManagementSlot()
internal
pure
returns (bytes32 slot)
{
return bytes32(uint256(BASE_STRATEGY_STORAGE) + 10);
}
/**
* @notice Get the storage slot for pendingManagement
* @return slot The storage slot for pendingManagement address
*/
function pendingManagementSlot() internal pure returns (bytes32 slot) {
return bytes32(uint256(BASE_STRATEGY_STORAGE) + 11);
}
/**
* @notice Get the storage slot for emergencyAdmin
* @return slot The storage slot for emergencyAdmin address
*/
function emergencyAdminSlot() internal pure returns (bytes32 slot) {
return bytes32(uint256(BASE_STRATEGY_STORAGE) + 12);
}
/**
* @notice Get the storage slot for entered and shutdown (packed)
* @return slot The storage slot containing entered (uint8) and shutdown (bool)
*/
function statusSlot() internal pure returns (bytes32 slot) {
return bytes32(uint256(BASE_STRATEGY_STORAGE) + 13);
}
/**
* @notice Calculate the storage slot for a specific nonce
* @param owner The address to get the nonce slot for
* @return slot The storage slot for the owner's nonce
*/
function noncesSlot(address owner) internal pure returns (bytes32 slot) {
// nonces mapping is at slot position 3 from BASE_STRATEGY_STORAGE
bytes32 noncesPosition = bytes32(uint256(BASE_STRATEGY_STORAGE) + 3);
return keccak256(abi.encode(owner, noncesPosition));
}
/**
* @notice Calculate the storage slot for a specific balance
* @param account The address to get the balance slot for
* @return slot The storage slot for the account's balance
*/
function balancesSlot(
address account
) internal pure returns (bytes32 slot) {
// balances mapping is at slot position 4 from BASE_STRATEGY_STORAGE
bytes32 balancesPosition = bytes32(uint256(BASE_STRATEGY_STORAGE) + 4);
return keccak256(abi.encode(account, balancesPosition));
}
/**
* @notice Calculate the storage slot for a specific allowance
* @param owner The address that owns the tokens
* @param spender The address that can spend the tokens
* @return slot The storage slot for the allowance
*/
function allowancesSlot(
address owner,
address spender
) internal pure returns (bytes32 slot) {
// allowances mapping is at slot position 5 from BASE_STRATEGY_STORAGE
bytes32 allowancesPosition = bytes32(
uint256(BASE_STRATEGY_STORAGE) + 5
);
// For nested mappings: keccak256(spender . keccak256(owner . slot))
bytes32 ownerSlot = keccak256(abi.encode(owner, allowancesPosition));
return keccak256(abi.encode(spender, ownerSlot));
}
/**
* @notice Helper to load the StrategyData struct from storage
* @dev This can be used in external contracts to load the full struct
* @return S The StrategyData struct from storage
*/
function getStrategyStorage()
internal
pure
returns (StrategyData storage S)
{
bytes32 slot = BASE_STRATEGY_STORAGE;
assembly {
S.slot := slot
}
}
}// SPDX-License-Identifier: GPL-20later
pragma solidity ^0.8.18;
/// @notice Interface for the ProtocolConfig contract
// Struct to hold market parameters
struct MarketConfig {
uint256 gracePeriod; // Duration of grace period after cycle end
uint256 delinquencyPeriod; // Duration of delinquency period before default
uint256 minBorrow; // Minimum outstanding loan balance to prevent dust
uint256 irp; // Penalty rate per second for delinquent borrowers
}
// Struct to hold credit line parameters
struct CreditLineConfig {
uint256 maxLTV;
uint256 maxVV;
uint256 maxCreditLine;
uint256 minCreditLine;
uint256 maxDRP;
}
// Struct to hold IRM parameters
struct IRMConfig {
uint256 curveSteepness;
uint256 adjustmentSpeed;
uint256 targetUtilization;
uint256 initialRateAtTarget;
uint256 minRateAtTarget;
uint256 maxRateAtTarget;
}
/// @notice Struct to hold IRM parameters with int256 types for internal calculations
struct IRMConfigTyped {
int256 curveSteepness;
int256 adjustmentSpeed;
int256 targetUtilization;
int256 initialRateAtTarget;
int256 minRateAtTarget;
int256 maxRateAtTarget;
}
interface IProtocolConfig {
/// @dev Initialize the contract with the owner
/// @param newOwner The address of the new owner
function initialize(address newOwner) external;
/// @dev Set a configuration value
/// @param key The configuration key
/// @param value The configuration value
function setConfig(bytes32 key, uint256 value) external;
// Credit Line getters
/// @dev Get the credit line parameters
/// @return The credit line parameters
function getCreditLineConfig() external view returns (CreditLineConfig memory);
// Market getters
/// @dev Get the pause status
/// @return The pause status value
function getIsPaused() external view returns (uint256);
/// @dev Get the maximum on credit
/// @return The max on credit value
function getMaxOnCredit() external view returns (uint256);
/// @dev Get the market parameters
/// @return The market parameters
function getMarketConfig() external view returns (MarketConfig memory);
/// @dev Get the cycle duration for payment cycles
/// @return The cycle duration in seconds
function getCycleDuration() external view returns (uint256);
// IRM getters
/// @dev Get the IRM parameters
/// @return The IRM parameters
function getIRMConfig() external view returns (IRMConfig memory);
// USD3 & sUSD3 getters
/// @dev Get the tranche ratio
/// @return The tranche ratio value
function getTrancheRatio() external view returns (uint256);
/// @dev Get the tranche share variant
/// @return The tranche share variant value
function getTrancheShareVariant() external view returns (uint256);
/// @dev Get the SUSD3 lock duration
/// @return The SUSD3 lock duration value
function getSusd3LockDuration() external view returns (uint256);
/// @dev Get the SUSD3 cooldown period
/// @return The SUSD3 cooldown period value
function getSusd3CooldownPeriod() external view returns (uint256);
/// @dev Get the USD3 commitment time
/// @return The lock period in seconds
function getUsd3CommitmentTime() external view returns (uint256);
/// @dev Get the sUSD3 withdrawal window
/// @return The withdrawal window duration in seconds after cooldown
function getSusd3WithdrawalWindow() external view returns (uint256);
/// @dev Get the USD3 supply cap
/// @return The supply cap in asset units (0 means no cap)
function getUsd3SupplyCap() external view returns (uint256);
/// @dev Get configuration value by key
/// @param key The configuration key
/// @return The configuration value
function config(bytes32 key) external view returns (uint256);
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
/// @title ProtocolConfigLib
/// @notice Library containing all configuration keys for ProtocolConfig
/// @dev Centralizes all configuration keys to avoid magic strings and improve maintainability
library ProtocolConfigLib {
// Market Control Keys
bytes32 internal constant IS_PAUSED = keccak256("IS_PAUSED");
bytes32 internal constant MAX_ON_CREDIT = keccak256("MAX_ON_CREDIT");
bytes32 internal constant DEBT_CAP = keccak256("DEBT_CAP");
// Credit Line Keys
bytes32 internal constant MIN_LOAN_DURATION = keccak256("MIN_LOAN_DURATION");
bytes32 internal constant LATE_REPAYMENT_THRESHOLD = keccak256("LATE_REPAYMENT_THRESHOLD");
bytes32 internal constant DEFAULT_THRESHOLD = keccak256("DEFAULT_THRESHOLD");
bytes32 internal constant GRACE_PERIOD = keccak256("GRACE_PERIOD");
// Interest Rate Keys
bytes32 internal constant MIN_RATE_AT_TARGET = keccak256("MIN_RATE_AT_TARGET");
bytes32 internal constant MAX_RATE_AT_TARGET = keccak256("MAX_RATE_AT_TARGET");
// Tranche Keys (USD3 & sUSD3)
bytes32 internal constant TRANCHE_RATIO = keccak256("TRANCHE_RATIO");
bytes32 internal constant TRANCHE_SHARE_VARIANT = keccak256("TRANCHE_SHARE_VARIANT");
bytes32 internal constant MIN_SUSD3_BACKING_RATIO = keccak256("MIN_SUSD3_BACKING_RATIO");
// Timing Keys
bytes32 internal constant SUSD3_LOCK_DURATION = keccak256("SUSD3_LOCK_DURATION");
bytes32 internal constant SUSD3_COOLDOWN_PERIOD = keccak256("SUSD3_COOLDOWN_PERIOD");
bytes32 internal constant USD3_COMMITMENT_TIME = keccak256("USD3_COMMITMENT_TIME");
bytes32 internal constant SUSD3_WITHDRAWAL_WINDOW = keccak256("SUSD3_WITHDRAWAL_WINDOW");
// Supply Cap Keys
bytes32 internal constant USD3_SUPPLY_CAP = keccak256("USD3_SUPPLY_CAP");
// Markdown Keys
bytes32 internal constant FULL_MARKDOWN_DURATION = keccak256("FULL_MARKDOWN_DURATION");
}// SPDX-License-Identifier: AGPL-3.0
pragma solidity >=0.8.18;
import {Initializable} from "../../../lib/openzeppelin/contracts/proxy/utils/Initializable.sol";
// TokenizedStrategy interface used for internal view delegateCalls.
import {ITokenizedStrategy, ERC20} from "@tokenized-strategy/interfaces/ITokenizedStrategy.sol";
/**
* @title BaseStrategyUpgradeable
* @author Modified from yearn.finance BaseStrategy for upgradeability
* @notice
* BaseStrategyUpgradeable is a fork of Yearn V3's BaseStrategy that works
* with upgradeable proxy patterns. The main change is replacing immutable
* variables with storage variables and constructor with initializer.
*
* It maintains full compatibility with TokenizedStrategy singleton pattern
* while allowing the strategy to be deployed behind a proxy.
*/
abstract contract BaseStrategyUpgradeable is Initializable {
/*//////////////////////////////////////////////////////////////
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;
/*//////////////////////////////////////////////////////////////
STORAGE
//////////////////////////////////////////////////////////////*/
/**
* @dev Underlying asset the Strategy is earning yield on.
* Stored here for cheap retrievals within the strategy.
*/
ERC20 internal asset;
/**
* @dev This variable is set to ITokenizedStrategy(address(this)) during initialization.
*
* This provides a convenient way to call TokenizedStrategy functions
* using clean syntax like TokenizedStrategy.totalAssets()
*/
ITokenizedStrategy internal TokenizedStrategy;
/**
* @dev Storage gap for future upgrades
*/
uint256[48] private __gap;
/*//////////////////////////////////////////////////////////////
INITIALIZATION
//////////////////////////////////////////////////////////////*/
/**
* @notice Initialize the BaseStrategy.
*
* @param _asset Address of the underlying asset.
* @param _name Name the strategy will use.
* @param _management Address to set as management.
* @param _performanceFeeRecipient Address to receive performance fees.
* @param _keeper Address allowed to call tend() and report().
*/
function __BaseStrategy_init(
address _asset,
string memory _name,
address _management,
address _performanceFeeRecipient,
address _keeper
) internal onlyInitializing {
asset = ERC20(_asset);
TokenizedStrategy = ITokenizedStrategy(address(this));
// Initialize the strategy's storage variables via delegatecall.
_delegateCall(
abi.encodeCall(
ITokenizedStrategy.initialize, (_asset, _name, _management, _performanceFeeRecipient, _keeper)
)
);
}
/*//////////////////////////////////////////////////////////////
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.18;
contract DepositHooks {
function _preDepositHook(
uint256 assets,
uint256 shares,
address receiver
) internal virtual {}
function _postDepositHook(
uint256 assets,
uint256 shares,
address receiver
) internal virtual {}
}
contract WithdrawHooks {
function _preWithdrawHook(
uint256 assets,
uint256 shares,
address receiver,
address owner,
uint256 maxLoss
) internal virtual {}
function _postWithdrawHook(
uint256 assets,
uint256 shares,
address receiver,
address owner,
uint256 maxLoss
) internal virtual {}
}
contract TransferHooks {
function _preTransferHook(
address from,
address to,
uint256 amount
) internal virtual {}
function _postTransferHook(
address from,
address to,
uint256 amount,
bool success
) internal virtual {}
}
contract ReportHooks {
function _preReportHook() internal virtual {}
function _postReportHook(uint256 profit, uint256 loss) internal virtual {}
}
contract Hooks is DepositHooks, WithdrawHooks, TransferHooks, ReportHooks {}// 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: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the amount of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the amount of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves `amount` tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 amount) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 amount) external returns (bool);
/**
* @dev Moves `amount` tokens from `from` to `to` using the
* allowance mechanism. `amount` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 amount) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.0;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol)
pragma solidity ^0.8.1;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev Returns true if `account` is a contract.
*
* [IMPORTANT]
* ====
* It is unsafe to assume that an address for which this function returns
* false is an externally-owned account (EOA) and not a contract.
*
* Among others, `isContract` will return false for the following
* types of addresses:
*
* - an externally-owned account
* - a contract in construction
* - an address where a contract will be created
* - an address where a contract lived, but was destroyed
*
* Furthermore, `isContract` will also return true if the target contract within
* the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
* which only has an effect at the end of a transaction.
* ====
*
* [IMPORTANT]
* ====
* You shouldn't rely on `isContract` to protect against flash loan attacks!
*
* Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
* like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
* constructor.
* ====
*/
function isContract(address account) internal view returns (bool) {
// This method relies on extcodesize/address.code.length, which returns 0
// for contracts in construction, since the code is only stored at the end
// of the constructor execution.
return account.code.length > 0;
}
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
require(address(this).balance >= amount, "Address: insufficient balance");
(bool success, ) = recipient.call{value: amount}("");
require(success, "Address: unable to send value, recipient may have reverted");
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason, it is bubbled up by this
* function (like regular Solidity function calls).
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*
* _Available since v3.1._
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, "Address: low-level call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
* `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*
* _Available since v3.1._
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
}
/**
* @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
* with `errorMessage` as a fallback revert reason when `target` reverts.
*
* _Available since v3.1._
*/
function functionCallWithValue(
address target,
bytes memory data,
uint256 value,
string memory errorMessage
) internal returns (bytes memory) {
require(address(this).balance >= value, "Address: insufficient balance for call");
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
return functionStaticCall(target, data, "Address: low-level static call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a static call.
*
* _Available since v3.3._
*/
function functionStaticCall(
address target,
bytes memory data,
string memory errorMessage
) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
return functionDelegateCall(target, data, "Address: low-level delegate call failed");
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
* but performing a delegate call.
*
* _Available since v3.4._
*/
function functionDelegateCall(
address target,
bytes memory data,
string memory errorMessage
) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata, errorMessage);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
* the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
*
* _Available since v4.8._
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata,
string memory errorMessage
) internal view returns (bytes memory) {
if (success) {
if (returndata.length == 0) {
// only check isContract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
require(isContract(target), "Address: call to non-contract");
}
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
/**
* @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
* revert reason or using the provided one.
*
* _Available since v4.3._
*/
function verifyCallResult(
bool success,
bytes memory returndata,
string memory errorMessage
) internal pure returns (bytes memory) {
if (success) {
return returndata;
} else {
_revert(returndata, errorMessage);
}
}
function _revert(bytes memory returndata, string memory errorMessage) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert(errorMessage);
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated 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: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Id} from "../../interfaces/IMorpho.sol";
/// @title MorphoStorageLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Helper library exposing getters to access Morpho storage variables' slot.
/// @dev This library is not used in Morpho itself and is intended to be used by integrators.
library MorphoStorageLib {
/* SLOTS */
uint256 internal constant OWNER_SLOT = 0;
uint256 internal constant FEE_RECIPIENT_SLOT = 1;
uint256 internal constant POSITION_SLOT = 2;
uint256 internal constant MARKET_SLOT = 3;
uint256 internal constant IS_IRM_ENABLED_SLOT = 4;
uint256 internal constant IS_LLTV_ENABLED_SLOT = 5;
uint256 internal constant NONCE_SLOT = 6;
uint256 internal constant ID_TO_MARKET_PARAMS_SLOT = 7;
/* SLOT OFFSETS */
uint256 internal constant LOAN_TOKEN_OFFSET = 0;
uint256 internal constant COLLATERAL_TOKEN_OFFSET = 1;
uint256 internal constant ORACLE_OFFSET = 2;
uint256 internal constant IRM_OFFSET = 3;
uint256 internal constant LLTV_OFFSET = 4;
uint256 internal constant CREDIT_LINE_OFFSET = 5;
uint256 internal constant SUPPLY_SHARES_OFFSET = 0;
uint256 internal constant BORROW_SHARES_AND_COLLATERAL_OFFSET = 1;
uint256 internal constant TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET = 0;
uint256 internal constant TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET = 1;
uint256 internal constant LAST_UPDATE_AND_FEE_OFFSET = 2;
/* GETTERS */
function ownerSlot() internal pure returns (bytes32) {
return bytes32(OWNER_SLOT);
}
function feeRecipientSlot() internal pure returns (bytes32) {
return bytes32(FEE_RECIPIENT_SLOT);
}
function positionSupplySharesSlot(Id id, address user) internal pure returns (bytes32) {
return
bytes32(
uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT))))) + SUPPLY_SHARES_OFFSET
);
}
function positionBorrowSharesAndCollateralSlot(Id id, address user) internal pure returns (bytes32) {
return bytes32(
uint256(keccak256(abi.encode(user, keccak256(abi.encode(id, POSITION_SLOT)))))
+ BORROW_SHARES_AND_COLLATERAL_OFFSET
);
}
function marketTotalSupplyAssetsAndSharesSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_SUPPLY_ASSETS_AND_SHARES_OFFSET);
}
function marketTotalBorrowAssetsAndSharesSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + TOTAL_BORROW_ASSETS_AND_SHARES_OFFSET);
}
function marketLastUpdateAndFeeSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, MARKET_SLOT))) + LAST_UPDATE_AND_FEE_OFFSET);
}
function isIrmEnabledSlot(address irm) internal pure returns (bytes32) {
return keccak256(abi.encode(irm, IS_IRM_ENABLED_SLOT));
}
function isLltvEnabledSlot(uint256 lltv) internal pure returns (bytes32) {
return keccak256(abi.encode(lltv, IS_LLTV_ENABLED_SLOT));
}
function nonceSlot(address authorizer) internal pure returns (bytes32) {
return keccak256(abi.encode(authorizer, NONCE_SLOT));
}
function idToLoanTokenSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LOAN_TOKEN_OFFSET);
}
function idToCollateralTokenSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + COLLATERAL_TOKEN_OFFSET);
}
function idToOracleSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + ORACLE_OFFSET);
}
function idToIrmSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + IRM_OFFSET);
}
function idToLltvSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + LLTV_OFFSET);
}
function idToCreditLineSlot(Id id) internal pure returns (bytes32) {
return bytes32(uint256(keccak256(abi.encode(id, ID_TO_MARKET_PARAMS_SLOT))) + CREDIT_LINE_OFFSET);
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.18;
import {MarketParams, Market} from "./IMorpho.sol";
/// @title IIrm
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Interface that Interest Rate Models (IRMs) used by Morpho must implement.
interface IIrm {
/// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams`.
/// @dev Assumes that `market` corresponds to `marketParams`.
function borrowRate(MarketParams memory marketParams, Market memory market) external returns (uint256);
/// @notice Returns the borrow rate per second (scaled by WAD) of the market `marketParams` without modifying any
/// storage.
/// @dev Assumes that `market` corresponds to `marketParams`.
function borrowRateView(MarketParams memory marketParams, Market memory market) external view returns (uint256);
}// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; uint256 constant WAD = 1e18; /// @title MathLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Library to manage fixed-point arithmetic. library MathLib { /// @dev Returns (`x` * `y`) / `WAD` rounded down. function wMulDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, y, WAD); } /// @dev Returns (`x` * `WAD`) / `y` rounded down. function wDivDown(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivDown(x, WAD, y); } /// @dev Returns (`x` * `WAD`) / `y` rounded up. function wDivUp(uint256 x, uint256 y) internal pure returns (uint256) { return mulDivUp(x, WAD, y); } /// @dev Returns (`x` * `y`) / `d` rounded down. function mulDivDown(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) { return (x * y) / d; } /// @dev Returns (`x` * `y`) / `d` rounded up. function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256) { return (x * y + (d - 1)) / d; } /// @dev Returns the sum of the first three non-zero terms of a Taylor expansion of e^(nx) - 1, to approximate a /// continuous compound interest rate. function wTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) { uint256 firstTerm = x * n; uint256 secondTerm = mulDivDown(firstTerm, firstTerm, 2 * WAD); uint256 thirdTerm = mulDivDown(secondTerm, firstTerm, 3 * WAD); return firstTerm + secondTerm + thirdTerm; } /// @dev Computes the inverse of wTaylorCompounded, finding the rate that produces the given growth factor. /// Uses a 3-term Taylor series approximation of ln(x) to solve for rate in the compound interest formula. /// Formula: rate = ln(x) / n ≈ [(x-1) - (x-1)²/2 + (x-1)³/3] / n /// /// Accuracy notes: /// - The Taylor approximation of ln(x) is most accurate for x close to 1 /// - At growth factor x = 1.69*WAD (69% growth), approximation error < 2% /// - At growth factor x = 2*WAD (100% growth, where ln(2) ≈ 0.69), approximation error < 5% /// - Accuracy decreases for larger growth factors; not recommended for x > 2.5*WAD (150% growth) /// /// Example: If debt grew from 1000 to 1105 over 1 year (10.5% growth): /// - x = 1.105*WAD (growth factor) /// - n = 365 days (time period) /// - Returns ~10% APR as rate per second /// /// @param x The growth factor scaled by WAD (e.g., 1.1*WAD for 10% growth). Must be >= WAD. /// @param n The time period over which the growth occurred (in seconds) /// @return The continuously compounded rate per second that would produce this growth (scaled by WAD) function wInverseTaylorCompounded(uint256 x, uint256 n) internal pure returns (uint256) { require(x >= WAD, "ln undefined"); uint256 firstTerm = x - WAD; uint256 secondTerm = wMulDown(firstTerm, firstTerm); uint256 thirdTerm = wMulDown(secondTerm, firstTerm); uint256 series = firstTerm - secondTerm / 2 + thirdTerm / 3; return series / n; } }
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {ErrorsLib} from "../libraries/ErrorsLib.sol";
/// @title UtilsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library exposing helpers.
/// @dev Inspired by https://github.com/morpho-org/morpho-utils.
library UtilsLib {
/// @dev Returns true if there is exactly one zero among `x` and `y`.
function exactlyOneZero(uint256 x, uint256 y) internal pure returns (bool z) {
assembly {
z := xor(iszero(x), iszero(y))
}
}
/// @dev Returns the min of `x` and `y`.
function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := xor(x, mul(xor(x, y), lt(y, x)))
}
}
/// @dev Returns `x` safely cast to uint128.
function toUint128(uint256 x) internal pure returns (uint128) {
if (x > type(uint128).max) revert ErrorsLib.MaxUint128Exceeded();
return uint128(x);
}
/// @dev Returns max(0, x - y).
function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
assembly {
z := mul(gt(x, y), sub(x, y))
}
}
}// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;
import {Id, MarketParams} from "../interfaces/IMorpho.sol";
/// @title MarketParamsLib
/// @author Morpho Labs
/// @custom:contact [email protected]
/// @notice Library to convert a market to its id.
library MarketParamsLib {
/// @notice The length of the data used to compute the id of a market.
/// @dev The length is 6 * 32 because `MarketParams` has 6 variables of 32 bytes each.
uint256 internal constant MARKET_PARAMS_BYTES_LENGTH = 6 * 32;
/// @notice Returns the id of the market `marketParams`.
function id(MarketParams memory marketParams) internal pure returns (Id marketParamsId) {
assembly ("memory-safe") {
marketParamsId := keccak256(marketParams, MARKET_PARAMS_BYTES_LENGTH)
}
}
}// 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.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 v4.9.0) (token/ERC20/ERC20.sol)
pragma solidity ^0.8.0;
import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";
/**
* @dev Implementation of the {IERC20} interface.
*
* This implementation is agnostic to the way tokens are created. This means
* that a supply mechanism has to be added in a derived contract using {_mint}.
* For a generic mechanism see {ERC20PresetMinterPauser}.
*
* TIP: For a detailed writeup see our guide
* https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
* to implement supply mechanisms].
*
* The default value of {decimals} is 18. To change this, you should override
* this function so it returns a different value.
*
* We have followed general OpenZeppelin Contracts guidelines: functions revert
* instead returning `false` on failure. This behavior is nonetheless
* conventional and does not conflict with the expectations of ERC20
* applications.
*
* Additionally, an {Approval} event is emitted on calls to {transferFrom}.
* This allows applications to reconstruct the allowance for all accounts just
* by listening to said events. Other implementations of the EIP may not emit
* these events, as it isn't required by the specification.
*
* Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
* functions have been added to mitigate the well-known issues around setting
* allowances. See {IERC20-approve}.
*/
contract ERC20 is Context, IERC20, IERC20Metadata {
mapping(address => uint256) private _balances;
mapping(address => mapping(address => uint256)) private _allowances;
uint256 private _totalSupply;
string private _name;
string private _symbol;
/**
* @dev Sets the values for {name} and {symbol}.
*
* All two of these values are immutable: they can only be set once during
* construction.
*/
constructor(string memory name_, string memory symbol_) {
_name = name_;
_symbol = symbol_;
}
/**
* @dev Returns the name of the token.
*/
function name() public view virtual override returns (string memory) {
return _name;
}
/**
* @dev Returns the symbol of the token, usually a shorter version of the
* name.
*/
function symbol() public view virtual override returns (string memory) {
return _symbol;
}
/**
* @dev Returns the number of decimals used to get its user representation.
* For example, if `decimals` equals `2`, a balance of `505` tokens should
* be displayed to a user as `5.05` (`505 / 10 ** 2`).
*
* Tokens usually opt for a value of 18, imitating the relationship between
* Ether and Wei. This is the 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 override returns (uint8) {
return 18;
}
/**
* @dev See {IERC20-totalSupply}.
*/
function totalSupply() public view virtual override returns (uint256) {
return _totalSupply;
}
/**
* @dev See {IERC20-balanceOf}.
*/
function balanceOf(address account) public view virtual override returns (uint256) {
return _balances[account];
}
/**
* @dev See {IERC20-transfer}.
*
* Requirements:
*
* - `to` cannot be the zero address.
* - the caller must have a balance of at least `amount`.
*/
function transfer(address to, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_transfer(owner, to, amount);
return true;
}
/**
* @dev See {IERC20-allowance}.
*/
function allowance(address owner, address spender) public view virtual override returns (uint256) {
return _allowances[owner][spender];
}
/**
* @dev See {IERC20-approve}.
*
* NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
* `transferFrom`. This is semantically equivalent to an infinite approval.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function approve(address spender, uint256 amount) public virtual override returns (bool) {
address owner = _msgSender();
_approve(owner, spender, amount);
return true;
}
/**
* @dev See {IERC20-transferFrom}.
*
* Emits an {Approval} event indicating the updated allowance. This is not
* required by the EIP. See the note at the beginning of {ERC20}.
*
* NOTE: Does not update the allowance if the current allowance
* is the maximum `uint256`.
*
* Requirements:
*
* - `from` and `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
* - the caller must have allowance for ``from``'s tokens of at least
* `amount`.
*/
function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
address spender = _msgSender();
_spendAllowance(from, spender, amount);
_transfer(from, to, amount);
return true;
}
/**
* @dev Atomically increases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
*/
function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
address owner = _msgSender();
_approve(owner, spender, allowance(owner, spender) + addedValue);
return true;
}
/**
* @dev Atomically decreases the allowance granted to `spender` by the caller.
*
* This is an alternative to {approve} that can be used as a mitigation for
* problems described in {IERC20-approve}.
*
* Emits an {Approval} event indicating the updated allowance.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `spender` must have allowance for the caller of at least
* `subtractedValue`.
*/
function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
address owner = _msgSender();
uint256 currentAllowance = allowance(owner, spender);
require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
unchecked {
_approve(owner, spender, currentAllowance - subtractedValue);
}
return true;
}
/**
* @dev Moves `amount` of tokens from `from` to `to`.
*
* This internal function is equivalent to {transfer}, and can be used to
* e.g. implement automatic token fees, slashing mechanisms, etc.
*
* Emits a {Transfer} event.
*
* Requirements:
*
* - `from` cannot be the zero address.
* - `to` cannot be the zero address.
* - `from` must have a balance of at least `amount`.
*/
function _transfer(address from, address to, uint256 amount) internal virtual {
require(from != address(0), "ERC20: transfer from the zero address");
require(to != address(0), "ERC20: transfer to the zero address");
_beforeTokenTransfer(from, to, amount);
uint256 fromBalance = _balances[from];
require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
unchecked {
_balances[from] = fromBalance - amount;
// Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
// decrementing then incrementing.
_balances[to] += amount;
}
emit Transfer(from, to, amount);
_afterTokenTransfer(from, to, amount);
}
/** @dev Creates `amount` tokens and assigns them to `account`, increasing
* the total supply.
*
* Emits a {Transfer} event with `from` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
*/
function _mint(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: mint to the zero address");
_beforeTokenTransfer(address(0), account, amount);
_totalSupply += amount;
unchecked {
// Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
_balances[account] += amount;
}
emit Transfer(address(0), account, amount);
_afterTokenTransfer(address(0), account, amount);
}
/**
* @dev Destroys `amount` tokens from `account`, reducing the
* total supply.
*
* Emits a {Transfer} event with `to` set to the zero address.
*
* Requirements:
*
* - `account` cannot be the zero address.
* - `account` must have at least `amount` tokens.
*/
function _burn(address account, uint256 amount) internal virtual {
require(account != address(0), "ERC20: burn from the zero address");
_beforeTokenTransfer(account, address(0), amount);
uint256 accountBalance = _balances[account];
require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
unchecked {
_balances[account] = accountBalance - amount;
// Overflow not possible: amount <= accountBalance <= totalSupply.
_totalSupply -= amount;
}
emit Transfer(account, address(0), amount);
_afterTokenTransfer(account, address(0), amount);
}
/**
* @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
*
* This internal function is equivalent to `approve`, and can be used to
* e.g. set automatic allowances for certain subsystems, etc.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `owner` cannot be the zero address.
* - `spender` cannot be the zero address.
*/
function _approve(address owner, address spender, uint256 amount) internal virtual {
require(owner != address(0), "ERC20: approve from the zero address");
require(spender != address(0), "ERC20: approve to the zero address");
_allowances[owner][spender] = amount;
emit Approval(owner, spender, amount);
}
/**
* @dev Updates `owner` s allowance for `spender` based on spent `amount`.
*
* Does not update the allowance amount in case of infinite allowance.
* Revert if not enough allowance is available.
*
* Might emit an {Approval} event.
*/
function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
uint256 currentAllowance = allowance(owner, spender);
if (currentAllowance != type(uint256).max) {
require(currentAllowance >= amount, "ERC20: insufficient allowance");
unchecked {
_approve(owner, spender, currentAllowance - amount);
}
}
}
/**
* @dev Hook that is called before any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* will be transferred to `to`.
* - when `from` is zero, `amount` tokens will be minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens will be burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}
/**
* @dev Hook that is called after any transfer of tokens. This includes
* minting and burning.
*
* Calling conditions:
*
* - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
* has been transferred to `to`.
* - when `from` is zero, `amount` tokens have been minted for `to`.
* - when `to` is zero, `amount` of ``from``'s tokens have been burned.
* - `from` and `to` are never both zero.
*
* To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
*/
function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reinitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Pointer to storage slot. Allows integrators to override it with a custom storage location.
*
* NOTE: Consider following the ERC-7201 formula to derive storage locations.
*/
function _initializableStorageSlot() internal pure virtual returns (bytes32) {
return INITIALIZABLE_STORAGE;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
bytes32 slot = _initializableStorageSlot();
assembly {
$.slot := slot
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC4626.sol)
pragma solidity ^0.8.0;
import "../token/ERC20/IERC20.sol";
import "../token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
* https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
*
* _Available since v4.7._
*/
interface IERC4626 is IERC20, IERC20Metadata {
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed sender,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
/**
* @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
*
* - MUST be an ERC-20 token contract.
* - MUST NOT revert.
*/
function asset() external view returns (address assetTokenAddress);
/**
* @dev Returns the total amount of the underlying asset that is “managed” by Vault.
*
* - SHOULD include any compounding that occurs from yield.
* - MUST be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT revert.
*/
function totalAssets() external view returns (uint256 totalManagedAssets);
/**
* @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
* through a deposit call.
*
* - MUST return a limited value if receiver is subject to some deposit limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
* - MUST NOT revert.
*/
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
* call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
* in the same transaction.
* - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
* deposit would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/**
* @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* deposit execution, and are accounted for during deposit.
* - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
* - MUST return a limited value if receiver is subject to some mint limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
* - MUST NOT revert.
*/
function maxMint(address receiver) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
* in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
* same transaction.
* - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
* would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by minting.
*/
function previewMint(uint256 shares) external view returns (uint256 assets);
/**
* @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
* execution, and are accounted for during mint.
* - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function mint(uint256 shares, address receiver) external returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
* call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
* called
* in the same transaction.
* - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
* the withdrawal would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/**
* @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* withdraw execution, and are accounted for during withdraw.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
* through a redeem call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxRedeem(address owner) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
* in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
* same transaction.
* - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
* redemption would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by redeeming.
*/
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/**
* @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* redeem execution, and are accounted for during redeem.
* - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; /// @title ErrorsLib /// @author Morpho Labs /// @custom:contact [email protected] /// @notice Library exposing custom errors. library ErrorsLib { /// @notice Thrown when the caller is not the owner. error NotOwner(); /// @notice Thrown when the caller is not the market's credit line. error NotCreditLine(); /// @notice Thrown when the caller is not the market's helper. error NotHelper(); /// @notice Thrown when the caller is not the market's usd3. error NotUsd3(); /// @notice Thrown when the caller is not the owner or ozd. error NotOwnerOrOzd(); /// @notice Thrown when the user is unverified. error Unverified(); /// @notice Thrown when the LLTV to enable exceeds the maximum LLTV. error MaxLltvExceeded(); /// @notice Thrown when the LTV to enable exceeds the maximum LTV. error MaxLtvExceeded(); /// @notice Thrown when the VV to enable exceeds the maximum VV. error MaxVvExceeded(); /// @notice Thrown when the credit to enable exceeds the maximum credit. error MaxCreditLineExceeded(); /// @notice Thrown when the credit to enable is below the minimum credit. error MinCreditLineExceeded(); /// @notice Thrown when the fee to set exceeds the maximum fee. error MaxFeeExceeded(); /// @notice Thrown when the value is already set. error AlreadySet(); /// @notice Thrown when the IRM is not enabled at market creation. error IrmNotEnabled(); /// @notice Thrown when the LLTV is not enabled at market creation. error LltvNotEnabled(); /// @notice Thrown when the market is already created. error MarketAlreadyCreated(); /// @notice Thrown when a token to transfer doesn't have code. error NoCode(); /// @notice Thrown when the market is not created. error MarketNotCreated(); /// @notice Thrown when not exactly one of the input amount is zero. error InconsistentInput(); /// @notice Thrown when zero assets is passed as input. error ZeroAssets(); /// @notice Thrown when a zero address is passed as input. error ZeroAddress(); /// @notice Thrown when an array has an invalid length. error InvalidArrayLength(); /// @notice Thrown when the caller is not authorized to conduct an action. error Unauthorized(); /// @notice Thrown when the collateral is insufficient to `borrow` or `withdrawCollateral`. error InsufficientCollateral(); /// @notice Thrown when the liquidity is insufficient to `withdraw` or `borrow`. error InsufficientLiquidity(); /// @notice Thrown when borrowing shares would result in borrowing zero assets. error InsufficientBorrowAmount(); /// @notice Thrown when a token transfer reverted. error TransferReverted(); /// @notice Thrown when a token transfer returned false. error TransferReturnedFalse(); /// @notice Thrown when a token transferFrom reverted. error TransferFromReverted(); /// @notice Thrown when a token transferFrom returned false error TransferFromReturnedFalse(); /// @notice Thrown when the maximum uint128 is exceeded. error MaxUint128Exceeded(); /// @notice Thrown when the premium rate exceeds the maximum allowed. error MaxDrpExceeded(); /// @notice Thrown when the borrower has outstanding repayment obligations. error OutstandingRepayment(); /// @notice Thrown when the protocol is paused. error Paused(); /// @notice Thrown when trying to close a future cycle. error CannotCloseFutureCycle(); /// @notice Thrown when cycle duration is invalid. error InvalidCycleDuration(); /// @notice Thrown when no payment cycles exist. error NoCyclesExist(); /// @notice Thrown when cycle ID is invalid. error InvalidCycleId(); /// @notice Thrown when partial payment is attempted but full obligation payment is required. error MustPayFullObligation(); /// @notice Thrown when repayment basis points exceed 100%. error RepaymentExceedsHundredPercent(); /// @notice Thrown when an invalid markdown manager is set. error InvalidMarkdownManager(); /// @notice Thrown when trying to settle non-existent debt. error NoAccountToSettle(); /// @notice Thrown when the cover amount exceeds the assets amount. error InvalidCoverAmount(); /// @notice Thrown when attempting operations on a frozen market. error MarketFrozen(); /// @notice Thrown when a borrow would exceed the protocol debt cap. error DebtCapExceeded(); /// @notice Thrown when borrow or repay would result in debt below minimum borrow amount. error BelowMinimumBorrow(); }
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.0;
import "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*
* _Available since v4.1._
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol)
pragma solidity ^0.8.0;
/**
* @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;
}
}{
"remappings": [
"forge-std/=lib/forge-std/src/",
"halmos-cheatcodes/=lib/halmos-cheatcodes/src/",
"@tokenized-strategy/=lib/tokenized-strategy/src/",
"@periphery/=lib/tokenized-strategy-periphery/src/",
"openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
"@openzeppelin/=lib/tokenized-strategy-periphery/lib/openzeppelin-contracts/",
"@yearn-vaults/=lib/tokenized-strategy-periphery/lib/yearn-vaults-v3/contracts/",
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/tokenized-strategy/lib/erc4626-tests/",
"openzeppelin-contracts/=lib/tokenized-strategy/lib/openzeppelin-contracts/",
"openzeppelin/=lib/openzeppelin/",
"tokenized-strategy-periphery/=lib/tokenized-strategy-periphery/",
"tokenized-strategy/=lib/tokenized-strategy/",
"yearn-vaults-v3/=lib/tokenized-strategy-periphery/lib/yearn-vaults-v3/"
],
"optimizer": {
"enabled": true,
"runs": 999999
},
"metadata": {
"useLiteralContent": false,
"bytecodeHash": "none",
"appendCBOR": false
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"evmVersion": "shanghai",
"viaIR": true
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"MaxUint128Exceeded","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"depositor","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"DepositorWhitelistUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"newMinDeposit","type":"uint256"}],"name":"MinDepositUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldStrategy","type":"address"},{"indexed":false,"internalType":"address","name":"newStrategy","type":"address"}],"name":"SUSD3StrategyUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"trancheShare","type":"uint256"}],"name":"TrancheShareSynced","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"bool","name":"allowed","type":"bool"}],"name":"WhitelistUpdated","type":"event"},{"stateMutability":"nonpayable","type":"fallback"},{"inputs":[],"name":"WAUSDC","outputs":[{"internalType":"contract IERC4626","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"availableDepositLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_owner","type":"address"}],"name":"availableWithdrawLimit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"balanceOfWaUSDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"deployFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"deposit","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"depositTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"depositorWhitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"freeFunds","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getMarketLiquidity","outputs":[{"internalType":"uint256","name":"totalSupplyAssets","type":"uint256"},{"internalType":"uint256","name":"totalShares","type":"uint256"},{"internalType":"uint256","name":"totalBorrowAssets","type":"uint256"},{"internalType":"uint256","name":"waUSDCLiquidity","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"harvestAndReport","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_morphoCredit","type":"address"},{"internalType":"Id","name":"_marketId","type":"bytes32"},{"internalType":"address","name":"_management","type":"address"},{"internalType":"address","name":"_keeper","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"marketId","outputs":[{"internalType":"Id","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"marketParams","outputs":[{"components":[{"internalType":"address","name":"loanToken","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"oracle","type":"address"},{"internalType":"address","name":"irm","type":"address"},{"internalType":"uint256","name":"lltv","type":"uint256"},{"internalType":"address","name":"creditLine","type":"address"}],"internalType":"struct MarketParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"maxOnCredit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minCommitmentTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"minDeposit","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"}],"name":"mint","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"morphoCredit","outputs":[{"internalType":"contract IMorpho","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"maxLoss","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"shares","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"assets","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"reinitialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"report","outputs":[{"internalType":"uint256","name":"profit","type":"uint256"},{"internalType":"uint256","name":"loss","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sUSD3","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_depositor","type":"address"},{"internalType":"bool","name":"_allowed","type":"bool"}],"name":"setDepositorWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_minDeposit","type":"uint256"}],"name":"setMinDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_sUSD3","type":"address"}],"name":"setSUSD3","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_user","type":"address"},{"internalType":"bool","name":"_allowed","type":"bool"}],"name":"setWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_enabled","type":"bool"}],"name":"setWhitelistEnabled","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"shutdownWithdraw","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"suppliedWaUSDC","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"supplyCap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"syncTrancheShare","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":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"whitelist","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"whitelistEnabled","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"maxLoss","type":"uint256"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"assets","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"withdraw","outputs":[{"internalType":"uint256","name":"shares","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]Contract Creation Code
60808060405234620000bd577ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a009081549060ff8260401c16620000ae57506001600160401b036002600160401b03198282160162000068575b6040516158249081620000c28239f35b6001600160401b031990911681179091556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d290602090a15f808062000058565b63f92ee8a960e01b8152600490fd5b5f80fdfe60806040526004361015610019575b3415613c48575b5f80fd5b5f3560e01c806304bd4629146102c45780630517bbab146102bf578063052d9e7e146102ba57806323b872dd146102b55780632606a10b146102b05780632b832dbc146102ab5780632f6c1f53146102a65780633d6cb575146101f757806341b3d185146102a15780634251c3541461029c57806346aa2f121461029757806346b00d041461029257806349317f1d1461028d578063503160d91461028857806351fb012d1461028357806353d6fd591461027e57806359ddbab2146102795780635d265d3f146102745780636c2eb3501461026f5780636e553f651461026a5780636ed71ede146102655780637b9e68f2146102605780638f770ad01461025b5780638fcc9cfb1461025657806394bf804d1461025157806395d89b411461024c57806396c9f17e146102475780639b19251a146102425780639d7fb70c1461023d5780639f40a7b314610238578063a318c1a414610233578063a46537571461022e578063a9059cbb14610229578063a9b89c0714610224578063b460af941461021f578063ba0876521461021a578063cb6d71bc14610215578063d00330ab14610210578063d19a3bb81461020b578063e61582b514610206578063efb5f91114610201578063f7f6ba53146101fc5763fde813a80361000e575b610bbe565b6128be565b61286d565b612835565b6127e9565b61277d565b612731565b6126aa565b612625565b6125a6565b6124e2565b6122b8565b612230565b6120e3565b612053565b611fe7565b611ddf565b611d92565b611a2e565b611953565b61191b565b611857565b6117b8565b61140d565b611260565b6111df565b611133565b61101f565b610fdc565b610f9a565b610d21565b610cba565b610c75565b610c3d565b610c02565b610a9e565b61083d565b610567565b610480565b610380565b61033e565b6102e7565b73ffffffffffffffffffffffffffffffffffffffff81160361001557565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c600435610327816102c9565b612a74565b604051908152f35b5f91031261001557565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c612e60565b8015150361001557565b34610015575f60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576004356103bc81610376565b73ffffffffffffffffffffffffffffffffffffffff60015416803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57610468575b507fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff0000000000000000000000000000000000000000603a5492151560a01b16911617603a5580f35b61047391925061293c565b5f905f61041d565b612a15565b346100155760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576105636105516105426004356104c4816102c9565b602435906104d1826102c9565b6104db8282613d8a565b604051917f23b872dd00000000000000000000000000000000000000000000000000000000602084015273ffffffffffffffffffffffffffffffffffffffff809216602484015216604482015260443560648201526064815261053d81612971565b613f45565b60208082518301019101612a20565b60405190151581529081906020820190565b0390f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576040516105e16105d36020927f2606a10b00000000000000000000000000000000000000000000000000000000848201526004815261053d8161298d565b828082518301019101612f55565b9182151580610800575b610602575b50604080519182526020820192909252f35b6106b09061064161062860015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b81610661603a5473ffffffffffffffffffffffffffffffffffffffff1690565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909116600482015293849081906024820190565b0381845afa92831561047b575f936107e1575b50826106d1575b50506105f0565b6040517f18160ddd0000000000000000000000000000000000000000000000000000000081528281600481855afa90811561047b5783905f926107c1575b5060049192604051928380927f01e1d1140000000000000000000000000000000000000000000000000000000082525afa92831561047b576107669361075f9288925f92610794575b5050612a62565b9085615653565b9080821161078c575b508061077d575b80806106ca565b61078690614ea2565b5f610776565b90505f61076f565b6107b39250803d106107ba575b6107ab81836129c5565b810190612a06565b5f80610758565b503d6107a1565b600492506107db90823d84116107ba576107ab81836129c5565b9161070f565b6107f9919350823d84116107ba576107ab81836129c5565b915f6106c3565b5073ffffffffffffffffffffffffffffffffffffffff610835603a5473ffffffffffffffffffffffffffffffffffffffff1690565b1615156105eb565b34610015575f60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557600435610879816102c9565b73ffffffffffffffffffffffffffffffffffffffff8060015416803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57610a45575b5080603a54166109e75781161561098957610983816109567fc3212a26ea3792ed00e630d63eb8ec2a9295974fe90e94ff25a8f70987fcdeeb9373ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000603a541617603a55565b604080515f815273ffffffffffffffffffffffffffffffffffffffff909216602083015290918291820190565b0390a180f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f496e76616c6964206164647265737300000000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f735553443320616c7265616479207365740000000000000000000000000000006044820152fd5b610a5091935061293c565b5f915f6108db565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc604091011261001557600435610a8e816102c9565b90602435610a9b81610376565b90565b3461001557610aac36610a58565b6001545f9273ffffffffffffffffffffffffffffffffffffffff809216803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57610b84575b507f8bb3c9e62c5e823d77565af4d29339378715313d3cb898327e576392ffa79b00916020911692835f52603c8252610b788160405f209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b6040519015158152a280f35b602091945091610bb47f8bb3c9e62c5e823d77565af4d29339378715313d3cb898327e576392ffa79b009361293c565b5f94915091610b11565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610bf5613f81565b610c00600435613fe8565b005b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576020603d54604051908152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c612f6b565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c600435610cb5816102c9565b612feb565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff600435610d0a816102c9565b165f52603e602052602060405f2054604051908152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610d57613f81565b610d5f613269565b610d8161062860325473ffffffffffffffffffffffffffffffffffffffff1690565b803b15610015576040517f1e4711cf000000000000000000000000000000000000000000000000000000008152905f908290818381610dc388600483016117f3565b03925af1801561047b57610f81575b50610df46106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b6040517f70a082310000000000000000000000000000000000000000000000000000000080825230600483015291602091908290829060249082905afa801561047b57610e48915f91610f64575b50614d8b565b610e9d81610e65610e57613a6b565b610e5f612f6b565b90612a62565b604051809381927f07a2d13a000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b038173d4fa2d31b7968e448877f69a96de69f5de8cd23e5afa90811561047b575f91610f47575b5081610ee76106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b604051948552306004860152849060249082905afa90811561047b5761056393610f18935f93610f28575050612a62565b6040519081529081906020820190565b610f3f929350803d106107ba576107ab81836129c5565b908480610758565b610f5e9150823d84116107ba576107ab81836129c5565b83610ec4565b610f7b9150833d85116107ba576107ab81836129c5565b84610e42565b80610f8e610f949261293c565b80610334565b80610dd2565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610fd1613f81565b610c00600435614395565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602060ff603a5460a01c166040519015158152f35b346100155761102d36610a58565b6001545f9273ffffffffffffffffffffffffffffffffffffffff809216803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b576110f9575b507ff93f9a76c1bf3444d22400a00cb9fe990e6abe9dbb333fda48859cfee864543d916020911692835f52603b8252610b788160405f209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b6020919450916111297ff93f9a76c1bf3444d22400a00cb9fe990e6abe9dbb333fda48859cfee864543d9361293c565b5f94915091611092565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557608061116b6132c4565b91604051938452602084015260408301526060820152f35b91908251928382525f5b8481106111cb5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6020809697860101520116010190565b60208183018101518483018201520161118d565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576105636040517f440368a3000000000000000000000000000000000000000000000000000000006020820152600481526112468161298d565b6040519182915f8352604060208401526040830190611183565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015577ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805460ff8160401c169081156113f7575b506113cd576113147ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0060027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000825416179055565b680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff82541617905561134d61330a565b6113997ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff8154169055565b604051600281527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1005b60046040517ff92ee8a9000000000000000000000000000000000000000000000000000000008152fd5b6002915067ffffffffffffffff1610155f6112c0565b34610015576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576004359060243561144d816102c9565b82908315806117b1575b611719575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821461168a575b6114a661062860015473ffffffffffffffffffffffffffffffffffffffff1690565b83517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316600482015260209290918390839060249082905afa91821561047b57610563966115b5956115a89461053d935f9161166d575b5015611659575b50611525612e60565b6115c4575b86517f6e553f650000000000000000000000000000000000000000000000000000000086820152602481019190915273ffffffffffffffffffffffffffffffffffffffff909216604483015281606481015b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826129c5565b8051810182019101612a06565b90519081529081906020820190565b73ffffffffffffffffffffffffffffffffffffffff83163314801561161c575b6115ed90614bca565b426116168473ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b5561152a565b506115ed61165261164b3373ffffffffffffffffffffffffffffffffffffffff165f52603c60205260405f2090565b5460ff1690565b90506115e4565b61166790603d541115614b65565b5f61151c565b6116849150873d89116107ba576107ab81836129c5565b5f611515565b90506116ad6106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b82517f70a0823100000000000000000000000000000000000000000000000000000000815233600482015290602090829060249082905afa90811561047b575f916116fa575b5090611484565b611713915060203d6020116107ba576107ab81836129c5565b5f6116f3565b905061173d61062860015473ffffffffffffffffffffffffffffffffffffffff1690565b6020835180927fb3d7f6b9000000000000000000000000000000000000000000000000000000008252818061177960048201905f602083019252565b03915afa90811561047b575f91611792575b509061145c565b6117ab915060203d6020116107ba576107ab81836129c5565b5f61178b565b505f611457565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576020603354604051908152f35b6118559092919260c081019360a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565b565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015575f60a060405161189481612955565b828152826020820152826040820152826060820152826080820152015260c06118bb613269565b611919604051809260a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565bf35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c6134d3565b34610015575f60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155760043573ffffffffffffffffffffffffffffffffffffffff60015416803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57611a1a575b506020817f96a008f96f1c0ab9fa3d9ddd43cdfc614848c4d054d51f43662ed900e9d094c892603d55604051908152a180f35b611a2591925061293c565b5f9060206119e7565b34610015576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155760043590602435611a6e816102c9565b5f9083611ce8575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214611c59575b611ac061062860015473ffffffffffffffffffffffffffffffffffffffff1690565b83517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316600482015260209290918390839060249082905afa91821561047b57610563966115b5956115a89461053d935f91611c3c575b5015611c28575b50611b3f612e60565b611b9a575b86517f94bf804d0000000000000000000000000000000000000000000000000000000086820152602481019190915273ffffffffffffffffffffffffffffffffffffffff9092166044830152816064810161157c565b73ffffffffffffffffffffffffffffffffffffffff831633148015611bf2575b611bc390614bca565b42611bec8473ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b55611b44565b50611bc3611c2161164b3373ffffffffffffffffffffffffffffffffffffffff165f52603c60205260405f2090565b9050611bba565b611c3690603d541115614b65565b5f611b36565b611c539150873d89116107ba576107ab81836129c5565b5f611b2f565b9050611c7c6106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b82517f70a0823100000000000000000000000000000000000000000000000000000000815233600482015290602090829060249082905afa90811561047b575f91611cc9575b5090611a9e565b611ce2915060203d6020116107ba576107ab81836129c5565b5f611cc2565b9050611d0c61062860015473ffffffffffffffffffffffffffffffffffffffff1690565b6020835180927fb3d7f6b90000000000000000000000000000000000000000000000000000000082528180611d4989600483019190602083019252565b03915afa90811561047b575f91611d62575b5090611a76565b611d7b915060203d6020116107ba576107ab81836129c5565b5f611d5b565b906020610a9b928181520190611183565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610563611dcb6135e1565b604051918291602083526020830190611183565b346100155760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557600435611e1a816102c9565b60443590611e27826102c9565b606435611e33816102c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00549267ffffffffffffffff60ff8560401c1615941680159081611fdf575b6001149081611fd5575b159081611fcc575b506113cd57611eeb9284611edd7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0060017fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000825416179055565b611f70575b60243590613842565b611ef157005b611f3d7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff8154169055565b604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081016113c8565b611fc77ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff825416179055565b611ee2565b9050155f611e84565b303b159150611e7c565b859150611e72565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff600435612037816102c9565b165f52603b602052602060ff60405f2054166040519015158152f35b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155761208a613f81565b610c00600435614d8b565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc608091011261001557600435906024356120cf816102c9565b906044356120dc816102c9565b9060643590565b346100155761157c61053d61217961216b6120fd36612095565b6040517f9f40a7b300000000000000000000000000000000000000000000000000000000602080830191909152602482019590955273ffffffffffffffffffffffffffffffffffffffff938416604482015292821660648401526084830152919591949091829060a4820190565b838082518301019101612a06565b9073ffffffffffffffffffffffffffffffffffffffff838160015416916024604051809481937f70a08231000000000000000000000000000000000000000000000000000000008352871660048301525afa90811561047b575f91612213575b50156121e9575b50604051908152f35b73ffffffffffffffffffffffffffffffffffffffff165f908152603e60205260408120555f6121e0565b61222a9150843d86116107ba576107ab81836129c5565b5f6121d9565b346100155761157c61053d61217961216b61224a36612095565b6040517fa318c1a400000000000000000000000000000000000000000000000000000000602080830191909152602482019590955273ffffffffffffffffffffffffffffffffffffffff938416604482015292821660648401526084830152919591949091829060a4820190565b34610015575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff8060015416803b15610015575f602491604051928380927fd43fdcf70000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b576124cf575b5061237261062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b9060405180927ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209586935afa801561047b5783915f916124a2575b506004604051809481937f76e66a1c000000000000000000000000000000000000000000000000000000008352165afa801561047b577f617d69aa6f8f8f7158e168208ffd102c878214b34e8510746322af83f775aab792610983925f92612483575b50612426612710831115613a06565b7fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b990827fffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffff835416911b179055604051918291829190602083019252565b8161249b9293503d84116107ba576107ab81836129c5565b905f612417565b6124c29150823d84116124c8575b6124ba81836129c5565b810190612e4b565b5f6123b4565b503d6124b0565b6124da91925061293c565b5f905f612349565b346100155760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061259c61258e61157c61053d60043561252b816102c9565b6125358133613d8a565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008782015273ffffffffffffffffffffffffffffffffffffffff9091166024808301919091523560448201529182906064820190565b828082518301019101612a20565b6040519015158152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c613a6b565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126100155760043590602435612618816102c9565b90604435610a9b816102c9565b346100155761216b61053d61217961263c366125de565b6040517fa318c1a400000000000000000000000000000000000000000000000000000000602080830191909152602482019490945273ffffffffffffffffffffffffffffffffffffffff928316604482015291811660648301525f6084830152919491938160a4810161157c565b346100155761216b61053d6121796126c1366125de565b6040517f9f40a7b300000000000000000000000000000000000000000000000000000000602080830191909152602482019490945273ffffffffffffffffffffffffffffffffffffffff928316604482015291811660648301526127106084830152919491938160a4810161157c565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602060405173d4fa2d31b7968e448877f69a96de69f5de8cd23e8152f35b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff6004356127cd816102c9565b165f52603c602052602060ff60405f2054166040519015158152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602060405173d377919fa87120584b21279a491f82d5265a139c8152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c613b69565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602073ffffffffffffffffffffffffffffffffffffffff60325416604051908152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602073ffffffffffffffffffffffffffffffffffffffff603a5416604051908152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b67ffffffffffffffff811161295057604052565b61290f565b60c0810190811067ffffffffffffffff82111761295057604052565b60a0810190811067ffffffffffffffff82111761295057604052565b6040810190811067ffffffffffffffff82111761295057604052565b6060810190811067ffffffffffffffff82111761295057604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761295057604052565b90816020910312610015575190565b6040513d5f823e3d90fd5b908160209103126100155751610a9b81610376565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b91908201809211612a6f57565b612a35565b612a956106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048083019190915291939260209291908390829060249082905afa90811561047b575f91612e2e575b50612af0613c95565b91508651907f5c975abb00000000000000000000000000000000000000000000000000000000825273d4fa2d31b7968e448877f69a96de69f5de8cd23e9086838781855afa801561047b5787935f91612e11575b5015612cc15750612b8a92505f905b885180809581947f07a2d13a0000000000000000000000000000000000000000000000000000000083528983019190602083019252565b03915afa801561047b578492612ba7925f92612ca2575b50612a62565b94612bca61062860015473ffffffffffffffffffffffffffffffffffffffff1690565b9051928380927fbf86d6900000000000000000000000000000000000000000000000000000000082525afa91821561047b575f92612c75575b5050612c7157612c11612e60565b9081612c1c57505090565b612c449073ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b54908115159182612c5e575b5050612c595790565b505f90565b612c689250612a62565b42105f80612c50565b5090565b612c949250803d10612c9b575b612c8c81836129c5565b810190612a20565b5f80612c03565b503d612c82565b612cba919250843d86116107ba576107ab81836129c5565b905f612ba1565b9150612ccb612f6b565b8851937fd905777e0000000000000000000000000000000000000000000000000000000091828652888680612d1f308c830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0381875afa91821561047b57612d4b8a93612d5793612da9995f92612df9575b50808218908211021890565b95818118908211021890565b91612d7a61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b908b51968792839283528a830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0381855afa92831561047b57610e5f8894612dd493612b8a975f92612dda5750808218908211021890565b90612b53565b612df2919250873d89116107ba576107ab81836129c5565b905f612d3f565b612df2919250863d88116107ba576107ab81836129c5565b612e289150843d8611612c9b57612c8c81836129c5565b5f612b44565b612e459150833d85116107ba576107ab81836129c5565b5f612ae7565b908160209103126100155751610a9b816102c9565b612e8861062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180917ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209485935afa90811561047b5773ffffffffffffffffffffffffffffffffffffffff9183915f91612f38575b506004604051809481937fe4c3b0da000000000000000000000000000000000000000000000000000000008352165afa91821561047b575f92612f2257505090565b610a9b9250803d106107ba576107ab81836129c5565b612f4f9150823d84116124c8576124ba81836129c5565b5f612ee0565b9190826040910312610015576020825192015190565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173d4fa2d31b7968e448877f69a96de69f5de8cd23e5afa90811561047b575f91612fc5575090565b610a9b915060203d6020116107ba576107ab81836129c5565b91908203918211612a6f57565b603a5460a01c60ff1680613231575b612c59576040517f402d267d00000000000000000000000000000000000000000000000000000000815230600482015260209173d4fa2d31b7968e448877f69a96de69f5de8cd23e918381602481865afa92831561047b5784915f9461320d575b5090600491604051928380927f5c975abb0000000000000000000000000000000000000000000000000000000082525afa90811561047b575f916131f0575b5080156131e8575b61316c576130d2906130c960325473ffffffffffffffffffffffffffffffffffffffff1690565b60335490614234565b6131e2576130de6134d3565b801580156131b9575b6131925760048361311061062860015473ffffffffffffffffffffffffffffffffffffffff1690565b604051928380927f01e1d1140000000000000000000000000000000000000000000000000000000082525afa93841561047b575f94613173575b50508281111561316c57610a9b9261316191612fde565b818082109118021890565b5050505f90565b61318a929450803d106107ba576107ab81836129c5565b915f8061314a565b5050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146130e7565b50505f90565b5081156130a2565b6132079150843d8611612c9b57612c8c81836129c5565b5f61309a565b82919450613229906004933d84116107ba576107ab81836129c5565b93909161305b565b5061326461326061164b8373ffffffffffffffffffffffffffffffffffffffff165f52603b60205260405f2090565b1590565b612ffa565b6040519061327682612955565b8160a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b6132ee73ffffffffffffffffffffffffffffffffffffffff603254166132e8613269565b90614622565b509283919280948082115f146131e2578103908111612a6f5790565b73a0b86991c6218b36c1d19d4a2e9eb0ce3606eb487fffffffffffffffffffffffff000000000000000000000000000000000000000081815f5416175f55817fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b091825416179055604051602081017f095ea7b300000000000000000000000000000000000000000000000000000000928382525f80846133eb60248201907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6020604084019373d4fa2d31b7968e448877f69a96de69f5de8cd23e81520152565b039361341d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0958681018852876129c5565b85519082855af19061342d613ee8565b826134a1575b5081613496575b501561344557505050565b61185592613491613491926040519260208401528261348560248201905f6020604084019373d4fa2d31b7968e448877f69a96de69f5de8cd23e81520152565b039081018352826129c5565b6154c3565b90503b15155f61343a565b805191925081159182156134b9575b5050905f613433565b6134cc9250602080918301019101612a20565b5f806134b0565b6134fb61062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180917ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209485935afa90811561047b575f916135c4575b508160405180927fcc718f760000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff816135ad60048201907f4bba860c0c28b1a4ae0214c01f08e53b00bfe2e087690d7a04d73a15360ec6a7602083019252565b0392165afa91821561047b575f92612f2257505090565b6135db9150823d84116124c8576124ba81836129c5565b5f61353b565b604051906135ee8261298d565b600482527f55534433000000000000000000000000000000000000000000000000000000006020830152565b1561362157565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600760248201527f216d6f7270686f000000000000000000000000000000000000000000000000006044820152fd5b908160c09103126100155760a06040519161369983612955565b80516136a4816102c9565b835260208101516136b4816102c9565b602084015260408101516136c7816102c9565b604084015260608101516136da816102c9565b60608401526080810151608084015201516136f4816102c9565b60a082015290565b1561370357565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f496e76616c6964206d61726b65740000000000000000000000000000000000006044820152fd5b61380160a06118559273ffffffffffffffffffffffffffffffffffffffff80825116907fffffffffffffffffffffffff00000000000000000000000000000000000000009182603454161760345580602084015116826035541617603555806040840151168260365416176036556060830151169060375416176037556080810151603855015173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006039541617603955565b6139149392916138b073ffffffffffffffffffffffffffffffffffffffff60c0931661386f81151561361a565b73ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006032541617603255565b6138b981603355565b6138db61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180809781947f2c3c9157000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b03915afa801561047b5761397d829161398b955f916139d7575b5061395a613953610628835173ffffffffffffffffffffffffffffffffffffffff1690565b15156136fc565b61396381613761565b5173ffffffffffffffffffffffffffffffffffffffff1690565b6139856135e1565b90614c55565b6118556139af6106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b6139d161062860325473ffffffffffffffffffffffffffffffffffffffff1690565b906149e7565b6139f9915060c03d60c0116139ff575b6139f181836129c5565b81019061367f565b5f61392e565b503d6139e7565b15613a0d57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f496e76616c6964207472616e63686520736861726500000000000000000000006044820152fd5b613b1473ffffffffffffffffffffffffffffffffffffffff60325416613a8f613269565b5f613ae160c08320604051602081019182526002604082015260408152613ab5816129a9565b5190206040805130602082019081529181019290925290613ad9816060810161157c565b5190206152e4565b604051809581927f7784c685000000000000000000000000000000000000000000000000000000008352600483016141bf565b0381855afa90811561047b57613b38613b3f92610a9b955f91613b47575b506141fa565b5192614622565b505091614e7f565b613b6391503d805f833e613b5b81836129c5565b810190614145565b5f613b32565b613b9161062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180917ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209485935afa90811561047b5773ffffffffffffffffffffffffffffffffffffffff9183915f91613c2b575b506004604051809481937f111e3c08000000000000000000000000000000000000000000000000000000008352165afa91821561047b575f92612f2257505090565b613c429150823d84116124c8576124ba81836129c5565b5f613be9565b365f80375f80368173d377919fa87120584b21279a491f82d5265a139c5af43d5f803e15613c74573d5ff35b3d5ffd5b51906fffffffffffffffffffffffffffffffff8216820361001557565b73ffffffffffffffffffffffffffffffffffffffff6032541660606033546044604051809481937f93c5206200000000000000000000000000000000000000000000000000000000835260048301523060248301525afa90811561047b575f91613d15575b505190613d12613d086132c4565b9050929185614e7f565b91565b90506060813d606011613d82575b81613d30606093836129c5565b8101031261001557604051906060820182811067ffffffffffffffff82111761295057613d7791604091825280518452613d6c60208201613c78565b602085015201613c78565b60408201525f613cfa565b3d9150613d23565b9073ffffffffffffffffffffffffffffffffffffffff8083169182158015613ede575b613ed85781603a54169182911614918215613ece575b5050613ecb57613df18173ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b54613dfa612e60565b8101809111612a6f57421090811591613e9a575b5015613e1657565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f555344333a2043616e6e6f74207472616e7366657220647572696e6720636f6d60448201527f6d69746d656e7420706572696f640000000000000000000000000000000000006064820152fd5b613ec3915073ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b54155f613e0e565b50565b1490505f80613dc3565b50505050565b5081811615613dad565b3d15613f40573d9067ffffffffffffffff82116129505760405191613f3560207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846129c5565b82523d5f602084013e565b606090565b5f809160208151910173d377919fa87120584b21279a491f82d5265a139c5af4613f6d613ee8565b9015613f765790565b6040513d90815f823efd5b303303613f8a57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f2173656c660000000000000000000000000000000000000000000000000000006044820152fd5b8015613ecb576040517f0a28a47700000000000000000000000000000000000000000000000000000000815280614029602093600483019190602083019252565b0390828173d4fa2d31b7968e448877f69a96de69f5de8cd23e9381855afa801561047b57614074915f91614110575b50614061612f6b565b8181106140eb575b808218908211021890565b908161407f57505050565b6040517fba0876520000000000000000000000000000000000000000000000000000000081526004810192909252306024830181905260448301528290829060649082905f905af1801561047b576140d5575050565b81613ecb92903d106107ba576107ab81836129c5565b6140fd6140f88284612fde565b61508c565b15614069575061410b612f6b565b614069565b6141279150843d86116107ba576107ab81836129c5565b5f614058565b67ffffffffffffffff81116129505760051b60200190565b60209081818403126100155780519067ffffffffffffffff821161001557019180601f8401121561001557825161417b8161412d565b9361418960405195866129c5565b818552838086019260051b820101928311610015578301905b8282106141b0575050505090565b815181529083019083016141a2565b60209060206040818301928281528551809452019301915f5b8281106141e6575050505090565b8351855293810193928101926001016141d8565b8051156142075760200190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b919061429261157c9160405160208101918252600260408201526040815261425b816129a9565b5190206040519283916020830195866020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b5190209060018201809211612a6f576142f95f916142c473ffffffffffffffffffffffffffffffffffffffff946152e4565b6040519485809481937f7784c685000000000000000000000000000000000000000000000000000000008352600483016141bf565b0392165afa801561047b5761431b61433191610a9b935f91613b4757506141fa565b516fffffffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff1690565b81810292918115918404141715612a6f57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b8115614390570490565b614359565b8015613ecb576040517f6e553f6500000000000000000000000000000000000000000000000000000000815260048101919091523060248201526020816044815f73d4fa2d31b7968e448877f69a96de69f5de8cd23e5af1801561047b57614464575b50614401613b69565b8015613ecb5761440f613a6b565b61443561442d61441d612f6b565b936144288585612a62565b614346565b612710900490565b918183111561445f5761444e61445a92613ecb94612fde565b90818082109118021890565b615309565b505050565b61447c9060203d6020116107ba576107ab81836129c5565b505f6143f8565b908160e0910312610015576040519060e0820182811067ffffffffffffffff821117612950576145199160c0916040526144bc81613c78565b84526144ca60208201613c78565b60208501526144db60408201613c78565b60408501526144ec60608201613c78565b60608501526144fd60808201613c78565b608085015261450e60a08201613c78565b60a085015201613c78565b60c082015290565b61018060c061185593959461458c846101a081019860a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565b80516fffffffffffffffffffffffffffffffff908116858401526020820151811660e086015260408201518116610100860152606082015181166101208601526080820151811661014086015260a08201511661016085015201516fffffffffffffffffffffffffffffffff16910152565b9190916fffffffffffffffffffffffffffffffff80809416911601918211612a6f57565b60c082206040517f5c60e39a00000000000000000000000000000000000000000000000000000000815260048101919091529173ffffffffffffffffffffffffffffffffffffffff9160e0908490602490829086165afa92831561047b575f936149b6575b5082906146b36146ad61433160808501516fffffffffffffffffffffffffffffffff1690565b42612fde565b92831515908161497d575b81614951575b50614751575b50516fffffffffffffffffffffffffffffffff16905060208201516fffffffffffffffffffffffffffffffff1692614732606061471a60408601516fffffffffffffffffffffffffffffffff1690565b9401516fffffffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff928316948316938316921690565b6147b5918161477f6106286106286060602096015173ffffffffffffffffffffffffffffffffffffffff1690565b906040518095819482937f2a944b5500000000000000000000000000000000000000000000000000000000845260048401614521565b03915afa90811561047b5761483d91614801915f91614932575b506147fb60408601946147f561433187516fffffffffffffffffffffffffffffffff1690565b926153fb565b90615443565b9161482761480e8461545a565b82516fffffffffffffffffffffffffffffffff166145fe565b6fffffffffffffffffffffffffffffffff169052565b61487b61486561484c8361545a565b84516fffffffffffffffffffffffffffffffff166145fe565b6fffffffffffffffffffffffffffffffff168352565b6fffffffffffffffffffffffffffffffff6148a960a08401516fffffffffffffffffffffffffffffffff1690565b16806148b7575b82906146ca565b6148cd670de0b6b3a76400009161492b93614346565b0461482761480e6149266148fd846148f861433189516fffffffffffffffffffffffffffffffff1690565b612fde565b93602087019461492061433187516fffffffffffffffffffffffffffffffff1690565b916154a1565b61545a565b5f806148b0565b61494b915060203d6020116107ba576107ab81836129c5565b5f6147cf565b9050614974606083015173ffffffffffffffffffffffffffffffffffffffff1690565b1615155f6146c4565b90506fffffffffffffffffffffffffffffffff6149ad60408501516fffffffffffffffffffffffffffffffff1690565b161515906146be565b6149d991935060e03d60e0116149e0575b6149d181836129c5565b810190614483565b915f614687565b503d6149c7565b6040517f095ea7b3000000000000000000000000000000000000000000000000000000006020820181815273ffffffffffffffffffffffffffffffffffffffff851660248401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff604480850191909152835290939192917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe091614a8c6064866129c5565b5f8073ffffffffffffffffffffffffffffffffffffffff86169287519082855af190614ab6613ee8565b82614b33575b5081614b28575b5015614ad1575b5050505050565b604051602081019590955273ffffffffffffffffffffffffffffffffffffffff1660248501525f6044850152614b1e93614b1991614b13908260648101613485565b826155df565b6155df565b5f80808080614aca565b90503b15155f614ac3565b80519192508115918215614b4b575b5050905f614abc565b614b5e9250602080918301019101612a20565b5f80614b42565b15614b6c57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f42656c6f77206d696e696d756d206465706f73697400000000000000000000006044820152fd5b15614bd157565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f555344333a204f6e6c792073656c66206f722077686974656c6973746564206460448201527f65706f7369747320616c6c6f77656400000000000000000000000000000000006064820152fd5b939192909260ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c1615614d6157613ecb9461053d9373ffffffffffffffffffffffffffffffffffffffff80809316947fffffffffffffffffffffffff000000000000000000000000000000000000000086815f5416175f553090600154161760015581614d22604051998a987f97073ae60000000000000000000000000000000000000000000000000000000060208b015260248a015260a060448a015260c4890190611183565b951660648701521660848501521660a4830152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826129c5565b60046040517fd7e6bcf8000000000000000000000000000000000000000000000000000000008152fd5b80614e03575b50614d9a613a6b565b614da2612f6b565b90614dc161442d614db38484612a62565b614dbb613b69565b90614346565b9182821115614dd85750613ecb916140f891612fde565b81831180614dfa575b614dea57505050565b61444e61445a92613ecb94612fde565b50801515614de1565b6040517f6e553f6500000000000000000000000000000000000000000000000000000000815260048101919091523060248201526020816044815f73d4fa2d31b7968e448877f69a96de69f5de8cd23e5af1801561047b5715614d9157614e789060203d6020116107ba576107ab81836129c5565b505f614d91565b919060018101809111612a6f57620f42408201809211612a6f57610a9b92615649565b5f9073ffffffffffffffffffffffffffffffffffffffff80603a5416604051602081019182527fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b4604082015260408152614efb816129a9565b51902080547fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b291825491808611614f6a575b85900390558390039055603a5460405192835216907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3565b945084614f2d565b91610120919493610140840195614fd08560a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b60c08501525f60e085015273ffffffffffffffffffffffffffffffffffffffff80921661010085015216910152565b9161012091949361014084019561505d8560a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b5f60c086015260e085015273ffffffffffffffffffffffffffffffffffffffff80921661010085015216910152565b8015612c59576150b461062860325473ffffffffffffffffffffffffffffffffffffffff1690565b803b15610015576040905f825180927f1e4711cf0000000000000000000000000000000000000000000000000000000082528183816151426004820160345473ffffffffffffffffffffffffffffffffffffffff908116825260355481166020830152603654811660408301526037548116606083015260385460808301526039541660a082015260c00190565b03925af1801561047b576152d1575b5061516e61515d613c95565b909491928186188287100290911890565b80156152c8578082189181119190910218928361518c575b50505090565b82908410615239576151f45f926151bb61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b9083519485809481937fa0b16cc70000000000000000000000000000000000000000000000000000000083523090309060048501614fff565b03925af1801561047b5761520e575b50505b5f8080615186565b8161522d92903d10615232575b61522581836129c5565b810190612f55565b615203565b503d61521b565b505061525d61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b81805180927fa0b16cc7000000000000000000000000000000000000000000000000000000008252815f8161529730308b60048501614f72565b03925af1801561047b576152ad575b5050615206565b816152c392903d106152325761522581836129c5565b6152a6565b50505050505f90565b80610f8e6152de9261293c565b5f615151565b604051906152f18261298d565b6001825260203681840137615305826141fa565b5290565b8015612c59575f604073ffffffffffffffffffffffffffffffffffffffff603254166101648251809481937f370160a00000000000000000000000000000000000000000000000000000000083526153ab6004840160a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b8760c48401528160e484015230610104840152610140610124840152816101448401525af1801561047b576153de575090565b6153f69060403d6040116152325761522581836129c5565b505090565b9061540591614346565b671bc16d674ec800006154188280614346565b046729a2241af62c000061542c8383614346565b04908201809211612a6f578101809111612a6f5790565b670de0b6b3a76400009161545691614346565b0490565b6fffffffffffffffffffffffffffffffff90818111615477571690565b60046040517f6acc9c8f000000000000000000000000000000000000000000000000000000008152fd5b91620f42408101809111612a6f5760018201809211612a6f57610a9b92615649565b61552a6040516154d28161298d565b5f806020948584527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648685015285815191018273a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485af1615524613ee8565b9061571b565b8051908282159283156155c7575b505050156155435750565b608490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b6155d79350820181019101612a20565b5f8281615538565b60405161552a9173ffffffffffffffffffffffffffffffffffffffff166156058261298d565b5f806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af1615643613ee8565b916157f4565b9061438691614346565b9161565e82846156d9565b92909384156156cc57848311156156bf5790829109815f038216809204600280826003021880830282030280830282030280830282030280830282030280830282030280920290030293600183805f03040190848311900302920304170290565b821560030260111861570b565b505090610a9b9250614386565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8183099102908180821091030391565b634e487b715f526020526024601cfd5b909190156157aa575080511561572e5790565b73a0b86991c6218b36c1d19d4a2e9eb0ce3606eb483b1561574c5790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b8151156157ba5750805190602001fd5b6157f0906040519182917f08c379a000000000000000000000000000000000000000000000000000000000835260048301611d81565b0390fd5b919290156158115750815115615808575090565b3b1561574c5790565b8251909150156157ba5750805190602001fd
Deployed Bytecode
0x60806040526004361015610019575b3415613c48575b5f80fd5b5f3560e01c806304bd4629146102c45780630517bbab146102bf578063052d9e7e146102ba57806323b872dd146102b55780632606a10b146102b05780632b832dbc146102ab5780632f6c1f53146102a65780633d6cb575146101f757806341b3d185146102a15780634251c3541461029c57806346aa2f121461029757806346b00d041461029257806349317f1d1461028d578063503160d91461028857806351fb012d1461028357806353d6fd591461027e57806359ddbab2146102795780635d265d3f146102745780636c2eb3501461026f5780636e553f651461026a5780636ed71ede146102655780637b9e68f2146102605780638f770ad01461025b5780638fcc9cfb1461025657806394bf804d1461025157806395d89b411461024c57806396c9f17e146102475780639b19251a146102425780639d7fb70c1461023d5780639f40a7b314610238578063a318c1a414610233578063a46537571461022e578063a9059cbb14610229578063a9b89c0714610224578063b460af941461021f578063ba0876521461021a578063cb6d71bc14610215578063d00330ab14610210578063d19a3bb81461020b578063e61582b514610206578063efb5f91114610201578063f7f6ba53146101fc5763fde813a80361000e575b610bbe565b6128be565b61286d565b612835565b6127e9565b61277d565b612731565b6126aa565b612625565b6125a6565b6124e2565b6122b8565b612230565b6120e3565b612053565b611fe7565b611ddf565b611d92565b611a2e565b611953565b61191b565b611857565b6117b8565b61140d565b611260565b6111df565b611133565b61101f565b610fdc565b610f9a565b610d21565b610cba565b610c75565b610c3d565b610c02565b610a9e565b61083d565b610567565b610480565b610380565b61033e565b6102e7565b73ffffffffffffffffffffffffffffffffffffffff81160361001557565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c600435610327816102c9565b612a74565b604051908152f35b5f91031261001557565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c612e60565b8015150361001557565b34610015575f60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576004356103bc81610376565b73ffffffffffffffffffffffffffffffffffffffff60015416803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57610468575b507fffffffffffffffffffffff00ffffffffffffffffffffffffffffffffffffffff74ff0000000000000000000000000000000000000000603a5492151560a01b16911617603a5580f35b61047391925061293c565b5f905f61041d565b612a15565b346100155760607ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576105636105516105426004356104c4816102c9565b602435906104d1826102c9565b6104db8282613d8a565b604051917f23b872dd00000000000000000000000000000000000000000000000000000000602084015273ffffffffffffffffffffffffffffffffffffffff809216602484015216604482015260443560648201526064815261053d81612971565b613f45565b60208082518301019101612a20565b60405190151581529081906020820190565b0390f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576040516105e16105d36020927f2606a10b00000000000000000000000000000000000000000000000000000000848201526004815261053d8161298d565b828082518301019101612f55565b9182151580610800575b610602575b50604080519182526020820192909252f35b6106b09061064161062860015473ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff1690565b81610661603a5473ffffffffffffffffffffffffffffffffffffffff1690565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff909116600482015293849081906024820190565b0381845afa92831561047b575f936107e1575b50826106d1575b50506105f0565b6040517f18160ddd0000000000000000000000000000000000000000000000000000000081528281600481855afa90811561047b5783905f926107c1575b5060049192604051928380927f01e1d1140000000000000000000000000000000000000000000000000000000082525afa92831561047b576107669361075f9288925f92610794575b5050612a62565b9085615653565b9080821161078c575b508061077d575b80806106ca565b61078690614ea2565b5f610776565b90505f61076f565b6107b39250803d106107ba575b6107ab81836129c5565b810190612a06565b5f80610758565b503d6107a1565b600492506107db90823d84116107ba576107ab81836129c5565b9161070f565b6107f9919350823d84116107ba576107ab81836129c5565b915f6106c3565b5073ffffffffffffffffffffffffffffffffffffffff610835603a5473ffffffffffffffffffffffffffffffffffffffff1690565b1615156105eb565b34610015575f60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557600435610879816102c9565b73ffffffffffffffffffffffffffffffffffffffff8060015416803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57610a45575b5080603a54166109e75781161561098957610983816109567fc3212a26ea3792ed00e630d63eb8ec2a9295974fe90e94ff25a8f70987fcdeeb9373ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff0000000000000000000000000000000000000000603a541617603a55565b604080515f815273ffffffffffffffffffffffffffffffffffffffff909216602083015290918291820190565b0390a180f35b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600f60248201527f496e76616c6964206164647265737300000000000000000000000000000000006044820152fd5b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601160248201527f735553443320616c7265616479207365740000000000000000000000000000006044820152fd5b610a5091935061293c565b5f915f6108db565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc604091011261001557600435610a8e816102c9565b90602435610a9b81610376565b90565b3461001557610aac36610a58565b6001545f9273ffffffffffffffffffffffffffffffffffffffff809216803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57610b84575b507f8bb3c9e62c5e823d77565af4d29339378715313d3cb898327e576392ffa79b00916020911692835f52603c8252610b788160405f209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b6040519015158152a280f35b602091945091610bb47f8bb3c9e62c5e823d77565af4d29339378715313d3cb898327e576392ffa79b009361293c565b5f94915091610b11565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610bf5613f81565b610c00600435613fe8565b005b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576020603d54604051908152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c612f6b565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c600435610cb5816102c9565b612feb565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff600435610d0a816102c9565b165f52603e602052602060405f2054604051908152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610d57613f81565b610d5f613269565b610d8161062860325473ffffffffffffffffffffffffffffffffffffffff1690565b803b15610015576040517f1e4711cf000000000000000000000000000000000000000000000000000000008152905f908290818381610dc388600483016117f3565b03925af1801561047b57610f81575b50610df46106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b6040517f70a082310000000000000000000000000000000000000000000000000000000080825230600483015291602091908290829060249082905afa801561047b57610e48915f91610f64575b50614d8b565b610e9d81610e65610e57613a6b565b610e5f612f6b565b90612a62565b604051809381927f07a2d13a000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b038173d4fa2d31b7968e448877f69a96de69f5de8cd23e5afa90811561047b575f91610f47575b5081610ee76106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b604051948552306004860152849060249082905afa90811561047b5761056393610f18935f93610f28575050612a62565b6040519081529081906020820190565b610f3f929350803d106107ba576107ab81836129c5565b908480610758565b610f5e9150823d84116107ba576107ab81836129c5565b83610ec4565b610f7b9150833d85116107ba576107ab81836129c5565b84610e42565b80610f8e610f949261293c565b80610334565b80610dd2565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610fd1613f81565b610c00600435614395565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602060ff603a5460a01c166040519015158152f35b346100155761102d36610a58565b6001545f9273ffffffffffffffffffffffffffffffffffffffff809216803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b576110f9575b507ff93f9a76c1bf3444d22400a00cb9fe990e6abe9dbb333fda48859cfee864543d916020911692835f52603b8252610b788160405f209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b6020919450916111297ff93f9a76c1bf3444d22400a00cb9fe990e6abe9dbb333fda48859cfee864543d9361293c565b5f94915091611092565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557608061116b6132c4565b91604051938452602084015260408301526060820152f35b91908251928382525f5b8481106111cb5750507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f845f6020809697860101520116010190565b60208183018101518483018201520161118d565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576105636040517f440368a3000000000000000000000000000000000000000000000000000000006020820152600481526112468161298d565b6040519182915f8352604060208401526040830190611183565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015577ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805460ff8160401c169081156113f7575b506113cd576113147ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0060027fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000825416179055565b680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff82541617905561134d61330a565b6113997ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff8154169055565b604051600281527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081015b0390a1005b60046040517ff92ee8a9000000000000000000000000000000000000000000000000000000008152fd5b6002915067ffffffffffffffff1610155f6112c0565b34610015576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576004359060243561144d816102c9565b82908315806117b1575b611719575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821461168a575b6114a661062860015473ffffffffffffffffffffffffffffffffffffffff1690565b83517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316600482015260209290918390839060249082905afa91821561047b57610563966115b5956115a89461053d935f9161166d575b5015611659575b50611525612e60565b6115c4575b86517f6e553f650000000000000000000000000000000000000000000000000000000086820152602481019190915273ffffffffffffffffffffffffffffffffffffffff909216604483015281606481015b037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826129c5565b8051810182019101612a06565b90519081529081906020820190565b73ffffffffffffffffffffffffffffffffffffffff83163314801561161c575b6115ed90614bca565b426116168473ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b5561152a565b506115ed61165261164b3373ffffffffffffffffffffffffffffffffffffffff165f52603c60205260405f2090565b5460ff1690565b90506115e4565b61166790603d541115614b65565b5f61151c565b6116849150873d89116107ba576107ab81836129c5565b5f611515565b90506116ad6106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b82517f70a0823100000000000000000000000000000000000000000000000000000000815233600482015290602090829060249082905afa90811561047b575f916116fa575b5090611484565b611713915060203d6020116107ba576107ab81836129c5565b5f6116f3565b905061173d61062860015473ffffffffffffffffffffffffffffffffffffffff1690565b6020835180927fb3d7f6b9000000000000000000000000000000000000000000000000000000008252818061177960048201905f602083019252565b03915afa90811561047b575f91611792575b509061145c565b6117ab915060203d6020116107ba576107ab81836129c5565b5f61178b565b505f611457565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015576020603354604051908152f35b6118559092919260c081019360a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565b565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc360112610015575f60a060405161189481612955565b828152826020820152826040820152826060820152826080820152015260c06118bb613269565b611919604051809260a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565bf35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c6134d3565b34610015575f60207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155760043573ffffffffffffffffffffffffffffffffffffffff60015416803b15610015575f602491604051928380927f48e4a6490000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b57611a1a575b506020817f96a008f96f1c0ab9fa3d9ddd43cdfc614848c4d054d51f43662ed900e9d094c892603d55604051908152a180f35b611a2591925061293c565b5f9060206119e7565b34610015576040807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155760043590602435611a6e816102c9565b5f9083611ce8575b7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8214611c59575b611ac061062860015473ffffffffffffffffffffffffffffffffffffffff1690565b83517f70a0823100000000000000000000000000000000000000000000000000000000815273ffffffffffffffffffffffffffffffffffffffff8316600482015260209290918390839060249082905afa91821561047b57610563966115b5956115a89461053d935f91611c3c575b5015611c28575b50611b3f612e60565b611b9a575b86517f94bf804d0000000000000000000000000000000000000000000000000000000086820152602481019190915273ffffffffffffffffffffffffffffffffffffffff9092166044830152816064810161157c565b73ffffffffffffffffffffffffffffffffffffffff831633148015611bf2575b611bc390614bca565b42611bec8473ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b55611b44565b50611bc3611c2161164b3373ffffffffffffffffffffffffffffffffffffffff165f52603c60205260405f2090565b9050611bba565b611c3690603d541115614b65565b5f611b36565b611c539150873d89116107ba576107ab81836129c5565b5f611b2f565b9050611c7c6106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b82517f70a0823100000000000000000000000000000000000000000000000000000000815233600482015290602090829060249082905afa90811561047b575f91611cc9575b5090611a9e565b611ce2915060203d6020116107ba576107ab81836129c5565b5f611cc2565b9050611d0c61062860015473ffffffffffffffffffffffffffffffffffffffff1690565b6020835180927fb3d7f6b90000000000000000000000000000000000000000000000000000000082528180611d4989600483019190602083019252565b03915afa90811561047b575f91611d62575b5090611a76565b611d7b915060203d6020116107ba576107ab81836129c5565b5f611d5b565b906020610a9b928181520190611183565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557610563611dcb6135e1565b604051918291602083526020830190611183565b346100155760807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557600435611e1a816102c9565b60443590611e27826102c9565b606435611e33816102c9565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00549267ffffffffffffffff60ff8560401c1615941680159081611fdf575b6001149081611fd5575b159081611fcc575b506113cd57611eeb9284611edd7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0060017fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000825416179055565b611f70575b60243590613842565b611ef157005b611f3d7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff8154169055565b604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080602081016113c8565b611fc77ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00680100000000000000007fffffffffffffffffffffffffffffffffffffffffffffff00ffffffffffffffff825416179055565b611ee2565b9050155f611e84565b303b159150611e7c565b859150611e72565b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff600435612037816102c9565b165f52603b602052602060ff60405f2054166040519015158152f35b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155761208a613f81565b610c00600435614d8b565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc608091011261001557600435906024356120cf816102c9565b906044356120dc816102c9565b9060643590565b346100155761157c61053d61217961216b6120fd36612095565b6040517f9f40a7b300000000000000000000000000000000000000000000000000000000602080830191909152602482019590955273ffffffffffffffffffffffffffffffffffffffff938416604482015292821660648401526084830152919591949091829060a4820190565b838082518301019101612a06565b9073ffffffffffffffffffffffffffffffffffffffff838160015416916024604051809481937f70a08231000000000000000000000000000000000000000000000000000000008352871660048301525afa90811561047b575f91612213575b50156121e9575b50604051908152f35b73ffffffffffffffffffffffffffffffffffffffff165f908152603e60205260408120555f6121e0565b61222a9150843d86116107ba576107ab81836129c5565b5f6121d9565b346100155761157c61053d61217961216b61224a36612095565b6040517fa318c1a400000000000000000000000000000000000000000000000000000000602080830191909152602482019590955273ffffffffffffffffffffffffffffffffffffffff938416604482015292821660648401526084830152919591949091829060a4820190565b34610015575f807ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff8060015416803b15610015575f602491604051928380927fd43fdcf70000000000000000000000000000000000000000000000000000000082523360048301525afa801561047b576124cf575b5061237261062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b9060405180927ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209586935afa801561047b5783915f916124a2575b506004604051809481937f76e66a1c000000000000000000000000000000000000000000000000000000008352165afa801561047b577f617d69aa6f8f8f7158e168208ffd102c878214b34e8510746322af83f775aab792610983925f92612483575b50612426612710831115613a06565b7fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b990827fffffffffffffffffffffffffffffffffffffffffffffffffffff0000ffffffff835416911b179055604051918291829190602083019252565b8161249b9293503d84116107ba576107ab81836129c5565b905f612417565b6124c29150823d84116124c8575b6124ba81836129c5565b810190612e4b565b5f6123b4565b503d6124b0565b6124da91925061293c565b5f905f612349565b346100155760407ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061259c61258e61157c61053d60043561252b816102c9565b6125358133613d8a565b6040517fa9059cbb000000000000000000000000000000000000000000000000000000008782015273ffffffffffffffffffffffffffffffffffffffff9091166024808301919091523560448201529182906064820190565b828082518301019101612a20565b6040519015158152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c613a6b565b7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc60609101126100155760043590602435612618816102c9565b90604435610a9b816102c9565b346100155761216b61053d61217961263c366125de565b6040517fa318c1a400000000000000000000000000000000000000000000000000000000602080830191909152602482019490945273ffffffffffffffffffffffffffffffffffffffff928316604482015291811660648301525f6084830152919491938160a4810161157c565b346100155761216b61053d6121796126c1366125de565b6040517f9f40a7b300000000000000000000000000000000000000000000000000000000602080830191909152602482019490945273ffffffffffffffffffffffffffffffffffffffff928316604482015291811660648301526127106084830152919491938160a4810161157c565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602060405173d4fa2d31b7968e448877f69a96de69f5de8cd23e8152f35b346100155760207ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc3601126100155773ffffffffffffffffffffffffffffffffffffffff6004356127cd816102c9565b165f52603c602052602060ff60405f2054166040519015158152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602060405173d377919fa87120584b21279a491f82d5265a139c8152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602061032c613b69565b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602073ffffffffffffffffffffffffffffffffffffffff60325416604051908152f35b34610015575f7ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc36011261001557602073ffffffffffffffffffffffffffffffffffffffff603a5416604051908152f35b7f4e487b71000000000000000000000000000000000000000000000000000000005f52604160045260245ffd5b67ffffffffffffffff811161295057604052565b61290f565b60c0810190811067ffffffffffffffff82111761295057604052565b60a0810190811067ffffffffffffffff82111761295057604052565b6040810190811067ffffffffffffffff82111761295057604052565b6060810190811067ffffffffffffffff82111761295057604052565b90601f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0910116810190811067ffffffffffffffff82111761295057604052565b90816020910312610015575190565b6040513d5f823e3d90fd5b908160209103126100155751610a9b81610376565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601160045260245ffd5b91908201809211612a6f57565b612a35565b612a956106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b604080517f70a082310000000000000000000000000000000000000000000000000000000081523060048083019190915291939260209291908390829060249082905afa90811561047b575f91612e2e575b50612af0613c95565b91508651907f5c975abb00000000000000000000000000000000000000000000000000000000825273d4fa2d31b7968e448877f69a96de69f5de8cd23e9086838781855afa801561047b5787935f91612e11575b5015612cc15750612b8a92505f905b885180809581947f07a2d13a0000000000000000000000000000000000000000000000000000000083528983019190602083019252565b03915afa801561047b578492612ba7925f92612ca2575b50612a62565b94612bca61062860015473ffffffffffffffffffffffffffffffffffffffff1690565b9051928380927fbf86d6900000000000000000000000000000000000000000000000000000000082525afa91821561047b575f92612c75575b5050612c7157612c11612e60565b9081612c1c57505090565b612c449073ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b54908115159182612c5e575b5050612c595790565b505f90565b612c689250612a62565b42105f80612c50565b5090565b612c949250803d10612c9b575b612c8c81836129c5565b810190612a20565b5f80612c03565b503d612c82565b612cba919250843d86116107ba576107ab81836129c5565b905f612ba1565b9150612ccb612f6b565b8851937fd905777e0000000000000000000000000000000000000000000000000000000091828652888680612d1f308c830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0381875afa91821561047b57612d4b8a93612d5793612da9995f92612df9575b50808218908211021890565b95818118908211021890565b91612d7a61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b908b51968792839283528a830191909173ffffffffffffffffffffffffffffffffffffffff6020820193169052565b0381855afa92831561047b57610e5f8894612dd493612b8a975f92612dda5750808218908211021890565b90612b53565b612df2919250873d89116107ba576107ab81836129c5565b905f612d3f565b612df2919250863d88116107ba576107ab81836129c5565b612e289150843d8611612c9b57612c8c81836129c5565b5f612b44565b612e459150833d85116107ba576107ab81836129c5565b5f612ae7565b908160209103126100155751610a9b816102c9565b612e8861062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180917ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209485935afa90811561047b5773ffffffffffffffffffffffffffffffffffffffff9183915f91612f38575b506004604051809481937fe4c3b0da000000000000000000000000000000000000000000000000000000008352165afa91821561047b575f92612f2257505090565b610a9b9250803d106107ba576107ab81836129c5565b612f4f9150823d84116124c8576124ba81836129c5565b5f612ee0565b9190826040910312610015576020825192015190565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015260208160248173d4fa2d31b7968e448877f69a96de69f5de8cd23e5afa90811561047b575f91612fc5575090565b610a9b915060203d6020116107ba576107ab81836129c5565b91908203918211612a6f57565b603a5460a01c60ff1680613231575b612c59576040517f402d267d00000000000000000000000000000000000000000000000000000000815230600482015260209173d4fa2d31b7968e448877f69a96de69f5de8cd23e918381602481865afa92831561047b5784915f9461320d575b5090600491604051928380927f5c975abb0000000000000000000000000000000000000000000000000000000082525afa90811561047b575f916131f0575b5080156131e8575b61316c576130d2906130c960325473ffffffffffffffffffffffffffffffffffffffff1690565b60335490614234565b6131e2576130de6134d3565b801580156131b9575b6131925760048361311061062860015473ffffffffffffffffffffffffffffffffffffffff1690565b604051928380927f01e1d1140000000000000000000000000000000000000000000000000000000082525afa93841561047b575f94613173575b50508281111561316c57610a9b9261316191612fde565b818082109118021890565b5050505f90565b61318a929450803d106107ba576107ab81836129c5565b915f8061314a565b5050507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff90565b507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff81146130e7565b50505f90565b5081156130a2565b6132079150843d8611612c9b57612c8c81836129c5565b5f61309a565b82919450613229906004933d84116107ba576107ab81836129c5565b93909161305b565b5061326461326061164b8373ffffffffffffffffffffffffffffffffffffffff165f52603b60205260405f2090565b1590565b612ffa565b6040519061327682612955565b8160a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b6132ee73ffffffffffffffffffffffffffffffffffffffff603254166132e8613269565b90614622565b509283919280948082115f146131e2578103908111612a6f5790565b73a0b86991c6218b36c1d19d4a2e9eb0ce3606eb487fffffffffffffffffffffffff000000000000000000000000000000000000000081815f5416175f55817fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b091825416179055604051602081017f095ea7b300000000000000000000000000000000000000000000000000000000928382525f80846133eb60248201907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff6020604084019373d4fa2d31b7968e448877f69a96de69f5de8cd23e81520152565b039361341d7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0958681018852876129c5565b85519082855af19061342d613ee8565b826134a1575b5081613496575b501561344557505050565b61185592613491613491926040519260208401528261348560248201905f6020604084019373d4fa2d31b7968e448877f69a96de69f5de8cd23e81520152565b039081018352826129c5565b6154c3565b90503b15155f61343a565b805191925081159182156134b9575b5050905f613433565b6134cc9250602080918301019101612a20565b5f806134b0565b6134fb61062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180917ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209485935afa90811561047b575f916135c4575b508160405180927fcc718f760000000000000000000000000000000000000000000000000000000082528173ffffffffffffffffffffffffffffffffffffffff816135ad60048201907f4bba860c0c28b1a4ae0214c01f08e53b00bfe2e087690d7a04d73a15360ec6a7602083019252565b0392165afa91821561047b575f92612f2257505090565b6135db9150823d84116124c8576124ba81836129c5565b5f61353b565b604051906135ee8261298d565b600482527f55534433000000000000000000000000000000000000000000000000000000006020830152565b1561362157565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600760248201527f216d6f7270686f000000000000000000000000000000000000000000000000006044820152fd5b908160c09103126100155760a06040519161369983612955565b80516136a4816102c9565b835260208101516136b4816102c9565b602084015260408101516136c7816102c9565b604084015260608101516136da816102c9565b60608401526080810151608084015201516136f4816102c9565b60a082015290565b1561370357565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600e60248201527f496e76616c6964206d61726b65740000000000000000000000000000000000006044820152fd5b61380160a06118559273ffffffffffffffffffffffffffffffffffffffff80825116907fffffffffffffffffffffffff00000000000000000000000000000000000000009182603454161760345580602084015116826035541617603555806040840151168260365416176036556060830151169060375416176037556080810151603855015173ffffffffffffffffffffffffffffffffffffffff1690565b73ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006039541617603955565b6139149392916138b073ffffffffffffffffffffffffffffffffffffffff60c0931661386f81151561361a565b73ffffffffffffffffffffffffffffffffffffffff167fffffffffffffffffffffffff00000000000000000000000000000000000000006032541617603255565b6138b981603355565b6138db61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180809781947f2c3c9157000000000000000000000000000000000000000000000000000000008352600483019190602083019252565b03915afa801561047b5761397d829161398b955f916139d7575b5061395a613953610628835173ffffffffffffffffffffffffffffffffffffffff1690565b15156136fc565b61396381613761565b5173ffffffffffffffffffffffffffffffffffffffff1690565b6139856135e1565b90614c55565b6118556139af6106285f5473ffffffffffffffffffffffffffffffffffffffff1690565b6139d161062860325473ffffffffffffffffffffffffffffffffffffffff1690565b906149e7565b6139f9915060c03d60c0116139ff575b6139f181836129c5565b81019061367f565b5f61392e565b503d6139e7565b15613a0d57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f496e76616c6964207472616e63686520736861726500000000000000000000006044820152fd5b613b1473ffffffffffffffffffffffffffffffffffffffff60325416613a8f613269565b5f613ae160c08320604051602081019182526002604082015260408152613ab5816129a9565b5190206040805130602082019081529181019290925290613ad9816060810161157c565b5190206152e4565b604051809581927f7784c685000000000000000000000000000000000000000000000000000000008352600483016141bf565b0381855afa90811561047b57613b38613b3f92610a9b955f91613b47575b506141fa565b5192614622565b505091614e7f565b613b6391503d805f833e613b5b81836129c5565b810190614145565b5f613b32565b613b9161062861062861062860325473ffffffffffffffffffffffffffffffffffffffff1690565b60405180917ff5efbb4f00000000000000000000000000000000000000000000000000000000825281600460209485935afa90811561047b5773ffffffffffffffffffffffffffffffffffffffff9183915f91613c2b575b506004604051809481937f111e3c08000000000000000000000000000000000000000000000000000000008352165afa91821561047b575f92612f2257505090565b613c429150823d84116124c8576124ba81836129c5565b5f613be9565b365f80375f80368173d377919fa87120584b21279a491f82d5265a139c5af43d5f803e15613c74573d5ff35b3d5ffd5b51906fffffffffffffffffffffffffffffffff8216820361001557565b73ffffffffffffffffffffffffffffffffffffffff6032541660606033546044604051809481937f93c5206200000000000000000000000000000000000000000000000000000000835260048301523060248301525afa90811561047b575f91613d15575b505190613d12613d086132c4565b9050929185614e7f565b91565b90506060813d606011613d82575b81613d30606093836129c5565b8101031261001557604051906060820182811067ffffffffffffffff82111761295057613d7791604091825280518452613d6c60208201613c78565b602085015201613c78565b60408201525f613cfa565b3d9150613d23565b9073ffffffffffffffffffffffffffffffffffffffff8083169182158015613ede575b613ed85781603a54169182911614918215613ece575b5050613ecb57613df18173ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b54613dfa612e60565b8101809111612a6f57421090811591613e9a575b5015613e1657565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602e60248201527f555344333a2043616e6e6f74207472616e7366657220647572696e6720636f6d60448201527f6d69746d656e7420706572696f640000000000000000000000000000000000006064820152fd5b613ec3915073ffffffffffffffffffffffffffffffffffffffff165f52603e60205260405f2090565b54155f613e0e565b50565b1490505f80613dc3565b50505050565b5081811615613dad565b3d15613f40573d9067ffffffffffffffff82116129505760405191613f3560207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601846129c5565b82523d5f602084013e565b606090565b5f809160208151910173d377919fa87120584b21279a491f82d5265a139c5af4613f6d613ee8565b9015613f765790565b6040513d90815f823efd5b303303613f8a57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152600560248201527f2173656c660000000000000000000000000000000000000000000000000000006044820152fd5b8015613ecb576040517f0a28a47700000000000000000000000000000000000000000000000000000000815280614029602093600483019190602083019252565b0390828173d4fa2d31b7968e448877f69a96de69f5de8cd23e9381855afa801561047b57614074915f91614110575b50614061612f6b565b8181106140eb575b808218908211021890565b908161407f57505050565b6040517fba0876520000000000000000000000000000000000000000000000000000000081526004810192909252306024830181905260448301528290829060649082905f905af1801561047b576140d5575050565b81613ecb92903d106107ba576107ab81836129c5565b6140fd6140f88284612fde565b61508c565b15614069575061410b612f6b565b614069565b6141279150843d86116107ba576107ab81836129c5565b5f614058565b67ffffffffffffffff81116129505760051b60200190565b60209081818403126100155780519067ffffffffffffffff821161001557019180601f8401121561001557825161417b8161412d565b9361418960405195866129c5565b818552838086019260051b820101928311610015578301905b8282106141b0575050505090565b815181529083019083016141a2565b60209060206040818301928281528551809452019301915f5b8281106141e6575050505090565b8351855293810193928101926001016141d8565b8051156142075760200190565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52603260045260245ffd5b919061429261157c9160405160208101918252600260408201526040815261425b816129a9565b5190206040519283916020830195866020909392919373ffffffffffffffffffffffffffffffffffffffff60408201951681520152565b5190209060018201809211612a6f576142f95f916142c473ffffffffffffffffffffffffffffffffffffffff946152e4565b6040519485809481937f7784c685000000000000000000000000000000000000000000000000000000008352600483016141bf565b0392165afa801561047b5761431b61433191610a9b935f91613b4757506141fa565b516fffffffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff1690565b81810292918115918404141715612a6f57565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b8115614390570490565b614359565b8015613ecb576040517f6e553f6500000000000000000000000000000000000000000000000000000000815260048101919091523060248201526020816044815f73d4fa2d31b7968e448877f69a96de69f5de8cd23e5af1801561047b57614464575b50614401613b69565b8015613ecb5761440f613a6b565b61443561442d61441d612f6b565b936144288585612a62565b614346565b612710900490565b918183111561445f5761444e61445a92613ecb94612fde565b90818082109118021890565b615309565b505050565b61447c9060203d6020116107ba576107ab81836129c5565b505f6143f8565b908160e0910312610015576040519060e0820182811067ffffffffffffffff821117612950576145199160c0916040526144bc81613c78565b84526144ca60208201613c78565b60208501526144db60408201613c78565b60408501526144ec60608201613c78565b60608501526144fd60808201613c78565b608085015261450e60a08201613c78565b60a085015201613c78565b60c082015290565b61018060c061185593959461458c846101a081019860a0908173ffffffffffffffffffffffffffffffffffffffff9182815116855282602082015116602086015282604082015116604086015282606082015116606086015260808101516080860152015116910152565b80516fffffffffffffffffffffffffffffffff908116858401526020820151811660e086015260408201518116610100860152606082015181166101208601526080820151811661014086015260a08201511661016085015201516fffffffffffffffffffffffffffffffff16910152565b9190916fffffffffffffffffffffffffffffffff80809416911601918211612a6f57565b60c082206040517f5c60e39a00000000000000000000000000000000000000000000000000000000815260048101919091529173ffffffffffffffffffffffffffffffffffffffff9160e0908490602490829086165afa92831561047b575f936149b6575b5082906146b36146ad61433160808501516fffffffffffffffffffffffffffffffff1690565b42612fde565b92831515908161497d575b81614951575b50614751575b50516fffffffffffffffffffffffffffffffff16905060208201516fffffffffffffffffffffffffffffffff1692614732606061471a60408601516fffffffffffffffffffffffffffffffff1690565b9401516fffffffffffffffffffffffffffffffff1690565b6fffffffffffffffffffffffffffffffff928316948316938316921690565b6147b5918161477f6106286106286060602096015173ffffffffffffffffffffffffffffffffffffffff1690565b906040518095819482937f2a944b5500000000000000000000000000000000000000000000000000000000845260048401614521565b03915afa90811561047b5761483d91614801915f91614932575b506147fb60408601946147f561433187516fffffffffffffffffffffffffffffffff1690565b926153fb565b90615443565b9161482761480e8461545a565b82516fffffffffffffffffffffffffffffffff166145fe565b6fffffffffffffffffffffffffffffffff169052565b61487b61486561484c8361545a565b84516fffffffffffffffffffffffffffffffff166145fe565b6fffffffffffffffffffffffffffffffff168352565b6fffffffffffffffffffffffffffffffff6148a960a08401516fffffffffffffffffffffffffffffffff1690565b16806148b7575b82906146ca565b6148cd670de0b6b3a76400009161492b93614346565b0461482761480e6149266148fd846148f861433189516fffffffffffffffffffffffffffffffff1690565b612fde565b93602087019461492061433187516fffffffffffffffffffffffffffffffff1690565b916154a1565b61545a565b5f806148b0565b61494b915060203d6020116107ba576107ab81836129c5565b5f6147cf565b9050614974606083015173ffffffffffffffffffffffffffffffffffffffff1690565b1615155f6146c4565b90506fffffffffffffffffffffffffffffffff6149ad60408501516fffffffffffffffffffffffffffffffff1690565b161515906146be565b6149d991935060e03d60e0116149e0575b6149d181836129c5565b810190614483565b915f614687565b503d6149c7565b6040517f095ea7b3000000000000000000000000000000000000000000000000000000006020820181815273ffffffffffffffffffffffffffffffffffffffff851660248401527fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff604480850191909152835290939192917fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe091614a8c6064866129c5565b5f8073ffffffffffffffffffffffffffffffffffffffff86169287519082855af190614ab6613ee8565b82614b33575b5081614b28575b5015614ad1575b5050505050565b604051602081019590955273ffffffffffffffffffffffffffffffffffffffff1660248501525f6044850152614b1e93614b1991614b13908260648101613485565b826155df565b6155df565b5f80808080614aca565b90503b15155f614ac3565b80519192508115918215614b4b575b5050905f614abc565b614b5e9250602080918301019101612a20565b5f80614b42565b15614b6c57565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f42656c6f77206d696e696d756d206465706f73697400000000000000000000006044820152fd5b15614bd157565b60846040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602f60248201527f555344333a204f6e6c792073656c66206f722077686974656c6973746564206460448201527f65706f7369747320616c6c6f77656400000000000000000000000000000000006064820152fd5b939192909260ff7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005460401c1615614d6157613ecb9461053d9373ffffffffffffffffffffffffffffffffffffffff80809316947fffffffffffffffffffffffff000000000000000000000000000000000000000086815f5416175f553090600154161760015581614d22604051998a987f97073ae60000000000000000000000000000000000000000000000000000000060208b015260248a015260a060448a015260c4890190611183565b951660648701521660848501521660a4830152037fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe081018352826129c5565b60046040517fd7e6bcf8000000000000000000000000000000000000000000000000000000008152fd5b80614e03575b50614d9a613a6b565b614da2612f6b565b90614dc161442d614db38484612a62565b614dbb613b69565b90614346565b9182821115614dd85750613ecb916140f891612fde565b81831180614dfa575b614dea57505050565b61444e61445a92613ecb94612fde565b50801515614de1565b6040517f6e553f6500000000000000000000000000000000000000000000000000000000815260048101919091523060248201526020816044815f73d4fa2d31b7968e448877f69a96de69f5de8cd23e5af1801561047b5715614d9157614e789060203d6020116107ba576107ab81836129c5565b505f614d91565b919060018101809111612a6f57620f42408201809211612a6f57610a9b92615649565b5f9073ffffffffffffffffffffffffffffffffffffffff80603a5416604051602081019182527fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b4604082015260408152614efb816129a9565b51902080547fd2841a5d2692465040bd5e06a6f3b37483952c866e0f304dc0e03f76a1f8a0b291825491808611614f6a575b85900390558390039055603a5460405192835216907fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90602090a3565b945084614f2d565b91610120919493610140840195614fd08560a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b60c08501525f60e085015273ffffffffffffffffffffffffffffffffffffffff80921661010085015216910152565b9161012091949361014084019561505d8560a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b5f60c086015260e085015273ffffffffffffffffffffffffffffffffffffffff80921661010085015216910152565b8015612c59576150b461062860325473ffffffffffffffffffffffffffffffffffffffff1690565b803b15610015576040905f825180927f1e4711cf0000000000000000000000000000000000000000000000000000000082528183816151426004820160345473ffffffffffffffffffffffffffffffffffffffff908116825260355481166020830152603654811660408301526037548116606083015260385460808301526039541660a082015260c00190565b03925af1801561047b576152d1575b5061516e61515d613c95565b909491928186188287100290911890565b80156152c8578082189181119190910218928361518c575b50505090565b82908410615239576151f45f926151bb61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b9083519485809481937fa0b16cc70000000000000000000000000000000000000000000000000000000083523090309060048501614fff565b03925af1801561047b5761520e575b50505b5f8080615186565b8161522d92903d10615232575b61522581836129c5565b810190612f55565b615203565b503d61521b565b505061525d61062860325473ffffffffffffffffffffffffffffffffffffffff1690565b81805180927fa0b16cc7000000000000000000000000000000000000000000000000000000008252815f8161529730308b60048501614f72565b03925af1801561047b576152ad575b5050615206565b816152c392903d106152325761522581836129c5565b6152a6565b50505050505f90565b80610f8e6152de9261293c565b5f615151565b604051906152f18261298d565b6001825260203681840137615305826141fa565b5290565b8015612c59575f604073ffffffffffffffffffffffffffffffffffffffff603254166101648251809481937f370160a00000000000000000000000000000000000000000000000000000000083526153ab6004840160a073ffffffffffffffffffffffffffffffffffffffff80603454168352806035541660208401528060365416604084015280603754166060840152603854608084015260395416910152565b8760c48401528160e484015230610104840152610140610124840152816101448401525af1801561047b576153de575090565b6153f69060403d6040116152325761522581836129c5565b505090565b9061540591614346565b671bc16d674ec800006154188280614346565b046729a2241af62c000061542c8383614346565b04908201809211612a6f578101809111612a6f5790565b670de0b6b3a76400009161545691614346565b0490565b6fffffffffffffffffffffffffffffffff90818111615477571690565b60046040517f6acc9c8f000000000000000000000000000000000000000000000000000000008152fd5b91620f42408101809111612a6f5760018201809211612a6f57610a9b92615649565b61552a6040516154d28161298d565b5f806020948584527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c65648685015285815191018273a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485af1615524613ee8565b9061571b565b8051908282159283156155c7575b505050156155435750565b608490604051907f08c379a00000000000000000000000000000000000000000000000000000000082526004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152fd5b6155d79350820181019101612a20565b5f8281615538565b60405161552a9173ffffffffffffffffffffffffffffffffffffffff166156058261298d565b5f806020958685527f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c656487860152868151910182855af1615643613ee8565b916157f4565b9061438691614346565b9161565e82846156d9565b92909384156156cc57848311156156bf5790829109815f038216809204600280826003021880830282030280830282030280830282030280830282030280830282030280920290030293600183805f03040190848311900302920304170290565b821560030260111861570b565b505090610a9b9250614386565b907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8183099102908180821091030391565b634e487b715f526020526024601cfd5b909190156157aa575080511561572e5790565b73a0b86991c6218b36c1d19d4a2e9eb0ce3606eb483b1561574c5790565b60646040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152fd5b8151156157ba5750805190602001fd5b6157f0906040519182917f08c379a000000000000000000000000000000000000000000000000000000000835260048301611d81565b0390fd5b919290156158115750815115615808575090565b3b1561574c5790565b8251909150156157ba5750805190602001fd
Loading...
Loading
Loading...
Loading
Net Worth in USD
$0.00
Net Worth in ETH
0
Multichain Portfolio | 35 Chains
| Chain | Token | Portfolio % | Price | Amount | Value |
|---|
Loading...
Loading
Loading...
Loading
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.