Source Code
Overview
ETH Balance
0 ETH
ETH Value
$0.00View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Cross-Chain Transactions
Loading...
Loading
This contract may be a proxy contract. Click on More Options and select Is this a proxy? to confirm and enable the "Read as Proxy" & "Write as Proxy" tabs.
Contract Name:
Router
Compiler Version
v0.8.20+commit.a1b79de6
Optimization Enabled:
Yes with 200 runs
Other Settings:
shanghai EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import {Math} from "openzeppelin-math/Math.sol";
import {IERC3156FlashBorrower} from "openzeppelin-contracts/interfaces/IERC3156FlashBorrower.sol";
import {SafeERC20, IERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {AccessManagedUpgradeable} from "openzeppelin-contracts-upgradeable/access/manager/AccessManagedUpgradeable.sol";
import {PausableUpgradeable} from "openzeppelin-contracts-upgradeable/utils/PausableUpgradeable.sol";
import {RayMath} from "../libraries/RayMath.sol";
import {IRouter} from "../interfaces/IRouter.sol";
import {Dispatcher} from "./Dispatcher.sol";
/**
* @title Router contract
* @author Spectra Finance
* @notice Handles executions of complex sequences of actions in the Spectra protocol.
*/
contract Router is
Dispatcher,
AccessManagedUpgradeable,
PausableUpgradeable,
IRouter,
IERC3156FlashBorrower
{
using SafeERC20 for IERC20;
using Math for uint256;
/** @dev Maximum amount of tokens for which balance can be tracked in _previewRate(). */
uint256 private constant MAX_INVOLVED_TOKENS = 30;
/** @dev Expected return value from borrowers onFlashLoan function. */
bytes32 private immutable ON_FLASH_LOAN = keccak256("ERC3156FlashBorrower.onFlashLoan");
/* Events
*********************************************************************************************************/
event RouterUtilChange(address indexed previousRouterUtil, address indexed newRouterUtil);
event KyberRouterChange(address indexed previousKyberRouter, address indexed newKyberRouter);
/* Modifiers
*********************************************************************************************************/
modifier checkDeadline(uint256 deadline) {
if (block.timestamp > deadline) revert TransactionDeadlinePassed();
_;
}
/* Constructor
*********************************************************************************************************/
constructor(address _registry) Dispatcher(_registry) {
_disableInitializers(); // using this so that the deployed logic contract later cannot be initialized.
}
/* Initializer
*********************************************************************************************************/
function initialize(
address _routerUtil,
address _kyberRouter,
address _initialAuthority
) external initializer {
__Dispatcher_init(_routerUtil, _kyberRouter);
__AccessManaged_init(_initialAuthority);
}
/* Setters
*********************************************************************************************************/
/**
* @inheritdoc IRouter
*/
function pause() external override restricted {
_pause();
}
/**
* @inheritdoc IRouter
*/
function unPause() external override restricted {
_unpause();
}
/**
* @inheritdoc IRouter
*/
function setRouterUtil(address _routerUtil) external override restricted {
if (_routerUtil == address(0)) {
revert AddressError();
}
emit RouterUtilChange(routerUtil, _routerUtil);
routerUtil = _routerUtil;
}
/**
* @inheritdoc IRouter
*/
function setKyberRouter(address _kyberRouter) external override restricted {
emit KyberRouterChange(kyberRouter, _kyberRouter);
kyberRouter = _kyberRouter;
}
/* Getters
*********************************************************************************************************/
/**
* @inheritdoc IRouter
*/
function getRegistry() external view override returns (address) {
return registry;
}
/**
* @inheritdoc IRouter
*/
function getRouterUtil() external view override returns (address) {
return routerUtil;
}
/**
* @inheritdoc IRouter
*/
function getKyberRouter() external view override returns (address) {
return kyberRouter;
}
/* Executions
*********************************************************************************************************/
/**
* @inheritdoc IRouter
*/
function execute(
bytes calldata _commands,
bytes[] calldata _inputs,
uint256 _deadline
) external payable override checkDeadline(_deadline) {
execute(_commands, _inputs);
}
/**
* @inheritdoc IRouter
*/
function execute(
bytes calldata _commands,
bytes[] calldata _inputs
) public payable override whenNotPaused {
uint256 numCommands = _commands.length;
if (_inputs.length != numCommands) {
revert LengthMismatch();
}
// Relying on msg.sender is problematic as it changes during a flash loan.
// Thus, it's necessary to track who initiated the original Router execution.
bool topLevel;
if (msgSender == address(0)) {
msgSender = msg.sender;
topLevel = true;
msgValue = msg.value;
} else if (msg.sender != address(this)) {
revert UnauthorizedReentrantCall();
}
// loop through all given commands, execute them and pass along outputs as defined
for (uint256 commandIndex; commandIndex < numCommands; ) {
bytes1 command = _commands[commandIndex];
bytes calldata input = _inputs[commandIndex];
_dispatch(command, input);
unchecked {
commandIndex++;
}
}
if (topLevel) {
// top-level reset
msgSender = address(0);
msgValue = 0;
}
}
/* Previews
*********************************************************************************************************/
/**
* @dev Simulates the execution of a sequence of commands and returns the expected resulting rate
* @param _commands Encoded instructions passed to the dispatcher
* @param _inputs An array of byte strings containing ABI-encoded inputs for each command
* @param _spot If set to true, spot exchange rate is used for swaps. Additionally for all commands,
* input amounts are disregarded, and one unit of the token of interest is used instead.
* If set to false, the function includes price impact and curve pool fees for swaps.
* @return The preview rate value, which represents the amount of output token obtained at the end of execution
* for each wei of input token spent at the start of execution, multiplied by 1 ray unit.
*/
function _previewRate(
bytes calldata _commands,
bytes[] calldata _inputs,
bool _spot
) internal view whenNotPaused returns (uint256) {
uint256 numCommands = _commands.length;
if (_inputs.length != numCommands) {
revert LengthMismatch();
}
TokenBalance[] memory balances = new TokenBalance[](MAX_INVOLVED_TOKENS);
uint256 rate = RayMath.RAY_UNIT;
// loop through all given commands, execute them and pass along outputs as defined
for (uint256 commandIndex; commandIndex < numCommands; ) {
bytes1 command = _commands[commandIndex];
bytes calldata input = _inputs[commandIndex];
uint256 commandRate = _dispatchPreviewRate(command, input, _spot, balances);
if (commandRate != RayMath.RAY_UNIT) {
rate = rate.mulDiv(commandRate, RayMath.RAY_UNIT);
}
unchecked {
commandIndex++;
}
}
return rate;
}
/**
* @inheritdoc IRouter
*/
function previewRate(
bytes calldata _commands,
bytes[] calldata _inputs
) external view override returns (uint256) {
return _previewRate(_commands, _inputs, false);
}
/**
* @inheritdoc IRouter
*/
function previewSpotRate(
bytes calldata _commands,
bytes[] calldata _inputs
) external view override returns (uint256) {
return _previewRate(_commands, _inputs, true);
}
/* Flashloans
*********************************************************************************************************/
/**
* @inheritdoc IERC3156FlashBorrower
*/
function onFlashLoan(
address /* initiator */,
address _token,
uint256 _amount,
uint256 _fee,
bytes calldata _data
) external returns (bytes32) {
if (msgSender == address(0)) {
revert DirectOnFlashloanCall();
}
if (msg.sender != flashloanLender) {
revert UnauthorizedOnFlashloanCaller();
}
(bytes memory commands, bytes[] memory inputs) = abi.decode(_data, (bytes, bytes[]));
this.execute(commands, inputs); // https://ethereum.stackexchange.com/questions/103437/converting-bytes-memory-to-bytes-calldata
uint256 repayAmount = _amount + _fee;
uint256 allowance = IERC20(_token).allowance(address(this), msg.sender);
if (allowance < repayAmount) {
// Approve the lender to pull the funds if needed
IERC20(_token).forceApprove(msg.sender, repayAmount);
}
uint256 balance = IERC20(_token).balanceOf(address(this));
if (balance < repayAmount) {
// Collect remaining debt from the original sender if needed
IERC20(_token).safeTransferFrom(msgSender, address(this), repayAmount - balance);
}
return ON_FLASH_LOAN;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
// https://github.com/Uniswap/universal-router/blob/main/contracts/interfaces/IUniversalRouter.sol
interface IRouter {
/// @notice Thrown when executing commands with an expired deadline
error TransactionDeadlinePassed();
/// @notice Thrown when attempting to execute commands and an incorrect number of inputs are provided
error LengthMismatch();
/// @notice Thrown when onFlashloan() is called directly, rather than through a command execution
error DirectOnFlashloanCall();
/// @notice Thrown when onFlashloan() is called by an address other than flashloan lender
error UnauthorizedOnFlashloanCaller();
/// @notice Thrown when an address other than msgSender and Router reenters execute()
error UnauthorizedReentrantCall();
/**
* @notice Toggle Pause
* @dev Should only be called in extraordinary situations by the admin of the contract
* @dev See {PausableUpgradeable-_pause}
*/
function pause() external;
/**
* @notice Toggle UnPause
* @dev Should only be called in extraordinary situations by the admin of the contract
* @dev See {PausableUpgradeable-_unpause}
*/
function unPause() external;
/**
* @notice Getter for the registry
* @return The address of the registry
*/
function getRegistry() external view returns (address);
/**
* @dev Getter for the router utility contract
* @return The address of the router utility contract
*/
function getRouterUtil() external view returns (address);
/**
* @dev Getter for the Kyberswap Router
* @return The address of the Kyberswap Router
*/
function getKyberRouter() external view returns (address);
/**
* @dev Setter for the router utility contract
* @param _routerUtil The new address of the router utility contract
*/
function setRouterUtil(address _routerUtil) external;
/**
* @dev Setter for the Kyberswap Router
* @param _kyberRouter The new address of the Kyberswap Router
*/
function setKyberRouter(address _kyberRouter) external;
/**
* @dev Executes encoded commands along with provided inputs
* Reverts if deadline has expired
* @param _commands A set of concatenated commands, each 1 byte in length
* @param _inputs An array of byte strings containing ABI-encoded inputs for each command
* @param _deadline The deadline by which the transaction must be executed
*/
function execute(
bytes calldata _commands,
bytes[] calldata _inputs,
uint256 _deadline
) external payable;
/**
* @dev Executes encoded commands along with provided inputs
* @param _commands A set of concatenated commands, each 1 byte in length
* @param _inputs An array of byte strings containing ABI-encoded inputs for each command
*/
function execute(bytes calldata _commands, bytes[] calldata _inputs) external payable;
/**
* @dev Simulates encoded commands along with provided inputs and returns the resulting rate
* The rate is calculated as follows: rate = ray_unit * output_token_amount / input_token_amount
* @param _commands A set of concatenated commands, each 1 byte in length
* @param _inputs An array of byte strings containing ABI-encoded inputs for each command
* @return The preview rate value, which represents the amount of output token obtained at the end of execution
* for each wei of input token spent at the start of execution, multiplied by 1 ray unit.
*/
function previewRate(
bytes calldata _commands,
bytes[] calldata _inputs
) external view returns (uint256);
/**
* @dev Simulates encoded commands along with provided inputs and returns the resulting spot rate.
* As opposed to `previewRate`, spot exchange rates will be used for swaps. Additionally for all commands,
* input amounts are disregarded, and one unit of the token of interest is used instead.
* @param _commands A set of concatenated commands, each 1 byte in length
* @param _inputs An array of byte strings containing ABI-encoded inputs for each command
* @return The preview spot rate value, which represents the amount of output token obtained at the end of execution
* for each wei of input token spent at the start of execution, multiplied by 1 ray unit.
*/
function previewSpotRate(
bytes calldata _commands,
bytes[] calldata _inputs
) external view returns (uint256);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import {Math} from "openzeppelin-math/Math.sol";
import {IERC20Metadata} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {IERC4626} from "openzeppelin-contracts/interfaces/IERC4626.sol";
import {IERC3156FlashLender} from "openzeppelin-contracts/interfaces/IERC3156FlashLender.sol";
import {SafeCast} from "openzeppelin-contracts/utils/math/SafeCast.sol";
import {CurvePoolUtil} from "../../libraries/CurvePoolUtil.sol";
import {ICurvePool} from "../../interfaces/ICurvePool.sol";
import {IStableSwapNG} from "../../interfaces/IStableSwapNG.sol";
import {ICurveNGPool} from "../../interfaces/ICurveNGPool.sol";
import {IPrincipalToken} from "../../interfaces/IPrincipalToken.sol";
import {Constants} from "../Constants.sol";
/**
* @title Router Util contract
* @author Spectra Finance
* @notice Provides miscellaneous utils and preview functions related to Router executions.
*/
contract RouterUtil {
using Math for uint256;
using SafeCast for uint256;
using SafeCast for int256;
error InvalidTokenIndex(uint256 i, uint256 j);
error PoolLiquidityError();
error UnsufficientAmountForFlashFee();
error ResultNotFound();
/**
* @dev Gives the spot exchange rate of token i in terms of token j. Exchange rate is in 18 decimals
* @dev To be used with Curve Cryptoswap pools
* @param _curvePool PT/IBT curve pool
* @param _i token index, either 0 or 1
* @param _j token index, either 0 or 1, must be different than _i
* @return The spot exchange rate of _i in terms of _j
*/
function spotExchangeRate(
address _curvePool,
uint256 _i,
uint256 _j
) public view returns (uint256) {
if (_i == 0 && _j == 1) {
return
CurvePoolUtil.CURVE_UNIT.mulDiv(
CurvePoolUtil.CURVE_UNIT,
ICurvePool(_curvePool).last_prices()
);
} else if (_i == 1 && _j == 0) {
return ICurvePool(_curvePool).last_prices();
} else {
revert InvalidTokenIndex(_i, _j);
}
}
/**
* @dev Gives the spot exchange rate of token i in terms of token j. Exchange rate is in 18 decimals
* @dev To be used with Curve Stableswap pools
* @param _curvePool PT/IBT curve pool
* @param _i token index, either 0 or 1
* @param _j token index, either 0 or 1, must be different than _i
* @return The spot exchange rate of _i in terms of _j
*/
function spotExchangeRateSNG(
address _curvePool,
int128 _i,
int128 _j
) public view returns (uint256) {
uint256 last_prices = IStableSwapNG(_curvePool).last_price(0);
uint256[] memory stored_rates = IStableSwapNG(_curvePool).stored_rates();
if (_i == 0 && _j == 1) {
last_prices = stored_rates[0].mulDiv(CurvePoolUtil.CURVE_UNIT, stored_rates[1]).mulDiv(
CurvePoolUtil.CURVE_UNIT,
last_prices
);
return last_prices;
} else if (_i == 1 && _j == 0) {
last_prices = last_prices.mulDiv(stored_rates[1], stored_rates[0]);
return last_prices;
} else {
revert InvalidTokenIndex(uint256(uint128(_i)), uint256(uint128(_j)));
}
}
/**
* @dev To be used with Curve Cryptoswap pools
* @dev Gives the upper bound of the interval to perform bisection search in previewFlashSwapExactIBTForYT().
* @param _inputIBTAmount amount of IBT exchanged for YT
* @param _curvePool PT/IBT curve pool
* @return The upper bound for search interval in root finding algorithms
*/
function convertIBTToYTSpot(
uint256 _inputIBTAmount,
address _curvePool
) public view returns (uint256) {
// The spot exchange rate between IBT and YT is evaluated using the tokenization equation without fees.
// This equation reads: ptRate = 1 PT + 1 YT .
address pt = ICurvePool(_curvePool).coins(1);
uint256 ibtRate = IPrincipalToken(pt).getIBTRate(); // Ray
uint256 ptRate = IPrincipalToken(pt).getPTRate(); // Ray
uint256 ptInUnderlyingRay = spotExchangeRate(_curvePool, 1, 0).mulDiv(
ibtRate,
CurvePoolUtil.CURVE_UNIT
);
if (ptInUnderlyingRay > ptRate) {
revert PoolLiquidityError();
}
uint256 ytInUnderlyingRay = ptRate - ptInUnderlyingRay;
return _inputIBTAmount.mulDiv(ibtRate, ytInUnderlyingRay);
}
/**
* @dev Returns the maximal amount of YT one can obtain with a given amount of IBT (i.e without fees or slippage).
* @dev To be used with Curve Stableswap NG pools
* @dev Gives the upper bound of the interval to perform bisection search in previewFlashSwapExactIBTForYT().
* @param _inputIBTAmount amount of IBT exchanged for YT
* @param _curvePool PT/IBT curve pool
* @return The upper bound for search interval in root finding algorithms
*/
function convertIBTToYTSpotSNG(
uint256 _inputIBTAmount,
address _curvePool
) public view returns (uint256) {
// The spot exchange rate between IBT and YT is evaluated using the tokenization equation without fees.
// This equation reads: ptRate = 1 PT + 1 YT .
address pt = ICurvePool(_curvePool).coins(1);
uint256 ibtRate = IPrincipalToken(pt).getIBTRate(); // Ray
uint256 ptRate = IPrincipalToken(pt).getPTRate(); // Ray
uint256 ptInUnderlyingRay = spotExchangeRateSNG(_curvePool, 1, 0).mulDiv(
ibtRate,
CurvePoolUtil.CURVE_UNIT
);
if (ptInUnderlyingRay > ptRate) {
revert PoolLiquidityError();
}
uint256 ytInUnderlyingRay = ptRate - ptInUnderlyingRay;
return _inputIBTAmount.mulDiv(ibtRate, ytInUnderlyingRay);
}
/* PREVIEW FUNCTIONS FOR CURVE CRYPTOSWAP POOLS
*****************************************************************************************************************/
/**
* @dev Computes the amount of IBT required to buy a given output amount of YT.
* @dev Works for both Cryptoswap
* @param _curvePool PT/IBT curve pool
* @param _outputYTAmount desired output YT token amount
* @return inputIBTAmount The amount of IBT needed for obtaining the defined amount of YT
* @return borrowedIBTAmount the quantity of IBT borrowed to execute that swap
*/
function previewFlashSwapIBTToExactYT(
address _curvePool,
uint256 _outputYTAmount
) public view returns (uint256 inputIBTAmount, uint256 borrowedIBTAmount) {
// Tokens
address pt = ICurvePool(_curvePool).coins(1);
address ibt = IPrincipalToken(pt).getIBT();
// Units and rates
uint256 ibtRate = IPrincipalToken(pt).getIBTRate(); // Ray
uint256 ptRate = IPrincipalToken(pt).getPTRate(); // Ray
// Outputs
uint256 swapPTForIBT = ICurvePool(_curvePool).get_dy(1, 0, _outputYTAmount);
// y PT:YT = (x IBT * ((UNIT - tokenizationFee) / UNIT) * ibtRate) / ptRate
// <=> x IBT = (y PT:YT * ptRate * UNIT) / (ibtRate * (UNIT - tokenizationFee))
borrowedIBTAmount = (_outputYTAmount * ptRate * Constants.UNIT).ceilDiv(
ibtRate * (Constants.UNIT - IPrincipalToken(pt).getTokenizationFee())
);
if (swapPTForIBT > borrowedIBTAmount) {
revert PoolLiquidityError();
}
inputIBTAmount =
borrowedIBTAmount +
_getFlashFee(pt, ibt, borrowedIBTAmount) -
swapPTForIBT;
}
/**
* @dev Computes the amount of IBT required to buy a given output amount of YT.
* @dev Works for both Stableswap NG pools
* @param _curvePool PT/IBT curve pool
* @param _outputYTAmount desired output YT token amount
* @return inputIBTAmount The amount of IBT needed for obtaining the defined amount of YT
* @return borrowedIBTAmount the quantity of IBT borrowed to execute that swap
*/
function previewFlashSwapIBTToExactYTSNG(
address _curvePool,
uint256 _outputYTAmount
) public view returns (uint256 inputIBTAmount, uint256 borrowedIBTAmount) {
// Tokens
address pt = ICurvePool(_curvePool).coins(1);
address ibt = IPrincipalToken(pt).getIBT();
// Units and rates
uint256 ibtRate = IPrincipalToken(pt).getIBTRate(); // Ray
uint256 ptRate = IPrincipalToken(pt).getPTRate(); // Ray
// Outputs
uint256 swapPTForIBT = IStableSwapNG(_curvePool).get_dy(1, 0, _outputYTAmount);
// y PT:YT = (x IBT * ((UNIT - tokenizationFee) / UNIT) * ibtRate) / ptRate
// <=> x IBT = (y PT:YT * ptRate * UNIT) / (ibtRate * (UNIT - tokenizationFee))
borrowedIBTAmount = (_outputYTAmount * ptRate * Constants.UNIT).ceilDiv(
ibtRate * (Constants.UNIT - IPrincipalToken(pt).getTokenizationFee())
);
if (swapPTForIBT > borrowedIBTAmount) {
revert PoolLiquidityError();
}
inputIBTAmount =
borrowedIBTAmount +
_getFlashFee(pt, ibt, borrowedIBTAmount) -
swapPTForIBT;
}
/**
* @dev Approximates the expected output amount of YT corresponding to a given input amount of IBT.
* @dev To be used with Curve Cryptoswap pools
* @dev May return an output YT amount that corresponds to an input IBT amount lower than the given _inputIBTAmount.
* @dev This function can be expensive to execute and should only be called off-chain. Avoid using it within a transaction.
* @param _curvePool PT/IBT curve pool
* @param _inputIBTAmount amount of IBT exchanged for YT
* @return ytAmount The guess of YT obtained for the given amount of IBT
* @return borrowedIBTAmount The quantity of IBT borrowed to execute that swap.
*/
function previewFlashSwapExactIBTToYT(
address _curvePool,
uint256 _inputIBTAmount
) public view returns (uint256 ytAmount, uint256 borrowedIBTAmount) {
// initial guesses
address pt = ICurvePool(_curvePool).coins(1);
uint256 x0 = IPrincipalToken(pt).previewDepositIBT(_inputIBTAmount);
uint256 x1 = convertIBTToYTSpot(_inputIBTAmount, _curvePool);
uint256 ibtUnit = getUnit(ICurvePool(_curvePool).coins(0));
// Use secant method to approximate ytAmount
for (uint256 i = 0; i < Constants.MAX_ITERATIONS_SECANT; ++i) {
if (
_delta(x0, x1).mulDiv(ibtUnit, Math.max(x0, x1)) <
ibtUnit / Constants.PRECISION_DIVISOR
) {
break;
}
(uint256 inputIBTAmount0, ) = previewFlashSwapIBTToExactYT(_curvePool, x0);
(uint256 inputIBTAmount1, ) = previewFlashSwapIBTToExactYT(_curvePool, x1);
int256 answer0 = inputIBTAmount0.toInt256() - _inputIBTAmount.toInt256();
int256 answer1 = inputIBTAmount1.toInt256() - _inputIBTAmount.toInt256();
if (answer0 == answer1) {
break;
}
// x2 = x1 - (f(x1) * (x1 - x0) / (f(x1) - f(x0)))
// x0, x1 = x1, x2
uint256 x2 = (x1.toInt256() -
((answer1 * (x1.toInt256() - x0.toInt256())) / (answer1 - answer0))).toUint256();
x0 = x1;
x1 = x2;
}
ytAmount = Math.min(x0, x1);
uint256 resInputIBTAmount;
(resInputIBTAmount, borrowedIBTAmount) = previewFlashSwapIBTToExactYT(_curvePool, ytAmount);
// Run linear search if inputIBTAmount corresponding to ytAmount is higher than requested
if (resInputIBTAmount > _inputIBTAmount) {
// linear search
uint256 sf = Constants.SCALING_FACTOR_LINEAR_SEARCH;
for (uint256 i = 0; i < Constants.MAX_ITERATIONS_LINEAR_SEARCH; ++i) {
ytAmount = ytAmount.mulDiv(sf - 1, sf);
(resInputIBTAmount, borrowedIBTAmount) = previewFlashSwapIBTToExactYT(
_curvePool,
ytAmount
);
if (resInputIBTAmount <= _inputIBTAmount) {
break;
}
}
}
// if result is still higher or too far from requested value
if (
resInputIBTAmount > _inputIBTAmount ||
_delta(_inputIBTAmount, resInputIBTAmount).mulDiv(ibtUnit, _inputIBTAmount) >
ibtUnit / Constants.PRECISION_DIVISOR
) {
revert ResultNotFound();
}
}
/**
* @dev Approximates the expected output amount of YT corresponding to a given input amount of IBT.
* @dev To be used with Curve Stableswap NG pools
* @dev May return an output YT amount that corresponds to an input IBT amount lower than the given _inputIBTAmount.
* @dev This function can be expensive to execute and should only be called off-chain. Avoid using it within a transaction.
* @param _curvePool PT/IBT curve pool
* @param _inputIBTAmount amount of IBT exchanged for YT
* @return ytAmount The guess of YT obtained for the given amount of IBT
* @return borrowedIBTAmount The quantity of IBT borrowed to execute that swap.
*/
function previewFlashSwapExactIBTToYTSNG(
address _curvePool,
uint256 _inputIBTAmount
) public view returns (uint256 ytAmount, uint256 borrowedIBTAmount) {
// initial guesses
address pt = IStableSwapNG(_curvePool).coins(1);
uint256 x0 = IPrincipalToken(pt).previewDepositIBT(_inputIBTAmount);
uint256 x1 = convertIBTToYTSpotSNG(_inputIBTAmount, _curvePool);
uint256 ibtUnit = getUnit(ICurvePool(_curvePool).coins(0));
// Use secant method to approximate ytAmount
for (uint256 i = 0; i < Constants.MAX_ITERATIONS_SECANT; ++i) {
if (
_delta(x0, x1).mulDiv(ibtUnit, Math.max(x0, x1)) <
ibtUnit / Constants.PRECISION_DIVISOR
) {
break;
}
(uint256 inputIBTAmount0, ) = previewFlashSwapIBTToExactYTSNG(_curvePool, x0);
(uint256 inputIBTAmount1, ) = previewFlashSwapIBTToExactYTSNG(_curvePool, x1);
int256 answer0 = inputIBTAmount0.toInt256() - _inputIBTAmount.toInt256();
int256 answer1 = inputIBTAmount1.toInt256() - _inputIBTAmount.toInt256();
if (answer0 == answer1) {
break;
}
// x2 = x1 - (f(x1) * (x1 - x0) / (f(x1) - f(x0)))
// x0, x1 = x1, x2
uint256 x2 = (x1.toInt256() -
((answer1 * (x1.toInt256() - x0.toInt256())) / (answer1 - answer0))).toUint256();
x0 = x1;
x1 = x2;
}
ytAmount = Math.min(x0, x1);
uint256 resInputIBTAmount;
(resInputIBTAmount, borrowedIBTAmount) = previewFlashSwapIBTToExactYTSNG(
_curvePool,
ytAmount
);
// Run linear search if inputIBTAmount corresponding to ytAmount is higher than requested
if (resInputIBTAmount > _inputIBTAmount) {
// linear search
uint256 sf = Constants.SCALING_FACTOR_LINEAR_SEARCH;
for (uint256 i = 0; i < Constants.MAX_ITERATIONS_LINEAR_SEARCH; ++i) {
ytAmount = ytAmount.mulDiv(sf - 1, sf);
(resInputIBTAmount, borrowedIBTAmount) = previewFlashSwapIBTToExactYTSNG(
_curvePool,
ytAmount
);
if (resInputIBTAmount <= _inputIBTAmount) {
break;
}
}
}
// if result is still higher or too far from requested value
if (
resInputIBTAmount > _inputIBTAmount ||
_delta(_inputIBTAmount, resInputIBTAmount).mulDiv(ibtUnit, _inputIBTAmount) >
ibtUnit / Constants.PRECISION_DIVISOR
) {
revert ResultNotFound();
}
}
/**
* @dev Given an amount of YT, previews the amount of IBT received after exchange
* @dev To be used with Curve Cryptoswap pools
* @param _curvePool PT/IBT curve pool
* @param inputYTAmount amount of YT exchanged for IBT
* @return The amount of IBT obtained for the given amount of YT
* @return The amount of IBT borrowed to execute that swap.
*/
function previewFlashSwapExactYTToIBT(
address _curvePool,
uint256 inputYTAmount
) public view returns (uint256, uint256) {
// Tokens
address pt = ICurvePool(_curvePool).coins(1);
address ibt = IPrincipalToken(pt).getIBT();
// Units and Rates
uint256 ibtRate = IPrincipalToken(pt).getIBTRate();
uint256 ptRate = IPrincipalToken(pt).getPTRate();
// Outputs
uint256 borrowedIBTAmount = CurvePoolUtil.getDx(_curvePool, 0, 1, inputYTAmount);
uint256 inputYTAmountInIBT = inputYTAmount.mulDiv(ptRate, ibtRate);
uint256 flashFee = _getFlashFee(pt, ibt, borrowedIBTAmount);
if (borrowedIBTAmount > inputYTAmountInIBT) {
revert PoolLiquidityError();
} else if (borrowedIBTAmount + flashFee > inputYTAmountInIBT) {
revert UnsufficientAmountForFlashFee();
}
uint256 outputIBTAmount = inputYTAmountInIBT - borrowedIBTAmount - flashFee;
return (outputIBTAmount, borrowedIBTAmount);
}
/**
* @dev Given an amount of YT, previews the amount of IBT received after exchange
* @dev To be used with Curve StableSwap NG pools
* @param _curvePool PT/IBT curve pool
* @param inputYTAmount amount of YT exchanged for IBT
* @return The amount of IBT obtained for the given amount of YT
* @return The amount of IBT borrowed to execute that swap.
*/
function previewFlashSwapExactYTToIBTSNG(
address _curvePool,
uint256 inputYTAmount
) public view returns (uint256, uint256) {
// Tokens
address pt = ICurvePool(_curvePool).coins(1);
address ibt = IPrincipalToken(pt).getIBT();
// Units and Rates
uint256 ibtRate = IPrincipalToken(pt).getIBTRate();
uint256 ptRate = IPrincipalToken(pt).getPTRate();
// Outputs
uint256 borrowedIBTAmount = IStableSwapNG(_curvePool).get_dx(0, 1, inputYTAmount);
uint256 inputYTAmountInIBT = inputYTAmount.mulDiv(ptRate, ibtRate);
uint256 flashFee = _getFlashFee(pt, ibt, borrowedIBTAmount);
if (borrowedIBTAmount > inputYTAmountInIBT) {
revert PoolLiquidityError();
} else if (borrowedIBTAmount + flashFee > inputYTAmountInIBT) {
revert UnsufficientAmountForFlashFee();
}
uint256 outputIBTAmount = inputYTAmountInIBT - borrowedIBTAmount - flashFee;
return (outputIBTAmount, borrowedIBTAmount);
}
/**
* @notice Given an amount of asset, previews the amount of lp tokens received after depositing in the curve pool
* @notice To be used with Curve Cryptoswap pools
* @param _curvePool address of the curve pool
* @param _assets amount of assets to deposit into the curve pool
* @return minMintAmount amount of lp tokens received
*/
function previewAddLiquidityWithAsset(
address _curvePool,
uint256 _assets
) public view returns (uint256 minMintAmount) {
address ibt = ICurvePool(_curvePool).coins(0);
uint256 ibts = IERC4626(ibt).previewDeposit(_assets);
minMintAmount = previewAddLiquidityWithIBT(_curvePool, ibts);
}
/**
* @notice Given an amount of asset, previews the amount of lp tokens received after depositing in the curve pool
* @notice To be used with Curve Stableswap NG pools
* @param _curvePool address of the curve pool
* @param _assets amount of assets to deposit into the curve pool
* @return minMintAmount amount of lp tokens received
*/
function previewAddLiquidityWithAssetSNG(
address _curvePool,
uint256 _assets
) public view returns (uint256 minMintAmount) {
address ibt = ICurvePool(_curvePool).coins(0);
uint256 ibts = IERC4626(ibt).previewDeposit(_assets);
minMintAmount = previewAddLiquidityWithIBTSNG(_curvePool, ibts);
}
/**
* @notice Given an amount of ibt, previews the amount of lp tokens received after depositing in the curve pool
* @notice To be used with Curve Cryptoswap pools
* @param _curvePool address of the curve pool
* @param _ibts amount of ibt to deposit into the curve pool
* @return minMintAmount amount of lp tokens received
*/
function previewAddLiquidityWithIBT(
address _curvePool,
uint256 _ibts
) public view returns (uint256 minMintAmount) {
address pt = ICurvePool(_curvePool).coins(1);
uint256 ibtToDepositInPT = CurvePoolUtil.calcIBTsToTokenizeForCurvePool(
_ibts,
_curvePool,
pt
);
uint256 amount0 = _ibts - ibtToDepositInPT;
uint256 amount1 = IPrincipalToken(pt).previewDepositIBT(ibtToDepositInPT);
minMintAmount = previewAddLiquidity(_curvePool, [amount0, amount1]);
}
/**
* @notice Given an amount of ibt, previews the amount of lp tokens received after depositing in the curve pool
* @notice To be used with Curve Stableswap NG pools
* @param _curvePool address of the curve pool
* @param _ibts amount of ibt to deposit into the curve pool
* @return minMintAmount amount of lp tokens received
*/
function previewAddLiquidityWithIBTSNG(
address _curvePool,
uint256 _ibts
) public view returns (uint256 minMintAmount) {
address pt = IStableSwapNG(_curvePool).coins(1);
uint256 ibtToDepositInPT = CurvePoolUtil.calcIBTsToTokenizeForCurvePool(
_ibts,
_curvePool,
pt
);
uint256 amount0 = _ibts - ibtToDepositInPT;
uint256 amount1 = IPrincipalToken(pt).previewDepositIBT(ibtToDepositInPT);
uint256[] memory amounts = new uint256[](2);
amounts[0] = amount0;
amounts[1] = amount1;
minMintAmount = previewAddLiquiditySNG(_curvePool, amounts);
}
/**
* @notice Given an amount of ibts and pts, previews the amount of lp tokens received after depositing in the curve pool
* @notice To be used with Curve Cryptoswap pools
* @param _curvePool address of the curve pool
* @param _amounts array of length two containing the amount of ibt and pt to deposit into the pool respectively
* @return minMintAmount amount of lp tokens received
*/
function previewAddLiquidity(
address _curvePool,
uint256[2] memory _amounts
) public view returns (uint256 minMintAmount) {
minMintAmount = CurvePoolUtil.previewAddLiquidity(_curvePool, _amounts);
}
/**
* @notice Given an amount of ibts and pts, previews the amount of lp tokens received after depositing in the curve pool
* @notice To be used with Curve Stableswap NG pools
* @param _curvePool address of the curve pool
* @param _amounts array of length two containing the amount of ibt and pt to deposit into the pool respectively
* @return minMintAmount amount of lp tokens received
*/
function previewAddLiquiditySNG(
address _curvePool,
uint256[] memory _amounts
) public view returns (uint256 minMintAmount) {
minMintAmount = CurvePoolUtil.previewAddLiquiditySNG(_curvePool, _amounts);
}
/**
* @notice Given an amount of lp tokens, previews the amount of asset received after withdrawing from the curve pool
* @notice To be used with Curve Cryptoswap and Stableswap NG pools
* @param _curvePool address of the curve pool
* @param _lpAmount amount of lp tokens to withdraw from the curve pool
* @return assets amount of asset received
*/
function previewRemoveLiquidityForAsset(
address _curvePool,
uint256 _lpAmount
) public view returns (uint256 assets) {
uint256[2] memory minAmounts = CurvePoolUtil.previewRemoveLiquidity(_curvePool, _lpAmount);
assets =
IERC4626(ICurvePool(_curvePool).coins(0)).previewRedeem(minAmounts[0]) +
IPrincipalToken(ICurvePool(_curvePool).coins(1)).previewRedeem(minAmounts[1]);
}
/**
* @notice Given an amount of lp tokens, previews the amount of asset received after withdrawing from the curve pool
* @notice To be used with Curve Stableswap NG pools
* @param _curvePool address of the curve pool
* @param _lpAmount amount of lp tokens to withdraw from the curve pool
* @return assets amount of asset received
*/
function previewRemoveLiquidityForAssetSNG(
address _curvePool,
uint256 _lpAmount
) public view returns (uint256 assets) {
uint256[] memory minAmounts = CurvePoolUtil.previewRemoveLiquiditySNG(
_curvePool,
_lpAmount
);
assets =
IERC4626(ICurvePool(_curvePool).coins(0)).previewRedeem(minAmounts[0]) +
IPrincipalToken(ICurvePool(_curvePool).coins(1)).previewRedeem(minAmounts[1]);
}
/**
* @notice Given an amount of lp tokens, previews the amount of ibt received after withdrawing from the curve pool
* @notice To be used with Curve Cryptoswap
* @param _curvePool address of the curve pool
* @param _lpAmount amount of lp tokens to withdraw from the curve pool
* @return ibts amount of ibt received
*/
function previewRemoveLiquidityForIBT(
address _curvePool,
uint256 _lpAmount
) public view returns (uint256 ibts) {
uint256[2] memory minAmounts = CurvePoolUtil.previewRemoveLiquidity(_curvePool, _lpAmount);
ibts =
minAmounts[0] +
IPrincipalToken(ICurvePool(_curvePool).coins(1)).previewRedeemForIBT(minAmounts[1]);
}
/**
* @notice Given an amount of lp tokens, previews the amount of ibt received after withdrawing from the curve pool
* @notice To be used with Curve Stableswap NG pools
* @param _curvePool address of the curve pool
* @param _lpAmount amount of lp tokens to withdraw from the curve pool
* @return ibts amount of ibt received
*/
function previewRemoveLiquidityForIBTSNG(
address _curvePool,
uint256 _lpAmount
) public view returns (uint256 ibts) {
uint256[] memory minAmounts = CurvePoolUtil.previewRemoveLiquiditySNG(
_curvePool,
_lpAmount
);
ibts =
minAmounts[0] +
IPrincipalToken(ICurvePool(_curvePool).coins(1)).previewRedeemForIBT(minAmounts[1]);
}
/**
* @notice Given an amount of lp tokens, previews the amount of ibt and pt received after withdrawing from the curve pool
* @notice To be used with Curve Cryptoswap and Stableswap NG pools
* @param _curvePool address of the curve pool
* @param _lpAmount amount of lp tokens to withdraw from the curve pool
* @return minAmounts array of length two cointaining the amount of ibt and pt received after withdrawing from the curve pool
*/
function previewRemoveLiquidity(
address _curvePool,
uint256 _lpAmount
) public view returns (uint256[2] memory minAmounts) {
minAmounts = CurvePoolUtil.previewRemoveLiquidity(_curvePool, _lpAmount);
}
/**
* @notice Given an amount of lp tokens, previews the amount of ibt and pt received after withdrawing from the curve pool
* @notice To be used with Stableswap NG pools
* @param _curvePool address of the curve pool
* @param _lpAmount amount of lp tokens to withdraw from the curve pool
* @return minAmounts array of length two cointaining the amount of ibt and pt received after withdrawing from the curve pool
*/
function previewRemoveLiquiditySNG(
address _curvePool,
uint256 _lpAmount
) public view returns (uint256[] memory minAmounts) {
minAmounts = CurvePoolUtil.previewRemoveLiquiditySNG(_curvePool, _lpAmount);
}
/**
* @notice Given an amount of lp tokens, previews the amount of token at index _i received after withdrawing from the curve pool
* @notice To be used with Curve Cryptoswap and pools
* @param _curvePool address of the curve pool
* @param _lpAmount amount of lp tokens to withdraw from the curve pool
* @param _i Index of the token to withdraw in
* @return minAmount amount of token at index _i after withdrawing from the curve pool
*/
function previewRemoveLiquidityOneCoin(
address _curvePool,
uint256 _lpAmount,
uint256 _i
) public view returns (uint256 minAmount) {
minAmount = CurvePoolUtil.previewRemoveLiquidityOneCoin(_curvePool, _lpAmount, _i);
}
/**
* @notice Given an amount of lp tokens, previews the amount of token at index _i received after withdrawing from the curve pool
* @notice To be used with Curve Stableswap NG pools
* @param _curvePool address of the curve pool
* @param _lpAmount amount of lp tokens to withdraw from the curve pool
* @param _i Index of the token to withdraw in
* @return minAmount amount of token at index _i after withdrawing from the curve pool
*/
function previewRemoveLiquidityOneCoinSNG(
address _curvePool,
uint256 _lpAmount,
int128 _i
) public view returns (uint256 minAmount) {
minAmount = CurvePoolUtil.previewRemoveLiquidityOneCoinSNG(_curvePool, _lpAmount, _i);
}
/* PREVIEW FUNCTIONS FOR CURVE TWOCRYPTO-NG POOLS
*****************************************************************************************************************/
/**
* @dev Computes the amount of IBT required to buy a given output amount of YT.
* @param _curvePool PT/IBT curve pool
* @param _outputYTAmount desired output YT token amount
* @return inputIBTAmount The amount of IBT needed for obtaining the defined amount of YT
* @return borrowedIBTAmount the quantity of IBT borrowed to execute that swap
*/
function previewNGFlashSwapIBTToExactYT(
address _curvePool,
uint256 _outputYTAmount
) public view returns (uint256 inputIBTAmount, uint256 borrowedIBTAmount) {
return previewFlashSwapIBTToExactYT(_curvePool, _outputYTAmount);
}
/**
* @dev Approximates the expected output amount of YT corresponding to a given input amount of IBT.
* @dev May return an output YT amount that corresponds to an input IBT amount lower than the given _inputIBTAmount.
* @dev This function can be expensive to execute and should only be called off-chain. Avoid using it within a transaction.
* @param _curvePool PT/IBT curve pool
* @param _inputIBTAmount amount of IBT exchanged for YT
* @return ytAmount The guess of YT obtained for the given amount of IBT
* @return borrowedIBTAmount The quantity of IBT borrowed to execute that swap.
*/
function previewNGFlashSwapExactIBTToYT(
address _curvePool,
uint256 _inputIBTAmount
) public view returns (uint256 ytAmount, uint256 borrowedIBTAmount) {
return previewFlashSwapExactIBTToYT(_curvePool, _inputIBTAmount);
}
/**
* @dev Given an amount of YT, previews the amount of IBT received after exchange
* @param _curvePool PT/IBT curve pool
* @param inputYTAmount amount of YT exchanged for IBT
* @return The amount of IBT obtained for the given amount of YT
* @return The amount of IBT borrowed to execute that swap.
*/
function previewNGFlashSwapExactYTToIBT(
address _curvePool,
uint256 inputYTAmount
) public view returns (uint256, uint256) {
// Tokens
address pt = ICurvePool(_curvePool).coins(1);
address ibt = IPrincipalToken(pt).getIBT();
// Units and Rates
uint256 ibtRate = IPrincipalToken(pt).getIBTRate();
uint256 ptRate = IPrincipalToken(pt).getPTRate();
// Outputs
uint256 borrowedIBTAmount = ICurveNGPool(_curvePool).get_dx(0, 1, inputYTAmount);
uint256 inputYTAmountInIBT = inputYTAmount.mulDiv(ptRate, ibtRate);
uint256 flashFee = _getFlashFee(pt, ibt, borrowedIBTAmount);
if (borrowedIBTAmount > inputYTAmountInIBT) {
revert PoolLiquidityError();
} else if (borrowedIBTAmount + flashFee > inputYTAmountInIBT) {
revert UnsufficientAmountForFlashFee();
}
uint256 outputIBTAmount = inputYTAmountInIBT - borrowedIBTAmount - flashFee;
return (outputIBTAmount, borrowedIBTAmount);
}
function previewNGAddLiquidityWithAsset(
address _curvePool,
uint256 _assets
) public view returns (uint256 minMintAmount) {
address ibt = ICurveNGPool(_curvePool).coins(0);
uint256 ibts = IERC4626(ibt).previewDeposit(_assets);
minMintAmount = previewNGAddLiquidityWithIBT(_curvePool, ibts);
}
function previewNGAddLiquidityWithIBT(
address _curvePool,
uint256 _ibts
) public view returns (uint256 minMintAmount) {
address pt = ICurveNGPool(_curvePool).coins(1);
uint256 ibtToDepositInPT = CurvePoolUtil.calcIBTsToTokenizeForCurvePool(
_ibts,
_curvePool,
pt
);
uint256 amount0 = _ibts - ibtToDepositInPT;
uint256 amount1 = IPrincipalToken(pt).previewDepositIBT(ibtToDepositInPT);
minMintAmount = CurvePoolUtil.previewAddLiquidityNG(_curvePool, [amount0, amount1]);
}
function previewNGAddLiquidity(
address _curvePool,
uint256[2] memory _amounts
) public view returns (uint256 minMintAmount) {
minMintAmount = CurvePoolUtil.previewAddLiquidityNG(_curvePool, _amounts);
}
function previewNGRemoveLiquidityForAsset(
address _curvePool,
uint256 _lpAmount
) public view returns (uint256 assets) {
uint256[2] memory minAmounts = CurvePoolUtil.previewRemoveLiquidityNG(
_curvePool,
_lpAmount
);
assets =
IERC4626(ICurveNGPool(_curvePool).coins(0)).previewRedeem(minAmounts[0]) +
IPrincipalToken(ICurveNGPool(_curvePool).coins(1)).previewRedeem(minAmounts[1]);
}
function previewNGRemoveLiquidityForIBT(
address _curvePool,
uint256 _lpAmount
) public view returns (uint256 ibts) {
uint256[2] memory minAmounts = CurvePoolUtil.previewRemoveLiquidityNG(
_curvePool,
_lpAmount
);
ibts =
minAmounts[0] +
IPrincipalToken(ICurvePool(_curvePool).coins(1)).previewRedeemForIBT(minAmounts[1]);
}
function previewNGRemoveLiquidity(
address _curvePool,
uint256 _lpAmount
) public view returns (uint256[2] memory minAmounts) {
minAmounts = CurvePoolUtil.previewRemoveLiquidityNG(_curvePool, _lpAmount);
}
function previewNGRemoveLiquidityOneCoin(
address _curvePool,
uint256 _lpAmount,
uint256 _i
) public view returns (uint256 minAmount) {
minAmount = CurvePoolUtil.previewRemoveLiquidityOneCoinNG(_curvePool, _lpAmount, _i);
}
/* PUBLIC UTILS
*****************************************************************************************************************/
/**
* @dev Returns the unit element of the underlying asset of a PT
* @param _pt address of Principal Token
* @return The unit of underlying asset
*/
function getPTUnderlyingUnit(address _pt) external view returns (uint256) {
return getUnit(IPrincipalToken(_pt).underlying());
}
/**
* @dev Returns the unit element of the token
* @param _token address of token
* @return The unit of asset
*/
function getUnit(address _token) public view returns (uint256) {
return 10 ** IERC20Metadata(_token).decimals();
}
/* INTERNAL FUNCTIONS
*****************************************************************************************************************/
/**
* @dev Calculates the flash loan fee for borrowing a given quantity of IBT
* @param _pt address of Principal Token
* @param _ibt address of Interest Bearing Token
* @param _borrowedIBTAmount amount of Interest Bearing Tokens that have been borrowed in the flash loan
* @return The amount of fees charged for flash loan
*/
function _getFlashFee(
address _pt,
address _ibt,
uint256 _borrowedIBTAmount
) internal view returns (uint256) {
return IERC3156FlashLender(_pt).flashFee(_ibt, _borrowedIBTAmount);
}
/**
* @dev abs(a, b)
* @param a some integer
* @param b some integer
* @return The absolute value of a - b
*/
function _delta(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a - b : b - a;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/AccessManaged.sol)
pragma solidity ^0.8.20;
import {IAuthority} from "@openzeppelin/contracts/access/manager/IAuthority.sol";
import {AuthorityUtils} from "@openzeppelin/contracts/access/manager/AuthorityUtils.sol";
import {IAccessManager} from "@openzeppelin/contracts/access/manager/IAccessManager.sol";
import {IAccessManaged} from "@openzeppelin/contracts/access/manager/IAccessManaged.sol";
import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol";
import {Initializable} from "../../proxy/utils/Initializable.sol";
/**
* @dev This contract module makes available a {restricted} modifier. Functions decorated with this modifier will be
* permissioned according to an "authority": a contract like {AccessManager} that follows the {IAuthority} interface,
* implementing a policy that allows certain callers to access certain functions.
*
* IMPORTANT: The `restricted` modifier should never be used on `internal` functions, judiciously used in `public`
* functions, and ideally only used in `external` functions. See {restricted}.
*/
abstract contract AccessManagedUpgradeable is Initializable, ContextUpgradeable, IAccessManaged {
/// @custom:storage-location erc7201:openzeppelin.storage.AccessManaged
struct AccessManagedStorage {
address _authority;
bool _consumingSchedule;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessManaged")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant AccessManagedStorageLocation = 0xf3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00;
function _getAccessManagedStorage() private pure returns (AccessManagedStorage storage $) {
assembly {
$.slot := AccessManagedStorageLocation
}
}
/**
* @dev Initializes the contract connected to an initial authority.
*/
function __AccessManaged_init(address initialAuthority) internal onlyInitializing {
__AccessManaged_init_unchained(initialAuthority);
}
function __AccessManaged_init_unchained(address initialAuthority) internal onlyInitializing {
_setAuthority(initialAuthority);
}
/**
* @dev Restricts access to a function as defined by the connected Authority for this contract and the
* caller and selector of the function that entered the contract.
*
* [IMPORTANT]
* ====
* In general, this modifier should only be used on `external` functions. It is okay to use it on `public`
* functions that are used as external entry points and are not called internally. Unless you know what you're
* doing, it should never be used on `internal` functions. Failure to follow these rules can have critical security
* implications! This is because the permissions are determined by the function that entered the contract, i.e. the
* function at the bottom of the call stack, and not the function where the modifier is visible in the source code.
* ====
*
* [WARNING]
* ====
* Avoid adding this modifier to the https://docs.soliditylang.org/en/v0.8.20/contracts.html#receive-ether-function[`receive()`]
* function or the https://docs.soliditylang.org/en/v0.8.20/contracts.html#fallback-function[`fallback()`]. These
* functions are the only execution paths where a function selector cannot be unambiguosly determined from the calldata
* since the selector defaults to `0x00000000` in the `receive()` function and similarly in the `fallback()` function
* if no calldata is provided. (See {_checkCanCall}).
*
* The `receive()` function will always panic whereas the `fallback()` may panic depending on the calldata length.
* ====
*/
modifier restricted() {
_checkCanCall(_msgSender(), _msgData());
_;
}
/// @inheritdoc IAccessManaged
function authority() public view virtual returns (address) {
AccessManagedStorage storage $ = _getAccessManagedStorage();
return $._authority;
}
/// @inheritdoc IAccessManaged
function setAuthority(address newAuthority) public virtual {
address caller = _msgSender();
if (caller != authority()) {
revert AccessManagedUnauthorized(caller);
}
if (newAuthority.code.length == 0) {
revert AccessManagedInvalidAuthority(newAuthority);
}
_setAuthority(newAuthority);
}
/// @inheritdoc IAccessManaged
function isConsumingScheduledOp() public view returns (bytes4) {
AccessManagedStorage storage $ = _getAccessManagedStorage();
return $._consumingSchedule ? this.isConsumingScheduledOp.selector : bytes4(0);
}
/**
* @dev Transfers control to a new authority. Internal function with no access restriction. Allows bypassing the
* permissions set by the current authority.
*/
function _setAuthority(address newAuthority) internal virtual {
AccessManagedStorage storage $ = _getAccessManagedStorage();
$._authority = newAuthority;
emit AuthorityUpdated(newAuthority);
}
/**
* @dev Reverts if the caller is not allowed to call the function identified by a selector. Panics if the calldata
* is less than 4 bytes long.
*/
function _checkCanCall(address caller, bytes calldata data) internal virtual {
AccessManagedStorage storage $ = _getAccessManagedStorage();
(bool immediate, uint32 delay) = AuthorityUtils.canCallWithDelay(
authority(),
caller,
address(this),
bytes4(data[0:4])
);
if (!immediate) {
if (delay > 0) {
$._consumingSchedule = true;
IAccessManager(authority()).consumeScheduledOp(caller, data);
$._consumingSchedule = false;
} else {
revert AccessManagedUnauthorized(caller);
}
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
* https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
*/
interface IERC4626 is IERC20, IERC20Metadata {
event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);
event Withdraw(
address indexed sender,
address indexed receiver,
address indexed owner,
uint256 assets,
uint256 shares
);
/**
* @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
*
* - MUST be an ERC-20 token contract.
* - MUST NOT revert.
*/
function asset() external view returns (address assetTokenAddress);
/**
* @dev Returns the total amount of the underlying asset that is “managed” by Vault.
*
* - SHOULD include any compounding that occurs from yield.
* - MUST be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT revert.
*/
function totalAssets() external view returns (uint256 totalManagedAssets);
/**
* @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToShares(uint256 assets) external view returns (uint256 shares);
/**
* @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
* scenario where all the conditions are met.
*
* - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
* - MUST NOT show any variations depending on the caller.
* - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
* - MUST NOT revert.
*
* NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
* “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
* from.
*/
function convertToAssets(uint256 shares) external view returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
* through a deposit call.
*
* - MUST return a limited value if receiver is subject to some deposit limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
* - MUST NOT revert.
*/
function maxDeposit(address receiver) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
* call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
* in the same transaction.
* - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
* deposit would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewDeposit(uint256 assets) external view returns (uint256 shares);
/**
* @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* deposit execution, and are accounted for during deposit.
* - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
* - MUST return a limited value if receiver is subject to some mint limit.
* - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
* - MUST NOT revert.
*/
function maxMint(address receiver) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
* current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
* in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
* same transaction.
* - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
* would be accepted, regardless if the user has enough tokens approved, etc.
* - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by minting.
*/
function previewMint(uint256 shares) external view returns (uint256 assets);
/**
* @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
*
* - MUST emit the Deposit event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
* execution, and are accounted for during mint.
* - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
* approving enough underlying tokens to the Vault contract, etc).
*
* NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
*/
function mint(uint256 shares, address receiver) external returns (uint256 assets);
/**
* @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxWithdraw(address owner) external view returns (uint256 maxAssets);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
* call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
* called
* in the same transaction.
* - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
* the withdrawal would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by depositing.
*/
function previewWithdraw(uint256 assets) external view returns (uint256 shares);
/**
* @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* withdraw execution, and are accounted for during withdraw.
* - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);
/**
* @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
* through a redeem call.
*
* - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
* - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
* - MUST NOT revert.
*/
function maxRedeem(address owner) external view returns (uint256 maxShares);
/**
* @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
* given current on-chain conditions.
*
* - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
* in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
* same transaction.
* - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
* redemption would be accepted, regardless if the user has enough shares, etc.
* - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
* - MUST NOT revert.
*
* NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
* share price or some other type of condition, meaning the depositor will lose assets by redeeming.
*/
function previewRedeem(uint256 shares) external view returns (uint256 assets);
/**
* @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
*
* - MUST emit the Withdraw event.
* - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
* redeem execution, and are accounted for during redeem.
* - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
* not having enough shares, etc).
*
* NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
* Those methods should be performed separately.
*/
function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard math utilities missing in the Solidity language.
*/
library Math {
/**
* @dev Muldiv operation overflow.
*/
error MathOverflowedMulDiv();
enum Rounding {
Floor, // Toward negative infinity
Ceil, // Toward positive infinity
Trunc, // Toward zero
Expand // Away from zero
}
/**
* @dev Returns the addition of two unsigned integers, with an overflow flag.
*/
function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
uint256 c = a + b;
if (c < a) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the subtraction of two unsigned integers, with an overflow flag.
*/
function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b > a) return (false, 0);
return (true, a - b);
}
}
/**
* @dev Returns the multiplication of two unsigned integers, with an overflow flag.
*/
function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
// Gas optimization: this is cheaper than requiring 'a' not being zero, but the
// benefit is lost if 'b' is also tested.
// See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
if (a == 0) return (true, 0);
uint256 c = a * b;
if (c / a != b) return (false, 0);
return (true, c);
}
}
/**
* @dev Returns the division of two unsigned integers, with a division by zero flag.
*/
function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a / b);
}
}
/**
* @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
*/
function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
unchecked {
if (b == 0) return (false, 0);
return (true, a % b);
}
}
/**
* @dev Returns the largest of two numbers.
*/
function max(uint256 a, uint256 b) internal pure returns (uint256) {
return a > b ? a : b;
}
/**
* @dev Returns the smallest of two numbers.
*/
function min(uint256 a, uint256 b) internal pure returns (uint256) {
return a < b ? a : b;
}
/**
* @dev Returns the average of two numbers. The result is rounded towards
* zero.
*/
function average(uint256 a, uint256 b) internal pure returns (uint256) {
// (a + b) / 2 can overflow.
return (a & b) + (a ^ b) / 2;
}
/**
* @dev Returns the ceiling of the division of two numbers.
*
* This differs from standard division with `/` in that it rounds towards infinity instead
* of rounding towards zero.
*/
function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
if (b == 0) {
// Guarantee the same behavior as in a regular Solidity division.
return a / b;
}
// (a + b - 1) / b can overflow on addition, so we distribute.
return a == 0 ? 0 : (a - 1) / b + 1;
}
/**
* @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or
* denominator == 0.
* @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by
* Uniswap Labs also under MIT license.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
unchecked {
// 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
// use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
// variables such that product = prod1 * 2^256 + prod0.
uint256 prod0 = x * y; // Least significant 256 bits of the product
uint256 prod1; // Most significant 256 bits of the product
assembly {
let mm := mulmod(x, y, not(0))
prod1 := sub(sub(mm, prod0), lt(mm, prod0))
}
// Handle non-overflow cases, 256 by 256 division.
if (prod1 == 0) {
// Solidity will revert if denominator == 0, unlike the div opcode on its own.
// The surrounding unchecked block does not change this fact.
// See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
return prod0 / denominator;
}
// Make sure the result is less than 2^256. Also prevents denominator == 0.
if (denominator <= prod1) {
revert MathOverflowedMulDiv();
}
///////////////////////////////////////////////
// 512 by 256 division.
///////////////////////////////////////////////
// Make division exact by subtracting the remainder from [prod1 prod0].
uint256 remainder;
assembly {
// Compute remainder using mulmod.
remainder := mulmod(x, y, denominator)
// Subtract 256 bit number from 512 bit number.
prod1 := sub(prod1, gt(remainder, prod0))
prod0 := sub(prod0, remainder)
}
// Factor powers of two out of denominator and compute largest power of two divisor of denominator.
// Always >= 1. See https://cs.stackexchange.com/q/138556/92363.
uint256 twos = denominator & (0 - denominator);
assembly {
// Divide denominator by twos.
denominator := div(denominator, twos)
// Divide [prod1 prod0] by twos.
prod0 := div(prod0, twos)
// Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
twos := add(div(sub(0, twos), twos), 1)
}
// Shift in bits from prod1 into prod0.
prod0 |= prod1 * twos;
// Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
// that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
// four bits. That is, denominator * inv = 1 mod 2^4.
uint256 inverse = (3 * denominator) ^ 2;
// Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also
// works in modular arithmetic, doubling the correct bits in each step.
inverse *= 2 - denominator * inverse; // inverse mod 2^8
inverse *= 2 - denominator * inverse; // inverse mod 2^16
inverse *= 2 - denominator * inverse; // inverse mod 2^32
inverse *= 2 - denominator * inverse; // inverse mod 2^64
inverse *= 2 - denominator * inverse; // inverse mod 2^128
inverse *= 2 - denominator * inverse; // inverse mod 2^256
// Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
// This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
// less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
// is no longer required.
result = prod0 * inverse;
return result;
}
}
/**
* @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
*/
function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
uint256 result = mulDiv(x, y, denominator);
if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) {
result += 1;
}
return result;
}
/**
* @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded
* towards zero.
*
* Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
*/
function sqrt(uint256 a) internal pure returns (uint256) {
if (a == 0) {
return 0;
}
// For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
//
// We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
// `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
//
// This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
// ? `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
// ? `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
//
// Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
uint256 result = 1 << (log2(a) >> 1);
// At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
// since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
// every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
// into the expected uint128 result.
unchecked {
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
result = (result + a / result) >> 1;
return min(result, a / result);
}
}
/**
* @notice Calculates sqrt(a), following the selected rounding direction.
*/
function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = sqrt(a);
return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0);
}
}
/**
* @dev Return the log in base 2 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log2(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 128;
}
if (value >> 64 > 0) {
value >>= 64;
result += 64;
}
if (value >> 32 > 0) {
value >>= 32;
result += 32;
}
if (value >> 16 > 0) {
value >>= 16;
result += 16;
}
if (value >> 8 > 0) {
value >>= 8;
result += 8;
}
if (value >> 4 > 0) {
value >>= 4;
result += 4;
}
if (value >> 2 > 0) {
value >>= 2;
result += 2;
}
if (value >> 1 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 2, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log2(value);
return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 10 of a positive value rounded towards zero.
* Returns 0 if given 0.
*/
function log10(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >= 10 ** 64) {
value /= 10 ** 64;
result += 64;
}
if (value >= 10 ** 32) {
value /= 10 ** 32;
result += 32;
}
if (value >= 10 ** 16) {
value /= 10 ** 16;
result += 16;
}
if (value >= 10 ** 8) {
value /= 10 ** 8;
result += 8;
}
if (value >= 10 ** 4) {
value /= 10 ** 4;
result += 4;
}
if (value >= 10 ** 2) {
value /= 10 ** 2;
result += 2;
}
if (value >= 10 ** 1) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 10, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log10(value);
return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0);
}
}
/**
* @dev Return the log in base 256 of a positive value rounded towards zero.
* Returns 0 if given 0.
*
* Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
*/
function log256(uint256 value) internal pure returns (uint256) {
uint256 result = 0;
unchecked {
if (value >> 128 > 0) {
value >>= 128;
result += 16;
}
if (value >> 64 > 0) {
value >>= 64;
result += 8;
}
if (value >> 32 > 0) {
value >>= 32;
result += 4;
}
if (value >> 16 > 0) {
value >>= 16;
result += 2;
}
if (value >> 8 > 0) {
result += 1;
}
}
return result;
}
/**
* @dev Return the log in base 256, following the selected rounding direction, of a positive value.
* Returns 0 if given 0.
*/
function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
unchecked {
uint256 result = log256(value);
return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0);
}
}
/**
* @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
*/
function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
return uint8(rounding) % 2 == 1;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";
/**
* @title SafeERC20
* @dev Wrappers around ERC20 operations that throw on failure (when the token
* contract returns false). Tokens that return no value (and instead revert or
* throw on failure) are also supported, non-reverting calls are assumed to be
* successful.
* To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
* which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
*/
library SafeERC20 {
using Address for address;
/**
* @dev An operation with an ERC20 token failed.
*/
error SafeERC20FailedOperation(address token);
/**
* @dev Indicates a failed `decreaseAllowance` request.
*/
error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);
/**
* @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeTransfer(IERC20 token, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value)));
}
/**
* @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
* calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
*/
function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
_callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value)));
}
/**
* @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful.
*/
function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
uint256 oldAllowance = token.allowance(address(this), spender);
forceApprove(token, spender, oldAllowance + value);
}
/**
* @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
* value, non-reverting calls are assumed to be successful.
*/
function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
unchecked {
uint256 currentAllowance = token.allowance(address(this), spender);
if (currentAllowance < requestedDecrease) {
revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
}
forceApprove(token, spender, currentAllowance - requestedDecrease);
}
}
/**
* @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
* non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
* to be set to zero before setting it to a non-zero value, such as USDT.
*/
function forceApprove(IERC20 token, address spender, uint256 value) internal {
bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value));
if (!_callOptionalReturnBool(token, approvalCall)) {
_callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0)));
_callOptionalReturn(token, approvalCall);
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*/
function _callOptionalReturn(IERC20 token, bytes memory data) private {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
// the target address contains contract code and also asserts for success in the low-level call.
bytes memory returndata = address(token).functionCall(data);
if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
revert SafeERC20FailedOperation(address(token));
}
}
/**
* @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
* on the return value: the return value is optional (but if data is returned, it must not be false).
* @param token The token targeted by the call.
* @param data The call data (encoded using abi.encode or one of its variants).
*
* This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
*/
function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
// We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
// we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
// and not revert is the subcall reverts.
(bool success, bytes memory returndata) = address(token).call(data);
return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0;
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/types/Time.sol)
pragma solidity ^0.8.20;
import {Math} from "../math/Math.sol";
import {SafeCast} from "../math/SafeCast.sol";
/**
* @dev This library provides helpers for manipulating time-related objects.
*
* It uses the following types:
* - `uint48` for timepoints
* - `uint32` for durations
*
* While the library doesn't provide specific types for timepoints and duration, it does provide:
* - a `Delay` type to represent duration that can be programmed to change value automatically at a given point
* - additional helper functions
*/
library Time {
using Time for *;
/**
* @dev Get the block timestamp as a Timepoint.
*/
function timestamp() internal view returns (uint48) {
return SafeCast.toUint48(block.timestamp);
}
/**
* @dev Get the block number as a Timepoint.
*/
function blockNumber() internal view returns (uint48) {
return SafeCast.toUint48(block.number);
}
// ==================================================== Delay =====================================================
/**
* @dev A `Delay` is a uint32 duration that can be programmed to change value automatically at a given point in the
* future. The "effect" timepoint describes when the transitions happens from the "old" value to the "new" value.
* This allows updating the delay applied to some operation while keeping some guarantees.
*
* In particular, the {update} function guarantees that if the delay is reduced, the old delay still applies for
* some time. For example if the delay is currently 7 days to do an upgrade, the admin should not be able to set
* the delay to 0 and upgrade immediately. If the admin wants to reduce the delay, the old delay (7 days) should
* still apply for some time.
*
*
* The `Delay` type is 112 bits long, and packs the following:
*
* ```
* | [uint48]: effect date (timepoint)
* | | [uint32]: value before (duration)
* ? ? ? [uint32]: value after (duration)
* 0xAAAAAAAAAAAABBBBBBBBCCCCCCCC
* ```
*
* NOTE: The {get} and {withUpdate} functions operate using timestamps. Block number based delays are not currently
* supported.
*/
type Delay is uint112;
/**
* @dev Wrap a duration into a Delay to add the one-step "update in the future" feature
*/
function toDelay(uint32 duration) internal pure returns (Delay) {
return Delay.wrap(duration);
}
/**
* @dev Get the value at a given timepoint plus the pending value and effect timepoint if there is a scheduled
* change after this timepoint. If the effect timepoint is 0, then the pending value should not be considered.
*/
function _getFullAt(Delay self, uint48 timepoint) private pure returns (uint32, uint32, uint48) {
(uint32 valueBefore, uint32 valueAfter, uint48 effect) = self.unpack();
return effect <= timepoint ? (valueAfter, 0, 0) : (valueBefore, valueAfter, effect);
}
/**
* @dev Get the current value plus the pending value and effect timepoint if there is a scheduled change. If the
* effect timepoint is 0, then the pending value should not be considered.
*/
function getFull(Delay self) internal view returns (uint32, uint32, uint48) {
return _getFullAt(self, timestamp());
}
/**
* @dev Get the current value.
*/
function get(Delay self) internal view returns (uint32) {
(uint32 delay, , ) = self.getFull();
return delay;
}
/**
* @dev Update a Delay object so that it takes a new duration after a timepoint that is automatically computed to
* enforce the old delay at the moment of the update. Returns the updated Delay object and the timestamp when the
* new delay becomes effective.
*/
function withUpdate(
Delay self,
uint32 newValue,
uint32 minSetback
) internal view returns (Delay updatedDelay, uint48 effect) {
uint32 value = self.get();
uint32 setback = uint32(Math.max(minSetback, value > newValue ? value - newValue : 0));
effect = timestamp() + setback;
return (pack(value, newValue, effect), effect);
}
/**
* @dev Split a delay into its components: valueBefore, valueAfter and effect (transition timepoint).
*/
function unpack(Delay self) internal pure returns (uint32 valueBefore, uint32 valueAfter, uint48 effect) {
uint112 raw = Delay.unwrap(self);
valueAfter = uint32(raw);
valueBefore = uint32(raw >> 32);
effect = uint48(raw >> 64);
return (valueBefore, valueAfter, effect);
}
/**
* @dev pack the components into a Delay object.
*/
function pack(uint32 valueBefore, uint32 valueAfter, uint48 effect) internal pure returns (Delay) {
return Delay.wrap((uint112(effect) << 64) | (uint112(valueBefore) << 32) | uint112(valueAfter));
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
// Based on https://github.com/Uniswap/universal-router/blob/main/contracts/libraries/Commands.sol
library Commands {
bytes1 internal constant COMMAND_TYPE_MASK = 0x3f;
/**
* Transfers tokens from msg.sender to the Router.
* (address token, uint256 value)
*/
uint256 constant TRANSFER_FROM = 0x00;
/**
* Transfers tokens from msg.sender to the Router with a permit.
* (address token, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s)
*/
uint256 constant TRANSFER_FROM_WITH_PERMIT = 0x01;
/**
* Transfers tokens from the Router to a recipient.
* (address token, address recipient, uint256 value)
*/
uint256 constant TRANSFER = 0x02;
/**
* Performs a swap on Curve CryptoSwap pool.
* (address pool, uint256 i, uint256 j, uint256 amountIn, uint256 minAmountOut, address recipient)
*/
uint256 constant CURVE_SWAP = 0x03;
/**
* Deposits an ERC20 underlying into an ERC4626 IBT
* ibt represents the target IBT and assets represents the amount of underlying to deposit
* (address ibt, uint256 assets, address recipient)
*/
uint256 constant DEPOSIT_ASSET_IN_IBT = 0x04;
/**
* Deposits an ERC20 underlying into a PT
* assets represents the amount of underlying to deposit
* (address pt, uint256 assets, address ptRecipient, address ytRecipient, uint256 minShares)
*/
uint256 constant DEPOSIT_ASSET_IN_PT = 0x05;
/**
* Deposits an ERC4626 IBT into a PT
* ibts represents the amount of IBT to deposit
* (address pt, uint256 ibts, address ptRecipient, address ytRecipient, uint256 minShares)
*/
uint256 constant DEPOSIT_IBT_IN_PT = 0x06;
/**
* Redeems an ERC4626 IBT for the corresponding ERC20 underlying
* ibt represents the target IBT and shares represents the amount of IBT to redeem
* (address ibt, uint256 shares, address recipient)
*/
uint256 constant REDEEM_IBT_FOR_ASSET = 0x07;
/**
* Redeems a PT:YT pair for the corresponding ERC20 underlying
* shares represents the amount of PT to redeem
* (address pt, uint256 shares, address recipient, uint256 minAssets)
*/
uint256 constant REDEEM_PT_FOR_ASSET = 0x08;
/**
* Redeems a PT:YT pair for the corresponding ERC4626 IBT
* shares represents the amount of PT to redeem
* (address pt, uint256 shares, address recipient, uint256 minIbts)
*/
uint256 constant REDEEM_PT_FOR_IBT = 0x09;
/**
* Performs a flash loan
* data represents the sequence of commands and inputs to be executed during the loan
* (address lender, address token, uint256 amount, bytes calldata data)
*/
uint256 constant FLASH_LOAN = 0x0a;
/**
* Splits liquidity between IBT and PT before depositing in a Curve CryptoSwap pool
* ibts represents the amount of IBT to split between IBT and PT before depositing in the pool
* recipient represents the address that will receive the IBT/PT
* ytRecipient represents the address that will receive the YTs generated by the split
* (address pool, uint256 ibts, address recipient, address ytRecipient, uint256 minPTShares)
*/
uint256 constant CURVE_SPLIT_IBT_LIQUIDITY = 0x0b;
/**
* Deposits coins into a Curve CryptoSwap pool
* amounts includes the amounts of IBT and PT to deposit in the pool
* min_mint_amount represents the minimum amount of LP tokens to mint
* (address pool, uint256[2] amounts, uint256 min_mint_amount, address recipient)
*/
uint256 constant CURVE_ADD_LIQUIDITY = 0x0c;
/**
* Withdraws coins from a Curve CryptoSwap pool
* lps represents the amount of LP tokens to burn
* min_amounts represents the minimum amount of coins to receive
* (address pool, uint256 lps, uint256[2] min_amounts, address recipient)
*/
uint256 constant CURVE_REMOVE_LIQUIDITY = 0x0d;
/**
* Withdraws a single coin from a Curve CryptoSwap pool
* lps represents the amount of LP tokens to burn
* i represents the index of the coin to withdraw
* min_amount represents the minimum amount of coin to receive
* (address pool, uint256 lps, uint256 i, uint256 min_amount, address recipient)
*/
uint256 constant CURVE_REMOVE_LIQUIDITY_ONE_COIN = 0x0e;
/**
* Performs a minimum balance check.
* (address token, address owner, uint256 minValue)
*/
uint256 constant ASSERT_MIN_BALANCE = 0x0f;
/**
* Wraps shares of an interest-bearing vault into an ERC-4626 Wrapper
* vaultShares represents the amount of vault shares to unwrap
* (address wrapper, uint256 vaultShares, address recipient)
*/
uint256 constant WRAP_VAULT_IN_4626_ADAPTER = 0x10;
/**
* Unwraps shares of an interest-bearing vault from an ERC-4626 Wrapper
* wrapperShares represents the amount of wrapper shares to redeem
* (address wrapper, uint256 wrapperShares, address recipient)
*/
uint256 constant UNWRAP_VAULT_FROM_4626_ADAPTER = 0x11;
/**
* Performs a swap on Kyberswap.
* (address tokenIn, uint256 amountIn, address tokenOut, uint256 expectedAmountOut, bytes targetData)
*/
uint256 constant KYBER_SWAP = 0x12;
/**
* Removes liquidity from Pendle.
* (address receiver, address market, uint256 netLpToRemove, TokenOutput calldata output, LimitOrderData calldata limit)
*/
uint256 constant PENDLE_REMOVE_LIQUIDITY_SINGLE_TOKEN = 0x13;
/**
* Performs a swap on a Curve TwoCrypto NG pool.
* (address pool, uint256 i, uint256 j, uint256 amountIn, uint256 minAmountOut, address recipient)
*/
uint256 constant CURVE_NG_SWAP = 0x15;
/**
* Splits liquidity between IBT and PT before depositing in a Curve TwoCrypto NG pool.
* ibts represents the amount of IBT to split between IBT and PT before depositing in the pool
* recipient represents the address that will receive the IBT/PT
* ytRecipient represents the address that will receive the YTs generated by the split
* (address pool, uint256 ibts, address recipient, address ytRecipient, uint256 minPTShares)
*/
uint256 constant CURVE_NG_SPLIT_IBT_LIQUIDITY = 0x16;
/**
* Deposits coins into a Curve TwoCrypto NG pool.
* amounts includes the amounts of IBT and PT to deposit in the pool
* min_mint_amount represents the minimum amount of LP tokens to mint
* (address pool, uint256[2] amounts, uint256 min_mint_amount, address recipient)
*/
uint256 constant CURVE_NG_ADD_LIQUIDITY = 0x17;
/**
* Withdraws coins from a Curve TwoCrypto NG pool.
* lps represents the amount of LP token shares to burn
* min_amounts represents the minimum amount of coins to receive
* (address pool, uint256 lps, uint256[2] min_amounts, address recipient)
*/
uint256 constant CURVE_NG_REMOVE_LIQUIDITY = 0x18;
/**
* Withdraws a single coin from a Curve TwoCrypto NG pool.
* lps represents the amount of LP token shares to burn
* i represents the index of the coin to withdraw
* min_amount represents the minimum amount of coin to receive
* (address pool, uint256 lps, uint256 i, uint256 min_amount, address recipient)
*/
uint256 constant CURVE_NG_REMOVE_LIQUIDITY_ONE_COIN = 0x19;
/**
* Splits liquidity between IBT and PT before depositing in a Curve Stableswap NG pool
* ibts represents the amount of IBT to split between IBT and PT before depositing in the pool
* recipient represents the address that will receive the IBT/PT
* ytRecipient represents the address that will receive the YTs generated by the split
* (address pool, uint256 ibts, address recipient, address ytRecipient, uint256 minPTShares)
*/
uint256 constant CURVE_SPLIT_IBT_LIQUIDITY_SNG = 0x1A;
/**
* Deposits coins into a Curve Stableswap NG pool
* amounts includes the amounts of IBT and PT to deposit in the pool
* min_mint_amount represents the minimum amount of LP tokens to mint
* (address pool, uint256[2] amounts, uint256 min_mint_amount, address recipient)
*/
uint256 constant CURVE_ADD_LIQUIDITY_SNG = 0x1B;
/**
* Withdraws coins from a Curve Stableswap NG pool
* lps represents the amount of LP tokens to burn
* min_amounts represents the minimum amount of coins to receive
* (address pool, uint256 lps, uint256[2] min_amounts, address recipient)
*/
uint256 constant CURVE_REMOVE_LIQUIDITY_SNG = 0x1C;
/**
* Withdraws a single coin from a Curve Stableswap NG pool
* lps represents the amount of LP tokens to burn
* i represents the index of the coin to withdraw
* min_amount represents the minimum amount of coin to receive
* (address pool, uint256 lps, uint256 i, uint256 min_amount, address recipient)
*/
uint256 constant CURVE_REMOVE_LIQUIDITY_ONE_COIN_SNG = 0x1D;
/**
* Performs a swap on Curve Stableswap NG pool.
* (address pool, uint256 i, uint256 j, uint256 amountIn, uint256 minAmountOut, address recipient)
*/
uint256 constant CURVE_SWAP_SNG = 0x1E;
/**
* Given a ratio in which we want to add liquidity to Curve legacy Cryptoswap pools, calculates the amount of IBTs to tokenize in PTs and YTs
* so that the ratio of the IBTs left after tokenization with the PTs obtained via tokenization mathes the
* proportion given in function call arguments.
* (address pool, uint256 ibts, uint256 prop, address recipient, address ytRecipient, uint256 minPTShares)
*/
uint256 constant CURVE_SPLIT_IBT_LIQUIDITY_CUSTOM_PROP = 0x1F;
/**
* Given a ratio in which we want to add liquidity to Curve NG Cryptoswap pools, calculates the amount of IBTs to tokenize in PTs and YTs
* so that the ratio of the IBTs left after tokenization with the PTs obtained via tokenization mathes the
* proportion given in function call arguments.
* (address pool, uint256 ibts, uint256 prop, address recipient, address ytRecipient, uint256 minPTShares)
*/
uint256 constant CURVE_SPLIT_IBT_LIQUIDITY_CUSTOM_PROP_NG = 0x20;
/**
* Given a ratio in which we want to add liquidity to Curve StableSwap pools, calculates the amount of IBTs to tokenize in PTs and YTs
* so that the ratio of the IBTs left after tokenization with the PTs obtained via tokenization mathes the
* proportion given in function call arguments.
* (address pool, uint256 ibts, uint256 prop, address recipient, address ytRecipient, uint256 minPTShares)
*/
uint256 constant CURVE_SPLIT_IBT_LIQUIDITY_CUSTOM_PROP_SNG = 0x21;
/**
* Deposits native token into a wrapper.
* (address wrapper, uint256 amount)
*/
uint256 constant DEPOSIT_NATIVE_IN_WRAPPER = 0x22;
/**
* Withdraws native token from a wrapper.
* (address wrapper, uint256 amount)
*/
uint256 constant WITHDRAW_NATIVE_FROM_WRAPPER = 0x23;
/**
* Transfers native token to a recipient.
* (address recipient, uint256 amount)
*/
uint256 constant TRANSFER_NATIVE = 0x24;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../token/ERC20/IERC20.sol";// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import {Math} from "openzeppelin-math/Math.sol";
import {IERC20Permit} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Permit.sol";
import {IERC3156FlashBorrower} from "openzeppelin-contracts/interfaces/IERC3156FlashBorrower.sol";
import {IERC3156FlashLender} from "openzeppelin-contracts/interfaces/IERC3156FlashLender.sol";
import {IERC4626} from "openzeppelin-contracts/interfaces/IERC4626.sol";
import {SafeERC20, IERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol";
import {Initializable} from "openzeppelin-contracts-upgradeable/proxy/utils/Initializable.sol";
import {Commands} from "./Commands.sol";
import {Constants} from "./Constants.sol";
import {RayMath} from "../libraries/RayMath.sol";
import {CurvePoolUtil} from "../libraries/CurvePoolUtil.sol";
import {ICurvePool} from "../interfaces/ICurvePool.sol";
import {IStableSwapNG} from "../interfaces/IStableSwapNG.sol";
import {ICurveNGPool} from "../interfaces/ICurveNGPool.sol";
import {IPrincipalToken} from "src/interfaces/IPrincipalToken.sol";
import {IRegistry} from "../interfaces/IRegistry.sol";
import {ISpectra4626Wrapper} from "../interfaces/ISpectra4626Wrapper.sol";
import {RouterUtil} from "./util/RouterUtil.sol";
import {INATIVE} from "../interfaces/INATIVE.sol";
abstract contract Dispatcher is Initializable {
using SafeERC20 for IERC20;
using Math for uint256;
using RayMath for uint256;
error InvalidCommandType(uint256 commandType);
error MinimumBalanceNotReached(
address token,
address owner,
uint256 minimumBalance,
uint256 actualBalance
);
error InvalidFlashloanLender(address lender);
error AddressError();
error AmountError();
error CallFailed();
error PermitFailed();
error MaxInvolvedTokensExceeded();
error BalanceUnderflow();
error KyberRouterNotSet();
// used for tracking balance changes in _previewRate
struct TokenBalance {
address token;
uint256 balance;
}
/** @dev registry of the protocol */
address internal immutable registry;
/** @dev used during a router execution to track the initiator of the execution */
address internal msgSender;
/** @dev used during a flashloan execution to track the lender address */
address internal flashloanLender;
/** @notice Router Util contract */
address internal routerUtil;
/** @notice Kyberswap Router */
address internal kyberRouter;
/** @dev used during a router execution to track the msg.value */
uint256 internal msgValue;
constructor(address _registry) {
if (_registry == address(0)) {
revert AddressError();
}
registry = _registry;
}
function __Dispatcher_init(
address _routerUtil,
address _kyberRouter
) internal onlyInitializing {
if (_routerUtil == address(0)) {
revert AddressError();
}
routerUtil = _routerUtil;
kyberRouter = _kyberRouter;
}
receive() external payable {}
/**
* @dev Executes a single command along with its encoded input data
* @param _commandType The encoded representation of the command
* @param _inputs The encoded arguments for the specified command
*/
function _dispatch(bytes1 _commandType, bytes calldata _inputs) internal {
uint256 command = uint8(_commandType & Commands.COMMAND_TYPE_MASK);
if (command == Commands.TRANSFER_FROM) {
(address token, uint256 value) = abi.decode(_inputs, (address, uint256));
IERC20(token).safeTransferFrom(msgSender, address(this), value);
} else if (command == Commands.TRANSFER_FROM_WITH_PERMIT) {
(address token, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) = abi
.decode(_inputs, (address, uint256, uint256, uint8, bytes32, bytes32));
try IERC20Permit(token).permit(msgSender, address(this), value, deadline, v, r, s) {
// Permit executed successfully, proceed
} catch {
// Check allowance to see if permit was already executed
uint256 allowance = IERC20(token).allowance(msgSender, address(this));
if (allowance < value) {
revert PermitFailed();
}
}
IERC20(token).safeTransferFrom(msgSender, address(this), value);
} else if (command == Commands.TRANSFER) {
(address token, address recipient, uint256 value) = abi.decode(
_inputs,
(address, address, uint256)
);
recipient = _resolveAddress(recipient);
value = _resolveTokenValue(token, value);
if (value != 0) {
IERC20(token).safeTransfer(recipient, value);
}
} else if (
command == Commands.CURVE_SWAP ||
command == Commands.CURVE_NG_SWAP ||
command == Commands.CURVE_SWAP_SNG
) {
(
address pool,
uint256 i,
uint256 j,
uint256 amountIn,
uint256 minAmountOut,
address recipient
) = abi.decode(_inputs, (address, uint256, uint256, uint256, uint256, address));
// pool.coins(i) is the token to be swapped
address token = ICurvePool(pool).coins(i);
amountIn = _resolveTokenValue(token, amountIn);
recipient = _resolveAddress(recipient);
IERC20(token).forceApprove(pool, amountIn);
if (command == Commands.CURVE_SWAP) {
ICurvePool(pool).exchange(
i,
j,
amountIn,
minAmountOut,
false, // Do not use ETH
recipient
);
} else if (command == Commands.CURVE_NG_SWAP) {
ICurveNGPool(pool).exchange(i, j, amountIn, minAmountOut, recipient);
} else {
IStableSwapNG(pool).exchange(
int128(int256(i)),
int128(int256(j)),
amountIn,
minAmountOut,
recipient
);
}
IERC20(token).forceApprove(pool, 0);
} else if (command == Commands.WRAP_VAULT_IN_4626_ADAPTER) {
(
address wrapper,
uint256 vaultShares,
address recipient,
uint256 minWrapperShares
) = abi.decode(_inputs, (address, uint256, address, uint256));
address vault = ISpectra4626Wrapper(wrapper).vaultShare();
recipient = _resolveAddress(recipient);
vaultShares = _resolveTokenValue(vault, vaultShares);
IERC20(vault).forceApprove(wrapper, vaultShares);
ISpectra4626Wrapper(wrapper).wrap(vaultShares, recipient, minWrapperShares);
IERC20(vault).forceApprove(wrapper, 0);
} else if (command == Commands.UNWRAP_VAULT_FROM_4626_ADAPTER) {
(
address wrapper,
uint256 wrapperShares,
address recipient,
uint256 minVaultShares
) = abi.decode(_inputs, (address, uint256, address, uint256));
recipient = _resolveAddress(recipient);
wrapperShares = _resolveTokenValue(wrapper, wrapperShares);
ISpectra4626Wrapper(wrapper).unwrap(
wrapperShares,
recipient,
address(this),
minVaultShares
);
} else if (command == Commands.DEPOSIT_ASSET_IN_IBT) {
(address ibt, uint256 assets, address recipient) = abi.decode(
_inputs,
(address, uint256, address)
);
address asset = IERC4626(ibt).asset();
assets = _resolveTokenValue(asset, assets);
recipient = _resolveAddress(recipient);
IERC20(asset).forceApprove(ibt, assets);
IERC4626(ibt).deposit(assets, recipient);
IERC20(asset).forceApprove(ibt, 0);
} else if (command == Commands.DEPOSIT_ASSET_IN_PT) {
(
address pt,
uint256 assets,
address ptRecipient,
address ytRecipient,
uint256 minShares
) = abi.decode(_inputs, (address, uint256, address, address, uint256));
address asset = IPrincipalToken(pt).underlying();
assets = _resolveTokenValue(asset, assets);
ptRecipient = _resolveAddress(ptRecipient);
ytRecipient = _resolveAddress(ytRecipient);
bool isRegisteredPT = IRegistry(registry).isRegisteredPT(pt);
if (isRegisteredPT) {
_ensureApproved(asset, pt, assets);
} else {
IERC20(asset).forceApprove(pt, assets);
}
IPrincipalToken(pt).deposit(assets, ptRecipient, ytRecipient, minShares);
if (!isRegisteredPT) {
IERC20(asset).forceApprove(pt, 0);
}
} else if (command == Commands.DEPOSIT_IBT_IN_PT) {
(
address pt,
uint256 ibts,
address ptRecipient,
address ytRecipient,
uint256 minShares
) = abi.decode(_inputs, (address, uint256, address, address, uint256));
address ibt = IPrincipalToken(pt).getIBT();
ibts = _resolveTokenValue(ibt, ibts);
ptRecipient = _resolveAddress(ptRecipient);
ytRecipient = _resolveAddress(ytRecipient);
bool isRegisteredPT = IRegistry(registry).isRegisteredPT(pt);
if (isRegisteredPT) {
_ensureApproved(ibt, pt, ibts);
} else {
IERC20(ibt).forceApprove(pt, ibts);
}
IPrincipalToken(pt).depositIBT(ibts, ptRecipient, ytRecipient, minShares);
if (!isRegisteredPT) {
IERC20(ibt).forceApprove(pt, 0);
}
} else if (command == Commands.REDEEM_IBT_FOR_ASSET) {
(address ibt, uint256 shares, address recipient) = abi.decode(
_inputs,
(address, uint256, address)
);
shares = _resolveTokenValue(ibt, shares);
recipient = _resolveAddress(recipient);
IERC4626(ibt).redeem(shares, recipient, address(this));
} else if (
command == Commands.REDEEM_PT_FOR_ASSET || command == Commands.REDEEM_PT_FOR_IBT
) {
(address pt, uint256 shares, address recipient, uint256 minOut) = abi.decode(
_inputs,
(address, uint256, address, uint256)
);
recipient = _resolveAddress(recipient);
shares = _resolveTokenValue(pt, shares);
uint256 redeemShares = block.timestamp < IPrincipalToken(pt).maturity()
? Math.min(shares, IERC20(IPrincipalToken(pt).getYT()).balanceOf(address(this)))
: shares;
if (command == Commands.REDEEM_PT_FOR_ASSET) {
IPrincipalToken(pt).redeem(redeemShares, recipient, address(this), minOut);
} else {
IPrincipalToken(pt).redeemForIBT(redeemShares, recipient, address(this), minOut);
}
} else if (command == Commands.FLASH_LOAN) {
(address lender, address token, uint256 amount, bytes memory data) = abi.decode(
_inputs,
(address, address, uint256, bytes)
);
if (!IRegistry(registry).isRegisteredPT(lender)) {
revert InvalidFlashloanLender(lender);
}
flashloanLender = lender;
IERC3156FlashLender(lender).flashLoan(
IERC3156FlashBorrower(address(this)),
token,
amount,
data
);
flashloanLender = address(0);
} else if (
command == Commands.CURVE_SPLIT_IBT_LIQUIDITY ||
command == Commands.CURVE_NG_SPLIT_IBT_LIQUIDITY ||
command == Commands.CURVE_SPLIT_IBT_LIQUIDITY_SNG
) {
(
address pool,
uint256 ibts,
address recipient,
address ytRecipient,
uint256 minPTShares
) = abi.decode(_inputs, (address, uint256, address, address, uint256));
recipient = _resolveAddress(recipient);
ytRecipient = _resolveAddress(ytRecipient);
address ibt = ICurvePool(pool).coins(0);
address pt = ICurvePool(pool).coins(1);
ibts = _resolveTokenValue(ibt, ibts);
uint256 ibtToDepositInPT = CurvePoolUtil.calcIBTsToTokenizeForCurvePool(ibts, pool, pt);
if (ibtToDepositInPT != 0) {
bool isRegisteredPT = IRegistry(registry).isRegisteredPT(pt);
if (isRegisteredPT) {
_ensureApproved(ibt, pt, ibtToDepositInPT);
} else {
IERC20(ibt).forceApprove(pt, ibtToDepositInPT);
}
IPrincipalToken(pt).depositIBT(
ibtToDepositInPT,
recipient,
ytRecipient,
minPTShares
);
if (!isRegisteredPT) {
IERC20(ibt).forceApprove(pt, 0);
}
}
if (recipient != address(this) && (ibts - ibtToDepositInPT) != 0) {
IERC20(ibt).safeTransfer(recipient, ibts - ibtToDepositInPT);
}
} else if (
command == Commands.CURVE_SPLIT_IBT_LIQUIDITY_CUSTOM_PROP ||
command == Commands.CURVE_SPLIT_IBT_LIQUIDITY_CUSTOM_PROP_NG ||
command == Commands.CURVE_SPLIT_IBT_LIQUIDITY_CUSTOM_PROP_SNG
) {
(
address pool,
uint256 ibts,
uint256 prop,
address recipient,
address ytRecipient,
uint256 minPTShares
) = abi.decode(_inputs, (address, uint256, uint256, address, address, uint256));
recipient = _resolveAddress(recipient);
ytRecipient = _resolveAddress(ytRecipient);
address ibt = ICurvePool(pool).coins(0);
address pt = ICurvePool(pool).coins(1);
ibts = _resolveTokenValue(ibt, ibts);
uint256 ibtToDepositInPT = CurvePoolUtil.calcIBTsToTokenizeForCurvePoolCustomProp(
ibts,
prop,
pt
);
if (ibtToDepositInPT != 0) {
bool isRegisteredPT = IRegistry(registry).isRegisteredPT(pt);
if (isRegisteredPT) {
_ensureApproved(ibt, pt, ibtToDepositInPT);
} else {
IERC20(ibt).forceApprove(pt, ibtToDepositInPT);
}
IPrincipalToken(pt).depositIBT(
ibtToDepositInPT,
recipient,
ytRecipient,
minPTShares
);
if (!isRegisteredPT) {
IERC20(ibt).forceApprove(pt, 0);
}
}
if (recipient != address(this) && (ibts - ibtToDepositInPT) != 0) {
IERC20(ibt).safeTransfer(recipient, ibts - ibtToDepositInPT);
}
} else if (
command == Commands.CURVE_ADD_LIQUIDITY || command == Commands.CURVE_NG_ADD_LIQUIDITY
) {
(
address pool,
uint256[2] memory amounts,
uint256 min_mint_amount,
address recipient
) = abi.decode(_inputs, (address, uint256[2], uint256, address));
recipient = _resolveAddress(recipient);
address ibt = ICurvePool(pool).coins(0);
address pt = ICurvePool(pool).coins(1);
amounts[0] = _resolveTokenValue(ibt, amounts[0]);
amounts[1] = _resolveTokenValue(pt, amounts[1]);
IERC20(ibt).forceApprove(pool, amounts[0]);
IERC20(pt).forceApprove(pool, amounts[1]);
(command == Commands.CURVE_ADD_LIQUIDITY)
? ICurvePool(pool).add_liquidity(amounts, min_mint_amount, false, recipient)
: ICurveNGPool(pool).add_liquidity(amounts, min_mint_amount, recipient);
IERC20(ibt).forceApprove(pool, 0);
IERC20(pt).forceApprove(pool, 0);
} else if (
command == Commands.CURVE_REMOVE_LIQUIDITY ||
command == Commands.CURVE_NG_REMOVE_LIQUIDITY
) {
(address pool, uint256 lps, uint256[2] memory min_amounts, address recipient) = abi
.decode(_inputs, (address, uint256, uint256[2], address));
recipient = _resolveAddress(recipient);
address lpToken = (command == Commands.CURVE_REMOVE_LIQUIDITY)
? ICurvePool(pool).token()
: pool;
lps = _resolveTokenValue(lpToken, lps);
(command == Commands.CURVE_REMOVE_LIQUIDITY)
? ICurvePool(pool).remove_liquidity(lps, min_amounts, false, recipient)
: ICurveNGPool(pool).remove_liquidity(lps, min_amounts, recipient);
} else if (
command == Commands.CURVE_NG_REMOVE_LIQUIDITY_ONE_COIN ||
command == Commands.CURVE_REMOVE_LIQUIDITY_ONE_COIN
) {
(address pool, uint256 lps, uint256 i, uint256 min_amount, address recipient) = abi
.decode(_inputs, (address, uint256, uint256, uint256, address));
recipient = _resolveAddress(recipient);
address lpToken = (command == Commands.CURVE_REMOVE_LIQUIDITY_ONE_COIN)
? ICurvePool(pool).token()
: pool;
lps = _resolveTokenValue(lpToken, lps);
(command == Commands.CURVE_REMOVE_LIQUIDITY_ONE_COIN)
? ICurvePool(pool).remove_liquidity_one_coin(lps, i, min_amount, false, recipient)
: ICurveNGPool(pool).remove_liquidity_one_coin(lps, i, min_amount, recipient);
} else if (command == Commands.KYBER_SWAP) {
if (kyberRouter == address(0)) {
revert KyberRouterNotSet();
}
(address tokenIn, uint256 amountIn, address tokenOut, , bytes memory targetData) = abi
.decode(_inputs, (address, uint256, address, uint256, bytes));
if (tokenOut == Constants.ETH) {
revert AddressError();
}
if (tokenIn == Constants.ETH) {
if (msgValue != amountIn) {
revert AmountError();
}
(bool success, ) = kyberRouter.call{value: msgValue}(targetData);
if (!success) {
revert CallFailed();
}
} else {
amountIn = _resolveTokenValue(tokenIn, amountIn);
IERC20(tokenIn).forceApprove(kyberRouter, amountIn);
(bool success, ) = kyberRouter.call(targetData);
if (!success) {
revert CallFailed();
}
IERC20(tokenIn).forceApprove(kyberRouter, 0);
}
} else if (command == Commands.ASSERT_MIN_BALANCE) {
(address token, address owner, uint256 minValue) = abi.decode(
_inputs,
(address, address, uint256)
);
owner = _resolveAddress(owner);
uint256 balance = IERC20(token).balanceOf(owner);
if (balance < minValue) {
revert MinimumBalanceNotReached(token, owner, minValue, balance);
}
} else if (command == Commands.CURVE_ADD_LIQUIDITY_SNG) {
(
address pool,
uint256[] memory amounts,
uint256 min_mint_amount,
address recipient
) = abi.decode(_inputs, (address, uint256[], uint256, address));
recipient = _resolveAddress(recipient);
address ibt = IStableSwapNG(pool).coins(0);
address pt = IStableSwapNG(pool).coins(1);
amounts[0] = _resolveTokenValue(ibt, amounts[0]);
amounts[1] = _resolveTokenValue(pt, amounts[1]);
IERC20(ibt).forceApprove(pool, amounts[0]);
IERC20(pt).forceApprove(pool, amounts[1]);
IStableSwapNG(pool).add_liquidity(amounts, min_mint_amount, recipient);
IERC20(ibt).forceApprove(pool, 0);
IERC20(pt).forceApprove(pool, 0);
} else if (command == Commands.CURVE_REMOVE_LIQUIDITY_SNG) {
(address pool, uint256 lps, uint256[] memory min_amounts, address recipient) = abi
.decode(_inputs, (address, uint256, uint256[], address));
recipient = _resolveAddress(recipient);
lps = _resolveTokenValue(pool, lps);
IStableSwapNG(pool).remove_liquidity(lps, min_amounts, recipient);
} else if (command == Commands.CURVE_REMOVE_LIQUIDITY_ONE_COIN_SNG) {
(address pool, uint256 lps, int128 i, uint256 min_amount, address recipient) = abi
.decode(_inputs, (address, uint256, int128, uint256, address));
recipient = _resolveAddress(recipient);
lps = _resolveTokenValue(pool, lps);
IStableSwapNG(pool).remove_liquidity_one_coin(lps, i, min_amount, recipient);
} else if (command == Commands.DEPOSIT_NATIVE_IN_WRAPPER) {
(address wrapper, uint256 amount) = abi.decode(_inputs, (address, uint256));
INATIVE(wrapper).deposit{value: amount}();
} else if (command == Commands.WITHDRAW_NATIVE_FROM_WRAPPER) {
(address wrapper, uint256 amount) = abi.decode(_inputs, (address, uint256));
INATIVE(wrapper).withdraw(amount);
} else if (command == Commands.TRANSFER_NATIVE) {
(address recipient, uint256 amount) = abi.decode(_inputs, (address, uint256));
(bool success, ) = payable(recipient).call{value: amount}("");
if (!success) {
revert CallFailed();
}
} else {
revert InvalidCommandType(command);
}
}
/**
* @dev Returns either the input token value as is, or replaced with its corresponding behaviour in Constants.sol
* @param _token The address of the token
* @param _value The token amount
* @return The amount stored previously if current amount used for detecting contract balance, else current value
*/
function _resolveTokenValue(address _token, uint256 _value) internal view returns (uint256) {
return
(_value == Constants.CONTRACT_BALANCE)
? IERC20(_token).balanceOf(address(this))
: _value;
}
/**
* @dev Returns either the input address as is, or replaced with its corresponding behaviour in Constants.sol
* @param _input The input address
* @return The address corresponding to input
*/
function _resolveAddress(address _input) internal view returns (address) {
if (_input == Constants.ADDRESS_THIS) {
return address(this);
} else if (_input == Constants.MSG_SENDER) {
return msgSender;
} else {
return _input;
}
}
/**
* @dev Checks the allowance of a token and approves the spender if necessary
* @param _token address of the token to be approved
* @param _spender address of the spender
* @param _value token amount
*/
function _ensureApproved(address _token, address _spender, uint256 _value) internal {
uint256 allowance = IERC20(_token).allowance(address(this), _spender);
if (allowance < _value) {
// This approval will only be executed the first time to save gas for subsequent operations
IERC20(_token).forceApprove(_spender, type(uint256).max);
}
}
/**
* @dev Simulates the execution of a command and returns the expected resulting rate
* @param _commandType The encoded representation of the command
* @param _inputs The encoded arguments for the specified command
* @param _spot If set to true, spot exchange rate is used for swaps. Additionally for all commands,
* input amounts are disregarded, and one unit of the token of interest is used instead.
* If set to false, the function includes price impact and curve pool fees for swaps.
* @param _balances Array of balances to track balances changes during this preview
* @return The preview rate value, which represents the amount of output token obtained for each wei
* of input token, multiplied by 1 ray unit.
*/
function _dispatchPreviewRate(
bytes1 _commandType,
bytes calldata _inputs,
bool _spot,
TokenBalance[] memory _balances
) internal view returns (uint256) {
uint256 command = uint8(_commandType & Commands.COMMAND_TYPE_MASK);
if (command == Commands.TRANSFER_FROM || command == Commands.TRANSFER_FROM_WITH_PERMIT) {
if (!_spot) {
(address token, uint256 value) = abi.decode(_inputs, (address, uint256));
_increasePreviewTokenValue(value, token, _balances);
}
return RayMath.RAY_UNIT;
} else if (command == Commands.TRANSFER) {
if (!_spot) {
(address token, address recipient, uint256 value) = abi.decode(
_inputs,
(address, address, uint256)
);
recipient = _resolveAddress(recipient);
if (recipient != address(this)) {
_decreasePreviewTokenValue(value, token, _balances);
}
}
return RayMath.RAY_UNIT;
} else if (
command == Commands.CURVE_SWAP ||
command == Commands.CURVE_NG_SWAP ||
command == Commands.CURVE_SWAP_SNG
) {
(address pool, uint256 i, uint256 j, uint256 amountIn, , address recipient) = abi
.decode(_inputs, (address, uint256, uint256, uint256, uint256, address));
uint256 exchangeRate;
if (_spot) {
// rate : spotExchangeRate * (ibtUnit / curveUnit) * rayUnit / ibtUnit
uint256 rate = (command == Commands.CURVE_SWAP || command == Commands.CURVE_NG_SWAP)
? RouterUtil(routerUtil).spotExchangeRate(pool, i, j)
: RouterUtil(routerUtil).spotExchangeRateSNG(
pool,
int128(int256(i)),
int128(int256(j))
);
exchangeRate = rate.toRay(CurvePoolUtil.CURVE_DECIMALS);
} else {
amountIn = _decreasePreviewTokenValue(
amountIn,
ICurvePool(pool).coins(i),
_balances
);
uint256 dy;
if (command == Commands.CURVE_SWAP_SNG) {
dy = IStableSwapNG(pool).get_dy(int128(int256(i)), int128(int256(j)), amountIn);
} else {
dy = ICurvePool(pool).get_dy(i, j, amountIn);
}
recipient = _resolveAddress(recipient);
if (recipient == address(this)) {
_increasePreviewTokenValue(dy, ICurvePool(pool).coins(j), _balances);
}
// rate : dy * rayUnit / amountIn
exchangeRate = dy.mulDiv(RayMath.RAY_UNIT, amountIn);
}
return exchangeRate;
} else if (command == Commands.WRAP_VAULT_IN_4626_ADAPTER) {
(address wrapper, uint256 vaultShares, address recipient) = abi.decode(
_inputs,
(address, uint256, address)
);
address vault = ISpectra4626Wrapper(wrapper).vaultShare();
if (_spot) {
vaultShares = RouterUtil(routerUtil).getUnit(vault);
} else {
vaultShares = _decreasePreviewTokenValue(vaultShares, vault, _balances);
}
uint256 _expectedWrapperShares = ISpectra4626Wrapper(wrapper).previewWrap(vaultShares);
recipient = _resolveAddress(recipient);
if (recipient == address(this)) {
_increasePreviewTokenValue(_expectedWrapperShares, wrapper, _balances);
}
// rate : expectedWrapperShares * rayUnit / vaultShares
return _expectedWrapperShares.mulDiv(RayMath.RAY_UNIT, vaultShares);
} else if (command == Commands.UNWRAP_VAULT_FROM_4626_ADAPTER) {
(address wrapper, uint256 wrapperShares, address recipient) = abi.decode(
_inputs,
(address, uint256, address)
);
if (_spot) {
wrapperShares = RouterUtil(routerUtil).getUnit(wrapper);
} else {
wrapperShares = _decreasePreviewTokenValue(wrapperShares, wrapper, _balances);
}
uint256 _expectedVaultShares = ISpectra4626Wrapper(wrapper).previewUnwrap(
wrapperShares
);
recipient = _resolveAddress(recipient);
if (recipient == address(this)) {
_increasePreviewTokenValue(
_expectedVaultShares,
ISpectra4626Wrapper(wrapper).vaultShare(),
_balances
);
}
// rate : expectedVaultShares * rayUnit / wrapperShares
return _expectedVaultShares.mulDiv(RayMath.RAY_UNIT, wrapperShares);
} else if (command == Commands.DEPOSIT_ASSET_IN_IBT) {
(address ibt, uint256 assets, address recipient) = abi.decode(
_inputs,
(address, uint256, address)
);
address asset = IERC4626(ibt).asset();
if (_spot) {
assets = RouterUtil(routerUtil).getUnit(asset);
} else {
assets = _decreasePreviewTokenValue(assets, asset, _balances);
}
uint256 _expectedShares = IERC4626(ibt).previewDeposit(assets);
recipient = _resolveAddress(recipient);
if (recipient == address(this)) {
_increasePreviewTokenValue(_expectedShares, ibt, _balances);
}
// rate : shares * rayUnit / assets
return _expectedShares.mulDiv(RayMath.RAY_UNIT, assets);
} else if (command == Commands.DEPOSIT_ASSET_IN_PT) {
(address pt, uint256 assets, address ptRecipient, address ytRecipient) = abi.decode(
_inputs,
(address, uint256, address, address)
);
if (_spot) {
assets = RouterUtil(routerUtil).getPTUnderlyingUnit(pt);
} else {
assets = _decreasePreviewTokenValue(
assets,
IPrincipalToken(pt).underlying(),
_balances
);
}
uint256 _expectedShares = IPrincipalToken(pt).previewDeposit(assets);
ptRecipient = _resolveAddress(ptRecipient);
if (ptRecipient == address(this)) {
_increasePreviewTokenValue(_expectedShares, pt, _balances);
}
ytRecipient = _resolveAddress(ytRecipient);
if (ytRecipient == address(this)) {
_increasePreviewTokenValue(_expectedShares, IPrincipalToken(pt).getYT(), _balances);
}
// rate : shares * rayUnit / assets
return _expectedShares.mulDiv(RayMath.RAY_UNIT, assets);
} else if (command == Commands.DEPOSIT_IBT_IN_PT) {
(address pt, uint256 ibts, address ptRecipient, address ytRecipient) = abi.decode(
_inputs,
(address, uint256, address, address)
);
if (_spot) {
ibts = RouterUtil(routerUtil).getUnit(pt);
} else {
ibts = _decreasePreviewTokenValue(ibts, IPrincipalToken(pt).getIBT(), _balances);
}
uint256 _expectedShares = IPrincipalToken(pt).previewDepositIBT(ibts);
ptRecipient = _resolveAddress(ptRecipient);
if (ptRecipient == address(this)) {
_increasePreviewTokenValue(_expectedShares, pt, _balances);
}
ytRecipient = _resolveAddress(ytRecipient);
if (ytRecipient == address(this)) {
_increasePreviewTokenValue(_expectedShares, IPrincipalToken(pt).getYT(), _balances);
}
// rate : shares * rayUnit / ibts
return _expectedShares.mulDiv(RayMath.RAY_UNIT, ibts);
} else if (command == Commands.REDEEM_IBT_FOR_ASSET) {
(address ibt, uint256 shares, address recipient) = abi.decode(
_inputs,
(address, uint256, address)
);
if (_spot) {
shares = RouterUtil(routerUtil).getUnit(ibt);
} else {
shares = _decreasePreviewTokenValue(shares, ibt, _balances);
}
uint256 _expectedAssets = IERC4626(ibt).previewRedeem(shares);
recipient = _resolveAddress(recipient);
if (recipient == address(this)) {
_increasePreviewTokenValue(_expectedAssets, IERC4626(ibt).asset(), _balances);
}
// rate : assets * rayUnit / shares
return _expectedAssets.mulDiv(RayMath.RAY_UNIT, shares);
} else if (command == Commands.REDEEM_PT_FOR_ASSET) {
(address pt, uint256 shares, address recipient) = abi.decode(
_inputs,
(address, uint256, address)
);
if (_spot) {
shares = RouterUtil(routerUtil).getUnit(pt);
} else {
shares = _decreasePreviewTokenValue(shares, pt, _balances);
if (block.timestamp < IPrincipalToken(pt).maturity()) {
_decreasePreviewTokenValue(shares, IPrincipalToken(pt).getYT(), _balances);
}
}
uint256 _expectedAssets = IPrincipalToken(pt).previewRedeem(shares);
recipient = _resolveAddress(recipient);
if (recipient == address(this)) {
_increasePreviewTokenValue(
_expectedAssets,
IPrincipalToken(pt).underlying(),
_balances
);
}
// rate : assets * rayUnit / shares
return _expectedAssets.mulDiv(RayMath.RAY_UNIT, shares);
} else if (command == Commands.REDEEM_PT_FOR_IBT) {
(address pt, uint256 shares, address recipient) = abi.decode(
_inputs,
(address, uint256, address)
);
if (_spot) {
shares = RouterUtil(routerUtil).getUnit(pt);
} else {
shares = _decreasePreviewTokenValue(shares, pt, _balances);
if (block.timestamp < IPrincipalToken(pt).maturity()) {
_decreasePreviewTokenValue(shares, IPrincipalToken(pt).getYT(), _balances);
}
}
uint256 _expectedIBTs = IPrincipalToken(pt).previewRedeemForIBT(shares);
recipient = _resolveAddress(recipient);
if (recipient == address(this)) {
_increasePreviewTokenValue(_expectedIBTs, IPrincipalToken(pt).getIBT(), _balances);
}
// rate : ibts * rayUnit / shares
return _expectedIBTs.mulDiv(RayMath.RAY_UNIT, shares);
} else if (command == Commands.KYBER_SWAP) {
if (kyberRouter == address(0)) {
revert KyberRouterNotSet();
}
(address tokenIn, uint256 amountIn, address tokenOut, uint256 expectedAmountOut) = abi
.decode(_inputs, (address, uint256, address, uint256));
if (tokenOut == Constants.ETH) {
revert AddressError();
}
if (tokenIn != Constants.ETH) {
amountIn = _decreasePreviewTokenValue(amountIn, tokenIn, _balances);
}
_increasePreviewTokenValue(expectedAmountOut, tokenOut, _balances);
// rate : expectedAmountOut * rayUnit / amountIn
return expectedAmountOut.mulDiv(RayMath.RAY_UNIT, amountIn);
} else if (command == Commands.ASSERT_MIN_BALANCE) {
return (RayMath.RAY_UNIT);
} else {
revert InvalidCommandType(command);
}
}
/**
* @dev Decrease balance for given token by given value in provided balances array
* @param _value The value to subtract from token balance
* @param _token The token address
* @param _balances The TokenBalance array
* @return The actual value to subtract from token balance
*/
function _decreasePreviewTokenValue(
uint256 _value,
address _token,
TokenBalance[] memory _balances
) internal pure returns (uint256) {
if (_token == address(0)) {
revert AddressError();
}
uint256 _length = _balances.length;
for (uint256 i = 0; i < _length; ++i) {
if (_balances[i].token == address(0)) {
break;
} else if (_balances[i].token == _token) {
if (_value == Constants.CONTRACT_BALANCE) {
uint256 _res = _balances[i].balance;
_balances[i].balance = 0;
return _res;
} else {
if (_balances[i].balance < _value) {
break;
}
_balances[i].balance -= _value;
return _value;
}
}
}
revert BalanceUnderflow();
}
/**
* @dev Increase balance for given token by given value in provided balances array
* @param _value The value to subtract from token balance
* @param _token The token address
* @param _balances The TokenBalance array
* @return The token balance AFTER increase
*/
function _increasePreviewTokenValue(
uint256 _value,
address _token,
TokenBalance[] memory _balances
) internal pure returns (uint256) {
if (_token == address(0)) {
revert AddressError();
}
uint256 _length = _balances.length;
for (uint256 i = 0; i < _length; ++i) {
if (_balances[i].token == address(0)) {
_balances[i] = TokenBalance(_token, _value);
return _value;
} else if (_balances[i].token == _token) {
_balances[i].balance += _value;
return _balances[i].balance;
}
}
revert MaxInvolvedTokensExceeded();
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/IAccessManaged.sol)
pragma solidity ^0.8.20;
interface IAccessManaged {
/**
* @dev Authority that manages this contract was updated.
*/
event AuthorityUpdated(address authority);
error AccessManagedUnauthorized(address caller);
error AccessManagedRequiredDelay(address caller, uint32 delay);
error AccessManagedInvalidAuthority(address authority);
/**
* @dev Returns the current authority.
*/
function authority() external view returns (address);
/**
* @dev Transfers control to a new authority. The caller must be the current authority.
*/
function setAuthority(address) external;
/**
* @dev Returns true only in the context of a delayed restricted call, at the moment that the scheduled operation is
* being consumed. Prevents denial of service for delayed restricted calls in the case that the contract performs
* attacker controlled calls.
*/
function isConsumingScheduledOp() external view returns (bytes4);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 standard as defined in the EIP.
*/
interface IERC20 {
/**
* @dev Emitted when `value` tokens are moved from one account (`from`) to
* another (`to`).
*
* Note that `value` may be zero.
*/
event Transfer(address indexed from, address indexed to, uint256 value);
/**
* @dev Emitted when the allowance of a `spender` for an `owner` is set by
* a call to {approve}. `value` is the new allowance.
*/
event Approval(address indexed owner, address indexed spender, uint256 value);
/**
* @dev Returns the value of tokens in existence.
*/
function totalSupply() external view returns (uint256);
/**
* @dev Returns the value of tokens owned by `account`.
*/
function balanceOf(address account) external view returns (uint256);
/**
* @dev Moves a `value` amount of tokens from the caller's account to `to`.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transfer(address to, uint256 value) external returns (bool);
/**
* @dev Returns the remaining number of tokens that `spender` will be
* allowed to spend on behalf of `owner` through {transferFrom}. This is
* zero by default.
*
* This value changes when {approve} or {transferFrom} are called.
*/
function allowance(address owner, address spender) external view returns (uint256);
/**
* @dev Sets a `value` amount of tokens as the allowance of `spender` over the
* caller's tokens.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* IMPORTANT: Beware that changing an allowance with this method brings the risk
* that someone may use both the old and the new allowance by unfortunate
* transaction ordering. One possible solution to mitigate this race
* condition is to first reduce the spender's allowance to 0 and set the
* desired value afterwards:
* https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
*
* Emits an {Approval} event.
*/
function approve(address spender, uint256 value) external returns (bool);
/**
* @dev Moves a `value` amount of tokens from `from` to `to` using the
* allowance mechanism. `value` is then deducted from the caller's
* allowance.
*
* Returns a boolean value indicating whether the operation succeeded.
*
* Emits a {Transfer} event.
*/
function transferFrom(address from, address to, uint256 value) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/IAccessManager.sol)
pragma solidity ^0.8.20;
import {IAccessManaged} from "./IAccessManaged.sol";
import {Time} from "../../utils/types/Time.sol";
interface IAccessManager {
/**
* @dev A delayed operation was scheduled.
*/
event OperationScheduled(
bytes32 indexed operationId,
uint32 indexed nonce,
uint48 schedule,
address caller,
address target,
bytes data
);
/**
* @dev A scheduled operation was executed.
*/
event OperationExecuted(bytes32 indexed operationId, uint32 indexed nonce);
/**
* @dev A scheduled operation was canceled.
*/
event OperationCanceled(bytes32 indexed operationId, uint32 indexed nonce);
/**
* @dev Informational labelling for a roleId.
*/
event RoleLabel(uint64 indexed roleId, string label);
/**
* @dev Emitted when `account` is granted `roleId`.
*
* NOTE: The meaning of the `since` argument depends on the `newMember` argument.
* If the role is granted to a new member, the `since` argument indicates when the account becomes a member of the role,
* otherwise it indicates the execution delay for this account and roleId is updated.
*/
event RoleGranted(uint64 indexed roleId, address indexed account, uint32 delay, uint48 since, bool newMember);
/**
* @dev Emitted when `account` membership or `roleId` is revoked. Unlike granting, revoking is instantaneous.
*/
event RoleRevoked(uint64 indexed roleId, address indexed account);
/**
* @dev Role acting as admin over a given `roleId` is updated.
*/
event RoleAdminChanged(uint64 indexed roleId, uint64 indexed admin);
/**
* @dev Role acting as guardian over a given `roleId` is updated.
*/
event RoleGuardianChanged(uint64 indexed roleId, uint64 indexed guardian);
/**
* @dev Grant delay for a given `roleId` will be updated to `delay` when `since` is reached.
*/
event RoleGrantDelayChanged(uint64 indexed roleId, uint32 delay, uint48 since);
/**
* @dev Target mode is updated (true = closed, false = open).
*/
event TargetClosed(address indexed target, bool closed);
/**
* @dev Role required to invoke `selector` on `target` is updated to `roleId`.
*/
event TargetFunctionRoleUpdated(address indexed target, bytes4 selector, uint64 indexed roleId);
/**
* @dev Admin delay for a given `target` will be updated to `delay` when `since` is reached.
*/
event TargetAdminDelayUpdated(address indexed target, uint32 delay, uint48 since);
error AccessManagerAlreadyScheduled(bytes32 operationId);
error AccessManagerNotScheduled(bytes32 operationId);
error AccessManagerNotReady(bytes32 operationId);
error AccessManagerExpired(bytes32 operationId);
error AccessManagerLockedAccount(address account);
error AccessManagerLockedRole(uint64 roleId);
error AccessManagerBadConfirmation();
error AccessManagerUnauthorizedAccount(address msgsender, uint64 roleId);
error AccessManagerUnauthorizedCall(address caller, address target, bytes4 selector);
error AccessManagerUnauthorizedConsume(address target);
error AccessManagerUnauthorizedCancel(address msgsender, address caller, address target, bytes4 selector);
error AccessManagerInvalidInitialAdmin(address initialAdmin);
/**
* @dev Check if an address (`caller`) is authorised to call a given function on a given contract directly (with
* no restriction). Additionally, it returns the delay needed to perform the call indirectly through the {schedule}
* & {execute} workflow.
*
* This function is usually called by the targeted contract to control immediate execution of restricted functions.
* Therefore we only return true if the call can be performed without any delay. If the call is subject to a
* previously set delay (not zero), then the function should return false and the caller should schedule the operation
* for future execution.
*
* If `immediate` is true, the delay can be disregarded and the operation can be immediately executed, otherwise
* the operation can be executed if and only if delay is greater than 0.
*
* NOTE: The IAuthority interface does not include the `uint32` delay. This is an extension of that interface that
* is backward compatible. Some contracts may thus ignore the second return argument. In that case they will fail
* to identify the indirect workflow, and will consider calls that require a delay to be forbidden.
*
* NOTE: This function does not report the permissions of this manager itself. These are defined by the
* {_canCallSelf} function instead.
*/
function canCall(
address caller,
address target,
bytes4 selector
) external view returns (bool allowed, uint32 delay);
/**
* @dev Expiration delay for scheduled proposals. Defaults to 1 week.
*
* IMPORTANT: Avoid overriding the expiration with 0. Otherwise every contract proposal will be expired immediately,
* disabling any scheduling usage.
*/
function expiration() external view returns (uint32);
/**
* @dev Minimum setback for all delay updates, with the exception of execution delays. It
* can be increased without setback (and reset via {revokeRole} in the case event of an
* accidental increase). Defaults to 5 days.
*/
function minSetback() external view returns (uint32);
/**
* @dev Get whether the contract is closed disabling any access. Otherwise role permissions are applied.
*/
function isTargetClosed(address target) external view returns (bool);
/**
* @dev Get the role required to call a function.
*/
function getTargetFunctionRole(address target, bytes4 selector) external view returns (uint64);
/**
* @dev Get the admin delay for a target contract. Changes to contract configuration are subject to this delay.
*/
function getTargetAdminDelay(address target) external view returns (uint32);
/**
* @dev Get the id of the role that acts as an admin for the given role.
*
* The admin permission is required to grant the role, revoke the role and update the execution delay to execute
* an operation that is restricted to this role.
*/
function getRoleAdmin(uint64 roleId) external view returns (uint64);
/**
* @dev Get the role that acts as a guardian for a given role.
*
* The guardian permission allows canceling operations that have been scheduled under the role.
*/
function getRoleGuardian(uint64 roleId) external view returns (uint64);
/**
* @dev Get the role current grant delay.
*
* Its value may change at any point without an event emitted following a call to {setGrantDelay}.
* Changes to this value, including effect timepoint are notified in advance by the {RoleGrantDelayChanged} event.
*/
function getRoleGrantDelay(uint64 roleId) external view returns (uint32);
/**
* @dev Get the access details for a given account for a given role. These details include the timepoint at which
* membership becomes active, and the delay applied to all operation by this user that requires this permission
* level.
*
* Returns:
* [0] Timestamp at which the account membership becomes valid. 0 means role is not granted.
* [1] Current execution delay for the account.
* [2] Pending execution delay for the account.
* [3] Timestamp at which the pending execution delay will become active. 0 means no delay update is scheduled.
*/
function getAccess(uint64 roleId, address account) external view returns (uint48, uint32, uint32, uint48);
/**
* @dev Check if a given account currently has the permission level corresponding to a given role. Note that this
* permission might be associated with an execution delay. {getAccess} can provide more details.
*/
function hasRole(uint64 roleId, address account) external view returns (bool, uint32);
/**
* @dev Give a label to a role, for improved role discoverability by UIs.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleLabel} event.
*/
function labelRole(uint64 roleId, string calldata label) external;
/**
* @dev Add `account` to `roleId`, or change its execution delay.
*
* This gives the account the authorization to call any function that is restricted to this role. An optional
* execution delay (in seconds) can be set. If that delay is non 0, the user is required to schedule any operation
* that is restricted to members of this role. The user will only be able to execute the operation after the delay has
* passed, before it has expired. During this period, admin and guardians can cancel the operation (see {cancel}).
*
* If the account has already been granted this role, the execution delay will be updated. This update is not
* immediate and follows the delay rules. For example, if a user currently has a delay of 3 hours, and this is
* called to reduce that delay to 1 hour, the new delay will take some time to take effect, enforcing that any
* operation executed in the 3 hours that follows this update was indeed scheduled before this update.
*
* Requirements:
*
* - the caller must be an admin for the role (see {getRoleAdmin})
* - granted role must not be the `PUBLIC_ROLE`
*
* Emits a {RoleGranted} event.
*/
function grantRole(uint64 roleId, address account, uint32 executionDelay) external;
/**
* @dev Remove an account from a role, with immediate effect. If the account does not have the role, this call has
* no effect.
*
* Requirements:
*
* - the caller must be an admin for the role (see {getRoleAdmin})
* - revoked role must not be the `PUBLIC_ROLE`
*
* Emits a {RoleRevoked} event if the account had the role.
*/
function revokeRole(uint64 roleId, address account) external;
/**
* @dev Renounce role permissions for the calling account with immediate effect. If the sender is not in
* the role this call has no effect.
*
* Requirements:
*
* - the caller must be `callerConfirmation`.
*
* Emits a {RoleRevoked} event if the account had the role.
*/
function renounceRole(uint64 roleId, address callerConfirmation) external;
/**
* @dev Change admin role for a given role.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleAdminChanged} event
*/
function setRoleAdmin(uint64 roleId, uint64 admin) external;
/**
* @dev Change guardian role for a given role.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleGuardianChanged} event
*/
function setRoleGuardian(uint64 roleId, uint64 guardian) external;
/**
* @dev Update the delay for granting a `roleId`.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {RoleGrantDelayChanged} event.
*/
function setGrantDelay(uint64 roleId, uint32 newDelay) external;
/**
* @dev Set the role required to call functions identified by the `selectors` in the `target` contract.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {TargetFunctionRoleUpdated} event per selector.
*/
function setTargetFunctionRole(address target, bytes4[] calldata selectors, uint64 roleId) external;
/**
* @dev Set the delay for changing the configuration of a given target contract.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {TargetAdminDelayUpdated} event.
*/
function setTargetAdminDelay(address target, uint32 newDelay) external;
/**
* @dev Set the closed flag for a contract.
*
* Requirements:
*
* - the caller must be a global admin
*
* Emits a {TargetClosed} event.
*/
function setTargetClosed(address target, bool closed) external;
/**
* @dev Return the timepoint at which a scheduled operation will be ready for execution. This returns 0 if the
* operation is not yet scheduled, has expired, was executed, or was canceled.
*/
function getSchedule(bytes32 id) external view returns (uint48);
/**
* @dev Return the nonce for the latest scheduled operation with a given id. Returns 0 if the operation has never
* been scheduled.
*/
function getNonce(bytes32 id) external view returns (uint32);
/**
* @dev Schedule a delayed operation for future execution, and return the operation identifier. It is possible to
* choose the timestamp at which the operation becomes executable as long as it satisfies the execution delays
* required for the caller. The special value zero will automatically set the earliest possible time.
*
* Returns the `operationId` that was scheduled. Since this value is a hash of the parameters, it can reoccur when
* the same parameters are used; if this is relevant, the returned `nonce` can be used to uniquely identify this
* scheduled operation from other occurrences of the same `operationId` in invocations of {execute} and {cancel}.
*
* Emits a {OperationScheduled} event.
*
* NOTE: It is not possible to concurrently schedule more than one operation with the same `target` and `data`. If
* this is necessary, a random byte can be appended to `data` to act as a salt that will be ignored by the target
* contract if it is using standard Solidity ABI encoding.
*/
function schedule(address target, bytes calldata data, uint48 when) external returns (bytes32, uint32);
/**
* @dev Execute a function that is delay restricted, provided it was properly scheduled beforehand, or the
* execution delay is 0.
*
* Returns the nonce that identifies the previously scheduled operation that is executed, or 0 if the
* operation wasn't previously scheduled (if the caller doesn't have an execution delay).
*
* Emits an {OperationExecuted} event only if the call was scheduled and delayed.
*/
function execute(address target, bytes calldata data) external payable returns (uint32);
/**
* @dev Cancel a scheduled (delayed) operation. Returns the nonce that identifies the previously scheduled
* operation that is cancelled.
*
* Requirements:
*
* - the caller must be the proposer, a guardian of the targeted function, or a global admin
*
* Emits a {OperationCanceled} event.
*/
function cancel(address caller, address target, bytes calldata data) external returns (uint32);
/**
* @dev Consume a scheduled operation targeting the caller. If such an operation exists, mark it as consumed
* (emit an {OperationExecuted} event and clean the state). Otherwise, throw an error.
*
* This is useful for contract that want to enforce that calls targeting them were scheduled on the manager,
* with all the verifications that it implies.
*
* Emit a {OperationExecuted} event.
*/
function consumeScheduledOp(address caller, bytes calldata data) external;
/**
* @dev Hashing function for delayed operations.
*/
function hashOperation(address caller, address target, bytes calldata data) external view returns (bytes32);
/**
* @dev Changes the authority of a target managed by this manager instance.
*
* Requirements:
*
* - the caller must be a global admin
*/
function updateAuthority(address target, address newAuthority) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
import {IERC4626} from "@openzeppelin/contracts/interfaces/IERC4626.sol";
/// @dev Interface of Spectra4626Wrapper.
interface ISpectra4626Wrapper is IERC4626 {
/// @dev Emitted when vault shares are deposited in the wrapper.
event Wrap(
address indexed caller,
address indexed receiver,
uint256 vaultShares,
uint256 shares
);
/// @dev Emitted when vault shares are withdrawn from the wrapper.
event Unwrap(
address indexed caller,
address indexed receiver,
address indexed owner,
uint256 shares,
uint256 vaultShares
);
/// @dev Emitted when rewards proxy is updated.
event RewardsProxyUpdated(address oldRewardsProxy, address newRewardsProxy);
error ERC5143SlippageProtectionFailed();
error NoRewardsProxy();
error ClaimRewardsFailed();
/// @dev Returns the address of the wrapped vault share.
function vaultShare() external view returns (address);
/// @dev Returns the vault share balance of the wrapper.
function totalVaultShares() external view returns (uint256);
/// @dev Returns the rewards proxy of the wrapper.
function rewardsProxy() external view returns (address);
/// @dev Allows to preview the amount of minted wrapper shares for a given amount of deposited vault shares.
/// @param vaultShares The amount of vault shares to deposit.
/// @return The amount of minted vault shares.
function previewWrap(uint256 vaultShares) external view returns (uint256);
/// @dev Allows to preview the amount of withdrawn vault shares for a given amount of redeemed wrapper shares.
/// @param shares The amount of wrapper shares to redeem.
/// @return The amount of withdrawn vault shares.
function previewUnwrap(uint256 shares) external view returns (uint256);
/// @dev Allows the owner to deposit vault shares into the wrapper.
/// @param vaultShares The amount of vault shares to deposit.
/// @param receiver The address to receive the wrapper shares.
/// @return The amount of minted wrapper shares.
function wrap(uint256 vaultShares, address receiver) external returns (uint256);
/// @dev Allows the owner to deposit vault shares into the wrapper, with support for slippage protection.
/// @param vaultShares The amount of vault shares to deposit.
/// @param receiver The address to receive the wrapper shares.
/// @param minShares The minimum allowed wrapper shares from this deposit.
/// @return The amount of minted wrapper shares.
function wrap(
uint256 vaultShares,
address receiver,
uint256 minShares
) external returns (uint256);
/// @dev Allows the owner to withdraw vault shares from the wrapper.
/// @param shares The amount of wrapper shares to redeem.
/// @param receiver The address to receive the vault shares.
/// @param owner The address of the owner of the wrapper shares.
/// @return The amount of withdrawn vault shares.
function unwrap(uint256 shares, address receiver, address owner) external returns (uint256);
/// @dev Allows the owner to withdraw vault shares from the wrapper, with support for slippage protection.
/// @param shares The amount of wrapper shares to redeem.
/// @param receiver The address to receive the vault shares.
/// @param owner The address of the owner of the wrapper shares.
/// @param minVaultShares The minimum vault shares that should be returned.
/// @return The amount of withdrawn vault shares.
function unwrap(
uint256 shares,
address receiver,
address owner,
uint256 minVaultShares
) external returns (uint256);
/// @dev Setter for the rewards proxy.
/// @param newRewardsProxy The address of the new rewards proxy.
function setRewardsProxy(address newRewardsProxy) external;
/// @dev Claims rewards for the wrapped vault.
/// @param data The optional data used for claiming rewards.
function claimRewards(bytes calldata data) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20} from "../IERC20.sol";
/**
* @dev Interface for the optional metadata functions from the ERC20 standard.
*/
interface IERC20Metadata is IERC20 {
/**
* @dev Returns the name of the token.
*/
function name() external view returns (string memory);
/**
* @dev Returns the symbol of the token.
*/
function symbol() external view returns (string memory);
/**
* @dev Returns the decimals places of the token.
*/
function decimals() external view returns (uint8);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/IAuthority.sol)
pragma solidity ^0.8.20;
/**
* @dev Standard interface for permissioning originally defined in Dappsys.
*/
interface IAuthority {
/**
* @dev Returns true if the caller can invoke on a target the function identified by a function selector.
*/
function canCall(address caller, address target, bytes4 selector) external view returns (bool allowed);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
import "openzeppelin-contracts/interfaces/IERC20.sol";
import "openzeppelin-contracts/interfaces/IERC20Metadata.sol";
import "openzeppelin-contracts/interfaces/IERC3156FlashLender.sol";
interface IPrincipalToken is IERC20, IERC20Metadata, IERC3156FlashLender {
/* ERRORS
*****************************************************************************************************************/
error InvalidDecimals();
error BeaconNotSet();
error PTExpired();
error PTNotExpired();
error RateError();
error AddressError();
error UnauthorizedCaller();
error RatesAtExpiryAlreadyStored();
error ERC5143SlippageProtectionFailed();
error InsufficientBalance();
error FlashLoanExceedsMaxAmount();
error FlashLoanCallbackFailed();
error NoRewardsProxy();
error ClaimRewardsFailed();
/* Functions
*****************************************************************************************************************/
function initialize(address _ibt, uint256 _duration, address initialAuthority) external;
/**
* @notice Toggle Pause
* @dev Should only be called in extraordinary situations by the admin of the contract
*/
function pause() external;
/**
* @notice Toggle UnPause
* @dev Should only be called in extraordinary situations by the admin of the contract
*/
function unPause() external;
/**
* @notice Deposits amount of assets in the PT vault
* @param assets The amount of assets being deposited
* @param receiver The receiver address of the shares
* @return shares The amount of shares minted (same amount for PT & yt)
*/
function deposit(uint256 assets, address receiver) external returns (uint256 shares);
/**
* @notice Deposits amount of assets in the PT vault
* @param assets The amount of assets being deposited
* @param ptReceiver The receiver address of the PTs
* @param ytReceiver the receiver address of the YTs
* @return shares The amount of shares minted (same amount for PT & yt)
*/
function deposit(
uint256 assets,
address ptReceiver,
address ytReceiver
) external returns (uint256 shares);
/**
* @notice Deposits amount of assets with a lower bound on shares received
* @param assets The amount of assets being deposited
* @param ptReceiver The receiver address of the PTs
* @param ytReceiver The receiver address of the YTs
* @param minShares The minimum allowed shares from this deposit
* @return shares The amount of shares actually minted to the receiver
*/
function deposit(
uint256 assets,
address ptReceiver,
address ytReceiver,
uint256 minShares
) external returns (uint256 shares);
/**
* @notice Same as normal deposit but with IBTs
* @param ibts The amount of IBT being deposited
* @param receiver The receiver address of the shares
* @return shares The amount of shares minted to the receiver
*/
function depositIBT(uint256 ibts, address receiver) external returns (uint256 shares);
/**
* @notice Same as normal deposit but with IBTs
* @param ibts The amount of IBT being deposited
* @param ptReceiver The receiver address of the PTs
* @param ytReceiver the receiver address of the YTs
* @return shares The amount of shares minted to the receiver
*/
function depositIBT(
uint256 ibts,
address ptReceiver,
address ytReceiver
) external returns (uint256 shares);
/**
* @notice Same as normal deposit but with IBTs
* @param ibts The amount of IBT being deposited
* @param ptReceiver The receiver address of the PTs
* @param ytReceiver The receiver address of the YTs
* @param minShares The minimum allowed shares from this deposit
* @return shares The amount of shares minted to the receiver
*/
function depositIBT(
uint256 ibts,
address ptReceiver,
address ytReceiver,
uint256 minShares
) external returns (uint256 shares);
/**
* @notice Burns owner's shares (PTs and YTs before expiry, PTs after expiry)
* and sends assets to receiver
* @param shares The amount of shares to burn
* @param receiver The address that will receive the assets
* @param owner The owner of the shares
* @return assets The actual amount of assets received for burning the shares
*/
function redeem(
uint256 shares,
address receiver,
address owner
) external returns (uint256 assets);
/**
* @notice Burns owner's shares (PTs and YTs before expiry, PTs after expiry)
* and sends assets to receiver
* @param shares The amount of shares to burn
* @param receiver The address that will receive the assets
* @param owner The owner of the shares
* @param minAssets The minimum assets that should be returned to user
* @return assets The actual amount of assets received for burning the shares
*/
function redeem(
uint256 shares,
address receiver,
address owner,
uint256 minAssets
) external returns (uint256 assets);
/**
* @notice Burns owner's shares (PTs and YTs before expiry, PTs after expiry)
* and sends IBTs to receiver
* @param shares The amount of shares to burn
* @param receiver The address that will receive the IBTs
* @param owner The owner of the shares
* @return ibts The actual amount of IBT received for burning the shares
*/
function redeemForIBT(
uint256 shares,
address receiver,
address owner
) external returns (uint256 ibts);
/**
* @notice Burns owner's shares (PTs and YTs before expiry, PTs after expiry)
* and sends IBTs to receiver
* @param shares The amount of shares to burn
* @param receiver The address that will receive the IBTs
* @param owner The owner of the shares
* @param minIbts The minimum IBTs that should be returned to user
* @return ibts The actual amount of IBT received for burning the shares
*/
function redeemForIBT(
uint256 shares,
address receiver,
address owner,
uint256 minIbts
) external returns (uint256 ibts);
/**
* @notice Burns owner's shares (before expiry : PTs and YTs) and sends assets to receiver
* @param assets The amount of assets to be received
* @param receiver The address that will receive the assets
* @param owner The owner of the shares (PTs and YTs)
* @return shares The actual amount of shares burnt for receiving the assets
*/
function withdraw(
uint256 assets,
address receiver,
address owner
) external returns (uint256 shares);
/**
* @notice Burns owner's shares (before expiry : PTs and YTs) and sends assets to receiver
* @param assets The amount of assets to be received
* @param receiver The address that will receive the assets
* @param owner The owner of the shares (PTs and YTs)
* @param maxShares The maximum shares allowed to be burnt
* @return shares The actual amount of shares burnt for receiving the assets
*/
function withdraw(
uint256 assets,
address receiver,
address owner,
uint256 maxShares
) external returns (uint256 shares);
/**
* @notice Burns owner's shares (before expiry : PTs and YTs) and sends IBTs to receiver
* @param ibts The amount of IBT to be received
* @param receiver The address that will receive the IBTs
* @param owner The owner of the shares (PTs and YTs)
* @return shares The actual amount of shares burnt for receiving the IBTs
*/
function withdrawIBT(
uint256 ibts,
address receiver,
address owner
) external returns (uint256 shares);
/**
* @notice Burns owner's shares (before expiry : PTs and YTs) and sends IBTs to receiver
* @param ibts The amount of IBT to be received
* @param receiver The address that will receive the IBTs
* @param owner The owner of the shares (PTs and YTs)
* @param maxShares The maximum shares allowed to be burnt
* @return shares The actual amount of shares burnt for receiving the IBTs
*/
function withdrawIBT(
uint256 ibts,
address receiver,
address owner,
uint256 maxShares
) external returns (uint256 shares);
/**
* @notice Updates _user's yield since last update
* @param _user The user whose yield will be updated
* @return updatedUserYieldInIBT The unclaimed yield of the user in IBT (not just the updated yield)
*/
function updateYield(address _user) external returns (uint256 updatedUserYieldInIBT);
/**
* @notice Claims caller's unclaimed yield in asset
* @param _receiver The receiver of yield
* @param _minAssets The minimum amount of assets that should be received
* @return yieldInAsset The amount of yield claimed in asset
*/
function claimYield(
address _receiver,
uint256 _minAssets
) external returns (uint256 yieldInAsset);
/**
* @notice Claims caller's unclaimed yield in IBT
* @param _receiver The receiver of yield
* @param _minIBT The minimum amount of IBT that should be received
* @return yieldInIBT The amount of yield claimed in IBT
*/
function claimYieldInIBT(
address _receiver,
uint256 _minIBT
) external returns (uint256 yieldInIBT);
/**
* @notice Claims the collected ibt fees and redeems them to the fee collector
* @param _minAssets The minimum amount of assets that should be received
* @return assets The amount of assets sent to the fee collector
*/
function claimFees(uint256 _minAssets) external returns (uint256 assets);
/**
* @notice Updates yield of both sender and receiver of YTs
* @param _from the sender of YTs
* @param _to the receiver of YTs
*/
function beforeYtTransfer(address _from, address _to) external;
/**
* Call the claimRewards function of the rewards contract
* @param data The optional data to be passed to the rewards contract
*/
function claimRewards(bytes memory data) external;
/* SETTERS
*****************************************************************************************************************/
/**
* @notice Stores PT and IBT rates at expiry. Ideally, it should be called the day of expiry
*/
function storeRatesAtExpiry() external;
/** Set a new Rewards Proxy
* @param _rewardsProxy The address of the new reward proxy
*/
function setRewardsProxy(address _rewardsProxy) external;
/* GETTERS
*****************************************************************************************************************/
/**
* @notice Returns the amount of shares minted for the theorical deposited amount of assets
* @param assets The amount of assets deposited
* @return The amount of shares minted
*/
function previewDeposit(uint256 assets) external view returns (uint256);
/**
* @notice Returns the amount of shares minted for the theorical deposited amount of IBT
* @param ibts The amount of IBT deposited
* @return The amount of shares minted
*/
function previewDepositIBT(uint256 ibts) external view returns (uint256);
/**
* @notice Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
* through a deposit call.
* @param receiver The receiver of the shares
* @return The maximum amount of assets that can be deposited
*/
function maxDeposit(address receiver) external view returns (uint256);
/**
* @notice Returns the theorical amount of shares that need to be burnt to receive assets of underlying
* @param assets The amount of assets to receive
* @return The amount of shares burnt
*/
function previewWithdraw(uint256 assets) external view returns (uint256);
/**
* @notice Returns the theorical amount of shares that need to be burnt to receive amount of IBT
* @param ibts The amount of IBT to receive
* @return The amount of shares burnt
*/
function previewWithdrawIBT(uint256 ibts) external view returns (uint256);
/**
* @notice Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
* @param owner The owner of the Vault shares
* @return The maximum amount of assets that can be withdrawn
*/
function maxWithdraw(address owner) external view returns (uint256);
/**
* @notice Returns the maximum amount of the IBT that can be withdrawn from the owner balance in the
* Vault, through a withdraw call.
* @param owner The owner of the Vault shares
* @return The maximum amount of IBT that can be withdrawn
*/
function maxWithdrawIBT(address owner) external view returns (uint256);
/**
* @notice Returns the amount of assets received for the theorical amount of burnt shares
* @param shares The amount of shares to burn
* @return The amount of assets received
*/
function previewRedeem(uint256 shares) external view returns (uint256);
/**
* @notice Returns the amount of IBT received for the theorical amount of burnt shares
* @param shares The amount of shares to burn
* @return The amount of IBT received
*/
function previewRedeemForIBT(uint256 shares) external view returns (uint256);
/**
* @notice Returns the maximum amount of Vault shares that can be redeemed by the owner
* @notice This function behaves differently before and after expiry. Before expiry an equal amount of PT and YT
* needs to be burnt, while after expiry only PTs are burnt.
* @param owner The owner of the shares
* @return The maximum amount of shares that can be redeemed
*/
function maxRedeem(address owner) external view returns (uint256);
/**
* Returns the total amount of the underlying asset that is owned by the Vault in the form of IBT.
*/
function totalAssets() external view returns (uint256);
/**
* @notice Converts an underlying amount in principal. Equivalent to ERC-4626's convertToShares method.
* @param underlyingAmount The amount of underlying (or assets) to convert
* @return The resulting amount of principal (or shares)
*/
function convertToPrincipal(uint256 underlyingAmount) external view returns (uint256);
/**
* @notice Converts a principal amount in underlying. Equivalent to ERC-4626's convertToAssets method.
* @param principalAmount The amount of principal (or shares) to convert
* @return The resulting amount of underlying (or assets)
*/
function convertToUnderlying(uint256 principalAmount) external view returns (uint256);
/**
* @notice Returns whether or not the contract is paused.
* @return true if the contract is paused, and false otherwise
*/
function paused() external view returns (bool);
/**
* @notice Returns the unix timestamp (uint256) at which the PT contract expires
* @return The unix timestamp (uint256) when PTs become redeemable
*/
function maturity() external view returns (uint256);
/**
* @notice Returns the duration of the PT contract
* @return The duration (in s) to expiry/maturity of the PT contract
*/
function getDuration() external view returns (uint256);
/**
* @notice Returns the address of the underlying token (or asset). Equivalent to ERC-4626's asset method.
* @return The address of the underlying token (or asset)
*/
function underlying() external view returns (address);
/**
* @notice Returns the IBT address of the PT contract
* @return ibt The address of the IBT
*/
function getIBT() external view returns (address ibt);
/**
* @notice Returns the yt address of the PT contract
* @return yt The address of the yt
*/
function getYT() external view returns (address yt);
/**
* @notice Returns the current ibtRate
* @return The current ibtRate
*/
function getIBTRate() external view returns (uint256);
/**
* @notice Returns the current ptRate
* @return The current ptRate
*/
function getPTRate() external view returns (uint256);
/**
* @notice Returns 1 unit of IBT
* @return The IBT unit
*/
function getIBTUnit() external view returns (uint256);
/**
* @notice Get the unclaimed fees in IBT
* @return The unclaimed fees in IBT
*/
function getUnclaimedFeesInIBT() external view returns (uint256);
/**
* @notice Get the total collected fees in IBT (claimed and unclaimed)
* @return The total fees in IBT
*/
function getTotalFeesInIBT() external view returns (uint256);
/**
* @notice Get the tokenization fee of the PT
* @return The tokenization fee
*/
function getTokenizationFee() external view returns (uint256);
/**
* @notice Get the current IBT yield of the user
* @param _user The address of the user to get the current yield from
* @return The yield of the user in IBT
*/
function getCurrentYieldOfUserInIBT(address _user) external view returns (uint256);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC3156FlashBorrower.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC3156 FlashBorrower, as defined in
* https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
*/
interface IERC3156FlashBorrower {
/**
* @dev Receive a flash loan.
* @param initiator The initiator of the loan.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @param fee The additional amount of tokens to repay.
* @param data Arbitrary data structure, intended to contain user-defined parameters.
* @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan"
*/
function onFlashLoan(
address initiator,
address token,
uint256 amount,
uint256 fee,
bytes calldata data
) external returns (bytes32);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)
pragma solidity ^0.8.20;
import {Initializable} from "../proxy/utils/Initializable.sol";
/**
* @dev Provides information about the current execution context, including the
* sender of the transaction and its data. While these are generally available
* via msg.sender and msg.data, they should not be accessed in such a direct
* manner, since when dealing with meta-transactions the account sending and
* paying for execution may not be the actual sender (as far as an application
* is concerned).
*
* This contract is only required for intermediate, library-like contracts.
*/
abstract contract ContextUpgradeable is Initializable {
function __Context_init() internal onlyInitializing {
}
function __Context_init_unchained() internal onlyInitializing {
}
function _msgSender() internal view virtual returns (address) {
return msg.sender;
}
function _msgData() internal view virtual returns (bytes calldata) {
return msg.data;
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
interface INATIVE {
function deposit() external payable;
function withdraw(uint wad) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
import "../interfaces/ICurvePool.sol";
import "../interfaces/ICurveNGPool.sol";
import "../interfaces/IStableSwapNG.sol";
import "../interfaces/IPrincipalToken.sol";
import "./RayMath.sol";
import "openzeppelin-math/Math.sol";
/**
* @title CurvePoolUtil library
* @author Spectra Finance
* @notice Provides miscellaneous utils for computations related to Curve CryptoSwap pools.
*/
library CurvePoolUtil {
using Math for uint256;
using RayMath for uint256;
error SolutionNotFound();
error FailedToFetchExpectedLPTokenAmount();
error FailedToFetchExpectedCoinAmount();
/// @notice Decimal precision used internally in the Curve AMM
uint256 public constant CURVE_DECIMALS = 18;
/// @notice Base unit for Curve AMM calculations
uint256 public constant CURVE_UNIT = 1e18;
/// @notice Make rounding errors favoring other LPs a tiny bit
uint256 private constant APPROXIMATION_DECREMENT = 1;
/// @notice Maximal number of iterations in the binary search algorithm
uint256 private constant MAX_ITERATIONS_BINSEARCH = 255;
/**
* @notice Returns the expected LP token amount received for depositing given amounts of IBT and PT
* @notice Method to be used with legacy Curve Cryptoswap pools
* @param _curvePool The address of the Curve Pool in which liquidity will be deposited
* @param _amounts Array containing the amounts of IBT and PT to deposit in the Curve Pool
* @return minMintAmount The amount of expected LP tokens received for depositing the liquidity in the pool
*/
function previewAddLiquidity(
address _curvePool,
uint256[2] memory _amounts
) external view returns (uint256 minMintAmount) {
(bool success, bytes memory responseData) = _curvePool.staticcall(
abi.encodeCall(ICurvePool(address(0)).calc_token_amount, (_amounts))
);
if (!success) {
revert FailedToFetchExpectedLPTokenAmount();
}
minMintAmount = abi.decode(responseData, (uint256));
}
/**
* @notice Returns the expected LP token amount received for depositing given amounts of IBT and PT
* @notice Method to be used with legacy Curve Cryptoswap NG pools
* @param _curvePool The address of the Curve Pool in which liquidity will be deposited
* @param _amounts Array containing the amounts of IBT and PT to deposit in the Curve Pool
* @return minMintAmount The amount of expected LP tokens received for depositing the liquidity in the pool
*/
function previewAddLiquidityNG(
address _curvePool,
uint256[2] memory _amounts
) external view returns (uint256 minMintAmount) {
(bool success, bytes memory responseData) = _curvePool.staticcall(
abi.encodeCall(ICurveNGPool(address(0)).calc_token_amount, (_amounts, true))
);
if (!success) {
revert FailedToFetchExpectedLPTokenAmount();
}
minMintAmount = abi.decode(responseData, (uint256));
}
/**
* @notice Returns the expected LP token amount received for depositing given amounts of IBT and PT
* @notice Method to be used with StableSwap NG pools
* @param _curvePool The address of the Curve Pool in which liquidity will be deposited
* @param _amounts Array containing the amounts of IBT and PT to deposit in the Curve Pool
* @return minMintAmount The amount of expected LP tokens received for depositing the liquidity in the pool
*/
function previewAddLiquiditySNG(
address _curvePool,
uint256[] memory _amounts
) external view returns (uint256 minMintAmount) {
// @dev set the is_deposit to true
(bool success, bytes memory responseData) = _curvePool.staticcall(
abi.encodeCall(IStableSwapNG(address(0)).calc_token_amount, (_amounts, true))
);
if (!success) {
revert FailedToFetchExpectedLPTokenAmount();
}
minMintAmount = abi.decode(responseData, (uint256));
}
/**
* @notice Returns the IBT and PT amounts received for burning a given amount of LP tokens
* @notice Method to be used with legacy Curve Cryptoswap pools
* @param _curvePool The address of the curve pool
* @param _lpTokenAmount The amount of the lp token to burn
* @return minAmounts The expected respective amounts of IBT and PT withdrawn from the curve pool
*/
function previewRemoveLiquidity(
address _curvePool,
uint256 _lpTokenAmount
) external view returns (uint256[2] memory minAmounts) {
address lpToken = ICurvePool(_curvePool).token();
uint256 totalSupply = IERC20(lpToken).totalSupply();
(uint256 ibtBalance, uint256 ptBalance) = _getCurvePoolBalances(_curvePool);
// decrement following what Curve is doing
if (_lpTokenAmount > APPROXIMATION_DECREMENT && totalSupply != 0) {
_lpTokenAmount -= APPROXIMATION_DECREMENT;
minAmounts = [
(ibtBalance * _lpTokenAmount) / totalSupply,
(ptBalance * _lpTokenAmount) / totalSupply
];
} else {
minAmounts = [uint256(0), uint256(0)];
}
}
/**
* @notice Returns the IBT and PT amounts received for burning a given amount of LP tokens
* @notice Method to be used with Curve Cryptoswap NG pools
* @param _curvePool The address of the curve pool
* @param _lpTokenAmount The amount of the lp token to burn
* @return minAmounts The expected respective amounts of IBT and PT withdrawn from the curve pool
*/
function previewRemoveLiquidityNG(
address _curvePool,
uint256 _lpTokenAmount
) external view returns (uint256[2] memory minAmounts) {
uint256 totalSupply = ICurveNGPool(_curvePool).totalSupply();
(uint256 ibtBalance, uint256 ptBalance) = _getCurvePoolBalances(_curvePool);
// reproduces Curve implementation
if (_lpTokenAmount == totalSupply) {
minAmounts = [ibtBalance, ptBalance];
} else if (_lpTokenAmount > APPROXIMATION_DECREMENT && totalSupply != 0) {
_lpTokenAmount -= APPROXIMATION_DECREMENT;
minAmounts = [
ibtBalance.mulDiv(_lpTokenAmount, totalSupply),
ptBalance.mulDiv(_lpTokenAmount, totalSupply)
];
} else {
minAmounts = [uint256(0), uint256(0)];
}
}
/**
* @notice Returns the IBT and PT amounts received for burning a given amount of LP tokens
* @notice Method to be used with StableSwap NG pools
* @param _curvePool The address of the curve pool
* @param _lpTokenAmount The amount of the lp token to burn
* @return minAmounts The expected respective amounts of IBT and PT withdrawn from the curve pool
*/
function previewRemoveLiquiditySNG(
address _curvePool,
uint256 _lpTokenAmount
) external view returns (uint256[] memory) {
uint256 totalSupply = IERC20(_curvePool).totalSupply();
(uint256 ibtBalance, uint256 ptBalance) = _getCurvePoolBalances(_curvePool);
// decrement following what Curve is doing
uint256[] memory minAmounts = new uint256[](2);
if (_lpTokenAmount > APPROXIMATION_DECREMENT && totalSupply != 0) {
_lpTokenAmount -= APPROXIMATION_DECREMENT;
minAmounts[0] = (ibtBalance * _lpTokenAmount) / totalSupply;
minAmounts[1] = (ptBalance * _lpTokenAmount) / totalSupply;
} else {
minAmounts[0] = 0;
minAmounts[1] = 0;
}
return minAmounts;
}
/**
* @notice Returns the amount of coin i received for burning a given amount of LP tokens
* @notice Method to be used with legacy Curve CryptoSwap pools
* @param _curvePool The address of the curve pool
* @param _lpTokenAmount The amount of the LP tokens to burn
* @param _i The index of the unique coin to withdraw
* @return minAmount The expected amount of coin i withdrawn from the curve pool
*/
function previewRemoveLiquidityOneCoin(
address _curvePool,
uint256 _lpTokenAmount,
uint256 _i
) external view returns (uint256 minAmount) {
(bool success, bytes memory responseData) = _curvePool.staticcall(
abi.encodeCall(ICurvePool(address(0)).calc_withdraw_one_coin, (_lpTokenAmount, _i))
);
if (!success) {
revert FailedToFetchExpectedCoinAmount();
}
minAmount = abi.decode(responseData, (uint256));
}
/**
* @notice Returns the amount of coin i received for burning a given amount of LP tokens
* @notice Method to be used with Curve NG pools
* @param _curvePool The address of the curve pool
* @param _lpTokenAmount The amount of the LP tokens to burn
* @param _i The index of the unique coin to withdraw
* @return minAmount The expected amount of coin i withdrawn from the curve pool
*/
function previewRemoveLiquidityOneCoinNG(
address _curvePool,
uint256 _lpTokenAmount,
uint256 _i
) external view returns (uint256 minAmount) {
(bool success, bytes memory responseData) = _curvePool.staticcall(
abi.encodeCall(ICurveNGPool(address(0)).calc_withdraw_one_coin, (_lpTokenAmount, _i))
);
if (!success) {
revert FailedToFetchExpectedCoinAmount();
}
minAmount = abi.decode(responseData, (uint256));
}
/**
* @notice Returns the amount of coin i received for burning a given amount of LP tokens
* @notice Method to be used with StableSwap NG pools
* @param _curvePool The address of the curve pool
* @param _lpTokenAmount The amount of the LP tokens to burn
* @param _i The index of the unique coin to withdraw
* @return minAmount The expected amount of coin i withdrawn from the curve pool
*/
function previewRemoveLiquidityOneCoinSNG(
address _curvePool,
uint256 _lpTokenAmount,
int128 _i
) external view returns (uint256 minAmount) {
(bool success, bytes memory responseData) = _curvePool.staticcall(
abi.encodeCall(IStableSwapNG(address(0)).calc_withdraw_one_coin, (_lpTokenAmount, _i))
);
if (!success) {
revert FailedToFetchExpectedCoinAmount();
}
minAmount = abi.decode(responseData, (uint256));
}
/**
* @notice Return the amount of IBT to deposit in the curve pool, given the total amount of IBT available for deposit
* @param _amount The total amount of IBT available for deposit
* @param _curvePool The address of the pool to deposit the amounts
* @param _pt The address of the PT
* @return ibts The amount of IBT which will be deposited in the curve pool
*/
function calcIBTsToTokenizeForCurvePool(
uint256 _amount,
address _curvePool,
address _pt
) external view returns (uint256 ibts) {
(uint256 ibtBalance, uint256 ptBalance) = _getCurvePoolBalances(_curvePool);
uint256 ibtBalanceInPT = IPrincipalToken(_pt).previewDepositIBT(ibtBalance);
// Liquidity added in a ratio that (closely) matches the existing pool's ratio
ibts = _amount.mulDiv(ptBalance, ibtBalanceInPT + ptBalance);
}
/**
* @notice Return the amount of IBT to deposit in the curve pool given the proportion in which we want to deposit, given the total amount of IBT available for deposit
* @param _amount The total amount of IBT available for deposit
* @param _prop The proportion in which we want to make the deposit: _prop = nIBT / (nIBT + nPT)
* @param _pt The address of the PT
* @return ibts The amount of IBT which will be deposited in the curve pool
*/
function calcIBTsToTokenizeForCurvePoolCustomProp(
uint256 _amount,
uint256 _prop,
address _pt
) external view returns (uint256 ibts) {
uint256 rate = IPrincipalToken(_pt).previewDepositIBT(_amount).mulDiv(CURVE_UNIT, _amount);
ibts = _amount.mulDiv(CURVE_UNIT, CURVE_UNIT + _prop.mulDiv(rate, CURVE_UNIT));
}
/**
* @param _curvePool : PT/IBT curve pool
* @param _i token index
* @param _j token index
* @param _targetDy amount out desired
* @return dx The amount of token to provide in order to obtain _targetDy after swap
*/
function getDx(
address _curvePool,
uint256 _i,
uint256 _j,
uint256 _targetDy
) external view returns (uint256 dx) {
// Initial guesses
uint256 _minGuess = type(uint256).max;
uint256 _maxGuess = type(uint256).max;
uint256 _factor100;
uint256 _guess = ICurvePool(_curvePool).get_dy(_i, _j, _targetDy);
if (_guess > _targetDy) {
_maxGuess = _targetDy;
_factor100 = 10;
} else {
_minGuess = _targetDy;
_factor100 = 1000;
}
uint256 loops;
_guess = _targetDy;
while (!_dxSolved(_curvePool, _i, _j, _guess, _targetDy, _minGuess, _maxGuess)) {
loops++;
(_minGuess, _maxGuess, _guess) = _runLoop(
_minGuess,
_maxGuess,
_factor100,
_guess,
_targetDy,
_curvePool,
_i,
_j
);
if (loops >= MAX_ITERATIONS_BINSEARCH) {
revert SolutionNotFound();
}
}
dx = _guess;
}
/**
* @dev Runs bisection search
* @param _minGuess lower bound on searched value
* @param _maxGuess upper bound on searched value
* @param _factor100 search interval scaling factor
* @param _guess The previous guess for the `dx` value that is being refined through the search process
* @param _targetDy The target output of the `get_dy` function, which the search aims to achieve by adjusting `dx`.
* @param _curvePool PT/IBT curve pool
* @param _i token index, either 0 or 1
* @param _j token index, either 0 or 1, must be different than _i
* @return The lower bound on _guess, upper bound on _guess and next _guess
*/
function _runLoop(
uint256 _minGuess,
uint256 _maxGuess,
uint256 _factor100,
uint256 _guess,
uint256 _targetDy,
address _curvePool,
uint256 _i,
uint256 _j
) internal view returns (uint256, uint256, uint256) {
if (_minGuess == type(uint256).max || _maxGuess == type(uint256).max) {
_guess = (_guess * _factor100) / 100;
} else {
_guess = (_maxGuess + _minGuess) >> 1;
}
uint256 dy = ICurvePool(_curvePool).get_dy(_i, _j, _guess);
if (dy < _targetDy) {
_minGuess = _guess;
} else if (dy > _targetDy) {
_maxGuess = _guess;
}
return (_minGuess, _maxGuess, _guess);
}
/**
* @dev Returns true if algorithm converged
* @param _curvePool PT/IBT curve pool
* @param _i token index, either 0 or 1
* @param _j token index, either 0 or 1, must be different than _i
* @param _dx The current guess for the `dx` value that is being refined through the search process.
* @param _targetDy The target output of the `get_dy` function, which the search aims to achieve by adjusting `dx`.
* @param _minGuess lower bound on searched value
* @param _maxGuess upper bound on searched value
* @return true if the solution to the search problem was found, false otherwise
*/
function _dxSolved(
address _curvePool,
uint256 _i,
uint256 _j,
uint256 _dx,
uint256 _targetDy,
uint256 _minGuess,
uint256 _maxGuess
) internal view returns (bool) {
if (_minGuess == type(uint256).max || _maxGuess == type(uint256).max) {
return false;
}
uint256 dy = ICurvePool(_curvePool).get_dy(_i, _j, _dx);
if (dy == _targetDy) {
return true;
}
uint256 dy1 = ICurvePool(_curvePool).get_dy(_i, _j, _dx + 1);
if (dy < _targetDy && _targetDy < dy1) {
return true;
}
return false;
}
/**
* @notice Returns the balances of the two tokens in provided curve pool
* @param _curvePool address of the curve pool
* @return The IBT and PT balances of the curve pool
*/
function _getCurvePoolBalances(address _curvePool) internal view returns (uint256, uint256) {
return (ICurvePool(_curvePool).balances(0), ICurvePool(_curvePool).balances(1));
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC20Metadata.sol)
pragma solidity ^0.8.20;
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (access/manager/AuthorityUtils.sol)
pragma solidity ^0.8.20;
import {IAuthority} from "./IAuthority.sol";
library AuthorityUtils {
/**
* @dev Since `AccessManager` implements an extended IAuthority interface, invoking `canCall` with backwards compatibility
* for the preexisting `IAuthority` interface requires special care to avoid reverting on insufficient return data.
* This helper function takes care of invoking `canCall` in a backwards compatible way without reverting.
*/
function canCallWithDelay(
address authority,
address caller,
address target,
bytes4 selector
) internal view returns (bool immediate, uint32 delay) {
(bool success, bytes memory data) = authority.staticcall(
abi.encodeCall(IAuthority.canCall, (caller, target, selector))
);
if (success) {
if (data.length >= 0x40) {
(immediate, delay) = abi.decode(data, (bool, uint32));
} else if (data.length >= 0x20) {
immediate = abi.decode(data, (bool));
}
}
return (immediate, delay);
}
}// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.4;
interface IStableSwapNG {
function A() external view returns (uint256);
function A_precise() external view returns (uint256);
function DOMAIN_SEPARATOR() external view returns (bytes32);
function D_ma_time() external view returns (uint256);
function D_oracle() external view returns (uint256);
function N_COINS() external view returns (uint256);
function add_liquidity(
uint256[] memory _amounts,
uint256 _min_mint_amount,
address _receiver
) external returns (uint256);
function admin_balances(uint256 arg0) external view returns (uint256);
function admin_fee() external view returns (uint256);
function allowance(address arg0, address arg1) external view returns (uint256);
function approve(address _spender, uint256 _value) external returns (bool);
function balanceOf(address arg0) external view returns (uint256);
function balances(uint256 i) external view returns (uint256);
function calc_token_amount(
uint256[] memory _amounts,
bool _is_deposit
) external view returns (uint256);
function calc_withdraw_one_coin(uint256 _burn_amount, int128 i) external view returns (uint256);
function coins(uint256 arg0) external view returns (address);
function decimals() external view returns (uint8);
function dynamic_fee(int128 i, int128 j) external view returns (uint256);
function ema_price(uint256 i) external view returns (uint256);
function exchange(int128 i, int128 j, uint256 _dx, uint256 _min_dy) external returns (uint256);
function exchange(
int128 i,
int128 j,
uint256 _dx,
uint256 _min_dy,
address _receiver
) external returns (uint256);
function exchange_received(
int128 i,
int128 j,
uint256 _dx,
uint256 _min_dy
) external returns (uint256);
function exchange_received(
int128 i,
int128 j,
uint256 _dx,
uint256 _min_dy,
address _receiver
) external returns (uint256);
function fee() external view returns (uint256);
function future_A() external view returns (uint256);
function future_A_time() external view returns (uint256);
function get_balances() external view returns (uint256[] memory);
function get_dx(int128 i, int128 j, uint256 dy) external view returns (uint256);
function get_dy(int128 i, int128 j, uint256 dx) external view returns (uint256);
function get_p(uint256 i) external view returns (uint256);
function get_virtual_price() external view returns (uint256);
function initial_A() external view returns (uint256);
function initial_A_time() external view returns (uint256);
function last_price(uint256 i) external view returns (uint256);
function ma_exp_time() external view returns (uint256);
function ma_last_time() external view returns (uint256);
function name() external view returns (string memory);
function nonces(address arg0) external view returns (uint256);
function offpeg_fee_multiplier() external view returns (uint256);
function permit(
address _owner,
address _spender,
uint256 _value,
uint256 _deadline,
uint8 _v,
bytes32 _r,
bytes32 _s
) external returns (bool);
function price_oracle(uint256 i) external view returns (uint256);
function ramp_A(uint256 _future_A, uint256 _future_time) external;
function remove_liquidity(
uint256 _burn_amount,
uint256[] memory _min_amounts
) external returns (uint256[] memory);
function remove_liquidity(
uint256 _burn_amount,
uint256[] memory _min_amounts,
address _receiver
) external returns (uint256[] memory);
function remove_liquidity(
uint256 _burn_amount,
uint256[] memory _min_amounts,
address _receiver,
bool _claim_admin_fees
) external returns (uint256[] memory);
function remove_liquidity_imbalance(
uint256[] memory _amounts,
uint256 _max_burn_amount
) external returns (uint256);
function remove_liquidity_imbalance(
uint256[] memory _amounts,
uint256 _max_burn_amount,
address _receiver
) external returns (uint256);
function remove_liquidity_one_coin(
uint256 _burn_amount,
int128 i,
uint256 _min_received
) external returns (uint256);
function remove_liquidity_one_coin(
uint256 _burn_amount,
int128 i,
uint256 _min_received,
address _receiver
) external returns (uint256);
function salt() external view returns (bytes32);
function set_ma_exp_time(uint256 _ma_exp_time, uint256 _D_ma_time) external;
function set_new_fee(uint256 _new_fee, uint256 _new_offpeg_fee_multiplier) external;
function stop_ramp_A() external;
function stored_rates() external view returns (uint256[] memory);
function symbol() external view returns (string memory);
function totalSupply() external view returns (uint256);
function transfer(address _to, uint256 _value) external returns (bool);
function transferFrom(address _from, address _to, uint256 _value) external returns (bool);
function version() external view returns (string memory);
function withdraw_admin_fees() external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
/**
* @dev Interface for Curve CryptoSwap pool
*/
interface ICurvePool {
function coins(uint256 index) external view returns (address);
function balances(uint256 index) external view returns (uint256);
function A() external view returns (uint256);
function gamma() external view returns (uint256);
function D() external view returns (uint256);
function token() external view returns (address);
function price_scale() external view returns (uint256);
function future_A_gamma_time() external view returns (uint256);
function future_A_gamma() external view returns (uint256);
function initial_A_gamma_time() external view returns (uint256);
function initial_A_gamma() external view returns (uint256);
function fee_gamma() external view returns (uint256);
function mid_fee() external view returns (uint256);
function out_fee() external view returns (uint256);
function allowed_extra_profit() external view returns (uint256);
function adjustment_step() external view returns (uint256);
function admin_fee() external view returns (uint256);
function ma_half_time() external view returns (uint256);
function get_virtual_price() external view returns (uint256);
function fee() external view returns (uint256);
function get_dy(uint256 i, uint256 j, uint256 dx) external view returns (uint256);
function last_prices() external view returns (uint256);
function calc_token_amount(uint256[2] calldata amounts) external view returns (uint256);
function calc_withdraw_one_coin(
uint256 _token_amount,
uint256 i
) external view returns (uint256);
function exchange(
uint256 i,
uint256 j,
uint256 dx,
uint256 min_dy,
bool use_eth,
address receiver
) external returns (uint256);
function add_liquidity(
uint256[2] calldata amounts,
uint256 min_mint_amount
) external returns (uint256);
function add_liquidity(
uint256[2] calldata amounts,
uint256 min_mint_amount,
bool use_eth,
address receiver
) external returns (uint256);
function remove_liquidity(uint256 amount, uint256[2] calldata min_amounts) external;
function remove_liquidity(
uint256 amount,
uint256[2] calldata min_amounts,
bool use_eth,
address receiver
) external;
function remove_liquidity_one_coin(
uint256 token_amount,
uint256 i,
uint256 min_amount
) external;
function remove_liquidity_one_coin(
uint256 token_amount,
uint256 i,
uint256 min_amount,
bool use_eth,
address receiver
) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.
pragma solidity ^0.8.20;
/**
* @dev Wrappers over Solidity's uintXX/intXX 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);
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
/**
* @title RayMath library
* @author Spectra Finance
* @notice Provides conversions for/to any decimal tokens to/from ray.
* @dev Conversions from Ray are rounded down.
*/
library RayMath {
/// @dev 27 decimal unit
uint256 public constant RAY_UNIT = 1e27;
uint256 public constant RAY_DECIMALS = 27;
/**
* @dev Converts a value from Ray (27-decimal precision) to a representation with a specified number of decimals.
* @param _a The amount in Ray to be converted. Ray is a fixed-point representation with 27 decimals.
* @param _decimals The target decimal precision for the converted amount.
* @return b The amount converted from Ray to the specified decimal precision.
*/
function fromRay(uint256 _a, uint256 _decimals) internal pure returns (uint256 b) {
uint256 decimals_ratio = 10 ** (RAY_DECIMALS - _decimals);
assembly {
b := div(_a, decimals_ratio)
}
}
/**
* @dev Converts a value from Ray (27-decimal precision) to a representation with a specified number of decimals.
* @param _a The amount in Ray to be converted. Ray is a fixed-point representation with 27 decimals.
* @param _decimals The target decimal precision for the converted amount.
* @param _roundUp If true, the function rounds up the result to the nearest integer value.
* If false, it truncates (rounds down) to the nearest integer.
* @return b The amount converted from Ray to the specified decimal precision, rounded as specified.
*/
function fromRay(
uint256 _a,
uint256 _decimals,
bool _roundUp
) internal pure returns (uint256 b) {
uint256 decimals_ratio = 10 ** (RAY_DECIMALS - _decimals);
assembly {
b := div(_a, decimals_ratio)
if and(eq(_roundUp, 1), gt(mod(_a, decimals_ratio), 0)) {
b := add(b, 1)
}
}
}
/**
* @dev Converts a value with a specified number of decimals to Ray (27-decimal precision).
* @param _a The amount to be converted, specified in a decimal format.
* @param _decimals The number of decimals in the representation of 'a'.
* @return b The amount in Ray, converted from the specified decimal precision.
* Ensures that the conversion maintains the value's integrity (no overflow).
*/
function toRay(uint256 _a, uint256 _decimals) internal pure returns (uint256 b) {
uint256 decimals_ratio = 10 ** (RAY_DECIMALS - _decimals);
// to avoid overflow, b/decimals_ratio == _a
assembly {
b := mul(_a, decimals_ratio)
if iszero(eq(div(b, decimals_ratio), _a)) {
revert(0, 0)
}
}
}
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity 0.8.20;
library Constants {
/// @dev 18 decimal unit
uint256 internal constant UNIT = 1e18;
/// @dev identifier for native ETH
address public constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE;
/// @dev maximal number of iterations in the secant method algorithm
uint256 internal constant MAX_ITERATIONS_SECANT = 255;
/// @dev maximal number of iterations in the linear search following secant method algorithm
uint256 internal constant MAX_ITERATIONS_LINEAR_SEARCH = 255;
/// @dev determines the rate at which an input value is scaled in each iteration of linear search
uint256 internal constant SCALING_FACTOR_LINEAR_SEARCH = 1e6;
/// @dev precision divisor for the secant method
uint256 internal constant PRECISION_DIVISOR = 1000;
/// @dev Used for identifying cases when this contract's balance of a token is to be used as an input
/// This value is equivalent to 1<<255, i.e. a singular 1 in the most significant bit.
uint256 internal constant CONTRACT_BALANCE =
0x8000000000000000000000000000000000000000000000000000000000000000;
/// @dev Used as a flag for identifying that msg.sender should be used, saves gas by sending more 0 bytes
address internal constant MSG_SENDER = address(0xc0);
/// @dev Used as a flag for identifying address(this) should be used, saves gas by sending more 0 bytes
address internal constant ADDRESS_THIS = address(0xe0);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Pausable.sol)
pragma solidity ^0.8.20;
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.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 PausableUpgradeable is Initializable, ContextUpgradeable {
/// @custom:storage-location erc7201:openzeppelin.storage.Pausable
struct PausableStorage {
bool _paused;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;
function _getPausableStorage() private pure returns (PausableStorage storage $) {
assembly {
$.slot := PausableStorageLocation
}
}
/**
* @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 Initializes the contract in unpaused state.
*/
function __Pausable_init() internal onlyInitializing {
__Pausable_init_unchained();
}
function __Pausable_init_unchained() internal onlyInitializing {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
}
/**
* @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) {
PausableStorage storage $ = _getPausableStorage();
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 {
PausableStorage storage $ = _getPausableStorage();
$._paused = true;
emit Paused(_msgSender());
}
/**
* @dev Returns to normal state.
*
* Requirements:
*
* - The contract must be paused.
*/
function _unpause() internal virtual whenPaused {
PausableStorage storage $ = _getPausableStorage();
$._paused = false;
emit Unpaused(_msgSender());
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC3156FlashLender.sol)
pragma solidity ^0.8.20;
import {IERC3156FlashBorrower} from "./IERC3156FlashBorrower.sol";
/**
* @dev Interface of the ERC3156 FlashLender, as defined in
* https://eips.ethereum.org/EIPS/eip-3156[ERC-3156].
*/
interface IERC3156FlashLender {
/**
* @dev The amount of currency available to be lended.
* @param token The loan currency.
* @return The amount of `token` that can be borrowed.
*/
function maxFlashLoan(address token) external view returns (uint256);
/**
* @dev The fee to be charged for a given loan.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @return The amount of `token` to be charged for the loan, on top of the returned principal.
*/
function flashFee(address token, uint256 amount) external view returns (uint256);
/**
* @dev Initiate a flash loan.
* @param receiver The receiver of the tokens in the loan, and the receiver of the callback.
* @param token The loan currency.
* @param amount The amount of tokens lent.
* @param data Arbitrary data structure, intended to contain user-defined parameters.
*/
function flashLoan(
IERC3156FlashBorrower receiver,
address token,
uint256 amount,
bytes calldata data
) external returns (bool);
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)
pragma solidity ^0.8.20;
/**
* @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
* https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
*
* Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
* presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
* need to send a transaction, and thus is not required to hold Ether at all.
*
* ==== Security Considerations
*
* There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
* expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
* considered as an intention to spend the allowance in any specific way. The second is that because permits have
* built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
* take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
* generally recommended is:
*
* ```solidity
* function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
* try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
* doThing(..., value);
* }
*
* function doThing(..., uint256 value) public {
* token.safeTransferFrom(msg.sender, address(this), value);
* ...
* }
* ```
*
* Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
* `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
* {SafeERC20-safeTransferFrom}).
*
* Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
* contracts should have entry points that don't rely on permit.
*/
interface IERC20Permit {
/**
* @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
* given ``owner``'s signed approval.
*
* IMPORTANT: The same issues {IERC20-approve} has related to transaction
* ordering also apply here.
*
* Emits an {Approval} event.
*
* Requirements:
*
* - `spender` cannot be the zero address.
* - `deadline` must be a timestamp in the future.
* - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
* over the EIP712-formatted function arguments.
* - the signature must use ``owner``'s current nonce (see {nonces}).
*
* For more information on the signature format, see the
* https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
* section].
*
* CAUTION: See Security Considerations above.
*/
function permit(
address owner,
address spender,
uint256 value,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
) external;
/**
* @dev Returns the current nonce for `owner`. This value must be
* included whenever a signature is generated for {permit}.
*
* Every successful call to {permit} increases ``owner``'s nonce by one. This
* prevents a signature from being used multiple times.
*/
function nonces(address owner) external view returns (uint256);
/**
* @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
*/
// solhint-disable-next-line func-name-mixedcase
function DOMAIN_SEPARATOR() external view returns (bytes32);
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
import {IERC20Metadata} from "openzeppelin-contracts/token/ERC20/extensions/IERC20Metadata.sol";
/**
* @dev Interface for Curve TwoCrypto-NG pool
*/
interface ICurveNGPool is IERC20Metadata {
function coins(uint256 index) external view returns (address);
function balances(uint256 index) external view returns (uint256);
function A() external view returns (uint256);
function gamma() external view returns (uint256);
function D() external view returns (uint256);
function token() external view returns (address);
function price_scale() external view returns (uint256);
function price_oracle() external view returns (uint256);
function future_A_gamma_time() external view returns (uint256);
function future_A_gamma() external view returns (uint256);
function initial_A_gamma_time() external view returns (uint256);
function initial_A_gamma() external view returns (uint256);
function fee_gamma() external view returns (uint256);
function mid_fee() external view returns (uint256);
function out_fee() external view returns (uint256);
function allowed_extra_profit() external view returns (uint256);
function adjustment_step() external view returns (uint256);
function admin_fee() external view returns (uint256);
function ma_time() external view returns (uint256);
function get_virtual_price() external view returns (uint256);
function fee() external view returns (uint256);
function get_dy(uint256 i, uint256 j, uint256 dx) external view returns (uint256);
function get_dx(uint256 i, uint256 j, uint256 dy) external view returns (uint256);
function last_prices() external view returns (uint256);
function calc_token_amount(
uint256[2] calldata amounts,
bool deposit
) external view returns (uint256);
function calc_withdraw_one_coin(
uint256 _token_amount,
uint256 i
) external view returns (uint256);
function exchange(uint256 i, uint256 j, uint256 dx, uint256 min_dy) external returns (uint256);
function exchange(
uint256 i,
uint256 j,
uint256 dx,
uint256 min_dy,
address receiver
) external returns (uint256);
function add_liquidity(
uint256[2] calldata amounts,
uint256 min_mint_amount
) external returns (uint256);
function add_liquidity(
uint256[2] calldata amounts,
uint256 min_mint_amount,
address receiver
) external returns (uint256);
function remove_liquidity(uint256 amount, uint256[2] calldata min_amounts) external;
function remove_liquidity(
uint256 amount,
uint256[2] calldata min_amounts,
address receiver
) external;
function remove_liquidity_one_coin(
uint256 token_amount,
uint256 i,
uint256 min_amount
) external;
function remove_liquidity_one_coin(
uint256 token_amount,
uint256 i,
uint256 min_amount,
address receiver
) external;
}// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.20;
interface IRegistry {
/* Errors
*****************************************************************************************************************/
error FeeGreaterThanMaxValue();
error PTListUpdateFailed();
error ReductionTooBig();
error AddressError();
/* GETTERS
*****************************************************************************************************************/
/**
* @notice Getter for the factory address
* @return The address of token factory
*/
function getFactory() external view returns (address);
/**
* @notice Get the address of the router
* @return The address of the router
*/
function getRouter() external view returns (address);
/**
* @notice Get the address of the routerUtil
* @return The address of the routerUtil
*/
function getRouterUtil() external view returns (address);
/**
* @notice Get the address of the pt beacon
* @return The address of PT beacon
*/
function getPTBeacon() external view returns (address);
/**
* @notice Get the address of the yt beacon
* @return The address of yt beacon
*/
function getYTBeacon() external view returns (address);
/**
* @notice Get the value of tokenization fee
* @return The value of tokenization fee
*/
function getTokenizationFee() external view returns (uint256);
/**
* @notice Get the value of yield fee
* @return The value of yield fee
*/
function getYieldFee() external view returns (uint256);
/**
* @notice Get the value of PT flash loan fee
* @return The value of PT flash loan fee
*/
function getPTFlashLoanFee() external view returns (uint256);
/**
* @notice Get the address of the fee collector
* @return The address of fee collector
*/
function getFeeCollector() external view returns (address);
/**
* @notice Get the fee reduction of the given user for the given pt
* @param _pt The address of the pt
* @param _user The address of the user
* @return The fee reduction of the given user for the given pt
*/
function getFeeReduction(address _pt, address _user) external view returns (uint256);
/**
* @notice Getter to check if a pt is registered
* @param _pt the address of the pt to check the registration of
* @return true if it is, false otherwise
*/
function isRegisteredPT(address _pt) external view returns (bool);
/**
* @notice Getter for the pt registered at an index
* @param _index the index of the pt to return
* @return The address of the corresponding pt
*/
function getPTAt(uint256 _index) external view returns (address);
/**
* @notice Getter for number of PT registered
* @return The number of PT registered
*/
function pTCount() external view returns (uint256);
/* SETTERS
*****************************************************************************************************************/
/**
* @notice Setter for the tokens factory address
* @param _newFactory The address of the new factory
*/
function setFactory(address _newFactory) external;
/**
* @notice set the router
* @param _router The address of the router
*/
function setRouter(address _router) external;
/**
* @notice set the routerUtil
* @param _routerUtil The address of the routerUtil
*/
function setRouterUtil(address _routerUtil) external;
/**
* @notice set the tokenization fee
* @param _tokenizationFee The value of tokenization fee
*/
function setTokenizationFee(uint256 _tokenizationFee) external;
/**
* @notice set the yield fee
* @param _yieldFee The value of yield fee
*/
function setYieldFee(uint256 _yieldFee) external;
/**
* @notice set the PT flash loan fee
* @param _ptFlashLoanFee The value of PT flash loan fee
*/
function setPTFlashLoanFee(uint256 _ptFlashLoanFee) external;
/**
* @notice set the fee collector
* @param _feeCollector The address of fee collector
*/
function setFeeCollector(address _feeCollector) external;
/**
* @notice Set the fee reduction of the given pt for the given user
* @param _pt The address of the pt
* @param _user The address of the user
* @param _reduction The fee reduction
*/
function reduceFee(address _pt, address _user, uint256 _reduction) external;
/**
* @notice set the pt beacon
* @param _ptBeacon The address of PT beacon
*/
function setPTBeacon(address _ptBeacon) external;
/**
* @notice set the yt beacon
* @param _ytBeacon The address of yt beacon
*/
function setYTBeacon(address _ytBeacon) external;
/**
* @notice Add a pt to the registry
* @param _pt The address of the pt to add to the registry
*/
function addPT(address _pt) external;
/**
* @notice Remove a pt from the registry
* @param _pt The address of the pt to remove from the registry
*/
function removePT(address _pt) external;
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol)
pragma solidity ^0.8.20;
/**
* @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
* behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
* external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
* function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
*
* The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
* reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
* case an upgrade adds a module that needs to be initialized.
*
* For example:
*
* [.hljs-theme-light.nopadding]
* ```solidity
* contract MyToken is ERC20Upgradeable {
* function initialize() initializer public {
* __ERC20_init("MyToken", "MTK");
* }
* }
*
* contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
* function initializeV2() reinitializer(2) public {
* __ERC20Permit_init("MyToken");
* }
* }
* ```
*
* TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
* possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
*
* CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
* that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
*
* [CAUTION]
* ====
* Avoid leaving a contract uninitialized.
*
* An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
* contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
* the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
*
* [.hljs-theme-light.nopadding]
* ```
* /// @custom:oz-upgrades-unsafe-allow constructor
* constructor() {
* _disableInitializers();
* }
* ```
* ====
*/
abstract contract Initializable {
/**
* @dev Storage of the initializable contract.
*
* It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
* when using with upgradeable contracts.
*
* @custom:storage-location erc7201:openzeppelin.storage.Initializable
*/
struct InitializableStorage {
/**
* @dev Indicates that the contract has been initialized.
*/
uint64 _initialized;
/**
* @dev Indicates that the contract is in the process of being initialized.
*/
bool _initializing;
}
// keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;
/**
* @dev The contract is already initialized.
*/
error InvalidInitialization();
/**
* @dev The contract is not initializing.
*/
error NotInitializing();
/**
* @dev Triggered when the contract has been initialized or reinitialized.
*/
event Initialized(uint64 version);
/**
* @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
* `onlyInitializing` functions can be used to initialize parent contracts.
*
* Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
* number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
* production.
*
* Emits an {Initialized} event.
*/
modifier initializer() {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
// Cache values to avoid duplicated sloads
bool isTopLevelCall = !$._initializing;
uint64 initialized = $._initialized;
// Allowed calls:
// - initialSetup: the contract is not in the initializing state and no previous version was
// initialized
// - construction: the contract is initialized at version 1 (no reininitialization) and the
// current contract is just being deployed
bool initialSetup = initialized == 0 && isTopLevelCall;
bool construction = initialized == 1 && address(this).code.length == 0;
if (!initialSetup && !construction) {
revert InvalidInitialization();
}
$._initialized = 1;
if (isTopLevelCall) {
$._initializing = true;
}
_;
if (isTopLevelCall) {
$._initializing = false;
emit Initialized(1);
}
}
/**
* @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
* contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
* used to initialize parent contracts.
*
* A reinitializer may be used after the original initialization step. This is essential to configure modules that
* are added through upgrades and that require initialization.
*
* When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
* cannot be nested. If one is invoked in the context of another, execution will revert.
*
* Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
* a contract, executing them in the right order is up to the developer or operator.
*
* WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
*
* Emits an {Initialized} event.
*/
modifier reinitializer(uint64 version) {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing || $._initialized >= version) {
revert InvalidInitialization();
}
$._initialized = version;
$._initializing = true;
_;
$._initializing = false;
emit Initialized(version);
}
/**
* @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
* {initializer} and {reinitializer} modifiers, directly or indirectly.
*/
modifier onlyInitializing() {
_checkInitializing();
_;
}
/**
* @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
*/
function _checkInitializing() internal view virtual {
if (!_isInitializing()) {
revert NotInitializing();
}
}
/**
* @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
* Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
* to any version. It is recommended to use this to lock implementation contracts that are designed to be called
* through proxies.
*
* Emits an {Initialized} event the first time it is successfully executed.
*/
function _disableInitializers() internal virtual {
// solhint-disable-next-line var-name-mixedcase
InitializableStorage storage $ = _getInitializableStorage();
if ($._initializing) {
revert InvalidInitialization();
}
if ($._initialized != type(uint64).max) {
$._initialized = type(uint64).max;
emit Initialized(type(uint64).max);
}
}
/**
* @dev Returns the highest version that has been initialized. See {reinitializer}.
*/
function _getInitializedVersion() internal view returns (uint64) {
return _getInitializableStorage()._initialized;
}
/**
* @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
*/
function _isInitializing() internal view returns (bool) {
return _getInitializableStorage()._initializing;
}
/**
* @dev Returns a pointer to the storage namespace.
*/
// solhint-disable-next-line var-name-mixedcase
function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
assembly {
$.slot := INITIALIZABLE_STORAGE
}
}
}// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol)
pragma solidity ^0.8.20;
/**
* @dev Collection of functions related to the address type
*/
library Address {
/**
* @dev The ETH balance of the account is not enough to perform the operation.
*/
error AddressInsufficientBalance(address account);
/**
* @dev There's no code at `target` (it is not a contract).
*/
error AddressEmptyCode(address target);
/**
* @dev A call to an address target failed. The target may have reverted.
*/
error FailedInnerCall();
/**
* @dev Replacement for Solidity's `transfer`: sends `amount` wei to
* `recipient`, forwarding all available gas and reverting on errors.
*
* https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
* of certain opcodes, possibly making contracts go over the 2300 gas limit
* imposed by `transfer`, making them unable to receive funds via
* `transfer`. {sendValue} removes this limitation.
*
* https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
*
* IMPORTANT: because control is transferred to `recipient`, care must be
* taken to not create reentrancy vulnerabilities. Consider using
* {ReentrancyGuard} or the
* https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
*/
function sendValue(address payable recipient, uint256 amount) internal {
if (address(this).balance < amount) {
revert AddressInsufficientBalance(address(this));
}
(bool success, ) = recipient.call{value: amount}("");
if (!success) {
revert FailedInnerCall();
}
}
/**
* @dev Performs a Solidity function call using a low level `call`. A
* plain `call` is an unsafe replacement for a function call: use this
* function instead.
*
* If `target` reverts with a revert reason or custom error, it is bubbled
* up by this function (like regular Solidity function calls). However, if
* the call reverted with no returned reason, this function reverts with a
* {FailedInnerCall} error.
*
* Returns the raw returned data. To convert to the expected return value,
* use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
*
* Requirements:
*
* - `target` must be a contract.
* - calling `target` with `data` must not revert.
*/
function functionCall(address target, bytes memory data) internal returns (bytes memory) {
return functionCallWithValue(target, data, 0);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but also transferring `value` wei to `target`.
*
* Requirements:
*
* - the calling contract must have an ETH balance of at least `value`.
* - the called Solidity function must be `payable`.
*/
function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
if (address(this).balance < value) {
revert AddressInsufficientBalance(address(this));
}
(bool success, bytes memory returndata) = target.call{value: value}(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a static call.
*/
function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
(bool success, bytes memory returndata) = target.staticcall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
* but performing a delegate call.
*/
function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
(bool success, bytes memory returndata) = target.delegatecall(data);
return verifyCallResultFromTarget(target, success, returndata);
}
/**
* @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
* was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
* unsuccessful call.
*/
function verifyCallResultFromTarget(
address target,
bool success,
bytes memory returndata
) internal view returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
// only check if target is a contract if the call was successful and the return data is empty
// otherwise we already know that it was a contract
if (returndata.length == 0 && target.code.length == 0) {
revert AddressEmptyCode(target);
}
return returndata;
}
}
/**
* @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
* revert reason or with a default {FailedInnerCall} error.
*/
function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
if (!success) {
_revert(returndata);
} else {
return returndata;
}
}
/**
* @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}.
*/
function _revert(bytes memory returndata) private pure {
// Look for revert reason and bubble it up if present
if (returndata.length > 0) {
// The easiest way to bubble the revert reason is using memory via assembly
/// @solidity memory-safe-assembly
assembly {
let returndata_size := mload(returndata)
revert(add(32, returndata), returndata_size)
}
} else {
revert FailedInnerCall();
}
}
}{
"evmVersion": "shanghai",
"libraries": {
"src/libraries/CurvePoolUtil.sol": {
"CurvePoolUtil": "0x27857F8E0EEE20596e7cE5be3901efd663E91e10"
},
"src/libraries/NamingUtil.sol": {
"NamingUtil": "0x0347772f901f6324095aB8547FE5158B2eb93549"
},
"src/libraries/PrincipalTokenUtil.sol": {
"PrincipalTokenUtil": "0x622BCCf6B8A472a89be0ED4dBEe6C02600cE37f3"
}
},
"metadata": {
"appendCBOR": true,
"bytecodeHash": "ipfs",
"useLiteralContent": false
},
"optimizer": {
"enabled": true,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"remappings": [
"ds-test/=lib/forge-std/lib/ds-test/src/",
"erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
"forge-std/=lib/forge-std/src/",
"openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"openzeppelin-contracts/=lib/openzeppelin-contracts/contracts/",
"openzeppelin-erc20-basic/=lib/openzeppelin-contracts/contracts/token/ERC20/",
"openzeppelin-erc20-extensions/=lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/",
"openzeppelin-erc20/=lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/",
"openzeppelin-math/=lib/openzeppelin-contracts/contracts/utils/math/",
"openzeppelin-proxy/=lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/",
"openzeppelin-utils/=lib/openzeppelin-contracts/contracts/utils/",
"config/=lib/spectra-contracts-configs/script/",
"@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
"@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
"DiamondRouter/=lib/DiamondRouter/",
"halmos-cheatcodes/=lib/DiamondRouter/lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
"solidity-stringutils/=lib/DiamondRouter/lib/solidity-stringutils/",
"spectra-contracts-configs/=lib/spectra-contracts-configs/"
],
"viaIR": false
}Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
Contract ABI
API[{"inputs":[{"internalType":"address","name":"_registry","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[{"internalType":"address","name":"authority","type":"address"}],"name":"AccessManagedInvalidAuthority","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"},{"internalType":"uint32","name":"delay","type":"uint32"}],"name":"AccessManagedRequiredDelay","type":"error"},{"inputs":[{"internalType":"address","name":"caller","type":"address"}],"name":"AccessManagedUnauthorized","type":"error"},{"inputs":[{"internalType":"address","name":"target","type":"address"}],"name":"AddressEmptyCode","type":"error"},{"inputs":[],"name":"AddressError","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"AddressInsufficientBalance","type":"error"},{"inputs":[],"name":"AmountError","type":"error"},{"inputs":[],"name":"BalanceUnderflow","type":"error"},{"inputs":[],"name":"CallFailed","type":"error"},{"inputs":[],"name":"DirectOnFlashloanCall","type":"error"},{"inputs":[],"name":"EnforcedPause","type":"error"},{"inputs":[],"name":"ExpectedPause","type":"error"},{"inputs":[],"name":"FailedInnerCall","type":"error"},{"inputs":[{"internalType":"uint256","name":"commandType","type":"uint256"}],"name":"InvalidCommandType","type":"error"},{"inputs":[{"internalType":"address","name":"lender","type":"address"}],"name":"InvalidFlashloanLender","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"KyberRouterNotSet","type":"error"},{"inputs":[],"name":"LengthMismatch","type":"error"},{"inputs":[],"name":"MathOverflowedMulDiv","type":"error"},{"inputs":[],"name":"MaxInvolvedTokensExceeded","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"},{"internalType":"address","name":"owner","type":"address"},{"internalType":"uint256","name":"minimumBalance","type":"uint256"},{"internalType":"uint256","name":"actualBalance","type":"uint256"}],"name":"MinimumBalanceNotReached","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"PermitFailed","type":"error"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"SafeERC20FailedOperation","type":"error"},{"inputs":[],"name":"TransactionDeadlinePassed","type":"error"},{"inputs":[],"name":"UnauthorizedOnFlashloanCaller","type":"error"},{"inputs":[],"name":"UnauthorizedReentrantCall","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"authority","type":"address"}],"name":"AuthorityUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousKyberRouter","type":"address"},{"indexed":true,"internalType":"address","name":"newKyberRouter","type":"address"}],"name":"KyberRouterChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousRouterUtil","type":"address"},{"indexed":true,"internalType":"address","name":"newRouterUtil","type":"address"}],"name":"RouterUtilChange","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"authority","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_commands","type":"bytes"},{"internalType":"bytes[]","name":"_inputs","type":"bytes[]"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_commands","type":"bytes"},{"internalType":"bytes[]","name":"_inputs","type":"bytes[]"},{"internalType":"uint256","name":"_deadline","type":"uint256"}],"name":"execute","outputs":[],"stateMutability":"payable","type":"function"},{"inputs":[],"name":"getKyberRouter","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRegistry","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRouterUtil","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_routerUtil","type":"address"},{"internalType":"address","name":"_kyberRouter","type":"address"},{"internalType":"address","name":"_initialAuthority","type":"address"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isConsumingScheduledOp","outputs":[{"internalType":"bytes4","name":"","type":"bytes4"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"uint256","name":"_fee","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"onFlashLoan","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_commands","type":"bytes"},{"internalType":"bytes[]","name":"_inputs","type":"bytes[]"}],"name":"previewRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_commands","type":"bytes"},{"internalType":"bytes[]","name":"_inputs","type":"bytes[]"}],"name":"previewSpotRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newAuthority","type":"address"}],"name":"setAuthority","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_kyberRouter","type":"address"}],"name":"setKyberRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_routerUtil","type":"address"}],"name":"setRouterUtil","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"stateMutability":"payable","type":"receive"}]Contract Creation Code
60c06040527f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd960a05234801562000034575f80fd5b506040516200606b3803806200606b833981016040819052620000579162000151565b806001600160a01b0381166200008057604051630c59659760e31b815260040160405180910390fd5b6001600160a01b0316608052620000966200009d565b5062000180565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00805468010000000000000000900460ff1615620000ee5760405163f92ee8a960e01b815260040160405180910390fd5b80546001600160401b03908116146200014e5780546001600160401b0319166001600160401b0390811782556040519081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b50565b5f6020828403121562000162575f80fd5b81516001600160a01b038116811462000179575f80fd5b9392505050565b60805160a051615ea6620001c55f395f61052901525f81816101a1015281816113440152818161152f0152818161195901528181611c740152611fd50152615ea65ff3fe6080604052600436106100fd575f3560e01c80637a9e5e4b116100925780638fb36037116100625780638fb36037146102a0578063b748f092146102cd578063bf7e214f146102ec578063c0c53b8b14610300578063f7b188a51461031f575f80fd5b80637a9e5e4b1461022f5780638456cb591461024e5780638b0c9584146102625780638b33203c14610281575f80fd5b80635ab1bd53116100cd5780635ab1bd53146101935780635c975abb146101c557806361fbaaaa146101f357806369dfa6c214610210575f80fd5b806323e30c8b1461010857806324856bc31461013a5780633593564c1461014f578063466489fb14610162575f80fd5b3661010457005b5f80fd5b348015610113575f80fd5b50610127610122366004614e69565b610333565b6040519081526020015b60405180910390f35b61014d610148366004614f1f565b610557565b005b61014d61015d366004614f85565b61065b565b34801561016d575f80fd5b506002546001600160a01b03165b6040516001600160a01b039091168152602001610131565b34801561019e575f80fd5b507f000000000000000000000000000000000000000000000000000000000000000061017b565b3480156101d0575f80fd5b505f80516020615e518339815191525460ff166040519015158152602001610131565b3480156101fe575f80fd5b506003546001600160a01b031661017b565b34801561021b575f80fd5b5061012761022a366004614f1f565b610689565b34801561023a575f80fd5b5061014d610249366004614ff2565b6106a0565b348015610259575f80fd5b5061014d61072b565b34801561026d575f80fd5b5061014d61027c366004614ff2565b610741565b34801561028c575f80fd5b5061014d61029b366004614ff2565b6107cc565b3480156102ab575f80fd5b506102b4610830565b6040516001600160e01b03199091168152602001610131565b3480156102d8575f80fd5b506101276102e7366004614f1f565b610866565b3480156102f7575f80fd5b5061017b610875565b34801561030b575f80fd5b5061014d61031a36600461500d565b610890565b34801561032a575f80fd5b5061014d6109a8565b5f80546001600160a01b031661035c57604051630606fd0760e51b815260040160405180910390fd5b6001546001600160a01b031633146103875760405163242762f960e01b815260040160405180910390fd5b5f8061039584860186615126565b6040516324856bc360e01b8152919350915030906324856bc3906103bf9085908590600401615245565b5f604051808303815f87803b1580156103d6575f80fd5b505af11580156103e8573d5f803e3d5ffd5b505050505f86886103f991906152c9565b604051636eb1769f60e11b81523060048201523360248201529091505f906001600160a01b038b169063dd62ed3e90604401602060405180830381865afa158015610446573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061046a91906152dc565b905081811015610488576104886001600160a01b038b1633846109b9565b6040516370a0823160e01b81523060048201525f906001600160a01b038c16906370a0823190602401602060405180830381865afa1580156104cc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104f091906152dc565b905082811015610526575f54610526906001600160a01b03163061051484876152f3565b6001600160a01b038f16929190610a7c565b507f00000000000000000000000000000000000000000000000000000000000000009b9a5050505050505050505050565b61055f610ab5565b82818114610583576040516001621398b960e31b0319815260040160405180910390fd5b5f80546001600160a01b03166105b057505f80546001600160a01b031916331790553460045560016105d0565b3330146105d05760405163f9ce257360e01b815260040160405180910390fd5b5f5b82811015610639575f8787838181106105ed576105ed615306565b9050013560f81c60f81b9050365f87878581811061060d5761060d615306565b905060200281019061061f919061531a565b9150915061062e838383610ae5565b5050506001016105d2565b508015610653575f80546001600160a01b03191681556004555b505050505050565b808042111561067d57604051632dfb7c8b60e11b815260040160405180910390fd5b61065386868686610557565b5f610697858585855f612e8c565b95945050505050565b336106a9610875565b6001600160a01b0316816001600160a01b0316146106e95760405162d1953b60e31b81526001600160a01b03821660048201526024015b60405180910390fd5b816001600160a01b03163b5f0361071e576040516361798f2f60e11b81526001600160a01b03831660048201526024016106e0565b61072782612fb0565b5050565b610737335b5f36613010565b61073f613106565b565b61074a33610730565b6001600160a01b03811661077157604051630c59659760e31b815260040160405180910390fd5b6002546040516001600160a01b038084169216907fbacf07ffb3274576773b014d636ae5319ff022a18a5b0f958eaac799cf68439b905f90a3600280546001600160a01b0319166001600160a01b0392909216919091179055565b6107d533610730565b6003546040516001600160a01b038084169216907f775376a8cc8424fc322e2ac2b50230180c9622951b05587f5ab18ff432a03d49905f90a3600380546001600160a01b0319166001600160a01b0392909216919091179055565b5f80516020615e3183398151915280545f9190600160a01b900460ff16610857575f610860565b638fb3603760e01b5b91505090565b5f610697858585856001612e8c565b5f80516020615e31833981519152546001600160a01b031690565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156108d45750825b90505f826001600160401b031660011480156108ef5750303b155b9050811580156108fd575080155b1561091b5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561094557845460ff60401b1916600160401b1785555b61094f8888613168565b610958866131c5565b831561099e57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b6109b133610730565b61073f6131d9565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052610a0a848261321e565b610a76576040516001600160a01b0384811660248301525f6044830152610a6c91869182169063095ea7b3906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506132c1565b610a7684826132c1565b50505050565b6040516001600160a01b038481166024830152838116604483015260648201839052610a769186918216906323b872dd90608401610a3a565b5f80516020615e518339815191525460ff161561073f5760405163d93c066560e01b815260040160405180910390fd5b60f883901c603f1680610b26575f80610b008486018661535c565b5f549193509150610b1f906001600160a01b0380851691163084610a7c565b5050610a76565b60018103610c8e575f8080808080610b40888a018a615386565b5f5460405163d505accf60e01b81526001600160a01b039182166004820152306024820152604481018790526064810186905260ff8516608482015260a4810184905260c48101839052969c50949a509298509096509450925087169063d505accf9060e4015f604051808303815f87803b158015610bbd575f80fd5b505af1925050508015610bce575060015b610c69575f8054604051636eb1769f60e11b81526001600160a01b0391821660048201523060248201529088169063dd62ed3e90604401602060405180830381865afa158015610c20573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c4491906152dc565b905085811015610c675760405163b78cb0dd60e01b815260040160405180910390fd5b505b5f54610c83906001600160a01b0388811691163088610a7c565b505050505050610a76565b60028103610ce4575f8080610ca5858701876153e3565b925092509250610cb482613327565b9150610cc0838261336c565b90508015610cdc57610cdc6001600160a01b03841683836133eb565b505050610a76565b6003811480610cf35750601581145b80610cfe5750601e81145b15610f53575f8080808080610d15888a018a615421565b9550955095509550955095505f866001600160a01b031663c6610657876040518263ffffffff1660e01b8152600401610d5091815260200190565b602060405180830381865afa158015610d6b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d8f919061547b565b9050610d9b818561336c565b9350610da682613327565b9150610dbc6001600160a01b03821688866109b9565b60038803610e565760405163ce7d650360e01b8152600481018790526024810186905260448101859052606481018490525f60848201526001600160a01b0383811660a483015288169063ce7d65039060c4015b6020604051808303815f875af1158015610e2c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e5091906152dc565b50610f33565b60158803610ea857604051630532419d60e51b8152600481018790526024810186905260448101859052606481018490526001600160a01b03838116608483015288169063a64833a09060a401610e10565b60405163ddc1f59d60e01b8152600f87810b600483015286900b602482015260448101859052606481018490526001600160a01b03838116608483015288169063ddc1f59d9060a4016020604051808303815f875af1158015610f0d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f3191906152dc565b505b610f476001600160a01b038216885f6109b9565b50505050505050610a76565b60108103611097575f808080610f6b86880188615496565b93509350935093505f846001600160a01b031663732e86fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fb0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fd4919061547b565b9050610fdf83613327565b9250610feb818561336c565b93506110016001600160a01b03821686866109b9565b60405163680d5c7760e11b8152600481018590526001600160a01b0384811660248301526044820184905286169063d01ab8ee906064016020604051808303815f875af1158015611054573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061107891906152dc565b5061108d6001600160a01b038216865f6109b9565b5050505050610a76565b60118103611141575f8080806110af86880188615496565b93509350935093506110c082613327565b91506110cc848461336c565b604051631886c6df60e21b81529093506001600160a01b0385169063621b1b7c906111019086908690309087906004016154db565b6020604051808303815f875af115801561111d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061108d91906152dc565b6004810361127a575f808061115885870187615500565b9250925092505f836001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561119b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111bf919061547b565b90506111cb818461336c565b92506111d682613327565b91506111ec6001600160a01b03821685856109b9565b604051636e553f6560e01b8152600481018490526001600160a01b038381166024830152851690636e553f65906044016020604051808303815f875af1158015611238573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061125c91906152dc565b506112716001600160a01b038216855f6109b9565b50505050610a76565b60058103611465575f8080808061129387890189615534565b945094509450945094505f856001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112da573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112fe919061547b565b905061130a818661336c565b945061131584613327565b935061132083613327565b60405163f5e306f760e01b81526001600160a01b0388811660048301529194505f917f0000000000000000000000000000000000000000000000000000000000000000169063f5e306f790602401602060405180830381865afa158015611389573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113ad919061559a565b905080156113c5576113c082888861341c565b6113d9565b6113d96001600160a01b03831688886109b9565b604051630e4cca4b60e41b81526001600160a01b0388169063e4cca4b09061140b9089908990899089906004016154db565b6020604051808303815f875af1158015611427573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061144b91906152dc565b5080610f4757610f476001600160a01b038316885f6109b9565b600681036115f6575f8080808061147e87890189615534565b945094509450945094505f856001600160a01b031663c644fe946040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114c5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114e9919061547b565b90506114f5818661336c565b945061150084613327565b935061150b83613327565b60405163f5e306f760e01b81526001600160a01b0388811660048301529194505f917f0000000000000000000000000000000000000000000000000000000000000000169063f5e306f790602401602060405180830381865afa158015611574573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611598919061559a565b905080156115b0576115ab82888861341c565b6115c4565b6115c46001600160a01b03831688886109b9565b604051631520940360e11b81526001600160a01b03881690632a4128069061140b9089908990899089906004016154db565b600781036116a2575f808061160d85870187615500565b92509250925061161d838361336c565b915061162881613327565b604051635d043b2960e11b8152600481018490526001600160a01b0380831660248301523060448301529192509084169063ba087652906064016020604051808303815f875af115801561167e573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061127191906152dc565b60088114806116b15750600981145b15611917575f8080806116c686880188615496565b93509350935093506116d782613327565b91506116e3848461336c565b92505f846001600160a01b031663204f83f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015611722573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061174691906152dc565b42106117525783611823565b61182384866001600160a01b03166304aa50ad6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611792573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117b6919061547b565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa1580156117fa573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061181e91906152dc565b6134ac565b9050600886036118a557604051639f40a7b360e01b81526001600160a01b03861690639f40a7b39061185f9084908790309088906004016154db565b6020604051808303815f875af115801561187b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061189f91906152dc565b5061108d565b6040516385326f4560e01b81526001600160a01b038616906385326f45906118d79084908790309088906004016154db565b6020604051808303815f875af11580156118f3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c8391906152dc565b600a8103611a8c575f80808061192f868801886155b3565b60405163f5e306f760e01b81526001600160a01b03858116600483015294985092965090945092507f00000000000000000000000000000000000000000000000000000000000000009091169063f5e306f790602401602060405180830381865afa1580156119a0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119c4919061559a565b6119ec5760405163e8c4926760e01b81526001600160a01b03851660048201526024016106e0565b600180546001600160a01b0319166001600160a01b038616908117909155604051632e7ff4ef60e11b8152635cffe9de90611a3190309087908790879060040161561a565b6020604051808303815f875af1158015611a4d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a71919061559a565b5050600180546001600160a01b031916905550610a76915050565b600b811480611a9b5750601681145b80611aa65750601a81145b15611deb575f80808080611abc87890189615534565b94509450945094509450611acf83613327565b9250611ada82613327565b60405163c661065760e01b81525f600482018190529193506001600160a01b0387169063c661065790602401602060405180830381865afa158015611b21573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b45919061547b565b60405163c661065760e01b8152600160048201529091505f906001600160a01b0388169063c661065790602401602060405180830381865afa158015611b8d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bb1919061547b565b9050611bbd828761336c565b60405163fbfc779760e01b8152600481018290526001600160a01b03808a166024830152831660448201529096505f907327857f8e0eee20596e7ce5be3901efd663e91e109063fbfc779790606401602060405180830381865af4158015611c27573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c4b91906152dc565b90508015611d995760405163f5e306f760e01b81526001600160a01b0383811660048301525f917f00000000000000000000000000000000000000000000000000000000000000009091169063f5e306f790602401602060405180830381865afa158015611cbb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cdf919061559a565b90508015611cf757611cf284848461341c565b611d0b565b611d0b6001600160a01b03851684846109b9565b604051631520940360e11b81526001600160a01b03841690632a41280690611d3d9085908b908b908b906004016154db565b6020604051808303815f875af1158015611d59573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d7d91906152dc565b5080611d9757611d976001600160a01b038516845f6109b9565b505b6001600160a01b0386163014801590611dba5750611db781886152f3565b15155b15611dde57611dde86611dcd838a6152f3565b6001600160a01b03861691906133eb565b5050505050505050610a76565b601f811480611dfa5750602081145b80611e055750602181145b1561213c575f8080808080611e1c888a018a61564c565b955095509550955095509550611e3183613327565b9250611e3c82613327565b60405163c661065760e01b81525f600482018190529193506001600160a01b0388169063c661065790602401602060405180830381865afa158015611e83573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ea7919061547b565b60405163c661065760e01b8152600160048201529091505f906001600160a01b0389169063c661065790602401602060405180830381865afa158015611eef573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f13919061547b565b9050611f1f828861336c565b60405163013b28a760e51b815260048101829052602481018890526001600160a01b03831660448201529097505f907327857f8e0eee20596e7ce5be3901efd663e91e109063276514e090606401602060405180830381865af4158015611f88573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fac91906152dc565b905080156120fa5760405163f5e306f760e01b81526001600160a01b0383811660048301525f917f00000000000000000000000000000000000000000000000000000000000000009091169063f5e306f790602401602060405180830381865afa15801561201c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612040919061559a565b905080156120585761205384848461341c565b61206c565b61206c6001600160a01b03851684846109b9565b604051631520940360e11b81526001600160a01b03841690632a4128069061209e9085908b908b908b906004016154db565b6020604051808303815f875af11580156120ba573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120de91906152dc565b50806120f8576120f86001600160a01b038516845f6109b9565b505b6001600160a01b038616301480159061211b575061211881896152f3565b15155b1561212e5761212e86611dcd838b6152f3565b505050505050505050610a76565b600c81148061214b5750601781145b156123b9575f8080806121608688018861571b565b935093509350935061217181613327565b60405163c661065760e01b81525f600482018190529192506001600160a01b0386169063c661065790602401602060405180830381865afa1580156121b8573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121dc919061547b565b60405163c661065760e01b8152600160048201529091505f906001600160a01b0387169063c661065790602401602060405180830381865afa158015612224573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612248919061547b565b905061225b82865f5b602002015161336c565b855261226981866001612251565b60208601528451612286906001600160a01b0384169088906109b9565b60208501516122a1906001600160a01b0383169088906109b9565b600c871461231e5760405163030f92d560e21b81526001600160a01b03871690630c3e4b54906122d99088908890889060040161578c565b6020604051808303815f875af11580156122f5573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061231991906152dc565b612390565b604051637328333b60e01b81526001600160a01b03871690637328333b9061235090889088905f9089906004016157b9565b6020604051808303815f875af115801561236c573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061239091906152dc565b506123a56001600160a01b038316875f6109b9565b610c836001600160a01b038216875f6109b9565b600d8114806123c85750601881145b15612532575f8080806123dd868801886157eb565b93509350935093506123ee81613327565b90505f600d86146123ff578461245f565b846001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561243b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061245f919061547b565b905061246b818561336c565b9350600d86146124d757604051633eb1719f60e01b81526001600160a01b03861690633eb1719f906124a59087908790879060040161582f565b5f604051808303815f87803b1580156124bc575f80fd5b505af11580156124ce573d5f803e3d5ffd5b5050505061108d565b604051630c04742560e11b81526001600160a01b03861690631808e84a9061250990879087905f90889060040161585c565b5f604051808303815f87803b158015612520575f80fd5b505af115801561212e573d5f803e3d5ffd5b60198114806125415750600e81145b156126dd575f8080808061255787890189615890565b9450945094509450945061256a81613327565b90505f600e871461257b57856125db565b856001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125b7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125db919061547b565b90506125e7818661336c565b9450600e8714612661576040516307de773760e11b81526004810186905260248101859052604481018490526001600160a01b038381166064830152871690630fbcee6e906084015f604051808303815f87803b158015612646575f80fd5b505af1158015612658573d5f803e3d5ffd5b50505050610c83565b6040516307329bcd60e01b81526004810186905260248101859052604481018490525f60648201526001600160a01b0383811660848301528716906307329bcd9060a4015f604051808303815f87803b1580156126bc575f80fd5b505af11580156126ce573d5f803e3d5ffd5b50505050505050505050610a76565b601281036128fa576003546001600160a01b031661270e576040516327a8fdaf60e11b815260040160405180910390fd5b5f80808061271e868801886158e2565b94505093509350935073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6001600160a01b0316826001600160a01b03160361276d57604051630c59659760e31b815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0385160161283c5782600454146127b457604051634ff64a9f60e01b815260040160405180910390fd5b6003546004546040515f926001600160a01b031691906127d5908590615952565b5f6040518083038185875af1925050503d805f811461280f576040519150601f19603f3d011682016040523d82523d5f602084013e612814565b606091505b505090508061283657604051633204506f60e01b815260040160405180910390fd5b50611271565b612846848461336c565b600354909350612863906001600160a01b038681169116856109b9565b6003546040515f916001600160a01b031690612880908490615952565b5f604051808303815f865af19150503d805f81146128b9576040519150601f19603f3d011682016040523d82523d5f602084013e6128be565b606091505b50509050806128e057604051633204506f60e01b815260040160405180910390fd5b60035461108d906001600160a01b0387811691165f6109b9565b600f81036129d1575f8080612911858701876153e3565b92509250925061292082613327565b6040516370a0823160e01b81526001600160a01b0380831660048301529193505f918516906370a0823190602401602060405180830381865afa158015612969573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061298d91906152dc565b9050818110156112715760405163b250459b60e01b81526001600160a01b0380861660048301528416602482015260448101839052606481018290526084016106e0565b601b8103612be9575f8080806129e9868801886159d0565b93509350935093506129fa81613327565b60405163c661065760e01b81525f600482018190529192506001600160a01b0386169063c661065790602401602060405180830381865afa158015612a41573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a65919061547b565b60405163c661065760e01b8152600160048201529091505f906001600160a01b0387169063c661065790602401602060405180830381865afa158015612aad573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ad1919061547b565b9050612af682865f81518110612ae957612ae9615306565b602002602001015161336c565b855f81518110612b0857612b08615306565b602002602001018181525050612b2b8186600181518110612ae957612ae9615306565b85600181518110612b3e57612b3e615306565b602002602001018181525050612b8186865f81518110612b6057612b60615306565b6020026020010151846001600160a01b03166109b99092919063ffffffff16565b612bb98686600181518110612b9857612b98615306565b6020026020010151836001600160a01b03166109b99092919063ffffffff16565b60405163a7256d0960e01b81526001600160a01b0387169063a7256d099061235090889088908890600401615a65565b601c8103612c93575f808080612c0186880188615a97565b9350935093509350612c1281613327565b9050612c1e848461336c565b604051632f30266960e11b81529093506001600160a01b03851690635e604cd290612c5190869086908690600401615af3565b5f604051808303815f875af1158015612c6c573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261108d9190810190615b23565b601d8103612d15575f80808080612cac87890189615bae565b94509450945094509450612cbf81613327565b9050612ccb858561336c565b60405163081579a560e01b815260048101829052600f85900b6024820152604481018490526001600160a01b0383811660648301529195509086169063081579a5906084016118d7565b60228103612d7a575f80612d2b8486018661535c565b91509150816001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004015f604051808303818588803b158015612d68575f80fd5b505af1158015610f47573d5f803e3d5ffd5b60238103612de6575f80612d908486018661535c565b604051632e1a7d4d60e01b81526004810182905291935091506001600160a01b03831690632e1a7d4d906024015f604051808303815f87803b158015612dd4575f80fd5b505af1158015610c83573d5f803e3d5ffd5b60248103612e70575f80612dfc8486018661535c565b915091505f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114612e49576040519150601f19603f3d011682016040523d82523d5f602084013e612e4e565b606091505b5050905080610cdc57604051633204506f60e01b815260040160405180910390fd5b604051636bb50f4f60e11b8152600481018290526024016106e0565b5f612e95610ab5565b84838114612eb9576040516001621398b960e31b0319815260040160405180910390fd5b60408051601e8082526103e082019092525f91816020015b604080518082019091525f8082526020820152815260200190600190039081612ed1579050509050676765c793fa10079d601b1b5f5b83811015612fa3575f8a8a83818110612f2257612f22615306565b9050013560f81c60f81b9050365f8a8a85818110612f4257612f42615306565b9050602002810190612f54919061531a565b915091505f612f668484848d8b6134c1565b9050676765c793fa10079d601b1b8114612f9357612f908682676765c793fa10079d601b1b614782565b95505b505060019092019150612f079050565b5098975050505050505050565b5f80516020615e3183398151915280546001600160a01b0383166001600160a01b03199091168117825560408051918252517f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad9181900360200190a15050565b5f80516020615e318339815191525f8061304861302b610875565b873061303a60045f8a8c615c01565b61304391615c28565b614841565b91509150816106535763ffffffff8116156130e357825460ff60a01b1916600160a01b178355613076610875565b6001600160a01b03166394c7d7ee8787876040518463ffffffff1660e01b81526004016130a593929190615c58565b5f604051808303815f87803b1580156130bc575f80fd5b505af11580156130ce573d5f803e3d5ffd5b5050845460ff60a01b19168555506106539050565b60405162d1953b60e31b81526001600160a01b03871660048201526024016106e0565b61310e610ab5565b5f80516020615e51833981519152805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258335b6040516001600160a01b03909116815260200160405180910390a150565b613170614949565b6001600160a01b03821661319757604051630c59659760e31b815260040160405180910390fd5b600280546001600160a01b039384166001600160a01b03199182161790915560038054929093169116179055565b6131cd614949565b6131d681614992565b50565b6131e16149a3565b5f80516020615e51833981519152805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa3361314a565b5f805f846001600160a01b0316846040516132399190615952565b5f604051808303815f865af19150503d805f8114613272576040519150601f19603f3d011682016040523d82523d5f602084013e613277565b606091505b50915091508180156132a15750805115806132a15750808060200190518101906132a1919061559a565b80156132b657505f856001600160a01b03163b115b925050505b92915050565b5f6132d56001600160a01b038416836149d2565b905080515f141580156132f95750808060200190518101906132f7919061559a565b155b1561332257604051635274afe760e01b81526001600160a01b03841660048201526024016106e0565b505050565b5f60df196001600160a01b03831601613341575030919050565b60bf196001600160a01b038316016133635750505f546001600160a01b031690565b5090565b919050565b5f600160ff1b821461337e57816133e4565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa1580156133c0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133e491906152dc565b9392505050565b6040516001600160a01b0383811660248301526044820183905261332291859182169063a9059cbb90606401610a3a565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301525f919085169063dd62ed3e90604401602060405180830381865afa158015613469573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061348d91906152dc565b905081811015610a7657610a766001600160a01b038516845f196109b9565b5f8183106134ba57816133e4565b5090919050565b5f60f886901c603f168015806134d75750600181145b156135165783613502575f806134ef8789018961535c565b915091506134fe8183876149df565b5050505b676765c793fa10079d601b1b915050610697565b600281036135765783613502575f8080613532888a018a6153e3565b92509250925061354182613327565b91506001600160a01b03821630146134fe5761355e818488614b43565b50505050676765c793fa10079d601b1b915050610697565b60038114806135855750601581145b806135905750601e81145b1561390c575f808080806135a68a8c018c615421565b95505094509450945094505f89156136de575f60038814806135c85750601588145b61364f5760025460405163028c676d60e31b81526001600160a01b038981166004830152600f89810b602484015288900b6044830152909116906314633b6890606401602060405180830381865afa158015613626573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061364a91906152dc565b6136c9565b60025460405163044e3c3f60e31b81526001600160a01b038981166004830152602482018990526044820188905290911690632271e1f890606401602060405180830381865afa1580156136a5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136c991906152dc565b90506136d6816012614cc0565b9150506138fe565b60405163c661065760e01b8152600481018690526137519084906001600160a01b0389169063c661065790602401602060405180830381865afa158015613727573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061374b919061547b565b8b614b43565b92505f601e88036137dc57604051635e0d443f60e01b8152600f87810b600483015286900b6024820152604481018590526001600160a01b03881690635e0d443f90606401602060405180830381865afa1580156137b1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906137d591906152dc565b9050613854565b60405163556d6e9f60e01b81526004810187905260248101869052604481018590526001600160a01b0388169063556d6e9f90606401602060405180830381865afa15801561382d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061385191906152dc565b90505b61385d83613327565b9250306001600160a01b038416036138e45760405163c661065760e01b8152600481018690526138e29082906001600160a01b038a169063c661065790602401602060405180830381865afa1580156138b8573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906138dc919061547b565b8c6149df565b505b6138fa81676765c793fa10079d601b1b86614782565b9150505b965061069795505050505050565b60108103613aca575f8080613923888a018a615500565b9250925092505f836001600160a01b031663732e86fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613966573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061398a919061547b565b90508715613a0557600254604051635fa5a49760e01b81526001600160a01b03838116600483015290911690635fa5a49790602401602060405180830381865afa1580156139da573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906139fe91906152dc565b9250613a13565b613a10838289614b43565b92505b60405163404b9d8160e01b8152600481018490525f906001600160a01b0386169063404b9d81906024015b602060405180830381865afa158015613a59573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613a7d91906152dc565b9050613a8883613327565b9250306001600160a01b03841603613aa757613aa581868a6149df565b505b613abd81676765c793fa10079d601b1b86614782565b9650505050505050610697565b60118103613c82575f8080613ae1888a018a615500565b9250925092508615613b6057600254604051635fa5a49760e01b81526001600160a01b03858116600483015290911690635fa5a49790602401602060405180830381865afa158015613b35573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613b5991906152dc565b9150613b6e565b613b6b828488614b43565b91505b60405163554d83a760e11b8152600481018390525f906001600160a01b0385169063aa9b074e90602401602060405180830381865afa158015613bb3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613bd791906152dc565b9050613be282613327565b9150306001600160a01b03831603613c6057613c5e81856001600160a01b031663732e86fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c34573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613c58919061547b565b896149df565b505b613c7681676765c793fa10079d601b1b85614782565b95505050505050610697565b60048103613db8575f8080613c99888a018a615500565b9250925092505f836001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613cdc573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613d00919061547b565b90508715613d7b57600254604051635fa5a49760e01b81526001600160a01b03838116600483015290911690635fa5a49790602401602060405180830381865afa158015613d50573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613d7491906152dc565b9250613d89565b613d86838289614b43565b92505b60405163ef8b30f760e01b8152600481018490525f906001600160a01b0386169063ef8b30f790602401613a3e565b60058103613fd7575f808080613dd0898b018b615c97565b93509350935093508715613e5157600254604051633e3f205760e21b81526001600160a01b0386811660048301529091169063f8fc815c90602401602060405180830381865afa158015613e26573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613e4a91906152dc565b9250613ebe565b613ebb83856001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015613e91573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613eb5919061547b565b89614b43565b92505b60405163ef8b30f760e01b8152600481018490525f906001600160a01b0386169063ef8b30f7906024015b602060405180830381865afa158015613f04573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613f2891906152dc565b9050613f3383613327565b9250306001600160a01b03841603613f5257613f5081868a6149df565b505b613f5b82613327565b9150306001600160a01b03831603613aa757613aa581866001600160a01b03166304aa50ad6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613fad573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613fd1919061547b565b8a6149df565b600681036140e2575f808080613fef898b018b615c97565b9350935093509350871561407057600254604051635fa5a49760e01b81526001600160a01b03868116600483015290911690635fa5a49790602401602060405180830381865afa158015614045573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061406991906152dc565b92506140b3565b6140b083856001600160a01b031663c644fe946040518163ffffffff1660e01b8152600401602060405180830381865afa158015613e91573d5f803e3d5ffd5b92505b6040516302f6fa9560e11b8152600481018490525f906001600160a01b038616906305edf52a90602401613ee9565b6007810361424c575f80806140f9888a018a615500565b925092509250861561417857600254604051635fa5a49760e01b81526001600160a01b03858116600483015290911690635fa5a49790602401602060405180830381865afa15801561414d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061417191906152dc565b9150614186565b614183828488614b43565b91505b60405163266d6a8360e11b8152600481018390525f906001600160a01b03851690634cdad50690602401602060405180830381865afa1580156141cb573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906141ef91906152dc565b90506141fa82613327565b9150306001600160a01b03831603613c6057613c5e81856001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c34573d5f803e3d5ffd5b60088103614488575f8080614263888a018a615500565b92509250925086156142e257600254604051635fa5a49760e01b81526001600160a01b03858116600483015290911690635fa5a49790602401602060405180830381865afa1580156142b7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906142db91906152dc565b91506143c2565b6142ed828488614b43565b9150826001600160a01b031663204f83f96040518163ffffffff1660e01b8152600401602060405180830381865afa15801561432b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061434f91906152dc565b4210156143c2576143c082846001600160a01b03166304aa50ad6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614396573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906143ba919061547b565b88614b43565b505b60405163266d6a8360e11b8152600481018390525f906001600160a01b03851690634cdad50690602401602060405180830381865afa158015614407573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061442b91906152dc565b905061443682613327565b9150306001600160a01b03831603613c6057613c5e81856001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c34573d5f803e3d5ffd5b6009810361469a575f808061449f888a018a615500565b925092509250861561451e57600254604051635fa5a49760e01b81526001600160a01b03858116600483015290911690635fa5a49790602401602060405180830381865afa1580156144f3573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061451791906152dc565b91506145d4565b614529828488614b43565b9150826001600160a01b031663204f83f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015614567573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061458b91906152dc565b4210156145d4576145d282846001600160a01b03166304aa50ad6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614396573d5f803e3d5ffd5b505b604051633460fbfb60e11b8152600481018390525f906001600160a01b038516906368c1f7f690602401602060405180830381865afa158015614619573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061463d91906152dc565b905061464882613327565b9150306001600160a01b03831603613c6057613c5e81856001600160a01b031663c644fe946040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c34573d5f803e3d5ffd5b60128103614766576003546001600160a01b03166146cb576040516327a8fdaf60e11b815260040160405180910390fd5b5f8080806146db898b018b615496565b935093509350935073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6001600160a01b0316826001600160a01b03160361472957604051630c59659760e31b815260040160405180910390fd5b6001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461475b57614758838589614b43565b92505b613c5e8183896149df565b600f8103612e7057676765c793fa10079d601b1b915050610697565b5f838302815f1985870982811083820303915050805f036147b6578382816147ac576147ac615cdc565b04925050506133e4565b8084116147d65760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b6040516001600160a01b03848116602483015283811660448301526001600160e01b0319831660648301525f9182918291829189169060840160408051601f198184030181529181526020820180516001600160e01b031663b700961360e01b179052516148af9190615952565b5f60405180830381855afa9150503d805f81146148e7576040519150601f19603f3d011682016040523d82523d5f602084013e6148ec565b606091505b5091509150811561493e57604081511061491e57808060200190518101906149149190615cf0565b909450925061493e565b602081511061493e578080602001905181019061493b919061559a565b93505b505094509492505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661073f57604051631afcd79f60e31b815260040160405180910390fd5b61499a614949565b6131d681612fb0565b5f80516020615e518339815191525460ff1661073f57604051638dfc202b60e01b815260040160405180910390fd5b60606133e483835f614cf3565b5f6001600160a01b038316614a0757604051630c59659760e31b815260040160405180910390fd5b81515f5b81811015614b29575f6001600160a01b0316848281518110614a2f57614a2f615306565b60200260200101515f01516001600160a01b031603614a8d576040518060400160405280866001600160a01b0316815260200187815250848281518110614a7857614a78615306565b602002602001018190525085925050506133e4565b846001600160a01b0316848281518110614aa957614aa9615306565b60200260200101515f01516001600160a01b031603614b195785848281518110614ad557614ad5615306565b6020026020010151602001818151614aed91906152c9565b9052508351849082908110614b0457614b04615306565b602002602001015160200151925050506133e4565b614b2281615d2d565b9050614a0b565b50604051638b48412160e01b815260040160405180910390fd5b5f6001600160a01b038316614b6b57604051630c59659760e31b815260040160405180910390fd5b81515f5b81811015614ca6575f6001600160a01b0316848281518110614b9357614b93615306565b60200260200101515f01516001600160a01b03160315614ca657846001600160a01b0316848281518110614bc957614bc9615306565b60200260200101515f01516001600160a01b031603614c9657600160ff1b8603614c39575f848281518110614c0057614c00615306565b60200260200101516020015190505f858381518110614c2157614c21615306565b602090810291909101810151015292506133e4915050565b85848281518110614c4c57614c4c615306565b60200260200101516020015110614ca65785848281518110614c7057614c70615306565b6020026020010151602001818151614c8891906152f3565b9052508592506133e4915050565b614c9f81615d2d565b9050614b6f565b506040516305e72d3960e11b815260040160405180910390fd5b5f80614ccd83601b6152f3565b614cd890600a615e25565b905080840291508381830414614cec575f80fd5b5092915050565b606081471015614d185760405163cd78605960e01b81523060048201526024016106e0565b5f80856001600160a01b03168486604051614d339190615952565b5f6040518083038185875af1925050503d805f8114614d6d576040519150601f19603f3d011682016040523d82523d5f602084013e614d72565b606091505b5091509150614d82868383614d8c565b9695505050505050565b606082614da157614d9c82614de8565b6133e4565b8151158015614db857506001600160a01b0384163b155b15614de157604051639996b31560e01b81526001600160a01b03851660048201526024016106e0565b50806133e4565b805115614df85780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b03811681146131d6575f80fd5b5f8083601f840112614e35575f80fd5b5081356001600160401b03811115614e4b575f80fd5b602083019150836020828501011115614e62575f80fd5b9250929050565b5f805f805f8060a08789031215614e7e575f80fd5b8635614e8981614e11565b95506020870135614e9981614e11565b9450604087013593506060870135925060808701356001600160401b03811115614ec1575f80fd5b614ecd89828a01614e25565b979a9699509497509295939492505050565b5f8083601f840112614eef575f80fd5b5081356001600160401b03811115614f05575f80fd5b6020830191508360208260051b8501011115614e62575f80fd5b5f805f8060408587031215614f32575f80fd5b84356001600160401b0380821115614f48575f80fd5b614f5488838901614e25565b90965094506020870135915080821115614f6c575f80fd5b50614f7987828801614edf565b95989497509550505050565b5f805f805f60608688031215614f99575f80fd5b85356001600160401b0380821115614faf575f80fd5b614fbb89838a01614e25565b90975095506020880135915080821115614fd3575f80fd5b50614fe088828901614edf565b96999598509660400135949350505050565b5f60208284031215615002575f80fd5b81356133e481614e11565b5f805f6060848603121561501f575f80fd5b833561502a81614e11565b9250602084013561503a81614e11565b9150604084013561504a81614e11565b809150509250925092565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171561509157615091615055565b604052919050565b5f82601f8301126150a8575f80fd5b81356001600160401b038111156150c1576150c1615055565b6150d4601f8201601f1916602001615069565b8181528460208386010111156150e8575f80fd5b816020850160208301375f918101602001919091529392505050565b5f6001600160401b0382111561511c5761511c615055565b5060051b60200190565b5f8060408385031215615137575f80fd5b82356001600160401b038082111561514d575f80fd5b61515986838701615099565b935060209150818501358181111561516f575f80fd5b8501601f8101871361517f575f80fd5b803561519261518d82615104565b615069565b81815260059190911b820184019084810190898311156151b0575f80fd5b8584015b838110156151e7578035868111156151cb575f8081fd5b6151d98c8983890101615099565b8452509186019186016151b4565b508096505050505050509250929050565b5f5b838110156152125781810151838201526020016151fa565b50505f910152565b5f81518084526152318160208601602086016151f8565b601f01601f19169290920160200192915050565b604081525f615257604083018561521a565b6020838203818501528185518084528284019150828160051b8501018388015f5b838110156152a657601f1987840301855261529483835161521a565b94860194925090850190600101615278565b50909998505050505050505050565b634e487b7160e01b5f52601160045260245ffd5b808201808211156132bb576132bb6152b5565b5f602082840312156152ec575f80fd5b5051919050565b818103818111156132bb576132bb6152b5565b634e487b7160e01b5f52603260045260245ffd5b5f808335601e1984360301811261532f575f80fd5b8301803591506001600160401b03821115615348575f80fd5b602001915036819003821315614e62575f80fd5b5f806040838503121561536d575f80fd5b823561537881614e11565b946020939093013593505050565b5f805f805f8060c0878903121561539b575f80fd5b86356153a681614e11565b95506020870135945060408701359350606087013560ff811681146153c9575f80fd5b9598949750929560808101359460a0909101359350915050565b5f805f606084860312156153f5575f80fd5b833561540081614e11565b9250602084013561541081614e11565b929592945050506040919091013590565b5f805f805f8060c08789031215615436575f80fd5b863561544181614e11565b95506020870135945060408701359350606087013592506080870135915060a087013561546d81614e11565b809150509295509295509295565b5f6020828403121561548b575f80fd5b81516133e481614e11565b5f805f80608085870312156154a9575f80fd5b84356154b481614e11565b93506020850135925060408501356154cb81614e11565b9396929550929360600135925050565b9384526001600160a01b03928316602085015291166040830152606082015260800190565b5f805f60608486031215615512575f80fd5b833561551d81614e11565b925060208401359150604084013561504a81614e11565b5f805f805f60a08688031215615548575f80fd5b853561555381614e11565b945060208601359350604086013561556a81614e11565b9250606086013561557a81614e11565b949793965091946080013592915050565b80518015158114613367575f80fd5b5f602082840312156155aa575f80fd5b6133e48261558b565b5f805f80608085870312156155c6575f80fd5b84356155d181614e11565b935060208501356155e181614e11565b92506040850135915060608501356001600160401b03811115615602575f80fd5b61560e87828801615099565b91505092959194509250565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f90614d829083018461521a565b5f805f805f8060c08789031215615661575f80fd5b863561566c81614e11565b95506020870135945060408701359350606087013561568a81614e11565b9250608087013561569a81614e11565b8092505060a087013590509295509295509295565b5f82601f8301126156be575f80fd5b604051604081018181106001600160401b03821117156156e0576156e0615055565b80604052508060408401858111156156f6575f80fd5b845b818110156157105780358352602092830192016156f8565b509195945050505050565b5f805f8060a0858703121561572e575f80fd5b843561573981614e11565b935061574886602087016156af565b925060608501359150608085013561575f81614e11565b939692955090935050565b805f5b6002811015610a7657815184526020938401939091019060010161576d565b6080810161579a828661576a565b60408201939093526001600160a01b0391909116606090910152919050565b60a081016157c7828761576a565b604082019490945291151560608301526001600160a01b0316608090910152919050565b5f805f8060a085870312156157fe575f80fd5b843561580981614e11565b93506020850135925061581f86604087016156af565b9150608085013561575f81614e11565b83815260808101615843602083018561576a565b6001600160a01b03929092166060919091015292915050565b84815260a08101615870602083018661576a565b92151560608201526001600160a01b039190911660809091015292915050565b5f805f805f60a086880312156158a4575f80fd5b85356158af81614e11565b945060208601359350604086013592506060860135915060808601356158d481614e11565b809150509295509295909350565b5f805f805f60a086880312156158f6575f80fd5b853561590181614e11565b945060208601359350604086013561591881614e11565b92506060860135915060808601356001600160401b03811115615939575f80fd5b61594588828901615099565b9150509295509295909350565b5f82516159638184602087016151f8565b9190910192915050565b5f82601f83011261597c575f80fd5b8135602061598c61518d83615104565b82815260059290921b840181019181810190868411156159aa575f80fd5b8286015b848110156159c557803583529183019183016159ae565b509695505050505050565b5f805f80608085870312156159e3575f80fd5b84356159ee81614e11565b935060208501356001600160401b03811115615a08575f80fd5b615a148782880161596d565b93505060408501359150606085013561575f81614e11565b5f8151808452602080850194508084015f5b83811015615a5a57815187529582019590820190600101615a3e565b509495945050505050565b606081525f615a776060830186615a2c565b6020830194909452506001600160a01b0391909116604090910152919050565b5f805f8060808587031215615aaa575f80fd5b8435615ab581614e11565b93506020850135925060408501356001600160401b03811115615ad6575f80fd5b615ae28782880161596d565b925050606085013561575f81614e11565b838152606060208201525f615b0b6060830185615a2c565b905060018060a01b0383166040830152949350505050565b5f6020808385031215615b34575f80fd5b82516001600160401b03811115615b49575f80fd5b8301601f81018513615b59575f80fd5b8051615b6761518d82615104565b81815260059190911b82018301908381019087831115615b85575f80fd5b928401925b82841015615ba357835182529284019290840190615b8a565b979650505050505050565b5f805f805f60a08688031215615bc2575f80fd5b8535615bcd81614e11565b9450602086013593506040860135600f81900b8114615bea575f80fd5b92506060860135915060808601356158d481614e11565b5f8085851115615c0f575f80fd5b83861115615c1b575f80fd5b5050820193919092039150565b6001600160e01b03198135818116916004851015615c505780818660040360031b1b83161692505b505092915050565b6001600160a01b03841681526040602082018190528101829052818360608301375f818301606090810191909152601f909201601f1916010192915050565b5f805f8060808587031215615caa575f80fd5b8435615cb581614e11565b9350602085013592506040850135615ccc81614e11565b9150606085013561575f81614e11565b634e487b7160e01b5f52601260045260245ffd5b5f8060408385031215615d01575f80fd5b615d0a8361558b565b9150602083015163ffffffff81168114615d22575f80fd5b809150509250929050565b5f60018201615d3e57615d3e6152b5565b5060010190565b600181815b80851115615d7f57815f1904821115615d6557615d656152b5565b80851615615d7257918102915b93841c9390800290615d4a565b509250929050565b5f82615d95575060016132bb565b81615da157505f6132bb565b8160018114615db75760028114615dc157615ddd565b60019150506132bb565b60ff841115615dd257615dd26152b5565b50506001821b6132bb565b5060208310610133831016604e8410600b8410161715615e00575081810a6132bb565b615e0a8383615d45565b805f1904821115615e1d57615e1d6152b5565b029392505050565b5f6133e48383615d8756fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a264697066735822122057591e54a916f3b9443c691928a96f6d9c4d16f411ae8a4a77f39bb28fbcef9864736f6c6343000814003300000000000000000000000009176eacaa413cc0722aa5ad716820e8f19682b7
Deployed Bytecode
0x6080604052600436106100fd575f3560e01c80637a9e5e4b116100925780638fb36037116100625780638fb36037146102a0578063b748f092146102cd578063bf7e214f146102ec578063c0c53b8b14610300578063f7b188a51461031f575f80fd5b80637a9e5e4b1461022f5780638456cb591461024e5780638b0c9584146102625780638b33203c14610281575f80fd5b80635ab1bd53116100cd5780635ab1bd53146101935780635c975abb146101c557806361fbaaaa146101f357806369dfa6c214610210575f80fd5b806323e30c8b1461010857806324856bc31461013a5780633593564c1461014f578063466489fb14610162575f80fd5b3661010457005b5f80fd5b348015610113575f80fd5b50610127610122366004614e69565b610333565b6040519081526020015b60405180910390f35b61014d610148366004614f1f565b610557565b005b61014d61015d366004614f85565b61065b565b34801561016d575f80fd5b506002546001600160a01b03165b6040516001600160a01b039091168152602001610131565b34801561019e575f80fd5b507f00000000000000000000000009176eacaa413cc0722aa5ad716820e8f19682b761017b565b3480156101d0575f80fd5b505f80516020615e518339815191525460ff166040519015158152602001610131565b3480156101fe575f80fd5b506003546001600160a01b031661017b565b34801561021b575f80fd5b5061012761022a366004614f1f565b610689565b34801561023a575f80fd5b5061014d610249366004614ff2565b6106a0565b348015610259575f80fd5b5061014d61072b565b34801561026d575f80fd5b5061014d61027c366004614ff2565b610741565b34801561028c575f80fd5b5061014d61029b366004614ff2565b6107cc565b3480156102ab575f80fd5b506102b4610830565b6040516001600160e01b03199091168152602001610131565b3480156102d8575f80fd5b506101276102e7366004614f1f565b610866565b3480156102f7575f80fd5b5061017b610875565b34801561030b575f80fd5b5061014d61031a36600461500d565b610890565b34801561032a575f80fd5b5061014d6109a8565b5f80546001600160a01b031661035c57604051630606fd0760e51b815260040160405180910390fd5b6001546001600160a01b031633146103875760405163242762f960e01b815260040160405180910390fd5b5f8061039584860186615126565b6040516324856bc360e01b8152919350915030906324856bc3906103bf9085908590600401615245565b5f604051808303815f87803b1580156103d6575f80fd5b505af11580156103e8573d5f803e3d5ffd5b505050505f86886103f991906152c9565b604051636eb1769f60e11b81523060048201523360248201529091505f906001600160a01b038b169063dd62ed3e90604401602060405180830381865afa158015610446573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061046a91906152dc565b905081811015610488576104886001600160a01b038b1633846109b9565b6040516370a0823160e01b81523060048201525f906001600160a01b038c16906370a0823190602401602060405180830381865afa1580156104cc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906104f091906152dc565b905082811015610526575f54610526906001600160a01b03163061051484876152f3565b6001600160a01b038f16929190610a7c565b507f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd99b9a5050505050505050505050565b61055f610ab5565b82818114610583576040516001621398b960e31b0319815260040160405180910390fd5b5f80546001600160a01b03166105b057505f80546001600160a01b031916331790553460045560016105d0565b3330146105d05760405163f9ce257360e01b815260040160405180910390fd5b5f5b82811015610639575f8787838181106105ed576105ed615306565b9050013560f81c60f81b9050365f87878581811061060d5761060d615306565b905060200281019061061f919061531a565b9150915061062e838383610ae5565b5050506001016105d2565b508015610653575f80546001600160a01b03191681556004555b505050505050565b808042111561067d57604051632dfb7c8b60e11b815260040160405180910390fd5b61065386868686610557565b5f610697858585855f612e8c565b95945050505050565b336106a9610875565b6001600160a01b0316816001600160a01b0316146106e95760405162d1953b60e31b81526001600160a01b03821660048201526024015b60405180910390fd5b816001600160a01b03163b5f0361071e576040516361798f2f60e11b81526001600160a01b03831660048201526024016106e0565b61072782612fb0565b5050565b610737335b5f36613010565b61073f613106565b565b61074a33610730565b6001600160a01b03811661077157604051630c59659760e31b815260040160405180910390fd5b6002546040516001600160a01b038084169216907fbacf07ffb3274576773b014d636ae5319ff022a18a5b0f958eaac799cf68439b905f90a3600280546001600160a01b0319166001600160a01b0392909216919091179055565b6107d533610730565b6003546040516001600160a01b038084169216907f775376a8cc8424fc322e2ac2b50230180c9622951b05587f5ab18ff432a03d49905f90a3600380546001600160a01b0319166001600160a01b0392909216919091179055565b5f80516020615e3183398151915280545f9190600160a01b900460ff16610857575f610860565b638fb3603760e01b5b91505090565b5f610697858585856001612e8c565b5f80516020615e31833981519152546001600160a01b031690565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a008054600160401b810460ff1615906001600160401b03165f811580156108d45750825b90505f826001600160401b031660011480156108ef5750303b155b9050811580156108fd575080155b1561091b5760405163f92ee8a960e01b815260040160405180910390fd5b845467ffffffffffffffff19166001178555831561094557845460ff60401b1916600160401b1785555b61094f8888613168565b610958866131c5565b831561099e57845460ff60401b19168555604051600181527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29060200160405180910390a15b5050505050505050565b6109b133610730565b61073f6131d9565b604080516001600160a01b038416602482015260448082018490528251808303909101815260649091019091526020810180516001600160e01b031663095ea7b360e01b179052610a0a848261321e565b610a76576040516001600160a01b0384811660248301525f6044830152610a6c91869182169063095ea7b3906064015b604051602081830303815290604052915060e01b6020820180516001600160e01b0383818316178352505050506132c1565b610a7684826132c1565b50505050565b6040516001600160a01b038481166024830152838116604483015260648201839052610a769186918216906323b872dd90608401610a3a565b5f80516020615e518339815191525460ff161561073f5760405163d93c066560e01b815260040160405180910390fd5b60f883901c603f1680610b26575f80610b008486018661535c565b5f549193509150610b1f906001600160a01b0380851691163084610a7c565b5050610a76565b60018103610c8e575f8080808080610b40888a018a615386565b5f5460405163d505accf60e01b81526001600160a01b039182166004820152306024820152604481018790526064810186905260ff8516608482015260a4810184905260c48101839052969c50949a509298509096509450925087169063d505accf9060e4015f604051808303815f87803b158015610bbd575f80fd5b505af1925050508015610bce575060015b610c69575f8054604051636eb1769f60e11b81526001600160a01b0391821660048201523060248201529088169063dd62ed3e90604401602060405180830381865afa158015610c20573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c4491906152dc565b905085811015610c675760405163b78cb0dd60e01b815260040160405180910390fd5b505b5f54610c83906001600160a01b0388811691163088610a7c565b505050505050610a76565b60028103610ce4575f8080610ca5858701876153e3565b925092509250610cb482613327565b9150610cc0838261336c565b90508015610cdc57610cdc6001600160a01b03841683836133eb565b505050610a76565b6003811480610cf35750601581145b80610cfe5750601e81145b15610f53575f8080808080610d15888a018a615421565b9550955095509550955095505f866001600160a01b031663c6610657876040518263ffffffff1660e01b8152600401610d5091815260200190565b602060405180830381865afa158015610d6b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610d8f919061547b565b9050610d9b818561336c565b9350610da682613327565b9150610dbc6001600160a01b03821688866109b9565b60038803610e565760405163ce7d650360e01b8152600481018790526024810186905260448101859052606481018490525f60848201526001600160a01b0383811660a483015288169063ce7d65039060c4015b6020604051808303815f875af1158015610e2c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610e5091906152dc565b50610f33565b60158803610ea857604051630532419d60e51b8152600481018790526024810186905260448101859052606481018490526001600160a01b03838116608483015288169063a64833a09060a401610e10565b60405163ddc1f59d60e01b8152600f87810b600483015286900b602482015260448101859052606481018490526001600160a01b03838116608483015288169063ddc1f59d9060a4016020604051808303815f875af1158015610f0d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f3191906152dc565b505b610f476001600160a01b038216885f6109b9565b50505050505050610a76565b60108103611097575f808080610f6b86880188615496565b93509350935093505f846001600160a01b031663732e86fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610fb0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610fd4919061547b565b9050610fdf83613327565b9250610feb818561336c565b93506110016001600160a01b03821686866109b9565b60405163680d5c7760e11b8152600481018590526001600160a01b0384811660248301526044820184905286169063d01ab8ee906064016020604051808303815f875af1158015611054573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061107891906152dc565b5061108d6001600160a01b038216865f6109b9565b5050505050610a76565b60118103611141575f8080806110af86880188615496565b93509350935093506110c082613327565b91506110cc848461336c565b604051631886c6df60e21b81529093506001600160a01b0385169063621b1b7c906111019086908690309087906004016154db565b6020604051808303815f875af115801561111d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061108d91906152dc565b6004810361127a575f808061115885870187615500565b9250925092505f836001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561119b573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906111bf919061547b565b90506111cb818461336c565b92506111d682613327565b91506111ec6001600160a01b03821685856109b9565b604051636e553f6560e01b8152600481018490526001600160a01b038381166024830152851690636e553f65906044016020604051808303815f875af1158015611238573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061125c91906152dc565b506112716001600160a01b038216855f6109b9565b50505050610a76565b60058103611465575f8080808061129387890189615534565b945094509450945094505f856001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa1580156112da573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112fe919061547b565b905061130a818661336c565b945061131584613327565b935061132083613327565b60405163f5e306f760e01b81526001600160a01b0388811660048301529194505f917f00000000000000000000000009176eacaa413cc0722aa5ad716820e8f19682b7169063f5e306f790602401602060405180830381865afa158015611389573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113ad919061559a565b905080156113c5576113c082888861341c565b6113d9565b6113d96001600160a01b03831688886109b9565b604051630e4cca4b60e41b81526001600160a01b0388169063e4cca4b09061140b9089908990899089906004016154db565b6020604051808303815f875af1158015611427573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061144b91906152dc565b5080610f4757610f476001600160a01b038316885f6109b9565b600681036115f6575f8080808061147e87890189615534565b945094509450945094505f856001600160a01b031663c644fe946040518163ffffffff1660e01b8152600401602060405180830381865afa1580156114c5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906114e9919061547b565b90506114f5818661336c565b945061150084613327565b935061150b83613327565b60405163f5e306f760e01b81526001600160a01b0388811660048301529194505f917f00000000000000000000000009176eacaa413cc0722aa5ad716820e8f19682b7169063f5e306f790602401602060405180830381865afa158015611574573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611598919061559a565b905080156115b0576115ab82888861341c565b6115c4565b6115c46001600160a01b03831688886109b9565b604051631520940360e11b81526001600160a01b03881690632a4128069061140b9089908990899089906004016154db565b600781036116a2575f808061160d85870187615500565b92509250925061161d838361336c565b915061162881613327565b604051635d043b2960e11b8152600481018490526001600160a01b0380831660248301523060448301529192509084169063ba087652906064016020604051808303815f875af115801561167e573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061127191906152dc565b60088114806116b15750600981145b15611917575f8080806116c686880188615496565b93509350935093506116d782613327565b91506116e3848461336c565b92505f846001600160a01b031663204f83f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015611722573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061174691906152dc565b42106117525783611823565b61182384866001600160a01b03166304aa50ad6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611792573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906117b6919061547b565b6040516370a0823160e01b81523060048201526001600160a01b0391909116906370a0823190602401602060405180830381865afa1580156117fa573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061181e91906152dc565b6134ac565b9050600886036118a557604051639f40a7b360e01b81526001600160a01b03861690639f40a7b39061185f9084908790309088906004016154db565b6020604051808303815f875af115801561187b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061189f91906152dc565b5061108d565b6040516385326f4560e01b81526001600160a01b038616906385326f45906118d79084908790309088906004016154db565b6020604051808303815f875af11580156118f3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610c8391906152dc565b600a8103611a8c575f80808061192f868801886155b3565b60405163f5e306f760e01b81526001600160a01b03858116600483015294985092965090945092507f00000000000000000000000009176eacaa413cc0722aa5ad716820e8f19682b79091169063f5e306f790602401602060405180830381865afa1580156119a0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119c4919061559a565b6119ec5760405163e8c4926760e01b81526001600160a01b03851660048201526024016106e0565b600180546001600160a01b0319166001600160a01b038616908117909155604051632e7ff4ef60e11b8152635cffe9de90611a3190309087908790879060040161561a565b6020604051808303815f875af1158015611a4d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a71919061559a565b5050600180546001600160a01b031916905550610a76915050565b600b811480611a9b5750601681145b80611aa65750601a81145b15611deb575f80808080611abc87890189615534565b94509450945094509450611acf83613327565b9250611ada82613327565b60405163c661065760e01b81525f600482018190529193506001600160a01b0387169063c661065790602401602060405180830381865afa158015611b21573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b45919061547b565b60405163c661065760e01b8152600160048201529091505f906001600160a01b0388169063c661065790602401602060405180830381865afa158015611b8d573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611bb1919061547b565b9050611bbd828761336c565b60405163fbfc779760e01b8152600481018290526001600160a01b03808a166024830152831660448201529096505f907327857f8e0eee20596e7ce5be3901efd663e91e109063fbfc779790606401602060405180830381865af4158015611c27573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c4b91906152dc565b90508015611d995760405163f5e306f760e01b81526001600160a01b0383811660048301525f917f00000000000000000000000009176eacaa413cc0722aa5ad716820e8f19682b79091169063f5e306f790602401602060405180830381865afa158015611cbb573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611cdf919061559a565b90508015611cf757611cf284848461341c565b611d0b565b611d0b6001600160a01b03851684846109b9565b604051631520940360e11b81526001600160a01b03841690632a41280690611d3d9085908b908b908b906004016154db565b6020604051808303815f875af1158015611d59573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611d7d91906152dc565b5080611d9757611d976001600160a01b038516845f6109b9565b505b6001600160a01b0386163014801590611dba5750611db781886152f3565b15155b15611dde57611dde86611dcd838a6152f3565b6001600160a01b03861691906133eb565b5050505050505050610a76565b601f811480611dfa5750602081145b80611e055750602181145b1561213c575f8080808080611e1c888a018a61564c565b955095509550955095509550611e3183613327565b9250611e3c82613327565b60405163c661065760e01b81525f600482018190529193506001600160a01b0388169063c661065790602401602060405180830381865afa158015611e83573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ea7919061547b565b60405163c661065760e01b8152600160048201529091505f906001600160a01b0389169063c661065790602401602060405180830381865afa158015611eef573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611f13919061547b565b9050611f1f828861336c565b60405163013b28a760e51b815260048101829052602481018890526001600160a01b03831660448201529097505f907327857f8e0eee20596e7ce5be3901efd663e91e109063276514e090606401602060405180830381865af4158015611f88573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611fac91906152dc565b905080156120fa5760405163f5e306f760e01b81526001600160a01b0383811660048301525f917f00000000000000000000000009176eacaa413cc0722aa5ad716820e8f19682b79091169063f5e306f790602401602060405180830381865afa15801561201c573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612040919061559a565b905080156120585761205384848461341c565b61206c565b61206c6001600160a01b03851684846109b9565b604051631520940360e11b81526001600160a01b03841690632a4128069061209e9085908b908b908b906004016154db565b6020604051808303815f875af11580156120ba573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906120de91906152dc565b50806120f8576120f86001600160a01b038516845f6109b9565b505b6001600160a01b038616301480159061211b575061211881896152f3565b15155b1561212e5761212e86611dcd838b6152f3565b505050505050505050610a76565b600c81148061214b5750601781145b156123b9575f8080806121608688018861571b565b935093509350935061217181613327565b60405163c661065760e01b81525f600482018190529192506001600160a01b0386169063c661065790602401602060405180830381865afa1580156121b8573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906121dc919061547b565b60405163c661065760e01b8152600160048201529091505f906001600160a01b0387169063c661065790602401602060405180830381865afa158015612224573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612248919061547b565b905061225b82865f5b602002015161336c565b855261226981866001612251565b60208601528451612286906001600160a01b0384169088906109b9565b60208501516122a1906001600160a01b0383169088906109b9565b600c871461231e5760405163030f92d560e21b81526001600160a01b03871690630c3e4b54906122d99088908890889060040161578c565b6020604051808303815f875af11580156122f5573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061231991906152dc565b612390565b604051637328333b60e01b81526001600160a01b03871690637328333b9061235090889088905f9089906004016157b9565b6020604051808303815f875af115801561236c573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061239091906152dc565b506123a56001600160a01b038316875f6109b9565b610c836001600160a01b038216875f6109b9565b600d8114806123c85750601881145b15612532575f8080806123dd868801886157eb565b93509350935093506123ee81613327565b90505f600d86146123ff578461245f565b846001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561243b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061245f919061547b565b905061246b818561336c565b9350600d86146124d757604051633eb1719f60e01b81526001600160a01b03861690633eb1719f906124a59087908790879060040161582f565b5f604051808303815f87803b1580156124bc575f80fd5b505af11580156124ce573d5f803e3d5ffd5b5050505061108d565b604051630c04742560e11b81526001600160a01b03861690631808e84a9061250990879087905f90889060040161585c565b5f604051808303815f87803b158015612520575f80fd5b505af115801561212e573d5f803e3d5ffd5b60198114806125415750600e81145b156126dd575f8080808061255787890189615890565b9450945094509450945061256a81613327565b90505f600e871461257b57856125db565b856001600160a01b031663fc0c546a6040518163ffffffff1660e01b8152600401602060405180830381865afa1580156125b7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125db919061547b565b90506125e7818661336c565b9450600e8714612661576040516307de773760e11b81526004810186905260248101859052604481018490526001600160a01b038381166064830152871690630fbcee6e906084015f604051808303815f87803b158015612646575f80fd5b505af1158015612658573d5f803e3d5ffd5b50505050610c83565b6040516307329bcd60e01b81526004810186905260248101859052604481018490525f60648201526001600160a01b0383811660848301528716906307329bcd9060a4015f604051808303815f87803b1580156126bc575f80fd5b505af11580156126ce573d5f803e3d5ffd5b50505050505050505050610a76565b601281036128fa576003546001600160a01b031661270e576040516327a8fdaf60e11b815260040160405180910390fd5b5f80808061271e868801886158e2565b94505093509350935073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6001600160a01b0316826001600160a01b03160361276d57604051630c59659760e31b815260040160405180910390fd5b73eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeed196001600160a01b0385160161283c5782600454146127b457604051634ff64a9f60e01b815260040160405180910390fd5b6003546004546040515f926001600160a01b031691906127d5908590615952565b5f6040518083038185875af1925050503d805f811461280f576040519150601f19603f3d011682016040523d82523d5f602084013e612814565b606091505b505090508061283657604051633204506f60e01b815260040160405180910390fd5b50611271565b612846848461336c565b600354909350612863906001600160a01b038681169116856109b9565b6003546040515f916001600160a01b031690612880908490615952565b5f604051808303815f865af19150503d805f81146128b9576040519150601f19603f3d011682016040523d82523d5f602084013e6128be565b606091505b50509050806128e057604051633204506f60e01b815260040160405180910390fd5b60035461108d906001600160a01b0387811691165f6109b9565b600f81036129d1575f8080612911858701876153e3565b92509250925061292082613327565b6040516370a0823160e01b81526001600160a01b0380831660048301529193505f918516906370a0823190602401602060405180830381865afa158015612969573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061298d91906152dc565b9050818110156112715760405163b250459b60e01b81526001600160a01b0380861660048301528416602482015260448101839052606481018290526084016106e0565b601b8103612be9575f8080806129e9868801886159d0565b93509350935093506129fa81613327565b60405163c661065760e01b81525f600482018190529192506001600160a01b0386169063c661065790602401602060405180830381865afa158015612a41573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612a65919061547b565b60405163c661065760e01b8152600160048201529091505f906001600160a01b0387169063c661065790602401602060405180830381865afa158015612aad573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612ad1919061547b565b9050612af682865f81518110612ae957612ae9615306565b602002602001015161336c565b855f81518110612b0857612b08615306565b602002602001018181525050612b2b8186600181518110612ae957612ae9615306565b85600181518110612b3e57612b3e615306565b602002602001018181525050612b8186865f81518110612b6057612b60615306565b6020026020010151846001600160a01b03166109b99092919063ffffffff16565b612bb98686600181518110612b9857612b98615306565b6020026020010151836001600160a01b03166109b99092919063ffffffff16565b60405163a7256d0960e01b81526001600160a01b0387169063a7256d099061235090889088908890600401615a65565b601c8103612c93575f808080612c0186880188615a97565b9350935093509350612c1281613327565b9050612c1e848461336c565b604051632f30266960e11b81529093506001600160a01b03851690635e604cd290612c5190869086908690600401615af3565b5f604051808303815f875af1158015612c6c573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f1916820160405261108d9190810190615b23565b601d8103612d15575f80808080612cac87890189615bae565b94509450945094509450612cbf81613327565b9050612ccb858561336c565b60405163081579a560e01b815260048101829052600f85900b6024820152604481018490526001600160a01b0383811660648301529195509086169063081579a5906084016118d7565b60228103612d7a575f80612d2b8486018661535c565b91509150816001600160a01b031663d0e30db0826040518263ffffffff1660e01b81526004015f604051808303818588803b158015612d68575f80fd5b505af1158015610f47573d5f803e3d5ffd5b60238103612de6575f80612d908486018661535c565b604051632e1a7d4d60e01b81526004810182905291935091506001600160a01b03831690632e1a7d4d906024015f604051808303815f87803b158015612dd4575f80fd5b505af1158015610c83573d5f803e3d5ffd5b60248103612e70575f80612dfc8486018661535c565b915091505f826001600160a01b0316826040515f6040518083038185875af1925050503d805f8114612e49576040519150601f19603f3d011682016040523d82523d5f602084013e612e4e565b606091505b5050905080610cdc57604051633204506f60e01b815260040160405180910390fd5b604051636bb50f4f60e11b8152600481018290526024016106e0565b5f612e95610ab5565b84838114612eb9576040516001621398b960e31b0319815260040160405180910390fd5b60408051601e8082526103e082019092525f91816020015b604080518082019091525f8082526020820152815260200190600190039081612ed1579050509050676765c793fa10079d601b1b5f5b83811015612fa3575f8a8a83818110612f2257612f22615306565b9050013560f81c60f81b9050365f8a8a85818110612f4257612f42615306565b9050602002810190612f54919061531a565b915091505f612f668484848d8b6134c1565b9050676765c793fa10079d601b1b8114612f9357612f908682676765c793fa10079d601b1b614782565b95505b505060019092019150612f079050565b5098975050505050505050565b5f80516020615e3183398151915280546001600160a01b0383166001600160a01b03199091168117825560408051918252517f2f658b440c35314f52658ea8a740e05b284cdc84dc9ae01e891f21b8933e7cad9181900360200190a15050565b5f80516020615e318339815191525f8061304861302b610875565b873061303a60045f8a8c615c01565b61304391615c28565b614841565b91509150816106535763ffffffff8116156130e357825460ff60a01b1916600160a01b178355613076610875565b6001600160a01b03166394c7d7ee8787876040518463ffffffff1660e01b81526004016130a593929190615c58565b5f604051808303815f87803b1580156130bc575f80fd5b505af11580156130ce573d5f803e3d5ffd5b5050845460ff60a01b19168555506106539050565b60405162d1953b60e31b81526001600160a01b03871660048201526024016106e0565b61310e610ab5565b5f80516020615e51833981519152805460ff191660011781557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258335b6040516001600160a01b03909116815260200160405180910390a150565b613170614949565b6001600160a01b03821661319757604051630c59659760e31b815260040160405180910390fd5b600280546001600160a01b039384166001600160a01b03199182161790915560038054929093169116179055565b6131cd614949565b6131d681614992565b50565b6131e16149a3565b5f80516020615e51833981519152805460ff191681557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa3361314a565b5f805f846001600160a01b0316846040516132399190615952565b5f604051808303815f865af19150503d805f8114613272576040519150601f19603f3d011682016040523d82523d5f602084013e613277565b606091505b50915091508180156132a15750805115806132a15750808060200190518101906132a1919061559a565b80156132b657505f856001600160a01b03163b115b925050505b92915050565b5f6132d56001600160a01b038416836149d2565b905080515f141580156132f95750808060200190518101906132f7919061559a565b155b1561332257604051635274afe760e01b81526001600160a01b03841660048201526024016106e0565b505050565b5f60df196001600160a01b03831601613341575030919050565b60bf196001600160a01b038316016133635750505f546001600160a01b031690565b5090565b919050565b5f600160ff1b821461337e57816133e4565b6040516370a0823160e01b81523060048201526001600160a01b038416906370a0823190602401602060405180830381865afa1580156133c0573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906133e491906152dc565b9392505050565b6040516001600160a01b0383811660248301526044820183905261332291859182169063a9059cbb90606401610a3a565b604051636eb1769f60e11b81523060048201526001600160a01b0383811660248301525f919085169063dd62ed3e90604401602060405180830381865afa158015613469573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061348d91906152dc565b905081811015610a7657610a766001600160a01b038516845f196109b9565b5f8183106134ba57816133e4565b5090919050565b5f60f886901c603f168015806134d75750600181145b156135165783613502575f806134ef8789018961535c565b915091506134fe8183876149df565b5050505b676765c793fa10079d601b1b915050610697565b600281036135765783613502575f8080613532888a018a6153e3565b92509250925061354182613327565b91506001600160a01b03821630146134fe5761355e818488614b43565b50505050676765c793fa10079d601b1b915050610697565b60038114806135855750601581145b806135905750601e81145b1561390c575f808080806135a68a8c018c615421565b95505094509450945094505f89156136de575f60038814806135c85750601588145b61364f5760025460405163028c676d60e31b81526001600160a01b038981166004830152600f89810b602484015288900b6044830152909116906314633b6890606401602060405180830381865afa158015613626573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061364a91906152dc565b6136c9565b60025460405163044e3c3f60e31b81526001600160a01b038981166004830152602482018990526044820188905290911690632271e1f890606401602060405180830381865afa1580156136a5573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906136c991906152dc565b90506136d6816012614cc0565b9150506138fe565b60405163c661065760e01b8152600481018690526137519084906001600160a01b0389169063c661065790602401602060405180830381865afa158015613727573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061374b919061547b565b8b614b43565b92505f601e88036137dc57604051635e0d443f60e01b8152600f87810b600483015286900b6024820152604481018590526001600160a01b03881690635e0d443f90606401602060405180830381865afa1580156137b1573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906137d591906152dc565b9050613854565b60405163556d6e9f60e01b81526004810187905260248101869052604481018590526001600160a01b0388169063556d6e9f90606401602060405180830381865afa15801561382d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061385191906152dc565b90505b61385d83613327565b9250306001600160a01b038416036138e45760405163c661065760e01b8152600481018690526138e29082906001600160a01b038a169063c661065790602401602060405180830381865afa1580156138b8573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906138dc919061547b565b8c6149df565b505b6138fa81676765c793fa10079d601b1b86614782565b9150505b965061069795505050505050565b60108103613aca575f8080613923888a018a615500565b9250925092505f836001600160a01b031663732e86fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613966573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061398a919061547b565b90508715613a0557600254604051635fa5a49760e01b81526001600160a01b03838116600483015290911690635fa5a49790602401602060405180830381865afa1580156139da573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906139fe91906152dc565b9250613a13565b613a10838289614b43565b92505b60405163404b9d8160e01b8152600481018490525f906001600160a01b0386169063404b9d81906024015b602060405180830381865afa158015613a59573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613a7d91906152dc565b9050613a8883613327565b9250306001600160a01b03841603613aa757613aa581868a6149df565b505b613abd81676765c793fa10079d601b1b86614782565b9650505050505050610697565b60118103613c82575f8080613ae1888a018a615500565b9250925092508615613b6057600254604051635fa5a49760e01b81526001600160a01b03858116600483015290911690635fa5a49790602401602060405180830381865afa158015613b35573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613b5991906152dc565b9150613b6e565b613b6b828488614b43565b91505b60405163554d83a760e11b8152600481018390525f906001600160a01b0385169063aa9b074e90602401602060405180830381865afa158015613bb3573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613bd791906152dc565b9050613be282613327565b9150306001600160a01b03831603613c6057613c5e81856001600160a01b031663732e86fe6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c34573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613c58919061547b565b896149df565b505b613c7681676765c793fa10079d601b1b85614782565b95505050505050610697565b60048103613db8575f8080613c99888a018a615500565b9250925092505f836001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613cdc573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613d00919061547b565b90508715613d7b57600254604051635fa5a49760e01b81526001600160a01b03838116600483015290911690635fa5a49790602401602060405180830381865afa158015613d50573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613d7491906152dc565b9250613d89565b613d86838289614b43565b92505b60405163ef8b30f760e01b8152600481018490525f906001600160a01b0386169063ef8b30f790602401613a3e565b60058103613fd7575f808080613dd0898b018b615c97565b93509350935093508715613e5157600254604051633e3f205760e21b81526001600160a01b0386811660048301529091169063f8fc815c90602401602060405180830381865afa158015613e26573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613e4a91906152dc565b9250613ebe565b613ebb83856001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015613e91573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613eb5919061547b565b89614b43565b92505b60405163ef8b30f760e01b8152600481018490525f906001600160a01b0386169063ef8b30f7906024015b602060405180830381865afa158015613f04573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613f2891906152dc565b9050613f3383613327565b9250306001600160a01b03841603613f5257613f5081868a6149df565b505b613f5b82613327565b9150306001600160a01b03831603613aa757613aa581866001600160a01b03166304aa50ad6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613fad573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190613fd1919061547b565b8a6149df565b600681036140e2575f808080613fef898b018b615c97565b9350935093509350871561407057600254604051635fa5a49760e01b81526001600160a01b03868116600483015290911690635fa5a49790602401602060405180830381865afa158015614045573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061406991906152dc565b92506140b3565b6140b083856001600160a01b031663c644fe946040518163ffffffff1660e01b8152600401602060405180830381865afa158015613e91573d5f803e3d5ffd5b92505b6040516302f6fa9560e11b8152600481018490525f906001600160a01b038616906305edf52a90602401613ee9565b6007810361424c575f80806140f9888a018a615500565b925092509250861561417857600254604051635fa5a49760e01b81526001600160a01b03858116600483015290911690635fa5a49790602401602060405180830381865afa15801561414d573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061417191906152dc565b9150614186565b614183828488614b43565b91505b60405163266d6a8360e11b8152600481018390525f906001600160a01b03851690634cdad50690602401602060405180830381865afa1580156141cb573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906141ef91906152dc565b90506141fa82613327565b9150306001600160a01b03831603613c6057613c5e81856001600160a01b03166338d52e0f6040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c34573d5f803e3d5ffd5b60088103614488575f8080614263888a018a615500565b92509250925086156142e257600254604051635fa5a49760e01b81526001600160a01b03858116600483015290911690635fa5a49790602401602060405180830381865afa1580156142b7573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906142db91906152dc565b91506143c2565b6142ed828488614b43565b9150826001600160a01b031663204f83f96040518163ffffffff1660e01b8152600401602060405180830381865afa15801561432b573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061434f91906152dc565b4210156143c2576143c082846001600160a01b03166304aa50ad6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614396573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906143ba919061547b565b88614b43565b505b60405163266d6a8360e11b8152600481018390525f906001600160a01b03851690634cdad50690602401602060405180830381865afa158015614407573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061442b91906152dc565b905061443682613327565b9150306001600160a01b03831603613c6057613c5e81856001600160a01b0316636f307dc36040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c34573d5f803e3d5ffd5b6009810361469a575f808061449f888a018a615500565b925092509250861561451e57600254604051635fa5a49760e01b81526001600160a01b03858116600483015290911690635fa5a49790602401602060405180830381865afa1580156144f3573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061451791906152dc565b91506145d4565b614529828488614b43565b9150826001600160a01b031663204f83f96040518163ffffffff1660e01b8152600401602060405180830381865afa158015614567573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061458b91906152dc565b4210156145d4576145d282846001600160a01b03166304aa50ad6040518163ffffffff1660e01b8152600401602060405180830381865afa158015614396573d5f803e3d5ffd5b505b604051633460fbfb60e11b8152600481018390525f906001600160a01b038516906368c1f7f690602401602060405180830381865afa158015614619573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061463d91906152dc565b905061464882613327565b9150306001600160a01b03831603613c6057613c5e81856001600160a01b031663c644fe946040518163ffffffff1660e01b8152600401602060405180830381865afa158015613c34573d5f803e3d5ffd5b60128103614766576003546001600160a01b03166146cb576040516327a8fdaf60e11b815260040160405180910390fd5b5f8080806146db898b018b615496565b935093509350935073eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee6001600160a01b0316826001600160a01b03160361472957604051630c59659760e31b815260040160405180910390fd5b6001600160a01b03841673eeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee1461475b57614758838589614b43565b92505b613c5e8183896149df565b600f8103612e7057676765c793fa10079d601b1b915050610697565b5f838302815f1985870982811083820303915050805f036147b6578382816147ac576147ac615cdc565b04925050506133e4565b8084116147d65760405163227bc15360e01b815260040160405180910390fd5b5f848688095f868103871696879004966002600389028118808a02820302808a02820302808a02820302808a02820302808a02820302808a02909103029181900381900460010186841190950394909402919094039290920491909117919091029150509392505050565b6040516001600160a01b03848116602483015283811660448301526001600160e01b0319831660648301525f9182918291829189169060840160408051601f198184030181529181526020820180516001600160e01b031663b700961360e01b179052516148af9190615952565b5f60405180830381855afa9150503d805f81146148e7576040519150601f19603f3d011682016040523d82523d5f602084013e6148ec565b606091505b5091509150811561493e57604081511061491e57808060200190518101906149149190615cf0565b909450925061493e565b602081511061493e578080602001905181019061493b919061559a565b93505b505094509492505050565b7ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054600160401b900460ff1661073f57604051631afcd79f60e31b815260040160405180910390fd5b61499a614949565b6131d681612fb0565b5f80516020615e518339815191525460ff1661073f57604051638dfc202b60e01b815260040160405180910390fd5b60606133e483835f614cf3565b5f6001600160a01b038316614a0757604051630c59659760e31b815260040160405180910390fd5b81515f5b81811015614b29575f6001600160a01b0316848281518110614a2f57614a2f615306565b60200260200101515f01516001600160a01b031603614a8d576040518060400160405280866001600160a01b0316815260200187815250848281518110614a7857614a78615306565b602002602001018190525085925050506133e4565b846001600160a01b0316848281518110614aa957614aa9615306565b60200260200101515f01516001600160a01b031603614b195785848281518110614ad557614ad5615306565b6020026020010151602001818151614aed91906152c9565b9052508351849082908110614b0457614b04615306565b602002602001015160200151925050506133e4565b614b2281615d2d565b9050614a0b565b50604051638b48412160e01b815260040160405180910390fd5b5f6001600160a01b038316614b6b57604051630c59659760e31b815260040160405180910390fd5b81515f5b81811015614ca6575f6001600160a01b0316848281518110614b9357614b93615306565b60200260200101515f01516001600160a01b03160315614ca657846001600160a01b0316848281518110614bc957614bc9615306565b60200260200101515f01516001600160a01b031603614c9657600160ff1b8603614c39575f848281518110614c0057614c00615306565b60200260200101516020015190505f858381518110614c2157614c21615306565b602090810291909101810151015292506133e4915050565b85848281518110614c4c57614c4c615306565b60200260200101516020015110614ca65785848281518110614c7057614c70615306565b6020026020010151602001818151614c8891906152f3565b9052508592506133e4915050565b614c9f81615d2d565b9050614b6f565b506040516305e72d3960e11b815260040160405180910390fd5b5f80614ccd83601b6152f3565b614cd890600a615e25565b905080840291508381830414614cec575f80fd5b5092915050565b606081471015614d185760405163cd78605960e01b81523060048201526024016106e0565b5f80856001600160a01b03168486604051614d339190615952565b5f6040518083038185875af1925050503d805f8114614d6d576040519150601f19603f3d011682016040523d82523d5f602084013e614d72565b606091505b5091509150614d82868383614d8c565b9695505050505050565b606082614da157614d9c82614de8565b6133e4565b8151158015614db857506001600160a01b0384163b155b15614de157604051639996b31560e01b81526001600160a01b03851660048201526024016106e0565b50806133e4565b805115614df85780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6001600160a01b03811681146131d6575f80fd5b5f8083601f840112614e35575f80fd5b5081356001600160401b03811115614e4b575f80fd5b602083019150836020828501011115614e62575f80fd5b9250929050565b5f805f805f8060a08789031215614e7e575f80fd5b8635614e8981614e11565b95506020870135614e9981614e11565b9450604087013593506060870135925060808701356001600160401b03811115614ec1575f80fd5b614ecd89828a01614e25565b979a9699509497509295939492505050565b5f8083601f840112614eef575f80fd5b5081356001600160401b03811115614f05575f80fd5b6020830191508360208260051b8501011115614e62575f80fd5b5f805f8060408587031215614f32575f80fd5b84356001600160401b0380821115614f48575f80fd5b614f5488838901614e25565b90965094506020870135915080821115614f6c575f80fd5b50614f7987828801614edf565b95989497509550505050565b5f805f805f60608688031215614f99575f80fd5b85356001600160401b0380821115614faf575f80fd5b614fbb89838a01614e25565b90975095506020880135915080821115614fd3575f80fd5b50614fe088828901614edf565b96999598509660400135949350505050565b5f60208284031215615002575f80fd5b81356133e481614e11565b5f805f6060848603121561501f575f80fd5b833561502a81614e11565b9250602084013561503a81614e11565b9150604084013561504a81614e11565b809150509250925092565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b038111828210171561509157615091615055565b604052919050565b5f82601f8301126150a8575f80fd5b81356001600160401b038111156150c1576150c1615055565b6150d4601f8201601f1916602001615069565b8181528460208386010111156150e8575f80fd5b816020850160208301375f918101602001919091529392505050565b5f6001600160401b0382111561511c5761511c615055565b5060051b60200190565b5f8060408385031215615137575f80fd5b82356001600160401b038082111561514d575f80fd5b61515986838701615099565b935060209150818501358181111561516f575f80fd5b8501601f8101871361517f575f80fd5b803561519261518d82615104565b615069565b81815260059190911b820184019084810190898311156151b0575f80fd5b8584015b838110156151e7578035868111156151cb575f8081fd5b6151d98c8983890101615099565b8452509186019186016151b4565b508096505050505050509250929050565b5f5b838110156152125781810151838201526020016151fa565b50505f910152565b5f81518084526152318160208601602086016151f8565b601f01601f19169290920160200192915050565b604081525f615257604083018561521a565b6020838203818501528185518084528284019150828160051b8501018388015f5b838110156152a657601f1987840301855261529483835161521a565b94860194925090850190600101615278565b50909998505050505050505050565b634e487b7160e01b5f52601160045260245ffd5b808201808211156132bb576132bb6152b5565b5f602082840312156152ec575f80fd5b5051919050565b818103818111156132bb576132bb6152b5565b634e487b7160e01b5f52603260045260245ffd5b5f808335601e1984360301811261532f575f80fd5b8301803591506001600160401b03821115615348575f80fd5b602001915036819003821315614e62575f80fd5b5f806040838503121561536d575f80fd5b823561537881614e11565b946020939093013593505050565b5f805f805f8060c0878903121561539b575f80fd5b86356153a681614e11565b95506020870135945060408701359350606087013560ff811681146153c9575f80fd5b9598949750929560808101359460a0909101359350915050565b5f805f606084860312156153f5575f80fd5b833561540081614e11565b9250602084013561541081614e11565b929592945050506040919091013590565b5f805f805f8060c08789031215615436575f80fd5b863561544181614e11565b95506020870135945060408701359350606087013592506080870135915060a087013561546d81614e11565b809150509295509295509295565b5f6020828403121561548b575f80fd5b81516133e481614e11565b5f805f80608085870312156154a9575f80fd5b84356154b481614e11565b93506020850135925060408501356154cb81614e11565b9396929550929360600135925050565b9384526001600160a01b03928316602085015291166040830152606082015260800190565b5f805f60608486031215615512575f80fd5b833561551d81614e11565b925060208401359150604084013561504a81614e11565b5f805f805f60a08688031215615548575f80fd5b853561555381614e11565b945060208601359350604086013561556a81614e11565b9250606086013561557a81614e11565b949793965091946080013592915050565b80518015158114613367575f80fd5b5f602082840312156155aa575f80fd5b6133e48261558b565b5f805f80608085870312156155c6575f80fd5b84356155d181614e11565b935060208501356155e181614e11565b92506040850135915060608501356001600160401b03811115615602575f80fd5b61560e87828801615099565b91505092959194509250565b6001600160a01b03858116825284166020820152604081018390526080606082018190525f90614d829083018461521a565b5f805f805f8060c08789031215615661575f80fd5b863561566c81614e11565b95506020870135945060408701359350606087013561568a81614e11565b9250608087013561569a81614e11565b8092505060a087013590509295509295509295565b5f82601f8301126156be575f80fd5b604051604081018181106001600160401b03821117156156e0576156e0615055565b80604052508060408401858111156156f6575f80fd5b845b818110156157105780358352602092830192016156f8565b509195945050505050565b5f805f8060a0858703121561572e575f80fd5b843561573981614e11565b935061574886602087016156af565b925060608501359150608085013561575f81614e11565b939692955090935050565b805f5b6002811015610a7657815184526020938401939091019060010161576d565b6080810161579a828661576a565b60408201939093526001600160a01b0391909116606090910152919050565b60a081016157c7828761576a565b604082019490945291151560608301526001600160a01b0316608090910152919050565b5f805f8060a085870312156157fe575f80fd5b843561580981614e11565b93506020850135925061581f86604087016156af565b9150608085013561575f81614e11565b83815260808101615843602083018561576a565b6001600160a01b03929092166060919091015292915050565b84815260a08101615870602083018661576a565b92151560608201526001600160a01b039190911660809091015292915050565b5f805f805f60a086880312156158a4575f80fd5b85356158af81614e11565b945060208601359350604086013592506060860135915060808601356158d481614e11565b809150509295509295909350565b5f805f805f60a086880312156158f6575f80fd5b853561590181614e11565b945060208601359350604086013561591881614e11565b92506060860135915060808601356001600160401b03811115615939575f80fd5b61594588828901615099565b9150509295509295909350565b5f82516159638184602087016151f8565b9190910192915050565b5f82601f83011261597c575f80fd5b8135602061598c61518d83615104565b82815260059290921b840181019181810190868411156159aa575f80fd5b8286015b848110156159c557803583529183019183016159ae565b509695505050505050565b5f805f80608085870312156159e3575f80fd5b84356159ee81614e11565b935060208501356001600160401b03811115615a08575f80fd5b615a148782880161596d565b93505060408501359150606085013561575f81614e11565b5f8151808452602080850194508084015f5b83811015615a5a57815187529582019590820190600101615a3e565b509495945050505050565b606081525f615a776060830186615a2c565b6020830194909452506001600160a01b0391909116604090910152919050565b5f805f8060808587031215615aaa575f80fd5b8435615ab581614e11565b93506020850135925060408501356001600160401b03811115615ad6575f80fd5b615ae28782880161596d565b925050606085013561575f81614e11565b838152606060208201525f615b0b6060830185615a2c565b905060018060a01b0383166040830152949350505050565b5f6020808385031215615b34575f80fd5b82516001600160401b03811115615b49575f80fd5b8301601f81018513615b59575f80fd5b8051615b6761518d82615104565b81815260059190911b82018301908381019087831115615b85575f80fd5b928401925b82841015615ba357835182529284019290840190615b8a565b979650505050505050565b5f805f805f60a08688031215615bc2575f80fd5b8535615bcd81614e11565b9450602086013593506040860135600f81900b8114615bea575f80fd5b92506060860135915060808601356158d481614e11565b5f8085851115615c0f575f80fd5b83861115615c1b575f80fd5b5050820193919092039150565b6001600160e01b03198135818116916004851015615c505780818660040360031b1b83161692505b505092915050565b6001600160a01b03841681526040602082018190528101829052818360608301375f818301606090810191909152601f909201601f1916010192915050565b5f805f8060808587031215615caa575f80fd5b8435615cb581614e11565b9350602085013592506040850135615ccc81614e11565b9150606085013561575f81614e11565b634e487b7160e01b5f52601260045260245ffd5b5f8060408385031215615d01575f80fd5b615d0a8361558b565b9150602083015163ffffffff81168114615d22575f80fd5b809150509250929050565b5f60018201615d3e57615d3e6152b5565b5060010190565b600181815b80851115615d7f57815f1904821115615d6557615d656152b5565b80851615615d7257918102915b93841c9390800290615d4a565b509250929050565b5f82615d95575060016132bb565b81615da157505f6132bb565b8160018114615db75760028114615dc157615ddd565b60019150506132bb565b60ff841115615dd257615dd26152b5565b50506001821b6132bb565b5060208310610133831016604e8410600b8410161715615e00575081810a6132bb565b615e0a8383615d45565b805f1904821115615e1d57615e1d6152b5565b029392505050565b5f6133e48383615d8756fef3177357ab46d8af007ab3fdb9af81da189e1068fefdc0073dca88a2cab40a00cd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300a264697066735822122057591e54a916f3b9443c691928a96f6d9c4d16f411ae8a4a77f39bb28fbcef9864736f6c63430008140033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
00000000000000000000000009176eacaa413cc0722aa5ad716820e8f19682b7
-----Decoded View---------------
Arg [0] : _registry (address): 0x09176eacAA413cc0722aa5AD716820E8f19682b7
-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 00000000000000000000000009176eacaa413cc0722aa5ad716820e8f19682b7
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 34 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.