Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
Latest 18 from a total of 18 transactions
Transaction Hash |
Method
|
Block
|
From
|
To
|
Value | ||||
---|---|---|---|---|---|---|---|---|---|
Deploy | 19920159 | 45 days ago | IN | 0 ETH | 0.10596215 | ||||
Deploy | 19920153 | 45 days ago | IN | 0 ETH | 0.09806574 | ||||
Deploy | 19577283 | 93 days ago | IN | 0 ETH | 0.2020393 | ||||
Deploy | 19528762 | 100 days ago | IN | 0 ETH | 0.17728328 | ||||
Deploy | 19507646 | 103 days ago | IN | 0 ETH | 0.09585408 | ||||
Deploy | 19505000 | 103 days ago | IN | 0 ETH | 0.11980918 | ||||
Deploy | 19504992 | 103 days ago | IN | 0 ETH | 0.11006872 | ||||
Deploy | 19113801 | 158 days ago | IN | 0 ETH | 0.11892728 | ||||
Deploy | 19113798 | 158 days ago | IN | 0 ETH | 0.12757369 | ||||
Deploy | 18963925 | 179 days ago | IN | 0 ETH | 0.17210659 | ||||
Deploy | 18963923 | 179 days ago | IN | 0 ETH | 0.15250863 | ||||
Deploy | 18963861 | 179 days ago | IN | 0 ETH | 0.12760366 | ||||
Deploy | 18963836 | 179 days ago | IN | 0 ETH | 0.14145726 | ||||
Deploy | 18963816 | 179 days ago | IN | 0 ETH | 0.14174384 | ||||
Deploy | 18963692 | 179 days ago | IN | 0 ETH | 0.13532993 | ||||
Deploy | 18871645 | 192 days ago | IN | 0 ETH | 0.14375117 | ||||
Set Creation Cod... | 18871614 | 192 days ago | IN | 0 ETH | 0.18890238 | ||||
0x60803462 | 18871613 | 192 days ago | IN | Create: SturdyPairDeployer | 0 ETH | 0.05679187 |
Latest 18 internal transactions
Advanced mode:
Parent Transaction Hash | Block | From | To | Value | ||
---|---|---|---|---|---|---|
19920159 | 45 days ago | Contract Creation | 0 ETH | |||
19920153 | 45 days ago | Contract Creation | 0 ETH | |||
19577283 | 93 days ago | Contract Creation | 0 ETH | |||
19528762 | 100 days ago | Contract Creation | 0 ETH | |||
19507646 | 103 days ago | Contract Creation | 0 ETH | |||
19505000 | 103 days ago | Contract Creation | 0 ETH | |||
19504992 | 103 days ago | Contract Creation | 0 ETH | |||
19113801 | 158 days ago | Contract Creation | 0 ETH | |||
19113798 | 158 days ago | Contract Creation | 0 ETH | |||
18963925 | 179 days ago | Contract Creation | 0 ETH | |||
18963923 | 179 days ago | Contract Creation | 0 ETH | |||
18963861 | 179 days ago | Contract Creation | 0 ETH | |||
18963836 | 179 days ago | Contract Creation | 0 ETH | |||
18963816 | 179 days ago | Contract Creation | 0 ETH | |||
18963692 | 179 days ago | Contract Creation | 0 ETH | |||
18871645 | 192 days ago | Contract Creation | 0 ETH | |||
18871614 | 192 days ago | Contract Creation | 0 ETH | |||
18871614 | 192 days ago | Contract Creation | 0 ETH |
Loading...
Loading
This contract contains unverified libraries: __CACHE_BREAKER__
Contract Name:
SturdyPairDeployer
Compiler Version
v0.8.21+commit.d9974bed
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; // ====================== SturdyPairDeployer ======================== import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { SSTORE2 } from "@rari-capital/solmate/src/utils/SSTORE2.sol"; import { BytesLib } from "solidity-bytes-utils/contracts/BytesLib.sol"; import { ISturdyWhitelist } from "./interfaces/ISturdyWhitelist.sol"; import { ISturdyPair } from "./interfaces/ISturdyPair.sol"; import { ISturdyPairRegistry } from "./interfaces/ISturdyPairRegistry.sol"; import { SafeERC20 } from "./libraries/SafeERC20.sol"; // solhint-disable no-inline-assembly struct ConstructorParams { address circuitBreaker; address comptroller; address timelock; address sturdyWhitelist; address sturdyPairRegistry; } /// @title SturdyPairDeployer /// @author Drake Evans (Frax Finance) https://github.com/drakeevans /// @notice Deploys and initializes new SturdyPairs /// @dev Uses create2 to deploy the pairs, logs an event, and records a list of all deployed pairs contract SturdyPairDeployer is Ownable { using Strings for uint256; using SafeERC20 for IERC20; // Storage address public contractAddress1; address public contractAddress2; // Admin contracts address public circuitBreakerAddress; address public comptrollerAddress; address public timelockAddress; address public sturdyPairRegistryAddress; address public sturdyWhitelistAddress; // Default swappers address[] public defaultSwappers; /// @notice Emits when a new pair is deployed /// @notice The ```LogDeploy``` event is emitted when a new Pair is deployed /// @param address_ The address of the pair /// @param asset The address of the Asset Token contract /// @param collateral The address of the Collateral Token contract /// @param name The name of the Pair /// @param configData The config data of the Pair /// @param immutables The immutables of the Pair /// @param customConfigData The custom config data of the Pair event LogDeploy( address indexed address_, address indexed asset, address indexed collateral, string name, bytes configData, bytes immutables, bytes customConfigData ); /// @notice List of the names of all deployed Pairs address[] public deployedPairsArray; constructor(ConstructorParams memory _params) Ownable() { circuitBreakerAddress = _params.circuitBreaker; comptrollerAddress = _params.comptroller; timelockAddress = _params.timelock; sturdyWhitelistAddress = _params.sturdyWhitelist; sturdyPairRegistryAddress = _params.sturdyPairRegistry; } function version() external pure returns (uint256 _major, uint256 _minor, uint256 _patch) { return (4, 1, 0); } // ============================================================================================ // Functions: View Functions // ============================================================================================ /// @notice The ```deployedPairsLength``` function returns the length of the deployedPairsArray /// @return length of array function deployedPairsLength() external view returns (uint256) { return deployedPairsArray.length; } /// @notice The ```getAllPairAddresses``` function returns all pair addresses in deployedPairsArray /// @return _deployedPairs memory All deployed pair addresses function getAllPairAddresses() external view returns (address[] memory _deployedPairs) { _deployedPairs = deployedPairsArray; } function getNextNameSymbol( address _asset, address _collateral ) public view returns (string memory _name, string memory _symbol) { uint256 _length = ISturdyPairRegistry(sturdyPairRegistryAddress).deployedPairsLength(); _name = string( abi.encodePacked( "Sturdy Interest Bearing ", IERC20(_asset).safeSymbol(), " (", IERC20(_collateral).safeName(), ")", " - ", (_length + 1).toString() ) ); _symbol = string( abi.encodePacked( "f", IERC20(_asset).safeSymbol(), "(", IERC20(_collateral).safeSymbol(), ")", "-", (_length + 1).toString() ) ); } // ============================================================================================ // Functions: Setters // ============================================================================================ /// @notice The ```setCreationCode``` function sets the bytecode for the sturdyPair /// @dev splits the data if necessary to accommodate creation code that is slightly larger than 24kb /// @param _creationCode The creationCode for the Sturdy Pair function setCreationCode(bytes calldata _creationCode) external onlyOwner { bytes memory _firstHalf = BytesLib.slice(_creationCode, 0, 13_000); contractAddress1 = SSTORE2.write(_firstHalf); if (_creationCode.length > 13_000) { bytes memory _secondHalf = BytesLib.slice(_creationCode, 13_000, _creationCode.length - 13_000); contractAddress2 = SSTORE2.write(_secondHalf); } } /// @notice The ```setDefaultSwappers``` function is used to set default list of approved swappers /// @param _swappers The list of swappers to set as default allowed function setDefaultSwappers(address[] memory _swappers) external onlyOwner { defaultSwappers = _swappers; } /// @notice The ```SetTimelock``` event is emitted when the timelockAddress is set /// @param oldAddress The original address /// @param newAddress The new address event SetTimelock(address oldAddress, address newAddress); /// @notice The ```setTimelock``` function sets the timelockAddress /// @param _newAddress the new time lock address function setTimelock(address _newAddress) external onlyOwner { emit SetTimelock(timelockAddress, _newAddress); timelockAddress = _newAddress; } /// @notice The ```SetRegistry``` event is emitted when the sturdyPairRegistryAddress is set /// @param oldAddress The old address /// @param newAddress The new address event SetRegistry(address oldAddress, address newAddress); /// @notice The ```setRegistry``` function sets the sturdyPairRegistryAddress /// @param _newAddress The new address function setRegistry(address _newAddress) external onlyOwner { emit SetRegistry(sturdyPairRegistryAddress, _newAddress); sturdyPairRegistryAddress = _newAddress; } /// @notice The ```SetComptroller``` event is emitted when the comptrollerAddress is set /// @param oldAddress The old address /// @param newAddress The new address event SetComptroller(address oldAddress, address newAddress); /// @notice The ```setComptroller``` function sets the comptrollerAddress /// @param _newAddress The new address function setComptroller(address _newAddress) external onlyOwner { emit SetComptroller(comptrollerAddress, _newAddress); comptrollerAddress = _newAddress; } /// @notice The ```SetWhitelist``` event is emitted when the sturdyWhitelistAddress is set /// @param oldAddress The old address /// @param newAddress The new address event SetWhitelist(address oldAddress, address newAddress); /// @notice The ```setWhitelist``` function sets the sturdyWhitelistAddress /// @param _newAddress The new address function setWhitelist(address _newAddress) external onlyOwner { emit SetWhitelist(sturdyWhitelistAddress, _newAddress); sturdyWhitelistAddress = _newAddress; } /// @notice The ```SetCircuitBreaker``` event is emitted when the circuitBreakerAddress is set /// @param oldAddress The old address /// @param newAddress The new address event SetCircuitBreaker(address oldAddress, address newAddress); /// @notice The ```setCircuitBreaker``` function sets the circuitBreakerAddress /// @param _newAddress The new address function setCircuitBreaker(address _newAddress) external onlyOwner { emit SetCircuitBreaker(circuitBreakerAddress, _newAddress); circuitBreakerAddress = _newAddress; } // ============================================================================================ // Functions: Internal Methods // ============================================================================================ /// @notice The ```_deploy``` function is an internal function with deploys the pair /// @param _configData abi.encode(address _asset, address _collateral, address _oracle, uint32 _maxOracleDeviation, address _rateContract, uint64 _fullUtilizationRate, uint256 _maxLTV, uint256 _cleanLiquidationFee, uint256 _dirtyLiquidationFee, uint256 _protocolLiquidationFee) /// @param _immutables abi.encode(address _circuitBreakerAddress, address _comptrollerAddress, address _timelockAddress) /// @param _customConfigData abi.encode(string memory _nameOfContract, string memory _symbolOfContract, uint8 _decimalsOfContract) /// @return _pairAddress The address to which the Pair was deployed function _deploy( bytes memory _configData, bytes memory _immutables, bytes memory _customConfigData ) private returns (address _pairAddress) { // Get creation code bytes memory _creationCode = BytesLib.concat(SSTORE2.read(contractAddress1), SSTORE2.read(contractAddress2)); // Get bytecode bytes memory bytecode = abi.encodePacked( _creationCode, abi.encode(_configData, _immutables, _customConfigData) ); // Generate salt using constructor params bytes32 salt = keccak256(abi.encodePacked(_configData, _immutables, _customConfigData)); /// @solidity memory-safe-assembly assembly { _pairAddress := create2(0, add(bytecode, 32), mload(bytecode), salt) } if (_pairAddress == address(0)) revert Create2Failed(); deployedPairsArray.push(_pairAddress); // Set additional values for SturdyPair ISturdyPair _sturdyPair = ISturdyPair(_pairAddress); address[] memory _defaultSwappers = defaultSwappers; for (uint256 i = 0; i < _defaultSwappers.length; i++) { _sturdyPair.setSwapper(_defaultSwappers[i], true); } return _pairAddress; } // ============================================================================================ // Functions: External Deploy Methods // ============================================================================================ /// @notice The ```deploy``` function allows the deployment of a SturdyPair with default values /// @param _configData abi.encode(address _asset, address _collateral, address _oracle, uint32 _maxOracleDeviation, address _rateContract, uint64 _fullUtilizationRate, uint256 _maxLTV, uint256 _cleanLiquidationFee, uint256 _dirtyLiquidationFee, uint256 _protocolLiquidationFee) /// @return _pairAddress The address to which the Pair was deployed function deploy(bytes memory _configData) external returns (address _pairAddress) { if (!ISturdyWhitelist(sturdyWhitelistAddress).sturdyDeployerWhitelist(msg.sender)) { revert WhitelistedDeployersOnly(); } (address _asset, address _collateral, , , , , , , ) = abi.decode( _configData, (address, address, address, uint32, address, uint64, uint256, uint256, uint256) ); (string memory _name, string memory _symbol) = getNextNameSymbol(_asset, _collateral); bytes memory _immutables = abi.encode(circuitBreakerAddress, comptrollerAddress, timelockAddress); bytes memory _customConfigData = abi.encode(_name, _symbol, IERC20(_asset).safeDecimals()); _pairAddress = _deploy(_configData, _immutables, _customConfigData); ISturdyPairRegistry(sturdyPairRegistryAddress).addPair(_pairAddress); emit LogDeploy(_pairAddress, _asset, _collateral, _name, _configData, _immutables, _customConfigData); } // ============================================================================================ // Functions: Admin // ============================================================================================ /// @notice The ```globalPause``` function calls the pause() function on a given set of pair addresses /// @dev Ignores reverts when calling pause() /// @param _addresses Addresses to attempt to pause() /// @return _updatedAddresses Addresses for which pause() was successful function globalPause(address[] memory _addresses) external returns (address[] memory _updatedAddresses) { if (msg.sender != circuitBreakerAddress) revert CircuitBreakerOnly(); address _pairAddress; uint256 _lengthOfArray = _addresses.length; _updatedAddresses = new address[](_lengthOfArray); for (uint256 i = 0; i < _lengthOfArray; ) { _pairAddress = _addresses[i]; try ISturdyPair(_pairAddress).pause() { _updatedAddresses[i] = _addresses[i]; } catch {} unchecked { i = i + 1; } } } // ============================================================================================ // Errors // ============================================================================================ error CircuitBreakerOnly(); error WhitelistedDeployersOnly(); error Create2Failed(); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface AggregatorV3Interface { function decimals() external view returns (uint8); function description() external view returns (string memory); function version() external view returns (uint256); function getRoundData( uint80 _roundId ) external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); function latestRoundData() external view returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound); }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.7.6 <0.9.0; import '@uniswap/v3-core/contracts/interfaces/IUniswapV3Factory.sol'; /// @title Uniswap V3 Static Oracle /// @notice Oracle contract for calculating price quoting against Uniswap V3 interface IStaticOracle { /// @notice Returns the address of the Uniswap V3 factory /// @dev This value is assigned during deployment and cannot be changed /// @return The address of the Uniswap V3 factory function UNISWAP_V3_FACTORY() external view returns (IUniswapV3Factory); /// @notice Returns how many observations are needed per minute in Uniswap V3 oracles, on the deployed chain /// @dev This value is assigned during deployment and cannot be changed /// @return Number of observation that are needed per minute function CARDINALITY_PER_MINUTE() external view returns (uint8); /// @notice Returns all supported fee tiers /// @return The supported fee tiers function supportedFeeTiers() external view returns (uint24[] memory); /// @notice Returns whether a specific pair can be supported by the oracle /// @dev The pair can be provided in tokenA/tokenB or tokenB/tokenA order /// @return Whether the given pair can be supported by the oracle function isPairSupported(address tokenA, address tokenB) external view returns (bool); /// @notice Returns all existing pools for the given pair /// @dev The pair can be provided in tokenA/tokenB or tokenB/tokenA order /// @return All existing pools for the given pair function getAllPoolsForPair(address tokenA, address tokenB) external view returns (address[] memory); /// @notice Returns a quote, based on the given tokens and amount, by querying all of the pair's pools /// @dev If some pools are not configured correctly for the given period, then they will be ignored /// @dev Will revert if there are no pools available/configured for the pair and period combination /// @param baseAmount Amount of token to be converted /// @param baseToken Address of an ERC20 token contract used as the baseAmount denomination /// @param quoteToken Address of an ERC20 token contract used as the quoteAmount denomination /// @param period Number of seconds from which to calculate the TWAP /// @return quoteAmount Amount of quoteToken received for baseAmount of baseToken /// @return queriedPools The pools that were queried to calculate the quote function quoteAllAvailablePoolsWithTimePeriod( uint128 baseAmount, address baseToken, address quoteToken, uint32 period ) external view returns (uint256 quoteAmount, address[] memory queriedPools); /// @notice Returns a quote, based on the given tokens and amount, by querying only the specified fee tiers /// @dev Will revert if the pair does not have a pool for one of the given fee tiers, or if one of the pools /// is not prepared/configured correctly for the given period /// @param baseAmount Amount of token to be converted /// @param baseToken Address of an ERC20 token contract used as the baseAmount denomination /// @param quoteToken Address of an ERC20 token contract used as the quoteAmount denomination /// @param feeTiers The fee tiers to consider when calculating the quote /// @param period Number of seconds from which to calculate the TWAP /// @return quoteAmount Amount of quoteToken received for baseAmount of baseToken /// @return queriedPools The pools that were queried to calculate the quote function quoteSpecificFeeTiersWithTimePeriod( uint128 baseAmount, address baseToken, address quoteToken, uint24[] calldata feeTiers, uint32 period ) external view returns (uint256 quoteAmount, address[] memory queriedPools); /// @notice Returns a quote, based on the given tokens and amount, by querying only the specified pools /// @dev Will revert if one of the pools is not prepared/configured correctly for the given period /// @param baseAmount Amount of token to be converted /// @param baseToken Address of an ERC20 token contract used as the baseAmount denomination /// @param quoteToken Address of an ERC20 token contract used as the quoteAmount denomination /// @param pools The pools to consider when calculating the quote /// @param period Number of seconds from which to calculate the TWAP /// @return quoteAmount Amount of quoteToken received for baseAmount of baseToken function quoteSpecificPoolsWithTimePeriod( uint128 baseAmount, address baseToken, address quoteToken, address[] calldata pools, uint32 period ) external view returns (uint256 quoteAmount); /// @notice Will initialize all existing pools for the given pair, so that they can be queried with the given period in the future /// @dev Will revert if there are no pools available for the pair and period combination /// @param tokenA One of the pair's tokens /// @param tokenB The other of the pair's tokens /// @param period The period that will be guaranteed when quoting /// @return preparedPools The pools that were prepared function prepareAllAvailablePoolsWithTimePeriod( address tokenA, address tokenB, uint32 period ) external returns (address[] memory preparedPools); /// @notice Will initialize the pair's pools with the specified fee tiers, so that they can be queried with the given period in the future /// @dev Will revert if the pair does not have a pool for a given fee tier /// @param tokenA One of the pair's tokens /// @param tokenB The other of the pair's tokens /// @param feeTiers The fee tiers to consider when searching for the pair's pools /// @param period The period that will be guaranteed when quoting /// @return preparedPools The pools that were prepared function prepareSpecificFeeTiersWithTimePeriod( address tokenA, address tokenB, uint24[] calldata feeTiers, uint32 period ) external returns (address[] memory preparedPools); /// @notice Will initialize all given pools, so that they can be queried with the given period in the future /// @param pools The pools to initialize /// @param period The period that will be guaranteed when quoting function prepareSpecificPoolsWithTimePeriod(address[] calldata pools, uint32 period) external; /// @notice Will increase observations for all existing pools for the given pair, so they start accruing information for twap calculations /// @dev Will revert if there are no pools available for the pair and period combination /// @param tokenA One of the pair's tokens /// @param tokenB The other of the pair's tokens /// @param cardinality The cardinality that will be guaranteed when quoting /// @return preparedPools The pools that were prepared function prepareAllAvailablePoolsWithCardinality( address tokenA, address tokenB, uint16 cardinality ) external returns (address[] memory preparedPools); /// @notice Will increase the pair's pools with the specified fee tiers observations, so they start accruing information for twap calculations /// @dev Will revert if the pair does not have a pool for a given fee tier /// @param tokenA One of the pair's tokens /// @param tokenB The other of the pair's tokens /// @param feeTiers The fee tiers to consider when searching for the pair's pools /// @param cardinality The cardinality that will be guaranteed when quoting /// @return preparedPools The pools that were prepared function prepareSpecificFeeTiersWithCardinality( address tokenA, address tokenB, uint24[] calldata feeTiers, uint16 cardinality ) external returns (address[] memory preparedPools); /// @notice Will increase all given pools observations, so they start accruing information for twap calculations /// @param pools The pools to initialize /// @param cardinality The cardinality that will be guaranteed when quoting function prepareSpecificPoolsWithCardinality(address[] calldata pools, uint16 cardinality) external; /// @notice Adds support for a new fee tier /// @dev Will revert if the given tier is invalid, or already supported /// @param feeTier The new fee tier to add function addNewFeeTier(uint24 feeTier) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable.sol) pragma solidity ^0.8.0; import "../utils/Context.sol"; /** * @dev Contract module which provides a basic access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership}. * * This module is used through inheritance. It will make available the modifier * `onlyOwner`, which can be applied to your functions to restrict their use to * the owner. */ abstract contract Ownable is Context { address private _owner; event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the deployer as the initial owner. */ constructor() { _transferOwnership(_msgSender()); } /** * @dev Throws if called by any account other than the owner. */ modifier onlyOwner() { _checkOwner(); _; } /** * @dev Returns the address of the current owner. */ function owner() public view virtual returns (address) { return _owner; } /** * @dev Throws if the sender is not the owner. */ function _checkOwner() internal view virtual { require(owner() == _msgSender(), "Ownable: caller is not the owner"); } /** * @dev Leaves the contract without owner. It will not be possible to call * `onlyOwner` functions. Can only be called by the current owner. * * NOTE: Renouncing ownership will leave the contract without an owner, * thereby disabling any functionality that is only available to the owner. */ function renounceOwnership() public virtual onlyOwner { _transferOwnership(address(0)); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual onlyOwner { require(newOwner != address(0), "Ownable: new owner is the zero address"); _transferOwnership(newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`). * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual { address oldOwner = _owner; _owner = newOwner; emit OwnershipTransferred(oldOwner, newOwner); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/Ownable2Step.sol) pragma solidity ^0.8.0; import "./Ownable.sol"; /** * @dev Contract module which provides access control mechanism, where * there is an account (an owner) that can be granted exclusive access to * specific functions. * * By default, the owner account will be the one that deploys the contract. This * can later be changed with {transferOwnership} and {acceptOwnership}. * * This module is used through inheritance. It will make available all functions * from parent (Ownable). */ abstract contract Ownable2Step is Ownable { address private _pendingOwner; event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); /** * @dev Returns the address of the pending owner. */ function pendingOwner() public view virtual returns (address) { return _pendingOwner; } /** * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one. * Can only be called by the current owner. */ function transferOwnership(address newOwner) public virtual override onlyOwner { _pendingOwner = newOwner; emit OwnershipTransferStarted(owner(), newOwner); } /** * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner. * Internal function without access restriction. */ function _transferOwnership(address newOwner) internal virtual override { delete _pendingOwner; super._transferOwnership(newOwner); } /** * @dev The new owner accepts the ownership transfer. */ function acceptOwnership() public virtual { address sender = _msgSender(); require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner"); _transferOwnership(sender); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (interfaces/IERC20.sol) pragma solidity ^0.8.0; import "../token/ERC20/IERC20.sol";
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; /** * @dev Contract module that helps prevent reentrant calls to a function. * * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier * available, which can be applied to functions to make sure there are no nested * (reentrant) calls to them. * * Note that because there is a single `nonReentrant` guard, functions marked as * `nonReentrant` may not call one another. This can be worked around by making * those functions `private`, and then adding `external` `nonReentrant` entry * points to them. * * TIP: If you would like to learn more about reentrancy and alternative ways * to protect against it, check out our blog post * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul]. */ abstract contract ReentrancyGuard { // Booleans are more expensive than uint256 or any type that takes up a full // word because each write operation emits an extra SLOAD to first read the // slot's contents, replace the bits taken up by the boolean, and then write // back. This is the compiler's defense against contract upgrades and // pointer aliasing, and it cannot be disabled. // The values being non-zero value makes deployment a bit more expensive, // but in exchange the refund on every call to nonReentrant will be lower in // amount. Since refunds are capped to a percentage of the total // transaction's gas, it is best to keep them low in cases like this one, to // increase the likelihood of the full refund coming into effect. uint256 private constant _NOT_ENTERED = 1; uint256 private constant _ENTERED = 2; uint256 private _status; constructor() { _status = _NOT_ENTERED; } /** * @dev Prevents a contract from calling itself, directly or indirectly. * Calling a `nonReentrant` function from another `nonReentrant` * function is not supported. It is possible to prevent this from happening * by making the `nonReentrant` function external, and making it call a * `private` function that does the actual work. */ modifier nonReentrant() { _nonReentrantBefore(); _; _nonReentrantAfter(); } function _nonReentrantBefore() private { // On the first call to nonReentrant, _status will be _NOT_ENTERED require(_status != _ENTERED, "ReentrancyGuard: reentrant call"); // Any calls to nonReentrant after this point will fail _status = _ENTERED; } function _nonReentrantAfter() private { // By storing the original value once again, a refund is triggered (see // https://eips.ethereum.org/EIPS/eip-2200) _status = _NOT_ENTERED; } /** * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a * `nonReentrant` function in the call stack. */ function _reentrancyGuardEntered() internal view returns (bool) { return _status == _ENTERED; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20.sol"; import "./extensions/IERC20Metadata.sol"; import "../../utils/Context.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * For a generic mechanism see {ERC20PresetMinterPauser}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. * * Finally, the non-standard {decreaseAllowance} and {increaseAllowance} * functions have been added to mitigate the well-known issues around setting * allowances. See {IERC20-approve}. */ contract ERC20 is Context, IERC20, IERC20Metadata { mapping(address => uint256) private _balances; mapping(address => mapping(address => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * All two of these values are immutable: they can only be set once during * construction. */ constructor(string memory name_, string memory symbol_) { _name = name_; _symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual override returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual override returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the default value returned by this function, unless * it's overridden. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual override returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual override returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual override returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `amount`. */ function transfer(address to, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _transfer(owner, to, amount); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual override returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 amount) public virtual override returns (bool) { address owner = _msgSender(); _approve(owner, spender, amount); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. * - the caller must have allowance for ``from``'s tokens of at least * `amount`. */ function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, amount); _transfer(from, to, amount); return true; } /** * @dev Atomically increases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. */ function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, allowance(owner, spender) + addedValue); return true; } /** * @dev Atomically decreases the allowance granted to `spender` by the caller. * * This is an alternative to {approve} that can be used as a mitigation for * problems described in {IERC20-approve}. * * Emits an {Approval} event indicating the updated allowance. * * Requirements: * * - `spender` cannot be the zero address. * - `spender` must have allowance for the caller of at least * `subtractedValue`. */ function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) { address owner = _msgSender(); uint256 currentAllowance = allowance(owner, spender); require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero"); unchecked { _approve(owner, spender, currentAllowance - subtractedValue); } return true; } /** * @dev Moves `amount` of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `from` must have a balance of at least `amount`. */ function _transfer(address from, address to, uint256 amount) internal virtual { require(from != address(0), "ERC20: transfer from the zero address"); require(to != address(0), "ERC20: transfer to the zero address"); _beforeTokenTransfer(from, to, amount); uint256 fromBalance = _balances[from]; require(fromBalance >= amount, "ERC20: transfer amount exceeds balance"); unchecked { _balances[from] = fromBalance - amount; // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by // decrementing then incrementing. _balances[to] += amount; } emit Transfer(from, to, amount); _afterTokenTransfer(from, to, amount); } /** @dev Creates `amount` tokens and assigns them to `account`, increasing * the total supply. * * Emits a {Transfer} event with `from` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. */ function _mint(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: mint to the zero address"); _beforeTokenTransfer(address(0), account, amount); _totalSupply += amount; unchecked { // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above. _balances[account] += amount; } emit Transfer(address(0), account, amount); _afterTokenTransfer(address(0), account, amount); } /** * @dev Destroys `amount` tokens from `account`, reducing the * total supply. * * Emits a {Transfer} event with `to` set to the zero address. * * Requirements: * * - `account` cannot be the zero address. * - `account` must have at least `amount` tokens. */ function _burn(address account, uint256 amount) internal virtual { require(account != address(0), "ERC20: burn from the zero address"); _beforeTokenTransfer(account, address(0), amount); uint256 accountBalance = _balances[account]; require(accountBalance >= amount, "ERC20: burn amount exceeds balance"); unchecked { _balances[account] = accountBalance - amount; // Overflow not possible: amount <= accountBalance <= totalSupply. _totalSupply -= amount; } emit Transfer(account, address(0), amount); _afterTokenTransfer(account, address(0), amount); } /** * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. */ function _approve(address owner, address spender, uint256 amount) internal virtual { require(owner != address(0), "ERC20: approve from the zero address"); require(spender != address(0), "ERC20: approve to the zero address"); _allowances[owner][spender] = amount; emit Approval(owner, spender, amount); } /** * @dev Updates `owner` s allowance for `spender` based on spent `amount`. * * Does not update the allowance amount in case of infinite allowance. * Revert if not enough allowance is available. * * Might emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 amount) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { require(currentAllowance >= amount, "ERC20: insufficient allowance"); unchecked { _approve(owner, spender, currentAllowance - amount); } } } /** * @dev Hook that is called before any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * will be transferred to `to`. * - when `from` is zero, `amount` tokens will be minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens will be burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {} /** * @dev Hook that is called after any transfer of tokens. This includes * minting and burning. * * Calling conditions: * * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens * has been transferred to `to`. * - when `from` is zero, `amount` tokens have been minted for `to`. * - when `to` is zero, `amount` of ``from``'s tokens have been burned. * - `from` and `to` are never both zero. * * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks]. */ function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the amount of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the amount of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves `amount` tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 amount) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets `amount` as the allowance of `spender` over the caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 amount) external returns (bool); /** * @dev Moves `amount` tokens from `from` to `to` using the * allowance mechanism. `amount` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 amount) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.0; import "../IERC20.sol"; import "../extensions/IERC20Permit.sol"; import "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value)); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value)); } /** * @dev Deprecated. This function has issues similar to the ones found in * {IERC20-approve}, and its usage is discouraged. * * Whenever possible, use {safeIncreaseAllowance} and * {safeDecreaseAllowance} instead. */ function safeApprove(IERC20 token, address spender, uint256 value) internal { // safeApprove should only be called when setting an initial allowance, // or when resetting it to zero. To increase and decrease it, use // 'safeIncreaseAllowance' and 'safeDecreaseAllowance' require( (value == 0) || (token.allowance(address(this), spender) == 0), "SafeERC20: approve from non-zero to non-zero allowance" ); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value)); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value)); } /** * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal { unchecked { uint256 oldAllowance = token.allowance(address(this), spender); require(oldAllowance >= value, "SafeERC20: decreased allowance below zero"); _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value)); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0)); _callOptionalReturn(token, approvalCall); } } /** * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`. * Revert on invalid signature. */ function safePermit( IERC20Permit token, address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) internal { uint256 nonceBefore = token.nonces(owner); token.permit(owner, spender, value, deadline, v, r, s); uint256 nonceAfter = token.nonces(owner); require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed"); require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed"); } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol) pragma solidity ^0.8.0; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { enum Rounding { Down, // Toward negative infinity Up, // Toward infinity Zero // Toward zero } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds up instead * of rounding down. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0 * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) * with further edits by Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod0 := mul(x, y) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // 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. require(denominator > prod1, "Math: mulDiv overflow"); /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1. // See https://cs.stackexchange.com/q/138556/92363. // Does not overflow because the denominator cannot be zero at this stage in the function. uint256 twos = denominator & (~denominator + 1); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works // in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (rounding == Rounding.Up && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2, rounded down, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10, rounded down, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256, rounded down, of a positive value. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol) // This file was procedurally generated from scripts/generate/templates/SafeCast.js. pragma solidity ^0.8.0; /** * @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. * * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing * all math on `uint256` and `int256` and then downcasting. */ library SafeCast { /** * @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 * * _Available since v4.7._ */ function toUint248(uint256 value) internal pure returns (uint248) { require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits"); 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 * * _Available since v4.7._ */ function toUint240(uint256 value) internal pure returns (uint240) { require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits"); 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 * * _Available since v4.7._ */ function toUint232(uint256 value) internal pure returns (uint232) { require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits"); 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 * * _Available since v4.2._ */ function toUint224(uint256 value) internal pure returns (uint224) { require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits"); 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 * * _Available since v4.7._ */ function toUint216(uint256 value) internal pure returns (uint216) { require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits"); 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 * * _Available since v4.7._ */ function toUint208(uint256 value) internal pure returns (uint208) { require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits"); 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 * * _Available since v4.7._ */ function toUint200(uint256 value) internal pure returns (uint200) { require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits"); 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 * * _Available since v4.7._ */ function toUint192(uint256 value) internal pure returns (uint192) { require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits"); 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 * * _Available since v4.7._ */ function toUint184(uint256 value) internal pure returns (uint184) { require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits"); 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 * * _Available since v4.7._ */ function toUint176(uint256 value) internal pure returns (uint176) { require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits"); 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 * * _Available since v4.7._ */ function toUint168(uint256 value) internal pure returns (uint168) { require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits"); 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 * * _Available since v4.7._ */ function toUint160(uint256 value) internal pure returns (uint160) { require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits"); 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 * * _Available since v4.7._ */ function toUint152(uint256 value) internal pure returns (uint152) { require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits"); 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 * * _Available since v4.7._ */ function toUint144(uint256 value) internal pure returns (uint144) { require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits"); 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 * * _Available since v4.7._ */ function toUint136(uint256 value) internal pure returns (uint136) { require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits"); 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 * * _Available since v2.5._ */ function toUint128(uint256 value) internal pure returns (uint128) { require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits"); 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 * * _Available since v4.7._ */ function toUint120(uint256 value) internal pure returns (uint120) { require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits"); 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 * * _Available since v4.7._ */ function toUint112(uint256 value) internal pure returns (uint112) { require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits"); 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 * * _Available since v4.7._ */ function toUint104(uint256 value) internal pure returns (uint104) { require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits"); 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 * * _Available since v4.2._ */ function toUint96(uint256 value) internal pure returns (uint96) { require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits"); 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 * * _Available since v4.7._ */ function toUint88(uint256 value) internal pure returns (uint88) { require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits"); 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 * * _Available since v4.7._ */ function toUint80(uint256 value) internal pure returns (uint80) { require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits"); 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 * * _Available since v4.7._ */ function toUint72(uint256 value) internal pure returns (uint72) { require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits"); 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 * * _Available since v2.5._ */ function toUint64(uint256 value) internal pure returns (uint64) { require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits"); 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 * * _Available since v4.7._ */ function toUint56(uint256 value) internal pure returns (uint56) { require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits"); 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 * * _Available since v4.7._ */ function toUint48(uint256 value) internal pure returns (uint48) { require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits"); 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 * * _Available since v4.7._ */ function toUint40(uint256 value) internal pure returns (uint40) { require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits"); 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 * * _Available since v2.5._ */ function toUint32(uint256 value) internal pure returns (uint32) { require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits"); 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 * * _Available since v4.7._ */ function toUint24(uint256 value) internal pure returns (uint24) { require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits"); 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 * * _Available since v2.5._ */ function toUint16(uint256 value) internal pure returns (uint16) { require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits"); 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 * * _Available since v2.5._ */ function toUint8(uint256 value) internal pure returns (uint8) { require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits"); return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. * * _Available since v3.0._ */ function toUint256(int256 value) internal pure returns (uint256) { require(value >= 0, "SafeCast: value must be positive"); 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 * * _Available since v4.7._ */ function toInt248(int256 value) internal pure returns (int248 downcasted) { downcasted = int248(value); require(downcasted == value, "SafeCast: value doesn't fit in 248 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt240(int256 value) internal pure returns (int240 downcasted) { downcasted = int240(value); require(downcasted == value, "SafeCast: value doesn't fit in 240 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt232(int256 value) internal pure returns (int232 downcasted) { downcasted = int232(value); require(downcasted == value, "SafeCast: value doesn't fit in 232 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt224(int256 value) internal pure returns (int224 downcasted) { downcasted = int224(value); require(downcasted == value, "SafeCast: value doesn't fit in 224 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt216(int256 value) internal pure returns (int216 downcasted) { downcasted = int216(value); require(downcasted == value, "SafeCast: value doesn't fit in 216 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt208(int256 value) internal pure returns (int208 downcasted) { downcasted = int208(value); require(downcasted == value, "SafeCast: value doesn't fit in 208 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt200(int256 value) internal pure returns (int200 downcasted) { downcasted = int200(value); require(downcasted == value, "SafeCast: value doesn't fit in 200 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt192(int256 value) internal pure returns (int192 downcasted) { downcasted = int192(value); require(downcasted == value, "SafeCast: value doesn't fit in 192 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt184(int256 value) internal pure returns (int184 downcasted) { downcasted = int184(value); require(downcasted == value, "SafeCast: value doesn't fit in 184 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt176(int256 value) internal pure returns (int176 downcasted) { downcasted = int176(value); require(downcasted == value, "SafeCast: value doesn't fit in 176 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt168(int256 value) internal pure returns (int168 downcasted) { downcasted = int168(value); require(downcasted == value, "SafeCast: value doesn't fit in 168 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt160(int256 value) internal pure returns (int160 downcasted) { downcasted = int160(value); require(downcasted == value, "SafeCast: value doesn't fit in 160 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt152(int256 value) internal pure returns (int152 downcasted) { downcasted = int152(value); require(downcasted == value, "SafeCast: value doesn't fit in 152 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt144(int256 value) internal pure returns (int144 downcasted) { downcasted = int144(value); require(downcasted == value, "SafeCast: value doesn't fit in 144 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt136(int256 value) internal pure returns (int136 downcasted) { downcasted = int136(value); require(downcasted == value, "SafeCast: value doesn't fit in 136 bits"); } /** * @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 * * _Available since v3.1._ */ function toInt128(int256 value) internal pure returns (int128 downcasted) { downcasted = int128(value); require(downcasted == value, "SafeCast: value doesn't fit in 128 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt120(int256 value) internal pure returns (int120 downcasted) { downcasted = int120(value); require(downcasted == value, "SafeCast: value doesn't fit in 120 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt112(int256 value) internal pure returns (int112 downcasted) { downcasted = int112(value); require(downcasted == value, "SafeCast: value doesn't fit in 112 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt104(int256 value) internal pure returns (int104 downcasted) { downcasted = int104(value); require(downcasted == value, "SafeCast: value doesn't fit in 104 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt96(int256 value) internal pure returns (int96 downcasted) { downcasted = int96(value); require(downcasted == value, "SafeCast: value doesn't fit in 96 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt88(int256 value) internal pure returns (int88 downcasted) { downcasted = int88(value); require(downcasted == value, "SafeCast: value doesn't fit in 88 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt80(int256 value) internal pure returns (int80 downcasted) { downcasted = int80(value); require(downcasted == value, "SafeCast: value doesn't fit in 80 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt72(int256 value) internal pure returns (int72 downcasted) { downcasted = int72(value); require(downcasted == value, "SafeCast: value doesn't fit in 72 bits"); } /** * @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 * * _Available since v3.1._ */ function toInt64(int256 value) internal pure returns (int64 downcasted) { downcasted = int64(value); require(downcasted == value, "SafeCast: value doesn't fit in 64 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt56(int256 value) internal pure returns (int56 downcasted) { downcasted = int56(value); require(downcasted == value, "SafeCast: value doesn't fit in 56 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt48(int256 value) internal pure returns (int48 downcasted) { downcasted = int48(value); require(downcasted == value, "SafeCast: value doesn't fit in 48 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt40(int256 value) internal pure returns (int40 downcasted) { downcasted = int40(value); require(downcasted == value, "SafeCast: value doesn't fit in 40 bits"); } /** * @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 * * _Available since v3.1._ */ function toInt32(int256 value) internal pure returns (int32 downcasted) { downcasted = int32(value); require(downcasted == value, "SafeCast: value doesn't fit in 32 bits"); } /** * @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 * * _Available since v4.7._ */ function toInt24(int256 value) internal pure returns (int24 downcasted) { downcasted = int24(value); require(downcasted == value, "SafeCast: value doesn't fit in 24 bits"); } /** * @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 * * _Available since v3.1._ */ function toInt16(int256 value) internal pure returns (int16 downcasted) { downcasted = int16(value); require(downcasted == value, "SafeCast: value doesn't fit in 16 bits"); } /** * @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 * * _Available since v3.1._ */ function toInt8(int256 value) internal pure returns (int8 downcasted) { downcasted = int8(value); require(downcasted == value, "SafeCast: value doesn't fit in 8 bits"); } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. * * _Available since v3.0._ */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256"); return int256(value); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol) pragma solidity ^0.8.0; import "./math/Math.sol"; import "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant _SYMBOLS = "0123456789abcdef"; uint8 private constant _ADDRESS_LENGTH = 20; /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), _SYMBOLS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toString(int256 value) internal pure returns (string memory) { return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value)))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = _SYMBOLS[value & 0xf]; value >>= 4; } require(value == 0, "Strings: hex length insufficient"); return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; library Errors { // APPROX error ApproxFail(); error ApproxParamsInvalid(uint256 guessMin, uint256 guessMax, uint256 eps); error ApproxBinarySearchInputInvalid( uint256 approxGuessMin, uint256 approxGuessMax, uint256 minGuessMin, uint256 maxGuessMax ); // MARKET + MARKET MATH CORE error MarketExpired(); error MarketZeroAmountsInput(); error MarketZeroAmountsOutput(); error MarketZeroLnImpliedRate(); error MarketInsufficientPtForTrade(int256 currentAmount, int256 requiredAmount); error MarketInsufficientPtReceived(uint256 actualBalance, uint256 requiredBalance); error MarketInsufficientSyReceived(uint256 actualBalance, uint256 requiredBalance); error MarketZeroTotalPtOrTotalAsset(int256 totalPt, int256 totalAsset); error MarketExchangeRateBelowOne(int256 exchangeRate); error MarketProportionMustNotEqualOne(); error MarketRateScalarBelowZero(int256 rateScalar); error MarketScalarRootBelowZero(int256 scalarRoot); error MarketProportionTooHigh(int256 proportion, int256 maxProportion); error OracleUninitialized(); error OracleTargetTooOld(uint32 target, uint32 oldest); error OracleZeroCardinality(); error MarketFactoryExpiredPt(); error MarketFactoryInvalidPt(); error MarketFactoryMarketExists(); error MarketFactoryLnFeeRateRootTooHigh(uint80 lnFeeRateRoot, uint256 maxLnFeeRateRoot); error MarketFactoryReserveFeePercentTooHigh(uint8 reserveFeePercent, uint8 maxReserveFeePercent); error MarketFactoryZeroTreasury(); error MarketFactoryInitialAnchorTooLow(int256 initialAnchor, int256 minInitialAnchor); // ROUTER error RouterInsufficientLpOut(uint256 actualLpOut, uint256 requiredLpOut); error RouterInsufficientSyOut(uint256 actualSyOut, uint256 requiredSyOut); error RouterInsufficientPtOut(uint256 actualPtOut, uint256 requiredPtOut); error RouterInsufficientYtOut(uint256 actualYtOut, uint256 requiredYtOut); error RouterInsufficientPYOut(uint256 actualPYOut, uint256 requiredPYOut); error RouterInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut); error RouterInsufficientSyRepay(uint256 actualSyRepay, uint256 requiredSyRepay); error RouterInsufficientPtRepay(uint256 actualPtRepay, uint256 requiredPtRepay); error RouterNotAllSyUsed(uint256 netSyDesired, uint256 netSyUsed); error RouterTimeRangeZero(); error RouterCallbackNotPendleMarket(address caller); error RouterInvalidAction(bytes4 selector); error RouterInvalidFacet(address facet); error RouterKyberSwapDataZero(); error CallThenRevertError(bool success, bytes res); // YIELD CONTRACT error YCExpired(); error YCNotExpired(); error YieldContractInsufficientSy(uint256 actualSy, uint256 requiredSy); error YCNothingToRedeem(); error YCPostExpiryDataNotSet(); error YCNoFloatingSy(); // YieldFactory error YCFactoryInvalidExpiry(); error YCFactoryYieldContractExisted(); error YCFactoryZeroExpiryDivisor(); error YCFactoryZeroTreasury(); error YCFactoryInterestFeeRateTooHigh(uint256 interestFeeRate, uint256 maxInterestFeeRate); error YCFactoryRewardFeeRateTooHigh(uint256 newRewardFeeRate, uint256 maxRewardFeeRate); // SY error SYInvalidTokenIn(address token); error SYInvalidTokenOut(address token); error SYZeroDeposit(); error SYZeroRedeem(); error SYInsufficientSharesOut(uint256 actualSharesOut, uint256 requiredSharesOut); error SYInsufficientTokenOut(uint256 actualTokenOut, uint256 requiredTokenOut); // SY-specific error SYQiTokenMintFailed(uint256 errCode); error SYQiTokenRedeemFailed(uint256 errCode); error SYQiTokenRedeemRewardsFailed(uint256 rewardAccruedType0, uint256 rewardAccruedType1); error SYQiTokenBorrowRateTooHigh(uint256 borrowRate, uint256 borrowRateMax); error SYCurveInvalidPid(); error SYCurve3crvPoolNotFound(); error SYApeDepositAmountTooSmall(uint256 amountDeposited); error SYBalancerInvalidPid(); error SYInvalidRewardToken(address token); error SYStargateRedeemCapExceeded(uint256 amountLpDesired, uint256 amountLpRedeemable); error SYBalancerReentrancy(); error NotFromTrustedRemote(uint16 srcChainId, bytes path); // Liquidity Mining error VCInactivePool(address pool); error VCPoolAlreadyActive(address pool); error VCZeroVePendle(address user); error VCExceededMaxWeight(uint256 totalWeight, uint256 maxWeight); error VCEpochNotFinalized(uint256 wTime); error VCPoolAlreadyAddAndRemoved(address pool); error VEInvalidNewExpiry(uint256 newExpiry); error VEExceededMaxLockTime(); error VEInsufficientLockTime(); error VENotAllowedReduceExpiry(); error VEZeroAmountLocked(); error VEPositionNotExpired(); error VEZeroPosition(); error VEZeroSlope(uint128 bias, uint128 slope); error VEReceiveOldSupply(uint256 msgTime); error GCNotPendleMarket(address caller); error GCNotVotingController(address caller); error InvalidWTime(uint256 wTime); error ExpiryInThePast(uint256 expiry); error ChainNotSupported(uint256 chainId); error FDTotalAmountFundedNotMatch(uint256 actualTotalAmount, uint256 expectedTotalAmount); error FDEpochLengthMismatch(); error FDInvalidPool(address pool); error FDPoolAlreadyExists(address pool); error FDInvalidNewFinishedEpoch(uint256 oldFinishedEpoch, uint256 newFinishedEpoch); error FDInvalidStartEpoch(uint256 startEpoch); error FDInvalidWTimeFund(uint256 lastFunded, uint256 wTime); error FDFutureFunding(uint256 lastFunded, uint256 currentWTime); error BDInvalidEpoch(uint256 epoch, uint256 startTime); // Cross-Chain error MsgNotFromSendEndpoint(uint16 srcChainId, bytes path); error MsgNotFromReceiveEndpoint(address sender); error InsufficientFeeToSendMsg(uint256 currentFee, uint256 requiredFee); error ApproxDstExecutionGasNotSet(); error InvalidRetryData(); // GENERIC MSG error ArrayLengthMismatch(); error ArrayEmpty(); error ArrayOutOfBounds(); error ZeroAddress(); error FailedToSendEther(); error InvalidMerkleProof(); error OnlyLayerZeroEndpoint(); error OnlyYT(); error OnlyYCFactory(); error OnlyWhitelisted(); // Swap Aggregator error SAInsufficientTokenIn(address tokenIn, uint256 amountExpected, uint256 amountActual); error UnsupportedSelector(uint256 aggregatorType, bytes4 selector); }
// SPDX-License-Identifier: GPL-3.0-or-later // Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated // documentation files (the “Software”), to deal in the Software without restriction, including without limitation the // rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to // permit persons to whom the Software is furnished to do so, subject to the following conditions: // The above copyright notice and this permission notice shall be included in all copies or substantial portions of the // Software. // THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR // COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pragma solidity ^0.8.0; /* solhint-disable */ /** * @dev Exponentiation and logarithm functions for 18 decimal fixed point numbers (both base and exponent/argument). * * Exponentiation and logarithm with arbitrary bases (x^y and log_x(y)) are implemented by conversion to natural * exponentiation and logarithm (where the base is Euler's number). * * @author Fernando Martinelli - @fernandomartinelli * @author Sergio Yuhjtman - @sergioyuhjtman * @author Daniel Fernandez - @dmf7z */ library LogExpMath { // All fixed point multiplications and divisions are inlined. This means we need to divide by ONE when multiplying // two numbers, and multiply by ONE when dividing them. // All arguments and return values are 18 decimal fixed point numbers. int256 constant ONE_18 = 1e18; // Internally, intermediate values are computed with higher precision as 20 decimal fixed point numbers, and in the // case of ln36, 36 decimals. int256 constant ONE_20 = 1e20; int256 constant ONE_36 = 1e36; // The domain of natural exponentiation is bound by the word size and number of decimals used. // // Because internally the result will be stored using 20 decimals, the largest possible result is // (2^255 - 1) / 10^20, which makes the largest exponent ln((2^255 - 1) / 10^20) = 130.700829182905140221. // The smallest possible result is 10^(-18), which makes largest negative argument // ln(10^(-18)) = -41.446531673892822312. // We use 130.0 and -41.0 to have some safety margin. int256 constant MAX_NATURAL_EXPONENT = 130e18; int256 constant MIN_NATURAL_EXPONENT = -41e18; // Bounds for ln_36's argument. Both ln(0.9) and ln(1.1) can be represented with 36 decimal places in a fixed point // 256 bit integer. int256 constant LN_36_LOWER_BOUND = ONE_18 - 1e17; int256 constant LN_36_UPPER_BOUND = ONE_18 + 1e17; uint256 constant MILD_EXPONENT_BOUND = 2 ** 254 / uint256(ONE_20); // 18 decimal constants int256 constant x0 = 128000000000000000000; // 2ˆ7 int256 constant a0 = 38877084059945950922200000000000000000000000000000000000; // eˆ(x0) (no decimals) int256 constant x1 = 64000000000000000000; // 2ˆ6 int256 constant a1 = 6235149080811616882910000000; // eˆ(x1) (no decimals) // 20 decimal constants int256 constant x2 = 3200000000000000000000; // 2ˆ5 int256 constant a2 = 7896296018268069516100000000000000; // eˆ(x2) int256 constant x3 = 1600000000000000000000; // 2ˆ4 int256 constant a3 = 888611052050787263676000000; // eˆ(x3) int256 constant x4 = 800000000000000000000; // 2ˆ3 int256 constant a4 = 298095798704172827474000; // eˆ(x4) int256 constant x5 = 400000000000000000000; // 2ˆ2 int256 constant a5 = 5459815003314423907810; // eˆ(x5) int256 constant x6 = 200000000000000000000; // 2ˆ1 int256 constant a6 = 738905609893065022723; // eˆ(x6) int256 constant x7 = 100000000000000000000; // 2ˆ0 int256 constant a7 = 271828182845904523536; // eˆ(x7) int256 constant x8 = 50000000000000000000; // 2ˆ-1 int256 constant a8 = 164872127070012814685; // eˆ(x8) int256 constant x9 = 25000000000000000000; // 2ˆ-2 int256 constant a9 = 128402541668774148407; // eˆ(x9) int256 constant x10 = 12500000000000000000; // 2ˆ-3 int256 constant a10 = 113314845306682631683; // eˆ(x10) int256 constant x11 = 6250000000000000000; // 2ˆ-4 int256 constant a11 = 106449445891785942956; // eˆ(x11) /** * @dev Natural exponentiation (e^x) with signed 18 decimal fixed point exponent. * * Reverts if `x` is smaller than MIN_NATURAL_EXPONENT, or larger than `MAX_NATURAL_EXPONENT`. */ function exp(int256 x) internal pure returns (int256) { unchecked { require(x >= MIN_NATURAL_EXPONENT && x <= MAX_NATURAL_EXPONENT, "Invalid exponent"); if (x < 0) { // We only handle positive exponents: e^(-x) is computed as 1 / e^x. We can safely make x positive since it // fits in the signed 256 bit range (as it is larger than MIN_NATURAL_EXPONENT). // Fixed point division requires multiplying by ONE_18. return ((ONE_18 * ONE_18) / exp(-x)); } // First, we use the fact that e^(x+y) = e^x * e^y to decompose x into a sum of powers of two, which we call x_n, // where x_n == 2^(7 - n), and e^x_n = a_n has been precomputed. We choose the first x_n, x0, to equal 2^7 // because all larger powers are larger than MAX_NATURAL_EXPONENT, and therefore not present in the // decomposition. // At the end of this process we will have the product of all e^x_n = a_n that apply, and the remainder of this // decomposition, which will be lower than the smallest x_n. // exp(x) = k_0 * a_0 * k_1 * a_1 * ... + k_n * a_n * exp(remainder), where each k_n equals either 0 or 1. // We mutate x by subtracting x_n, making it the remainder of the decomposition. // The first two a_n (e^(2^7) and e^(2^6)) are too large if stored as 18 decimal numbers, and could cause // intermediate overflows. Instead we store them as plain integers, with 0 decimals. // Additionally, x0 + x1 is larger than MAX_NATURAL_EXPONENT, which means they will not both be present in the // decomposition. // For each x_n, we test if that term is present in the decomposition (if x is larger than it), and if so deduct // it and compute the accumulated product. int256 firstAN; if (x >= x0) { x -= x0; firstAN = a0; } else if (x >= x1) { x -= x1; firstAN = a1; } else { firstAN = 1; // One with no decimal places } // We now transform x into a 20 decimal fixed point number, to have enhanced precision when computing the // smaller terms. x *= 100; // `product` is the accumulated product of all a_n (except a0 and a1), which starts at 20 decimal fixed point // one. Recall that fixed point multiplication requires dividing by ONE_20. int256 product = ONE_20; if (x >= x2) { x -= x2; product = (product * a2) / ONE_20; } if (x >= x3) { x -= x3; product = (product * a3) / ONE_20; } if (x >= x4) { x -= x4; product = (product * a4) / ONE_20; } if (x >= x5) { x -= x5; product = (product * a5) / ONE_20; } if (x >= x6) { x -= x6; product = (product * a6) / ONE_20; } if (x >= x7) { x -= x7; product = (product * a7) / ONE_20; } if (x >= x8) { x -= x8; product = (product * a8) / ONE_20; } if (x >= x9) { x -= x9; product = (product * a9) / ONE_20; } // x10 and x11 are unnecessary here since we have high enough precision already. // Now we need to compute e^x, where x is small (in particular, it is smaller than x9). We use the Taylor series // expansion for e^x: 1 + x + (x^2 / 2!) + (x^3 / 3!) + ... + (x^n / n!). int256 seriesSum = ONE_20; // The initial one in the sum, with 20 decimal places. int256 term; // Each term in the sum, where the nth term is (x^n / n!). // The first term is simply x. term = x; seriesSum += term; // Each term (x^n / n!) equals the previous one times x, divided by n. Since x is a fixed point number, // multiplying by it requires dividing by ONE_20, but dividing by the non-fixed point n values does not. term = ((term * x) / ONE_20) / 2; seriesSum += term; term = ((term * x) / ONE_20) / 3; seriesSum += term; term = ((term * x) / ONE_20) / 4; seriesSum += term; term = ((term * x) / ONE_20) / 5; seriesSum += term; term = ((term * x) / ONE_20) / 6; seriesSum += term; term = ((term * x) / ONE_20) / 7; seriesSum += term; term = ((term * x) / ONE_20) / 8; seriesSum += term; term = ((term * x) / ONE_20) / 9; seriesSum += term; term = ((term * x) / ONE_20) / 10; seriesSum += term; term = ((term * x) / ONE_20) / 11; seriesSum += term; term = ((term * x) / ONE_20) / 12; seriesSum += term; // 12 Taylor terms are sufficient for 18 decimal precision. // We now have the first a_n (with no decimals), and the product of all other a_n present, and the Taylor // approximation of the exponentiation of the remainder (both with 20 decimals). All that remains is to multiply // all three (one 20 decimal fixed point multiplication, dividing by ONE_20, and one integer multiplication), // and then drop two digits to return an 18 decimal value. return (((product * seriesSum) / ONE_20) * firstAN) / 100; } } /** * @dev Natural logarithm (ln(a)) with signed 18 decimal fixed point argument. */ function ln(int256 a) internal pure returns (int256) { unchecked { // The real natural logarithm is not defined for negative numbers or zero. require(a > 0, "out of bounds"); if (LN_36_LOWER_BOUND < a && a < LN_36_UPPER_BOUND) { return _ln_36(a) / ONE_18; } else { return _ln(a); } } } /** * @dev Exponentiation (x^y) with unsigned 18 decimal fixed point base and exponent. * * Reverts if ln(x) * y is smaller than `MIN_NATURAL_EXPONENT`, or larger than `MAX_NATURAL_EXPONENT`. */ function pow(uint256 x, uint256 y) internal pure returns (uint256) { unchecked { if (y == 0) { // We solve the 0^0 indetermination by making it equal one. return uint256(ONE_18); } if (x == 0) { return 0; } // Instead of computing x^y directly, we instead rely on the properties of logarithms and exponentiation to // arrive at that r`esult. In particular, exp(ln(x)) = x, and ln(x^y) = y * ln(x). This means // x^y = exp(y * ln(x)). // The ln function takes a signed value, so we need to make sure x fits in the signed 256 bit range. require(x < 2 ** 255, "x out of bounds"); int256 x_int256 = int256(x); // We will compute y * ln(x) in a single step. Depending on the value of x, we can either use ln or ln_36. In // both cases, we leave the division by ONE_18 (due to fixed point multiplication) to the end. // This prevents y * ln(x) from overflowing, and at the same time guarantees y fits in the signed 256 bit range. require(y < MILD_EXPONENT_BOUND, "y out of bounds"); int256 y_int256 = int256(y); int256 logx_times_y; if (LN_36_LOWER_BOUND < x_int256 && x_int256 < LN_36_UPPER_BOUND) { int256 ln_36_x = _ln_36(x_int256); // ln_36_x has 36 decimal places, so multiplying by y_int256 isn't as straightforward, since we can't just // bring y_int256 to 36 decimal places, as it might overflow. Instead, we perform two 18 decimal // multiplications and add the results: one with the first 18 decimals of ln_36_x, and one with the // (downscaled) last 18 decimals. logx_times_y = ((ln_36_x / ONE_18) * y_int256 + ((ln_36_x % ONE_18) * y_int256) / ONE_18); } else { logx_times_y = _ln(x_int256) * y_int256; } logx_times_y /= ONE_18; // Finally, we compute exp(y * ln(x)) to arrive at x^y require( MIN_NATURAL_EXPONENT <= logx_times_y && logx_times_y <= MAX_NATURAL_EXPONENT, "product out of bounds" ); return uint256(exp(logx_times_y)); } } /** * @dev Internal natural logarithm (ln(a)) with signed 18 decimal fixed point argument. */ function _ln(int256 a) private pure returns (int256) { unchecked { if (a < ONE_18) { // Since ln(a^k) = k * ln(a), we can compute ln(a) as ln(a) = ln((1/a)^(-1)) = - ln((1/a)). If a is less // than one, 1/a will be greater than one, and this if statement will not be entered in the recursive call. // Fixed point division requires multiplying by ONE_18. return (-_ln((ONE_18 * ONE_18) / a)); } // First, we use the fact that ln^(a * b) = ln(a) + ln(b) to decompose ln(a) into a sum of powers of two, which // we call x_n, where x_n == 2^(7 - n), which are the natural logarithm of precomputed quantities a_n (that is, // ln(a_n) = x_n). We choose the first x_n, x0, to equal 2^7 because the exponential of all larger powers cannot // be represented as 18 fixed point decimal numbers in 256 bits, and are therefore larger than a. // At the end of this process we will have the sum of all x_n = ln(a_n) that apply, and the remainder of this // decomposition, which will be lower than the smallest a_n. // ln(a) = k_0 * x_0 + k_1 * x_1 + ... + k_n * x_n + ln(remainder), where each k_n equals either 0 or 1. // We mutate a by subtracting a_n, making it the remainder of the decomposition. // For reasons related to how `exp` works, the first two a_n (e^(2^7) and e^(2^6)) are not stored as fixed point // numbers with 18 decimals, but instead as plain integers with 0 decimals, so we need to multiply them by // ONE_18 to convert them to fixed point. // For each a_n, we test if that term is present in the decomposition (if a is larger than it), and if so divide // by it and compute the accumulated sum. int256 sum = 0; if (a >= a0 * ONE_18) { a /= a0; // Integer, not fixed point division sum += x0; } if (a >= a1 * ONE_18) { a /= a1; // Integer, not fixed point division sum += x1; } // All other a_n and x_n are stored as 20 digit fixed point numbers, so we convert the sum and a to this format. sum *= 100; a *= 100; // Because further a_n are 20 digit fixed point numbers, we multiply by ONE_20 when dividing by them. if (a >= a2) { a = (a * ONE_20) / a2; sum += x2; } if (a >= a3) { a = (a * ONE_20) / a3; sum += x3; } if (a >= a4) { a = (a * ONE_20) / a4; sum += x4; } if (a >= a5) { a = (a * ONE_20) / a5; sum += x5; } if (a >= a6) { a = (a * ONE_20) / a6; sum += x6; } if (a >= a7) { a = (a * ONE_20) / a7; sum += x7; } if (a >= a8) { a = (a * ONE_20) / a8; sum += x8; } if (a >= a9) { a = (a * ONE_20) / a9; sum += x9; } if (a >= a10) { a = (a * ONE_20) / a10; sum += x10; } if (a >= a11) { a = (a * ONE_20) / a11; sum += x11; } // a is now a small number (smaller than a_11, which roughly equals 1.06). This means we can use a Taylor series // that converges rapidly for values of `a` close to one - the same one used in ln_36. // Let z = (a - 1) / (a + 1). // ln(a) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1)) // Recall that 20 digit fixed point division requires multiplying by ONE_20, and multiplication requires // division by ONE_20. int256 z = ((a - ONE_20) * ONE_20) / (a + ONE_20); int256 z_squared = (z * z) / ONE_20; // num is the numerator of the series: the z^(2 * n + 1) term int256 num = z; // seriesSum holds the accumulated sum of each term in the series, starting with the initial z int256 seriesSum = num; // In each step, the numerator is multiplied by z^2 num = (num * z_squared) / ONE_20; seriesSum += num / 3; num = (num * z_squared) / ONE_20; seriesSum += num / 5; num = (num * z_squared) / ONE_20; seriesSum += num / 7; num = (num * z_squared) / ONE_20; seriesSum += num / 9; num = (num * z_squared) / ONE_20; seriesSum += num / 11; // 6 Taylor terms are sufficient for 36 decimal precision. // Finally, we multiply by 2 (non fixed point) to compute ln(remainder) seriesSum *= 2; // We now have the sum of all x_n present, and the Taylor approximation of the logarithm of the remainder (both // with 20 decimals). All that remains is to sum these two, and then drop two digits to return a 18 decimal // value. return (sum + seriesSum) / 100; } } /** * @dev Intrnal high precision (36 decimal places) natural logarithm (ln(x)) with signed 18 decimal fixed point argument, * for x close to one. * * Should only be used if x is between LN_36_LOWER_BOUND and LN_36_UPPER_BOUND. */ function _ln_36(int256 x) private pure returns (int256) { unchecked { // Since ln(1) = 0, a value of x close to one will yield a very small result, which makes using 36 digits // worthwhile. // First, we transform x to a 36 digit fixed point value. x *= ONE_18; // We will use the following Taylor expansion, which converges very rapidly. Let z = (x - 1) / (x + 1). // ln(x) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1)) // Recall that 36 digit fixed point division requires multiplying by ONE_36, and multiplication requires // division by ONE_36. int256 z = ((x - ONE_36) * ONE_36) / (x + ONE_36); int256 z_squared = (z * z) / ONE_36; // num is the numerator of the series: the z^(2 * n + 1) term int256 num = z; // seriesSum holds the accumulated sum of each term in the series, starting with the initial z int256 seriesSum = num; // In each step, the numerator is multiplied by z^2 num = (num * z_squared) / ONE_36; seriesSum += num / 3; num = (num * z_squared) / ONE_36; seriesSum += num / 5; num = (num * z_squared) / ONE_36; seriesSum += num / 7; num = (num * z_squared) / ONE_36; seriesSum += num / 9; num = (num * z_squared) / ONE_36; seriesSum += num / 11; num = (num * z_squared) / ONE_36; seriesSum += num / 13; num = (num * z_squared) / ONE_36; seriesSum += num / 15; // 8 Taylor terms are sufficient for 36 decimal precision. // All that remains is multiplying by 2 (non fixed point). return seriesSum * 2; } } }
// SPDX-License-Identifier: GPL-3.0-or-later // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // You should have received a copy of the GNU General Public License // along with this program. If not, see <http://www.gnu.org/licenses/>. pragma solidity ^0.8.0; /* solhint-disable private-vars-leading-underscore, reason-string */ library PMath { uint256 internal constant ONE = 1e18; // 18 decimal places int256 internal constant IONE = 1e18; // 18 decimal places function subMax0(uint256 a, uint256 b) internal pure returns (uint256) { unchecked { return (a >= b ? a - b : 0); } } function subNoNeg(int256 a, int256 b) internal pure returns (int256) { require(a >= b, "negative"); return a - b; // no unchecked since if b is very negative, a - b might overflow } function mulDown(uint256 a, uint256 b) internal pure returns (uint256) { uint256 product = a * b; unchecked { return product / ONE; } } function mulDown(int256 a, int256 b) internal pure returns (int256) { int256 product = a * b; unchecked { return product / IONE; } } function divDown(uint256 a, uint256 b) internal pure returns (uint256) { uint256 aInflated = a * ONE; unchecked { return aInflated / b; } } function divDown(int256 a, int256 b) internal pure returns (int256) { int256 aInflated = a * IONE; unchecked { return aInflated / b; } } function rawDivUp(uint256 a, uint256 b) internal pure returns (uint256) { return (a + b - 1) / b; } // @author Uniswap function sqrt(uint256 y) internal pure returns (uint256 z) { if (y > 3) { z = y; uint256 x = y / 2 + 1; while (x < z) { z = x; x = (y / x + x) / 2; } } else if (y != 0) { z = 1; } } function square(uint256 x) internal pure returns (uint256) { return x * x; } function squareDown(uint256 x) internal pure returns (uint256) { return mulDown(x, x); } function abs(int256 x) internal pure returns (uint256) { return uint256(x > 0 ? x : -x); } function neg(int256 x) internal pure returns (int256) { return x * (-1); } function neg(uint256 x) internal pure returns (int256) { return Int(x) * (-1); } function max(uint256 x, uint256 y) internal pure returns (uint256) { return (x > y ? x : y); } function max(int256 x, int256 y) internal pure returns (int256) { return (x > y ? x : y); } function min(uint256 x, uint256 y) internal pure returns (uint256) { return (x < y ? x : y); } function min(int256 x, int256 y) internal pure returns (int256) { return (x < y ? x : y); } /*/////////////////////////////////////////////////////////////// SIGNED CASTS //////////////////////////////////////////////////////////////*/ function Int(uint256 x) internal pure returns (int256) { require(x <= uint256(type(int256).max)); return int256(x); } function Int128(int256 x) internal pure returns (int128) { require(type(int128).min <= x && x <= type(int128).max); return int128(x); } function Int128(uint256 x) internal pure returns (int128) { return Int128(Int(x)); } /*/////////////////////////////////////////////////////////////// UNSIGNED CASTS //////////////////////////////////////////////////////////////*/ function Uint(int256 x) internal pure returns (uint256) { require(x >= 0); return uint256(x); } function Uint32(uint256 x) internal pure returns (uint32) { require(x <= type(uint32).max); return uint32(x); } function Uint112(uint256 x) internal pure returns (uint112) { require(x <= type(uint112).max); return uint112(x); } function Uint96(uint256 x) internal pure returns (uint96) { require(x <= type(uint96).max); return uint96(x); } function Uint128(uint256 x) internal pure returns (uint128) { require(x <= type(uint128).max); return uint128(x); } function isAApproxB( uint256 a, uint256 b, uint256 eps ) internal pure returns (bool) { return mulDown(b, ONE - eps) <= a && a <= mulDown(b, ONE + eps); } function isAGreaterApproxB( uint256 a, uint256 b, uint256 eps ) internal pure returns (bool) { return a >= b && a <= mulDown(b, ONE + eps); } function isASmallerApproxB( uint256 a, uint256 b, uint256 eps ) internal pure returns (bool) { return a <= b && a >= mulDown(b, ONE - eps); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; library MiniHelpers { function isCurrentlyExpired(uint256 expiry) internal view returns (bool) { return (expiry <= block.timestamp); } function isExpired(uint256 expiry, uint256 blockTime) internal pure returns (bool) { return (expiry <= blockTime); } function isTimeInThePast(uint256 timestamp) internal view returns (bool) { return (timestamp <= block.timestamp); // same definition as isCurrentlyExpired } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import "../libraries/math/PMath.sol"; import "../libraries/math/LogExpMath.sol"; import "../StandardizedYield/PYIndex.sol"; import "../libraries/MiniHelpers.sol"; import "../libraries/Errors.sol"; struct MarketState { int256 totalPt; int256 totalSy; int256 totalLp; address treasury; /// immutable variables /// int256 scalarRoot; uint256 expiry; /// fee data /// uint256 lnFeeRateRoot; uint256 reserveFeePercent; // base 100 /// last trade data /// uint256 lastLnImpliedRate; } // params that are expensive to compute, therefore we pre-compute them struct MarketPreCompute { int256 rateScalar; int256 totalAsset; int256 rateAnchor; int256 feeRate; } // solhint-disable ordering library MarketMathCore { using PMath for uint256; using PMath for int256; using LogExpMath for int256; using PYIndexLib for PYIndex; int256 internal constant MINIMUM_LIQUIDITY = 10 ** 3; int256 internal constant PERCENTAGE_DECIMALS = 100; uint256 internal constant DAY = 86400; uint256 internal constant IMPLIED_RATE_TIME = 365 * DAY; int256 internal constant MAX_MARKET_PROPORTION = (1e18 * 96) / 100; using PMath for uint256; using PMath for int256; /*/////////////////////////////////////////////////////////////// UINT FUNCTIONS TO PROXY TO CORE FUNCTIONS //////////////////////////////////////////////////////////////*/ function addLiquidity( MarketState memory market, uint256 syDesired, uint256 ptDesired, uint256 blockTime ) internal pure returns (uint256 lpToReserve, uint256 lpToAccount, uint256 syUsed, uint256 ptUsed) { ( int256 _lpToReserve, int256 _lpToAccount, int256 _syUsed, int256 _ptUsed ) = addLiquidityCore(market, syDesired.Int(), ptDesired.Int(), blockTime); lpToReserve = _lpToReserve.Uint(); lpToAccount = _lpToAccount.Uint(); syUsed = _syUsed.Uint(); ptUsed = _ptUsed.Uint(); } function removeLiquidity( MarketState memory market, uint256 lpToRemove ) internal pure returns (uint256 netSyToAccount, uint256 netPtToAccount) { (int256 _syToAccount, int256 _ptToAccount) = removeLiquidityCore(market, lpToRemove.Int()); netSyToAccount = _syToAccount.Uint(); netPtToAccount = _ptToAccount.Uint(); } function swapExactPtForSy( MarketState memory market, PYIndex index, uint256 exactPtToMarket, uint256 blockTime ) internal pure returns (uint256 netSyToAccount, uint256 netSyFee, uint256 netSyToReserve) { (int256 _netSyToAccount, int256 _netSyFee, int256 _netSyToReserve) = executeTradeCore( market, index, exactPtToMarket.neg(), blockTime ); netSyToAccount = _netSyToAccount.Uint(); netSyFee = _netSyFee.Uint(); netSyToReserve = _netSyToReserve.Uint(); } function swapSyForExactPt( MarketState memory market, PYIndex index, uint256 exactPtToAccount, uint256 blockTime ) internal pure returns (uint256 netSyToMarket, uint256 netSyFee, uint256 netSyToReserve) { (int256 _netSyToAccount, int256 _netSyFee, int256 _netSyToReserve) = executeTradeCore( market, index, exactPtToAccount.Int(), blockTime ); netSyToMarket = _netSyToAccount.neg().Uint(); netSyFee = _netSyFee.Uint(); netSyToReserve = _netSyToReserve.Uint(); } /*/////////////////////////////////////////////////////////////// CORE FUNCTIONS //////////////////////////////////////////////////////////////*/ function addLiquidityCore( MarketState memory market, int256 syDesired, int256 ptDesired, uint256 blockTime ) internal pure returns (int256 lpToReserve, int256 lpToAccount, int256 syUsed, int256 ptUsed) { /// ------------------------------------------------------------ /// CHECKS /// ------------------------------------------------------------ if (syDesired == 0 || ptDesired == 0) revert Errors.MarketZeroAmountsInput(); if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired(); /// ------------------------------------------------------------ /// MATH /// ------------------------------------------------------------ if (market.totalLp == 0) { lpToAccount = PMath.sqrt((syDesired * ptDesired).Uint()).Int() - MINIMUM_LIQUIDITY; lpToReserve = MINIMUM_LIQUIDITY; syUsed = syDesired; ptUsed = ptDesired; } else { int256 netLpByPt = (ptDesired * market.totalLp) / market.totalPt; int256 netLpBySy = (syDesired * market.totalLp) / market.totalSy; if (netLpByPt < netLpBySy) { lpToAccount = netLpByPt; ptUsed = ptDesired; syUsed = (market.totalSy * lpToAccount) / market.totalLp; } else { lpToAccount = netLpBySy; syUsed = syDesired; ptUsed = (market.totalPt * lpToAccount) / market.totalLp; } } if (lpToAccount <= 0) revert Errors.MarketZeroAmountsOutput(); /// ------------------------------------------------------------ /// WRITE /// ------------------------------------------------------------ market.totalSy += syUsed; market.totalPt += ptUsed; market.totalLp += lpToAccount + lpToReserve; } function removeLiquidityCore( MarketState memory market, int256 lpToRemove ) internal pure returns (int256 netSyToAccount, int256 netPtToAccount) { /// ------------------------------------------------------------ /// CHECKS /// ------------------------------------------------------------ if (lpToRemove == 0) revert Errors.MarketZeroAmountsInput(); /// ------------------------------------------------------------ /// MATH /// ------------------------------------------------------------ netSyToAccount = (lpToRemove * market.totalSy) / market.totalLp; netPtToAccount = (lpToRemove * market.totalPt) / market.totalLp; if (netSyToAccount == 0 && netPtToAccount == 0) revert Errors.MarketZeroAmountsOutput(); /// ------------------------------------------------------------ /// WRITE /// ------------------------------------------------------------ market.totalLp = market.totalLp.subNoNeg(lpToRemove); market.totalPt = market.totalPt.subNoNeg(netPtToAccount); market.totalSy = market.totalSy.subNoNeg(netSyToAccount); } function executeTradeCore( MarketState memory market, PYIndex index, int256 netPtToAccount, uint256 blockTime ) internal pure returns (int256 netSyToAccount, int256 netSyFee, int256 netSyToReserve) { /// ------------------------------------------------------------ /// CHECKS /// ------------------------------------------------------------ if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired(); if (market.totalPt <= netPtToAccount) revert Errors.MarketInsufficientPtForTrade(market.totalPt, netPtToAccount); /// ------------------------------------------------------------ /// MATH /// ------------------------------------------------------------ MarketPreCompute memory comp = getMarketPreCompute(market, index, blockTime); (netSyToAccount, netSyFee, netSyToReserve) = calcTrade( market, comp, index, netPtToAccount ); /// ------------------------------------------------------------ /// WRITE /// ------------------------------------------------------------ _setNewMarketStateTrade( market, comp, index, netPtToAccount, netSyToAccount, netSyToReserve, blockTime ); } function getMarketPreCompute( MarketState memory market, PYIndex index, uint256 blockTime ) internal pure returns (MarketPreCompute memory res) { if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired(); uint256 timeToExpiry = market.expiry - blockTime; res.rateScalar = _getRateScalar(market, timeToExpiry); res.totalAsset = index.syToAsset(market.totalSy); if (market.totalPt == 0 || res.totalAsset == 0) revert Errors.MarketZeroTotalPtOrTotalAsset(market.totalPt, res.totalAsset); res.rateAnchor = _getRateAnchor( market.totalPt, market.lastLnImpliedRate, res.totalAsset, res.rateScalar, timeToExpiry ); res.feeRate = _getExchangeRateFromImpliedRate(market.lnFeeRateRoot, timeToExpiry); } function calcTrade( MarketState memory market, MarketPreCompute memory comp, PYIndex index, int256 netPtToAccount ) internal pure returns (int256 netSyToAccount, int256 netSyFee, int256 netSyToReserve) { int256 preFeeExchangeRate = _getExchangeRate( market.totalPt, comp.totalAsset, comp.rateScalar, comp.rateAnchor, netPtToAccount ); int256 preFeeAssetToAccount = netPtToAccount.divDown(preFeeExchangeRate).neg(); int256 fee = comp.feeRate; if (netPtToAccount > 0) { int256 postFeeExchangeRate = preFeeExchangeRate.divDown(fee); if (postFeeExchangeRate < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(postFeeExchangeRate); fee = preFeeAssetToAccount.mulDown(PMath.IONE - fee); } else { fee = ((preFeeAssetToAccount * (PMath.IONE - fee)) / fee).neg(); } int256 netAssetToReserve = (fee * market.reserveFeePercent.Int()) / PERCENTAGE_DECIMALS; int256 netAssetToAccount = preFeeAssetToAccount - fee; netSyToAccount = netAssetToAccount < 0 ? index.assetToSyUp(netAssetToAccount) : index.assetToSy(netAssetToAccount); netSyFee = index.assetToSy(fee); netSyToReserve = index.assetToSy(netAssetToReserve); } function _setNewMarketStateTrade( MarketState memory market, MarketPreCompute memory comp, PYIndex index, int256 netPtToAccount, int256 netSyToAccount, int256 netSyToReserve, uint256 blockTime ) internal pure { uint256 timeToExpiry = market.expiry - blockTime; market.totalPt = market.totalPt.subNoNeg(netPtToAccount); market.totalSy = market.totalSy.subNoNeg(netSyToAccount + netSyToReserve); market.lastLnImpliedRate = _getLnImpliedRate( market.totalPt, index.syToAsset(market.totalSy), comp.rateScalar, comp.rateAnchor, timeToExpiry ); if (market.lastLnImpliedRate == 0) revert Errors.MarketZeroLnImpliedRate(); } function _getRateAnchor( int256 totalPt, uint256 lastLnImpliedRate, int256 totalAsset, int256 rateScalar, uint256 timeToExpiry ) internal pure returns (int256 rateAnchor) { int256 newExchangeRate = _getExchangeRateFromImpliedRate(lastLnImpliedRate, timeToExpiry); if (newExchangeRate < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(newExchangeRate); { int256 proportion = totalPt.divDown(totalPt + totalAsset); int256 lnProportion = _logProportion(proportion); rateAnchor = newExchangeRate - lnProportion.divDown(rateScalar); } } /// @notice Calculates the current market implied rate. /// @return lnImpliedRate the implied rate function _getLnImpliedRate( int256 totalPt, int256 totalAsset, int256 rateScalar, int256 rateAnchor, uint256 timeToExpiry ) internal pure returns (uint256 lnImpliedRate) { // This will check for exchange rates < PMath.IONE int256 exchangeRate = _getExchangeRate(totalPt, totalAsset, rateScalar, rateAnchor, 0); // exchangeRate >= 1 so its ln >= 0 uint256 lnRate = exchangeRate.ln().Uint(); lnImpliedRate = (lnRate * IMPLIED_RATE_TIME) / timeToExpiry; } /// @notice Converts an implied rate to an exchange rate given a time to expiry. The /// formula is E = e^rt function _getExchangeRateFromImpliedRate( uint256 lnImpliedRate, uint256 timeToExpiry ) internal pure returns (int256 exchangeRate) { uint256 rt = (lnImpliedRate * timeToExpiry) / IMPLIED_RATE_TIME; exchangeRate = LogExpMath.exp(rt.Int()); } function _getExchangeRate( int256 totalPt, int256 totalAsset, int256 rateScalar, int256 rateAnchor, int256 netPtToAccount ) internal pure returns (int256 exchangeRate) { int256 numerator = totalPt.subNoNeg(netPtToAccount); int256 proportion = (numerator.divDown(totalPt + totalAsset)); if (proportion > MAX_MARKET_PROPORTION) revert Errors.MarketProportionTooHigh(proportion, MAX_MARKET_PROPORTION); int256 lnProportion = _logProportion(proportion); exchangeRate = lnProportion.divDown(rateScalar) + rateAnchor; if (exchangeRate < PMath.IONE) revert Errors.MarketExchangeRateBelowOne(exchangeRate); } function _logProportion(int256 proportion) internal pure returns (int256 res) { if (proportion == PMath.IONE) revert Errors.MarketProportionMustNotEqualOne(); int256 logitP = proportion.divDown(PMath.IONE - proportion); res = logitP.ln(); } function _getRateScalar( MarketState memory market, uint256 timeToExpiry ) internal pure returns (int256 rateScalar) { rateScalar = (market.scalarRoot * IMPLIED_RATE_TIME.Int()) / timeToExpiry.Int(); if (rateScalar <= 0) revert Errors.MarketRateScalarBelowZero(rateScalar); } function setInitialLnImpliedRate( MarketState memory market, PYIndex index, int256 initialAnchor, uint256 blockTime ) internal pure { /// ------------------------------------------------------------ /// CHECKS /// ------------------------------------------------------------ if (MiniHelpers.isExpired(market.expiry, blockTime)) revert Errors.MarketExpired(); /// ------------------------------------------------------------ /// MATH /// ------------------------------------------------------------ int256 totalAsset = index.syToAsset(market.totalSy); uint256 timeToExpiry = market.expiry - blockTime; int256 rateScalar = _getRateScalar(market, timeToExpiry); /// ------------------------------------------------------------ /// WRITE /// ------------------------------------------------------------ market.lastLnImpliedRate = _getLnImpliedRate( market.totalPt, totalAsset, rateScalar, initialAnchor, timeToExpiry ); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import "../../interfaces/IPYieldToken.sol"; import "../../interfaces/IPPrincipalToken.sol"; import "./SYUtils.sol"; import "../libraries/math/PMath.sol"; type PYIndex is uint256; library PYIndexLib { using PMath for uint256; using PMath for int256; function newIndex(IPYieldToken YT) internal returns (PYIndex) { return PYIndex.wrap(YT.pyIndexCurrent()); } function syToAsset(PYIndex index, uint256 syAmount) internal pure returns (uint256) { return SYUtils.syToAsset(PYIndex.unwrap(index), syAmount); } function assetToSy(PYIndex index, uint256 assetAmount) internal pure returns (uint256) { return SYUtils.assetToSy(PYIndex.unwrap(index), assetAmount); } function assetToSyUp(PYIndex index, uint256 assetAmount) internal pure returns (uint256) { return SYUtils.assetToSyUp(PYIndex.unwrap(index), assetAmount); } function syToAssetUp(PYIndex index, uint256 syAmount) internal pure returns (uint256) { uint256 _index = PYIndex.unwrap(index); return SYUtils.syToAssetUp(_index, syAmount); } function syToAsset(PYIndex index, int256 syAmount) internal pure returns (int256) { int256 sign = syAmount < 0 ? int256(-1) : int256(1); return sign * (SYUtils.syToAsset(PYIndex.unwrap(index), syAmount.abs())).Int(); } function assetToSy(PYIndex index, int256 assetAmount) internal pure returns (int256) { int256 sign = assetAmount < 0 ? int256(-1) : int256(1); return sign * (SYUtils.assetToSy(PYIndex.unwrap(index), assetAmount.abs())).Int(); } function assetToSyUp(PYIndex index, int256 assetAmount) internal pure returns (int256) { int256 sign = assetAmount < 0 ? int256(-1) : int256(1); return sign * (SYUtils.assetToSyUp(PYIndex.unwrap(index), assetAmount.abs())).Int(); } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; library SYUtils { uint256 internal constant ONE = 1e18; function syToAsset(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) { return (syAmount * exchangeRate) / ONE; } function syToAssetUp(uint256 exchangeRate, uint256 syAmount) internal pure returns (uint256) { return (syAmount * exchangeRate + ONE - 1) / ONE; } function assetToSy(uint256 exchangeRate, uint256 assetAmount) internal pure returns (uint256) { return (assetAmount * ONE) / exchangeRate; } function assetToSyUp( uint256 exchangeRate, uint256 assetAmount ) internal pure returns (uint256) { return (assetAmount * ONE + exchangeRate - 1) / exchangeRate; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; interface IPGauge { function totalActiveSupply() external view returns (uint256); function activeBalance(address user) external view returns (uint256); // only available for newer factories. please check the verified contracts event RedeemRewards(address indexed user, uint256[] rewardsOut); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; interface IPInterestManagerYT { function userInterest( address user ) external view returns (uint128 lastPYIndex, uint128 accruedInterest); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "./IPPrincipalToken.sol"; import "./IPYieldToken.sol"; import "./IStandardizedYield.sol"; import "./IPGauge.sol"; import "../core/Market/MarketMathCore.sol"; interface IPMarket is IERC20Metadata, IPGauge { event Mint( address indexed receiver, uint256 netLpMinted, uint256 netSyUsed, uint256 netPtUsed ); event Burn( address indexed receiverSy, address indexed receiverPt, uint256 netLpBurned, uint256 netSyOut, uint256 netPtOut ); event Swap( address indexed caller, address indexed receiver, int256 netPtOut, int256 netSyOut, uint256 netSyFee, uint256 netSyToReserve ); event UpdateImpliedRate(uint256 indexed timestamp, uint256 lnLastImpliedRate); event IncreaseObservationCardinalityNext( uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew ); function mint( address receiver, uint256 netSyDesired, uint256 netPtDesired ) external returns (uint256 netLpOut, uint256 netSyUsed, uint256 netPtUsed); function burn( address receiverSy, address receiverPt, uint256 netLpToBurn ) external returns (uint256 netSyOut, uint256 netPtOut); function swapExactPtForSy( address receiver, uint256 exactPtIn, bytes calldata data ) external returns (uint256 netSyOut, uint256 netSyFee); function swapSyForExactPt( address receiver, uint256 exactPtOut, bytes calldata data ) external returns (uint256 netSyIn, uint256 netSyFee); function redeemRewards(address user) external returns (uint256[] memory); function readState(address router) external view returns (MarketState memory market); function observe( uint32[] memory secondsAgos ) external view returns (uint216[] memory lnImpliedRateCumulative); function increaseObservationsCardinalityNext(uint16 cardinalityNext) external; function readTokens() external view returns (IStandardizedYield _SY, IPPrincipalToken _PT, IPYieldToken _YT); function getRewardTokens() external view returns (address[] memory); function isExpired() external view returns (bool); function expiry() external view returns (uint256); function observations( uint256 index ) external view returns (uint32 blockTimestamp, uint216 lnImpliedRateCumulative, bool initialized); function _storage() external view returns ( int128 totalPt, int128 totalSy, uint96 lastLnImpliedRate, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext ); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; interface IPPrincipalToken is IERC20Metadata { function burnByYT(address user, uint256 amount) external; function mintByYT(address user, uint256 amount) external; function initialize(address _YT) external; function SY() external view returns (address); function YT() external view returns (address); function factory() external view returns (address); function expiry() external view returns (uint256); function isExpired() external view returns (bool); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import "./IRewardManager.sol"; import "./IPInterestManagerYT.sol"; interface IPYieldToken is IERC20Metadata, IRewardManager, IPInterestManagerYT { event NewInterestIndex(uint256 indexed newIndex); event Mint( address indexed caller, address indexed receiverPT, address indexed receiverYT, uint256 amountSyToMint, uint256 amountPYOut ); event Burn( address indexed caller, address indexed receiver, uint256 amountPYToRedeem, uint256 amountSyOut ); event RedeemRewards(address indexed user, uint256[] amountRewardsOut); event RedeemInterest(address indexed user, uint256 interestOut); event WithdrawFeeToTreasury(uint256[] amountRewardsOut, uint256 syOut); function mintPY(address receiverPT, address receiverYT) external returns (uint256 amountPYOut); function redeemPY(address receiver) external returns (uint256 amountSyOut); function redeemPYMulti( address[] calldata receivers, uint256[] calldata amountPYToRedeems ) external returns (uint256[] memory amountSyOuts); function redeemDueInterestAndRewards( address user, bool redeemInterest, bool redeemRewards ) external returns (uint256 interestOut, uint256[] memory rewardsOut); function rewardIndexesCurrent() external returns (uint256[] memory); function pyIndexCurrent() external returns (uint256); function pyIndexStored() external view returns (uint256); function getRewardTokens() external view returns (address[] memory); function SY() external view returns (address); function PT() external view returns (address); function factory() external view returns (address); function expiry() external view returns (uint256); function isExpired() external view returns (bool); function doCacheIndexSameBlock() external view returns (bool); function pyIndexLastUpdatedBlock() external view returns (uint128); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; interface IRewardManager { function userReward( address token, address user ) external view returns (uint128 index, uint128 accrued); }
// SPDX-License-Identifier: GPL-3.0-or-later /* * MIT License * =========== * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE */ pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; interface IStandardizedYield is IERC20Metadata { /// @dev Emitted when any base tokens is deposited to mint shares event Deposit( address indexed caller, address indexed receiver, address indexed tokenIn, uint256 amountDeposited, uint256 amountSyOut ); /// @dev Emitted when any shares are redeemed for base tokens event Redeem( address indexed caller, address indexed receiver, address indexed tokenOut, uint256 amountSyToRedeem, uint256 amountTokenOut ); /// @dev check `assetInfo()` for more information enum AssetType { TOKEN, LIQUIDITY } /// @dev Emitted when (`user`) claims their rewards event ClaimRewards(address indexed user, address[] rewardTokens, uint256[] rewardAmounts); /** * @notice mints an amount of shares by depositing a base token. * @param receiver shares recipient address * @param tokenIn address of the base tokens to mint shares * @param amountTokenToDeposit amount of base tokens to be transferred from (`msg.sender`) * @param minSharesOut reverts if amount of shares minted is lower than this * @return amountSharesOut amount of shares minted * @dev Emits a {Deposit} event * * Requirements: * - (`tokenIn`) must be a valid base token. */ function deposit( address receiver, address tokenIn, uint256 amountTokenToDeposit, uint256 minSharesOut ) external payable returns (uint256 amountSharesOut); /** * @notice redeems an amount of base tokens by burning some shares * @param receiver recipient address * @param amountSharesToRedeem amount of shares to be burned * @param tokenOut address of the base token to be redeemed * @param minTokenOut reverts if amount of base token redeemed is lower than this * @param burnFromInternalBalance if true, burns from balance of `address(this)`, otherwise burns from `msg.sender` * @return amountTokenOut amount of base tokens redeemed * @dev Emits a {Redeem} event * * Requirements: * - (`tokenOut`) must be a valid base token. */ function redeem( address receiver, uint256 amountSharesToRedeem, address tokenOut, uint256 minTokenOut, bool burnFromInternalBalance ) external returns (uint256 amountTokenOut); /** * @notice exchangeRate * syBalance / 1e18 must return the asset balance of the account * @notice vice-versa, if a user uses some amount of tokens equivalent to X asset, the amount of sy he can mint must be X * exchangeRate / 1e18 * @dev SYUtils's assetToSy & syToAsset should be used instead of raw multiplication & division */ function exchangeRate() external view returns (uint256 res); /** * @notice claims reward for (`user`) * @param user the user receiving their rewards * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens` * @dev * Emits a `ClaimRewards` event * See {getRewardTokens} for list of reward tokens */ function claimRewards(address user) external returns (uint256[] memory rewardAmounts); /** * @notice get the amount of unclaimed rewards for (`user`) * @param user the user to check for * @return rewardAmounts an array of reward amounts in the same order as `getRewardTokens` */ function accruedRewards(address user) external view returns (uint256[] memory rewardAmounts); function rewardIndexesCurrent() external returns (uint256[] memory indexes); function rewardIndexesStored() external view returns (uint256[] memory indexes); /** * @notice returns the list of reward token addresses */ function getRewardTokens() external view returns (address[] memory); /** * @notice returns the address of the underlying yield token */ function yieldToken() external view returns (address); /** * @notice returns all tokens that can mint this SY */ function getTokensIn() external view returns (address[] memory res); /** * @notice returns all tokens that can be redeemed by this SY */ function getTokensOut() external view returns (address[] memory res); function isValidTokenIn(address token) external view returns (bool); function isValidTokenOut(address token) external view returns (bool); function previewDeposit(address tokenIn, uint256 amountTokenToDeposit) external view returns (uint256 amountSharesOut); function previewRedeem(address tokenOut, uint256 amountSharesToRedeem) external view returns (uint256 amountTokenOut); /** * @notice This function contains information to interpret what the asset is * @return assetType the type of the asset (0 for ERC20 tokens, 1 for AMM liquidity tokens, 2 for bridged yield bearing tokens like wstETH, rETH on Arbi whose the underlying asset doesn't exist on the chain) * @return assetAddress the address of the asset * @return assetDecimals the decimals of the asset */ function assetInfo() external view returns ( AssetType assetType, address assetAddress, uint8 assetDecimals ); }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import "./PendlePtOracleLib.sol"; library PendleLpOracleLib { using PendlePtOracleLib for IPMarket; using PMath for uint256; using PMath for int256; using MarketMathCore for MarketState; /** * This function returns the approximated twap rate LP/asset on market, but take into account the current rate of SY This is to account for special cases where underlying asset becomes insolvent and has decreasing exchangeRate * @param market market to get rate from * @param duration twap duration */ function getLpToAssetRate(IPMarket market, uint32 duration) internal view returns (uint256) { (uint256 syIndex, uint256 pyIndex) = PendlePtOracleLib.getSYandPYIndexCurrent(market); uint256 lpToAssetRateRaw = _getLpToAssetRateRaw(market, duration, pyIndex); return (lpToAssetRateRaw * syIndex) / pyIndex; } function _getLpToAssetRateRaw( IPMarket market, uint32 duration, uint256 pyIndex ) private view returns (uint256 lpToAssetRateRaw) { MarketState memory state = market.readState(address(0)); MarketPreCompute memory comp = state.getMarketPreCompute( PYIndex.wrap(pyIndex), block.timestamp ); int256 totalHypotheticalAsset; if (state.expiry <= block.timestamp) { // 1 PT = 1 Asset post-expiry totalHypotheticalAsset = state.totalPt + comp.totalAsset; } else { (int256 rateOracle, int256 rateHypTrade) = _getPtRatesRaw(market, state, duration); int256 cParam = LogExpMath.exp( comp.rateScalar.mulDown((rateOracle - comp.rateAnchor)) ); int256 tradeSize = (cParam.mulDown(comp.totalAsset) - state.totalPt).divDown( PMath.IONE + cParam.divDown(rateHypTrade) ); totalHypotheticalAsset = comp.totalAsset - tradeSize.divDown(rateHypTrade) + (state.totalPt + tradeSize).divDown(rateOracle); } lpToAssetRateRaw = totalHypotheticalAsset.divDown(state.totalLp).Uint(); } function _getPtRatesRaw( IPMarket market, MarketState memory state, uint32 duration ) private view returns (int256 rateOracle, int256 rateHypTrade) { rateOracle = PMath.IONE.divDown(market.getPtToAssetRateRaw(duration).Int()); int256 rateLastTrade = MarketMathCore._getExchangeRateFromImpliedRate( state.lastLnImpliedRate, state.expiry - block.timestamp ); rateHypTrade = (rateLastTrade + rateOracle) / 2; } }
// SPDX-License-Identifier: GPL-3.0-or-later pragma solidity ^0.8.0; import "../interfaces/IPMarket.sol"; import "../core/libraries/math/PMath.sol"; library PendlePtOracleLib { using PMath for uint256; using PMath for int256; /** * This function returns the twap rate PT/Asset on market, but take into account the current rate of SY This is to account for special cases where underlying asset becomes insolvent and has decreasing exchangeRate * @param market market to get rate from * @param duration twap duration */ function getPtToAssetRate(IPMarket market, uint32 duration) internal view returns (uint256) { (uint256 syIndex, uint256 pyIndex) = getSYandPYIndexCurrent(market); return (getPtToAssetRateRaw(market,duration) * syIndex) / pyIndex; } function getPtToAssetRateRaw(IPMarket market, uint32 duration) internal view returns (uint256) { uint256 expiry = market.expiry(); if (expiry <= block.timestamp) { return PMath.ONE; } else { uint256 lnImpliedRate = _getMarketLnImpliedRate(market, duration); uint256 timeToExpiry = expiry - block.timestamp; uint256 assetToPtRate = MarketMathCore._getExchangeRateFromImpliedRate(lnImpliedRate, timeToExpiry).Uint(); return PMath.ONE.divDown(assetToPtRate); } } function getSYandPYIndexCurrent(IPMarket market) internal view returns (uint256 syIndex, uint256 pyIndex) { (IStandardizedYield SY, , IPYieldToken YT) = market.readTokens(); syIndex = SY.exchangeRate(); uint256 pyIndexStored = YT.pyIndexStored(); if (YT.doCacheIndexSameBlock() && YT.pyIndexLastUpdatedBlock() == block.number) { pyIndex = pyIndexStored; } else { pyIndex = PMath.max(syIndex, pyIndexStored); } } function _getMarketLnImpliedRate(IPMarket market, uint32 duration) private view returns (uint256) { uint32[] memory durations = new uint32[](2); durations[0] = duration; uint216[] memory lnImpliedRateCumulative = market.observe(durations); return (lnImpliedRateCumulative[1] - lnImpliedRateCumulative[0]) / duration; } }
// SPDX-License-Identifier: AGPL-3.0-only pragma solidity >=0.8.0; /// @notice Read and write to persistent storage at a fraction of the cost. /// @author Solmate (https://github.com/Rari-Capital/solmate/blob/main/src/utils/SSTORE2.sol) /// @author Modified from 0xSequence (https://github.com/0xSequence/sstore2/blob/master/contracts/SSTORE2.sol) library SSTORE2 { uint256 internal constant DATA_OFFSET = 1; // We skip the first byte as it's a STOP opcode to ensure the contract can't be called. /*////////////////////////////////////////////////////////////// WRITE LOGIC //////////////////////////////////////////////////////////////*/ function write(bytes memory data) internal returns (address pointer) { // Prefix the bytecode with a STOP opcode to ensure it cannot be called. bytes memory runtimeCode = abi.encodePacked(hex"00", data); bytes memory creationCode = abi.encodePacked( //---------------------------------------------------------------------------------------------------------------// // Opcode | Opcode + Arguments | Description | Stack View // //---------------------------------------------------------------------------------------------------------------// // 0x60 | 0x600B | PUSH1 11 | codeOffset // // 0x59 | 0x59 | MSIZE | 0 codeOffset // // 0x81 | 0x81 | DUP2 | codeOffset 0 codeOffset // // 0x38 | 0x38 | CODESIZE | codeSize codeOffset 0 codeOffset // // 0x03 | 0x03 | SUB | (codeSize - codeOffset) 0 codeOffset // // 0x80 | 0x80 | DUP | (codeSize - codeOffset) (codeSize - codeOffset) 0 codeOffset // // 0x92 | 0x92 | SWAP3 | codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // // 0x59 | 0x59 | MSIZE | 0 codeOffset (codeSize - codeOffset) 0 (codeSize - codeOffset) // // 0x39 | 0x39 | CODECOPY | 0 (codeSize - codeOffset) // // 0xf3 | 0xf3 | RETURN | // //---------------------------------------------------------------------------------------------------------------// hex"60_0B_59_81_38_03_80_92_59_39_F3", // Returns all code in the contract except for the first 11 (0B in hex) bytes. runtimeCode // The bytecode we want the contract to have after deployment. Capped at 1 byte less than the code size limit. ); assembly { // Deploy a new contract with the generated creation code. // We start 32 bytes into the code to avoid copying the byte length. pointer := create(0, add(creationCode, 32), mload(creationCode)) } require(pointer != address(0), "DEPLOYMENT_FAILED"); } /*////////////////////////////////////////////////////////////// READ LOGIC //////////////////////////////////////////////////////////////*/ function read(address pointer) internal view returns (bytes memory) { return readBytecode(pointer, DATA_OFFSET, pointer.code.length - DATA_OFFSET); } function read(address pointer, uint256 start) internal view returns (bytes memory) { start += DATA_OFFSET; return readBytecode(pointer, start, pointer.code.length - start); } function read( address pointer, uint256 start, uint256 end ) internal view returns (bytes memory) { start += DATA_OFFSET; end += DATA_OFFSET; require(pointer.code.length >= end, "OUT_OF_BOUNDS"); return readBytecode(pointer, start, end - start); } /*////////////////////////////////////////////////////////////// INTERNAL HELPER LOGIC //////////////////////////////////////////////////////////////*/ function readBytecode( address pointer, uint256 start, uint256 size ) private view returns (bytes memory data) { assembly { // Get a pointer to some free memory. data := mload(0x40) // Update the free memory pointer to prevent overriding our data. // We use and(x, not(31)) as a cheaper equivalent to sub(x, mod(x, 32)). // Adding 31 to size and running the result through the logic above ensures // the memory pointer remains word-aligned, following the Solidity convention. mstore(0x40, add(data, and(add(add(size, 32), 31), not(31)))) // Store the size of the data in the first 32 byte chunk of free memory. mstore(data, size) // Copy the code into memory right after the 32 bytes we used to store the size. extcodecopy(pointer, add(data, 32), start, size) } } }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity >=0.5.0; /// @title The interface for the Uniswap V3 Factory /// @notice The Uniswap V3 Factory facilitates creation of Uniswap V3 pools and control over the protocol fees interface IUniswapV3Factory { /// @notice Emitted when the owner of the factory is changed /// @param oldOwner The owner before the owner was changed /// @param newOwner The owner after the owner was changed event OwnerChanged(address indexed oldOwner, address indexed newOwner); /// @notice Emitted when a pool is created /// @param token0 The first token of the pool by address sort order /// @param token1 The second token of the pool by address sort order /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip /// @param tickSpacing The minimum number of ticks between initialized ticks /// @param pool The address of the created pool event PoolCreated( address indexed token0, address indexed token1, uint24 indexed fee, int24 tickSpacing, address pool ); /// @notice Emitted when a new fee amount is enabled for pool creation via the factory /// @param fee The enabled fee, denominated in hundredths of a bip /// @param tickSpacing The minimum number of ticks between initialized ticks for pools created with the given fee event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing); /// @notice Returns the current owner of the factory /// @dev Can be changed by the current owner via setOwner /// @return The address of the factory owner function owner() external view returns (address); /// @notice Returns the tick spacing for a given fee amount, if enabled, or 0 if not enabled /// @dev A fee amount can never be removed, so this value should be hard coded or cached in the calling context /// @param fee The enabled fee, denominated in hundredths of a bip. Returns 0 in case of unenabled fee /// @return The tick spacing function feeAmountTickSpacing(uint24 fee) external view returns (int24); /// @notice Returns the pool address for a given pair of tokens and a fee, or address 0 if it does not exist /// @dev tokenA and tokenB may be passed in either token0/token1 or token1/token0 order /// @param tokenA The contract address of either token0 or token1 /// @param tokenB The contract address of the other token /// @param fee The fee collected upon every swap in the pool, denominated in hundredths of a bip /// @return pool The pool address function getPool( address tokenA, address tokenB, uint24 fee ) external view returns (address pool); /// @notice Creates a pool for the given two tokens and fee /// @param tokenA One of the two tokens in the desired pool /// @param tokenB The other of the two tokens in the desired pool /// @param fee The desired fee for the pool /// @dev tokenA and tokenB may be passed in either order: token0/token1 or token1/token0. tickSpacing is retrieved /// from the fee. The call will revert if the pool already exists, the fee is invalid, or the token arguments /// are invalid. /// @return pool The address of the newly created pool function createPool( address tokenA, address tokenB, uint24 fee ) external returns (address pool); /// @notice Updates the owner of the factory /// @dev Must be called by the current owner /// @param _owner The new owner of the factory function setOwner(address _owner) external; /// @notice Enables a fee amount with the given tickSpacing /// @dev Fee amounts may never be removed once enabled /// @param fee The fee amount to enable, denominated in hundredths of a bip (i.e. 1e-6) /// @param tickSpacing The spacing between ticks to be enforced for all pools created with the given fee amount function enableFeeAmount(uint24 fee, int24 tickSpacing) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; /** * @title IFlashLoanReceiver interface * @notice Interface for the IFlashLoanReceiver. * @author Sturdy * @dev implement this interface to develop a flashloan-compatible flashLoanReceiver contract **/ interface IFlashLoanReceiver { function executeOperation( address[] calldata assets, uint256[] calldata amounts, uint256[] calldata premiums, address initiator, bytes calldata params ) external returns (bool); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; import {IPoolAddressesProvider} from "./IPoolAddressesProvider.sol"; import {DataTypesV3} from "../../../libraries/Aave/DataTypesV3.sol"; /** * @title IPool * @author Aave * @notice Defines the basic interface for an Aave Pool. **/ interface IPool { /** * @dev Emitted on mintUnbacked() * @param reserve The address of the underlying asset of the reserve * @param user The address initiating the supply * @param onBehalfOf The beneficiary of the supplied assets, receiving the aTokens * @param amount The amount of supplied assets * @param referralCode The referral code used **/ event MintUnbacked( address indexed reserve, address user, address indexed onBehalfOf, uint256 amount, uint16 indexed referralCode ); /** * @dev Emitted on backUnbacked() * @param reserve The address of the underlying asset of the reserve * @param backer The address paying for the backing * @param amount The amount added as backing * @param fee The amount paid in fees **/ event BackUnbacked( address indexed reserve, address indexed backer, uint256 amount, uint256 fee ); /** * @dev Emitted on supply() * @param reserve The address of the underlying asset of the reserve * @param user The address initiating the supply * @param onBehalfOf The beneficiary of the supply, receiving the aTokens * @param amount The amount supplied * @param referralCode The referral code used **/ event Supply( address indexed reserve, address user, address indexed onBehalfOf, uint256 amount, uint16 indexed referralCode ); /** * @dev Emitted on withdraw() * @param reserve The address of the underlying asset being withdrawn * @param user The address initiating the withdrawal, owner of aTokens * @param to The address that will receive the underlying * @param amount The amount to be withdrawn **/ event Withdraw( address indexed reserve, address indexed user, address indexed to, uint256 amount ); /** * @dev Emitted on borrow() and flashLoan() when debt needs to be opened * @param reserve The address of the underlying asset being borrowed * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just * initiator of the transaction on flashLoan() * @param onBehalfOf The address that will be getting the debt * @param amount The amount borrowed out * @param interestRateMode The rate mode: 1 for Stable, 2 for Variable * @param borrowRate The numeric rate at which the user has borrowed, expressed in ray * @param referralCode The referral code used **/ event Borrow( address indexed reserve, address user, address indexed onBehalfOf, uint256 amount, DataTypesV3.InterestRateMode interestRateMode, uint256 borrowRate, uint16 indexed referralCode ); /** * @dev Emitted on repay() * @param reserve The address of the underlying asset of the reserve * @param user The beneficiary of the repayment, getting his debt reduced * @param repayer The address of the user initiating the repay(), providing the funds * @param amount The amount repaid * @param useATokens True if the repayment is done using aTokens, `false` if done with underlying asset directly **/ event Repay( address indexed reserve, address indexed user, address indexed repayer, uint256 amount, bool useATokens ); /** * @dev Emitted on swapBorrowRateMode() * @param reserve The address of the underlying asset of the reserve * @param user The address of the user swapping his rate mode * @param interestRateMode The current interest rate mode of the position being swapped: 1 for Stable, 2 for Variable **/ event SwapBorrowRateMode( address indexed reserve, address indexed user, DataTypesV3.InterestRateMode interestRateMode ); /** * @dev Emitted on borrow(), repay() and liquidationCall() when using isolated assets * @param asset The address of the underlying asset of the reserve * @param totalDebt The total isolation mode debt for the reserve */ event IsolationModeTotalDebtUpdated( address indexed asset, uint256 totalDebt ); /** * @dev Emitted when the user selects a certain asset category for eMode * @param user The address of the user * @param categoryId The category id **/ event UserEModeSet(address indexed user, uint8 categoryId); /** * @dev Emitted on setUserUseReserveAsCollateral() * @param reserve The address of the underlying asset of the reserve * @param user The address of the user enabling the usage as collateral **/ event ReserveUsedAsCollateralEnabled( address indexed reserve, address indexed user ); /** * @dev Emitted on setUserUseReserveAsCollateral() * @param reserve The address of the underlying asset of the reserve * @param user The address of the user enabling the usage as collateral **/ event ReserveUsedAsCollateralDisabled( address indexed reserve, address indexed user ); /** * @dev Emitted on rebalanceStableBorrowRate() * @param reserve The address of the underlying asset of the reserve * @param user The address of the user for which the rebalance has been executed **/ event RebalanceStableBorrowRate( address indexed reserve, address indexed user ); /** * @dev Emitted on flashLoan() * @param target The address of the flash loan receiver contract * @param initiator The address initiating the flash loan * @param asset The address of the asset being flash borrowed * @param amount The amount flash borrowed * @param interestRateMode The flashloan mode: 0 for regular flashloan, 1 for Stable debt, 2 for Variable debt * @param premium The fee flash borrowed * @param referralCode The referral code used **/ event FlashLoan( address indexed target, address initiator, address indexed asset, uint256 amount, DataTypesV3.InterestRateMode interestRateMode, uint256 premium, uint16 indexed referralCode ); /** * @dev Emitted when a borrower is liquidated. * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation * @param user The address of the borrower getting liquidated * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover * @param liquidatedCollateralAmount The amount of collateral received by the liquidator * @param liquidator The address of the liquidator * @param receiveAToken True if the liquidators wants to receive the collateral aTokens, `false` if he wants * to receive the underlying collateral asset directly **/ event LiquidationCall( address indexed collateralAsset, address indexed debtAsset, address indexed user, uint256 debtToCover, uint256 liquidatedCollateralAmount, address liquidator, bool receiveAToken ); /** * @dev Emitted when the state of a reserve is updated. * @param reserve The address of the underlying asset of the reserve * @param liquidityRate The next liquidity rate * @param stableBorrowRate The next stable borrow rate * @param variableBorrowRate The next variable borrow rate * @param liquidityIndex The next liquidity index * @param variableBorrowIndex The next variable borrow index **/ event ReserveDataUpdated( address indexed reserve, uint256 liquidityRate, uint256 stableBorrowRate, uint256 variableBorrowRate, uint256 liquidityIndex, uint256 variableBorrowIndex ); /** * @dev Emitted when the protocol treasury receives minted aTokens from the accrued interest. * @param reserve The address of the reserve * @param amountMinted The amount minted to the treasury **/ event MintedToTreasury(address indexed reserve, uint256 amountMinted); /** * @dev Mints an `amount` of aTokens to the `onBehalfOf` * @param asset The address of the underlying asset to mint * @param amount The amount to mint * @param onBehalfOf The address that will receive the aTokens * @param referralCode Code used to register the integrator originating the operation, for potential rewards. * 0 if the action is executed directly by the user, without any middle-man **/ function mintUnbacked( address asset, uint256 amount, address onBehalfOf, uint16 referralCode ) external; /** * @dev Back the current unbacked underlying with `amount` and pay `fee`. * @param asset The address of the underlying asset to back * @param amount The amount to back * @param fee The amount paid in fees **/ function backUnbacked(address asset, uint256 amount, uint256 fee) external; /** * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. * - E.g. User supplies 100 USDC and gets in return 100 aUSDC * @param asset The address of the underlying asset to supply * @param amount The amount to be supplied * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens * is a different wallet * @param referralCode Code used to register the integrator originating the operation, for potential rewards. * 0 if the action is executed directly by the user, without any middle-man **/ function supply( address asset, uint256 amount, address onBehalfOf, uint16 referralCode ) external; /** * @notice Supply with transfer approval of asset to be supplied done via permit function * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713 * @param asset The address of the underlying asset to supply * @param amount The amount to be supplied * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens * is a different wallet * @param deadline The deadline timestamp that the permit is valid * @param referralCode Code used to register the integrator originating the operation, for potential rewards. * 0 if the action is executed directly by the user, without any middle-man * @param permitV The V parameter of ERC712 permit sig * @param permitR The R parameter of ERC712 permit sig * @param permitS The S parameter of ERC712 permit sig **/ function supplyWithPermit( address asset, uint256 amount, address onBehalfOf, uint16 referralCode, uint256 deadline, uint8 permitV, bytes32 permitR, bytes32 permitS ) external; /** * @notice Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC * @param asset The address of the underlying asset to withdraw * @param amount The underlying amount to be withdrawn * - Send the value type(uint256).max in order to withdraw the whole aToken balance * @param to The address that will receive the underlying, same as msg.sender if the user * wants to receive it on his own wallet, or a different address if the beneficiary is a * different wallet * @return The final amount withdrawn **/ function withdraw( address asset, uint256 amount, address to ) external returns (uint256); /** * @notice Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower * already supplied enough collateral, or he was given enough allowance by a credit delegator on the * corresponding debt token (StableDebtToken or VariableDebtToken) * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet * and 100 stable/variable debt tokens, depending on the `interestRateMode` * @param asset The address of the underlying asset to borrow * @param amount The amount to be borrowed * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable * @param referralCode The code used to register the integrator originating the operation, for potential rewards. * 0 if the action is executed directly by the user, without any middle-man * @param onBehalfOf The address of the user who will receive the debt. Should be the address of the borrower itself * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator * if he has been given credit delegation allowance **/ function borrow( address asset, uint256 amount, uint256 interestRateMode, uint16 referralCode, address onBehalfOf ) external; /** * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address * @param asset The address of the borrowed underlying asset previously borrowed * @param amount The amount to repay * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode` * @param interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable * @param onBehalfOf The address of the user who will get his debt reduced/removed. Should be the address of the * user calling the function if he wants to reduce/remove his own debt, or the address of any other * other borrower whose debt should be removed * @return The final amount repaid **/ function repay( address asset, uint256 amount, uint256 interestRateMode, address onBehalfOf ) external returns (uint256); /** * @notice Repay with transfer approval of asset to be repaid done via permit function * see: https://eips.ethereum.org/EIPS/eip-2612 and https://eips.ethereum.org/EIPS/eip-713 * @param asset The address of the borrowed underlying asset previously borrowed * @param amount The amount to repay * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode` * @param interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the * user calling the function if he wants to reduce/remove his own debt, or the address of any other * other borrower whose debt should be removed * @param deadline The deadline timestamp that the permit is valid * @param permitV The V parameter of ERC712 permit sig * @param permitR The R parameter of ERC712 permit sig * @param permitS The S parameter of ERC712 permit sig * @return The final amount repaid **/ function repayWithPermit( address asset, uint256 amount, uint256 interestRateMode, address onBehalfOf, uint256 deadline, uint8 permitV, bytes32 permitR, bytes32 permitS ) external returns (uint256); /** * @notice Repays a borrowed `amount` on a specific reserve using the reserve aTokens, burning the * equivalent debt tokens * - E.g. User repays 100 USDC using 100 aUSDC, burning 100 variable/stable debt tokens * @dev Passing uint256.max as amount will clean up any residual aToken dust balance, if the user aToken * balance is not enough to cover the whole debt * @param asset The address of the borrowed underlying asset previously borrowed * @param amount The amount to repay * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode` * @param interestRateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable * @return The final amount repaid **/ function repayWithATokens( address asset, uint256 amount, uint256 interestRateMode ) external returns (uint256); /** * @notice Allows a borrower to swap his debt between stable and variable mode, or vice versa * @param asset The address of the underlying asset borrowed * @param interestRateMode The current interest rate mode of the position being swapped: 1 for Stable, 2 for Variable **/ function swapBorrowRateMode( address asset, uint256 interestRateMode ) external; /** * @notice Rebalances the stable interest rate of a user to the current stable rate defined on the reserve. * - Users can be rebalanced if the following conditions are satisfied: * 1. Usage ratio is above 95% * 2. the current supply APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too * much has been borrowed at a stable rate and suppliers are not earning enough * @param asset The address of the underlying asset borrowed * @param user The address of the user to be rebalanced **/ function rebalanceStableBorrowRate(address asset, address user) external; /** * @notice Allows suppliers to enable/disable a specific supplied asset as collateral * @param asset The address of the underlying asset supplied * @param useAsCollateral True if the user wants to use the supply as collateral, false otherwise **/ function setUserUseReserveAsCollateral( address asset, bool useAsCollateral ) external; /** * @notice Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1 * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives * a proportionally amount of the `collateralAsset` plus a bonus to cover market risk * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation * @param user The address of the borrower getting liquidated * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover * @param receiveAToken True if the liquidators wants to receive the collateral aTokens, `false` if he wants * to receive the underlying collateral asset directly **/ function liquidationCall( address collateralAsset, address debtAsset, address user, uint256 debtToCover, bool receiveAToken ) external; /** * @notice Allows smartcontracts to access the liquidity of the pool within one transaction, * as long as the amount taken plus a fee is returned. * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept * into consideration. For further details please visit https://developers.aave.com * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanReceiver interface * @param assets The addresses of the assets being flash-borrowed * @param amounts The amounts of the assets being flash-borrowed * @param interestRateModes Types of the debt to open if the flash loan is not returned: * 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver * 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address * 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address * @param onBehalfOf The address that will receive the debt in the case of using on `modes` 1 or 2 * @param params Variadic packed params to pass to the receiver as extra information * @param referralCode The code used to register the integrator originating the operation, for potential rewards. * 0 if the action is executed directly by the user, without any middle-man **/ function flashLoan( address receiverAddress, address[] calldata assets, uint256[] calldata amounts, uint256[] calldata interestRateModes, address onBehalfOf, bytes calldata params, uint16 referralCode ) external; /** * @notice Allows smartcontracts to access the liquidity of the pool within one transaction, * as long as the amount taken plus a fee is returned. * @dev IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept * into consideration. For further details please visit https://developers.aave.com * @param receiverAddress The address of the contract receiving the funds, implementing IFlashLoanSimpleReceiver interface * @param asset The address of the asset being flash-borrowed * @param amount The amount of the asset being flash-borrowed * @param params Variadic packed params to pass to the receiver as extra information * @param referralCode The code used to register the integrator originating the operation, for potential rewards. * 0 if the action is executed directly by the user, without any middle-man **/ function flashLoanSimple( address receiverAddress, address asset, uint256 amount, bytes calldata params, uint16 referralCode ) external; /** * @notice Returns the user account data across all the reserves * @param user The address of the user * @return totalCollateralBase The total collateral of the user in the base currency used by the price feed * @return totalDebtBase The total debt of the user in the base currency used by the price feed * @return availableBorrowsBase The borrowing power left of the user in the base currency used by the price feed * @return currentLiquidationThreshold The liquidation threshold of the user * @return ltv The loan to value of The user * @return healthFactor The current health factor of the user **/ function getUserAccountData( address user ) external view returns ( uint256 totalCollateralBase, uint256 totalDebtBase, uint256 availableBorrowsBase, uint256 currentLiquidationThreshold, uint256 ltv, uint256 healthFactor ); /** * @notice Initializes a reserve, activating it, assigning an aToken and debt tokens and an * interest rate strategy * @dev Only callable by the PoolConfigurator contract * @param asset The address of the underlying asset of the reserve * @param aTokenAddress The address of the aToken that will be assigned to the reserve * @param stableDebtAddress The address of the StableDebtToken that will be assigned to the reserve * @param variableDebtAddress The address of the VariableDebtToken that will be assigned to the reserve * @param interestRateStrategyAddress The address of the interest rate strategy contract **/ function initReserve( address asset, address aTokenAddress, address stableDebtAddress, address variableDebtAddress, address interestRateStrategyAddress ) external; /** * @notice Drop a reserve * @dev Only callable by the PoolConfigurator contract * @param asset The address of the underlying asset of the reserve **/ function dropReserve(address asset) external; /** * @notice Updates the address of the interest rate strategy contract * @dev Only callable by the PoolConfigurator contract * @param asset The address of the underlying asset of the reserve * @param rateStrategyAddress The address of the interest rate strategy contract **/ function setReserveInterestRateStrategyAddress( address asset, address rateStrategyAddress ) external; /** * @notice Sets the configuration bitmap of the reserve as a whole * @dev Only callable by the PoolConfigurator contract * @param asset The address of the underlying asset of the reserve * @param configuration The new configuration bitmap **/ function setConfiguration( address asset, DataTypesV3.ReserveConfigurationMap calldata configuration ) external; /** * @notice Returns the configuration of the reserve * @param asset The address of the underlying asset of the reserve * @return The configuration of the reserve **/ function getConfiguration( address asset ) external view returns (DataTypesV3.ReserveConfigurationMap memory); /** * @notice Returns the configuration of the user across all the reserves * @param user The user address * @return The configuration of the user **/ function getUserConfiguration( address user ) external view returns (DataTypesV3.UserConfigurationMap memory); /** * @notice Returns the normalized income normalized income of the reserve * @param asset The address of the underlying asset of the reserve * @return The reserve's normalized income */ function getReserveNormalizedIncome( address asset ) external view returns (uint256); /** * @notice Returns the normalized variable debt per unit of asset * @param asset The address of the underlying asset of the reserve * @return The reserve normalized variable debt */ function getReserveNormalizedVariableDebt( address asset ) external view returns (uint256); /** * @notice Returns the state and configuration of the reserve * @param asset The address of the underlying asset of the reserve * @return The state and configuration data of the reserve **/ function getReserveData( address asset ) external view returns (DataTypesV3.ReserveData memory); /** * @notice Validates and finalizes an aToken transfer * @dev Only callable by the overlying aToken of the `asset` * @param asset The address of the underlying asset of the aToken * @param from The user from which the aTokens are transferred * @param to The user receiving the aTokens * @param amount The amount being transferred/withdrawn * @param balanceFromBefore The aToken balance of the `from` user before the transfer * @param balanceToBefore The aToken balance of the `to` user before the transfer */ function finalizeTransfer( address asset, address from, address to, uint256 amount, uint256 balanceFromBefore, uint256 balanceToBefore ) external; /** * @notice Returns the list of the underlying assets of all the initialized reserves * @dev It does not include dropped reserves * @return The addresses of the underlying assets of the initialized reserves **/ function getReservesList() external view returns (address[] memory); /** * @notice Returns the address of the underlying asset of a reserve by the reserve id as stored in the DataTypesV3.ReserveData struct * @param id The id of the reserve as stored in the DataTypesV3.ReserveData struct * @return The address of the reserve associated with id **/ function getReserveAddressById(uint16 id) external view returns (address); /** * @notice Returns the PoolAddressesProvider connected to this contract * @return The address of the PoolAddressesProvider **/ function ADDRESSES_PROVIDER() external view returns (IPoolAddressesProvider); /** * @notice Updates the protocol fee on the bridging * @param bridgeProtocolFee The part of the premium sent to the protocol treasury */ function updateBridgeProtocolFee(uint256 bridgeProtocolFee) external; /** * @notice Updates flash loan premiums. Flash loan premium consists of two parts: * - A part is sent to aToken holders as extra, one time accumulated interest * - A part is collected by the protocol treasury * @dev The total premium is calculated on the total borrowed amount * @dev The premium to protocol is calculated on the total premium, being a percentage of `flashLoanPremiumTotal` * @dev Only callable by the PoolConfigurator contract * @param flashLoanPremiumTotal The total premium, expressed in bps * @param flashLoanPremiumToProtocol The part of the premium sent to the protocol treasury, expressed in bps */ function updateFlashloanPremiums( uint128 flashLoanPremiumTotal, uint128 flashLoanPremiumToProtocol ) external; /** * @notice Configures a new category for the eMode. * @dev In eMode, the protocol allows very high borrowing power to borrow assets of the same category. * The category 0 is reserved as it's the default for volatile assets * @param id The id of the category * @param config The configuration of the category */ function configureEModeCategory( uint8 id, DataTypesV3.EModeCategory memory config ) external; /** * @notice Returns the data of an eMode category * @param id The id of the category * @return The configuration data of the category */ function getEModeCategoryData( uint8 id ) external view returns (DataTypesV3.EModeCategory memory); /** * @notice Allows a user to use the protocol in eMode * @param categoryId The id of the category */ function setUserEMode(uint8 categoryId) external; /** * @notice Returns the eMode the user is using * @param user The address of the user * @return The eMode id */ function getUserEMode(address user) external view returns (uint256); /** * @notice Resets the isolation mode total debt of the given asset to zero * @dev It requires the given asset has zero debt ceiling * @param asset The address of the underlying asset to reset the isolationModeTotalDebt */ function resetIsolationModeTotalDebt(address asset) external; /** * @notice Returns the percentage of available liquidity that can be borrowed at once at stable rate * @return The percentage of available liquidity to borrow, expressed in bps */ function MAX_STABLE_RATE_BORROW_SIZE_PERCENT() external view returns (uint256); /** * @notice Returns the total fee on flash loans * @return The total fee on flashloans */ function FLASHLOAN_PREMIUM_TOTAL() external view returns (uint128); /** * @notice Returns the part of the bridge fees sent to protocol * @return The bridge fee sent to the protocol treasury */ function BRIDGE_PROTOCOL_FEE() external view returns (uint256); /** * @notice Returns the part of the flashloan fees sent to protocol * @return The flashloan fee sent to the protocol treasury */ function FLASHLOAN_PREMIUM_TO_PROTOCOL() external view returns (uint128); /** * @notice Returns the maximum number of reserves supported to be listed in this Pool * @return The maximum number of reserves supported */ function MAX_NUMBER_RESERVES() external view returns (uint16); /** * @notice Mints the assets accrued through the reserve factor to the treasury in the form of aTokens * @param assets The list of reserves for which the minting needs to be executed **/ function mintToTreasury(address[] calldata assets) external; /** * @notice Rescue and transfer tokens locked in this contract * @param token The address of the token * @param to The address of the recipient * @param amount The amount of token to transfer */ function rescueTokens(address token, address to, uint256 amount) external; /** * @notice Supplies an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. * - E.g. User supplies 100 USDC and gets in return 100 aUSDC * @dev Deprecated: Use the `supply` function instead * @param asset The address of the underlying asset to supply * @param amount The amount to be supplied * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens * is a different wallet * @param referralCode Code used to register the integrator originating the operation, for potential rewards. * 0 if the action is executed directly by the user, without any middle-man **/ function deposit( address asset, uint256 amount, address onBehalfOf, uint16 referralCode ) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; /** * @title IPoolAddressesProvider * @author Aave * @notice Defines the basic interface for a Pool Addresses Provider. **/ interface IPoolAddressesProvider { /** * @dev Emitted when the market identifier is updated. * @param oldMarketId The old id of the market * @param newMarketId The new id of the market */ event MarketIdSet(string indexed oldMarketId, string indexed newMarketId); /** * @dev Emitted when the pool is updated. * @param oldAddress The old address of the Pool * @param newAddress The new address of the Pool */ event PoolUpdated(address indexed oldAddress, address indexed newAddress); /** * @dev Emitted when the pool configurator is updated. * @param oldAddress The old address of the PoolConfigurator * @param newAddress The new address of the PoolConfigurator */ event PoolConfiguratorUpdated( address indexed oldAddress, address indexed newAddress ); /** * @dev Emitted when the price oracle is updated. * @param oldAddress The old address of the PriceOracle * @param newAddress The new address of the PriceOracle */ event PriceOracleUpdated( address indexed oldAddress, address indexed newAddress ); /** * @dev Emitted when the ACL manager is updated. * @param oldAddress The old address of the ACLManager * @param newAddress The new address of the ACLManager */ event ACLManagerUpdated( address indexed oldAddress, address indexed newAddress ); /** * @dev Emitted when the ACL admin is updated. * @param oldAddress The old address of the ACLAdmin * @param newAddress The new address of the ACLAdmin */ event ACLAdminUpdated( address indexed oldAddress, address indexed newAddress ); /** * @dev Emitted when the price oracle sentinel is updated. * @param oldAddress The old address of the PriceOracleSentinel * @param newAddress The new address of the PriceOracleSentinel */ event PriceOracleSentinelUpdated( address indexed oldAddress, address indexed newAddress ); /** * @dev Emitted when the pool data provider is updated. * @param oldAddress The old address of the PoolDataProvider * @param newAddress The new address of the PoolDataProvider */ event PoolDataProviderUpdated( address indexed oldAddress, address indexed newAddress ); /** * @dev Emitted when a new proxy is created. * @param id The identifier of the proxy * @param proxyAddress The address of the created proxy contract * @param implementationAddress The address of the implementation contract */ event ProxyCreated( bytes32 indexed id, address indexed proxyAddress, address indexed implementationAddress ); /** * @dev Emitted when a new non-proxied contract address is registered. * @param id The identifier of the contract * @param oldAddress The address of the old contract * @param newAddress The address of the new contract */ event AddressSet( bytes32 indexed id, address indexed oldAddress, address indexed newAddress ); /** * @dev Emitted when the implementation of the proxy registered with id is updated * @param id The identifier of the contract * @param proxyAddress The address of the proxy contract * @param oldImplementationAddress The address of the old implementation contract * @param newImplementationAddress The address of the new implementation contract */ event AddressSetAsProxy( bytes32 indexed id, address indexed proxyAddress, address oldImplementationAddress, address indexed newImplementationAddress ); /** * @notice Returns the id of the Aave market to which this contract points to. * @return The market id **/ function getMarketId() external view returns (string memory); /** * @notice Associates an id with a specific PoolAddressesProvider. * @dev This can be used to create an onchain registry of PoolAddressesProviders to * identify and validate multiple Aave markets. * @param newMarketId The market id */ function setMarketId(string calldata newMarketId) external; /** * @notice Returns an address by its identifier. * @dev The returned address might be an EOA or a contract, potentially proxied * @dev It returns ZERO if there is no registered address with the given id * @param id The id * @return The address of the registered for the specified id */ function getAddress(bytes32 id) external view returns (address); /** * @notice General function to update the implementation of a proxy registered with * certain `id`. If there is no proxy registered, it will instantiate one and * set as implementation the `newImplementationAddress`. * @dev IMPORTANT Use this function carefully, only for ids that don't have an explicit * setter function, in order to avoid unexpected consequences * @param id The id * @param newImplementationAddress The address of the new implementation */ function setAddressAsProxy( bytes32 id, address newImplementationAddress ) external; /** * @notice Sets an address for an id replacing the address saved in the addresses map. * @dev IMPORTANT Use this function carefully, as it will do a hard replacement * @param id The id * @param newAddress The address to set */ function setAddress(bytes32 id, address newAddress) external; /** * @notice Returns the address of the Pool proxy. * @return The Pool proxy address **/ function getPool() external view returns (address); /** * @notice Updates the implementation of the Pool, or creates a proxy * setting the new `pool` implementation when the function is called for the first time. * @param newPoolImpl The new Pool implementation **/ function setPoolImpl(address newPoolImpl) external; /** * @notice Returns the address of the PoolConfigurator proxy. * @return The PoolConfigurator proxy address **/ function getPoolConfigurator() external view returns (address); /** * @notice Updates the implementation of the PoolConfigurator, or creates a proxy * setting the new `PoolConfigurator` implementation when the function is called for the first time. * @param newPoolConfiguratorImpl The new PoolConfigurator implementation **/ function setPoolConfiguratorImpl(address newPoolConfiguratorImpl) external; /** * @notice Returns the address of the price oracle. * @return The address of the PriceOracle */ function getPriceOracle() external view returns (address); /** * @notice Updates the address of the price oracle. * @param newPriceOracle The address of the new PriceOracle */ function setPriceOracle(address newPriceOracle) external; /** * @notice Returns the address of the ACL manager. * @return The address of the ACLManager */ function getACLManager() external view returns (address); /** * @notice Updates the address of the ACL manager. * @param newAclManager The address of the new ACLManager **/ function setACLManager(address newAclManager) external; /** * @notice Returns the address of the ACL admin. * @return The address of the ACL admin */ function getACLAdmin() external view returns (address); /** * @notice Updates the address of the ACL admin. * @param newAclAdmin The address of the new ACL admin */ function setACLAdmin(address newAclAdmin) external; /** * @notice Returns the address of the price oracle sentinel. * @return The address of the PriceOracleSentinel */ function getPriceOracleSentinel() external view returns (address); /** * @notice Updates the address of the price oracle sentinel. * @param newPriceOracleSentinel The address of the new PriceOracleSentinel **/ function setPriceOracleSentinel(address newPriceOracleSentinel) external; /** * @notice Returns the address of the data provider. * @return The address of the DataProvider */ function getPoolDataProvider() external view returns (address); /** * @notice Updates the address of the data provider. * @param newDataProvider The address of the new DataProvider **/ function setPoolDataProvider(address newDataProvider) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; interface IBalancerVault { // Pools // // There are three specialization settings for Pools, which allow for cheaper swaps at the cost of reduced // functionality: // // - General: no specialization, suited for all Pools. IGeneralPool is used for swap request callbacks, passing the // balance of all tokens in the Pool. These Pools have the largest swap costs (because of the extra storage reads), // which increase with the number of registered tokens. // // - Minimal Swap Info: IMinimalSwapInfoPool is used instead of IGeneralPool, which saves gas by only passing the // balance of the two tokens involved in the swap. This is suitable for some pricing algorithms, like the weighted // constant product one popularized by Balancer V1. Swap costs are smaller compared to general Pools, and are // independent of the number of registered tokens. // // - Two Token: only allows two tokens to be registered. This achieves the lowest possible swap gas cost. Like // minimal swap info Pools, these are called via IMinimalSwapInfoPool. enum PoolSpecialization { GENERAL, MINIMAL_SWAP_INFO, TWO_TOKEN } /** * @dev Returns a Pool's contract address and specialization setting. */ function getPool(bytes32 poolId) external view returns (address, PoolSpecialization); // Swaps // // Users can swap tokens with Pools by calling the `swap` and `batchSwap` functions. To do this, // they need not trust Pool contracts in any way: all security checks are made by the Vault. They must however be // aware of the Pools' pricing algorithms in order to estimate the prices Pools will quote. // // The `swap` function executes a single swap, while `batchSwap` can perform multiple swaps in sequence. // In each individual swap, tokens of one kind are sent from the sender to the Pool (this is the 'token in'), // and tokens of another kind are sent from the Pool to the recipient in exchange (this is the 'token out'). // More complex swaps, such as one token in to multiple tokens out can be achieved by batching together // individual swaps. // // There are two swap kinds: // - 'given in' swaps, where the amount of tokens in (sent to the Pool) is known, and the Pool determines (via the // `onSwap` hook) the amount of tokens out (to send to the recipient). // - 'given out' swaps, where the amount of tokens out (received from the Pool) is known, and the Pool determines // (via the `onSwap` hook) the amount of tokens in (to receive from the sender). // // Additionally, it is possible to chain swaps using a placeholder input amount, which the Vault replaces with // the calculated output of the previous swap. If the previous swap was 'given in', this will be the calculated // tokenOut amount. If the previous swap was 'given out', it will use the calculated tokenIn amount. These extended // swaps are known as 'multihop' swaps, since they 'hop' through a number of intermediate tokens before arriving at // the final intended token. // // In all cases, tokens are only transferred in and out of the Vault (or withdrawn from and deposited into Internal // Balance) after all individual swaps have been completed, and the net token balance change computed. This makes // certain swap patterns, such as multihops, or swaps that interact with the same token pair in multiple Pools, cost // much less gas than they would otherwise. // // It also means that under certain conditions it is possible to perform arbitrage by swapping with multiple // Pools in a way that results in net token movement out of the Vault (profit), with no tokens being sent in (only // updating the Pool's internal accounting). // // To protect users from front-running or the market changing rapidly, they supply a list of 'limits' for each token // involved in the swap, where either the maximum number of tokens to send (by passing a positive value) or the // minimum amount of tokens to receive (by passing a negative value) is specified. // // Additionally, a 'deadline' timestamp can also be provided, forcing the swap to fail if it occurs after // this point in time (e.g. if the transaction failed to be included in a block promptly). // // If interacting with Pools that hold WETH, it is possible to both send and receive ETH directly: the Vault will do // the wrapping and unwrapping. To enable this mechanism, the IAsset sentinel value (the zero address) must be // passed in the `assets` array instead of the WETH address. Note that it is possible to combine ETH and WETH in the // same swap. Any excess ETH will be sent back to the caller (not the sender, which is relevant for relayers). // // Finally, Internal Balance can be used when either sending or receiving tokens. enum SwapKind { GIVEN_IN, GIVEN_OUT } /** * @dev Performs a swap with a single Pool. * * If the swap is 'given in' (the number of tokens to send to the Pool is known), it returns the amount of tokens * taken from the Pool, which must be greater than or equal to `limit`. * * If the swap is 'given out' (the number of tokens to take from the Pool is known), it returns the amount of tokens * sent to the Pool, which must be less than or equal to `limit`. * * Internal Balance usage and the recipient are determined by the `funds` struct. * * Emits a `Swap` event. */ function swap( SingleSwap memory singleSwap, FundManagement memory funds, uint256 limit, uint256 deadline ) external payable returns (uint256); /** * @dev Data for a single swap executed by `swap`. `amount` is either `amountIn` or `amountOut` depending on * the `kind` value. * * `assetIn` and `assetOut` are either token addresses, or the IAsset sentinel value for ETH (the zero address). * Note that Pools never interact with ETH directly: it will be wrapped to or unwrapped from WETH by the Vault. * * The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be * used to extend swap behavior. */ struct SingleSwap { bytes32 poolId; SwapKind kind; address assetIn; address assetOut; uint256 amount; bytes userData; } /** * @dev All tokens in a swap are either sent from the `sender` account to the Vault, or from the Vault to the * `recipient` account. * * If the caller is not `sender`, it must be an authorized relayer for them. * * If `fromInternalBalance` is true, the `sender`'s Internal Balance will be preferred, performing an ERC20 * transfer for the difference between the requested amount and the User's Internal Balance (if any). The `sender` * must have allowed the Vault to use their tokens via `IERC20.approve()`. This matches the behavior of * `joinPool`. * * If `toInternalBalance` is true, tokens will be deposited to `recipient`'s internal balance instead of * transferred. This matches the behavior of `exitPool`. * * Note that ETH cannot be deposited to or withdrawn from Internal Balance: attempting to do so will trigger a * revert. */ struct FundManagement { address sender; bool fromInternalBalance; address payable recipient; bool toInternalBalance; } /** * @dev Called by users to exit a Pool, which transfers tokens from the Pool's balance to `recipient`. This will * trigger custom Pool behavior, which will typically ask for something in return from `sender` - often tokenized * Pool shares. The amount of tokens that can be withdrawn is limited by the Pool's `cash` balance (see * `getPoolTokenInfo`). * * If the caller is not `sender`, it must be an authorized relayer for them. * * The `tokens` and `minAmountsOut` arrays must have the same length, and each entry in these indicates the minimum * token amount to receive for each token contract. The amounts to send are decided by the Pool and not the Vault: * it just enforces these minimums. * * If exiting a Pool that holds WETH, it is possible to receive ETH directly: the Vault will do the unwrapping. To * enable this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead * of the WETH address. Note that it is not possible to combine ETH and WETH in the same exit. * * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when * interacting with Pools that register and deregister tokens frequently. If receiving ETH however, the array must * be sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the * final `assets` array might not be sorted. Pools with no registered tokens cannot be exited. * * If `toInternalBalance` is true, the tokens will be deposited to `recipient`'s Internal Balance. Otherwise, * an ERC20 transfer will be performed. Note that ETH cannot be deposited to Internal Balance: attempting to * do so will trigger a revert. * * `minAmountsOut` is the minimum amount of tokens the user expects to get out of the Pool, for each token in the * `tokens` array. This array must match the Pool's registered tokens. * * This causes the Vault to call the `IBasePool.onExitPool` hook on the Pool's contract, where Pools implement * their own custom logic. This typically requires additional information from the user (such as the expected number * of Pool shares to return). This can be encoded in the `userData` argument, which is ignored by the Vault and * passed directly to the Pool's contract. * * Emits a `PoolBalanceChanged` event. */ function exitPool( bytes32 poolId, address sender, address payable recipient, ExitPoolRequest memory request ) external; struct ExitPoolRequest { address[] assets; uint256[] minAmountsOut; bytes userData; bool toInternalBalance; } enum ExitKind { EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, EXACT_BPT_IN_FOR_TOKENS_OUT, BPT_IN_FOR_EXACT_TOKENS_OUT } function joinPool( bytes32 poolId, address sender, address recipient, JoinPoolRequest memory request ) external payable; struct JoinPoolRequest { address[] assets; uint256[] maxAmountsIn; bytes userData; bool fromInternalBalance; } enum JoinKind { INIT, EXACT_TOKENS_IN_FOR_BPT_OUT, TOKEN_IN_FOR_EXACT_BPT_OUT } function getPoolTokenInfo( bytes32 poolId, IERC20 token ) external view returns (uint256 cash, uint256 managed, uint256 lastChangeBlock, address assetManager); function getPoolTokens( bytes32 poolId ) external view returns (address[] memory tokens, uint256[] memory balances, uint256 lastChangeBlock); struct BatchSwapStep { bytes32 poolId; uint256 assetInIndex; uint256 assetOutIndex; uint256 amount; bytes userData; } function batchSwap( SwapKind kind, BatchSwapStep[] memory swaps, address[] memory assets, FundManagement memory funds, int256[] memory limits, uint256 deadline ) external payable returns (int256[] memory); function flashLoan( address recipient, IERC20[] memory tokens, uint256[] memory amounts, bytes memory userData ) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; /** * @title IFlashLoanRecipient interface * @notice Interface for the IFlashLoanRecipient. * @author Sturdy * @dev implement this interface to develop a flashloan-compatible IFlashLoanRecipient contract **/ interface IFlashLoanRecipient { /** * @dev When `flashLoan` is called on the Vault, it invokes the `receiveFlashLoan` hook on the recipient. * * At the time of the call, the Vault will have transferred `amounts` for `tokens` to the recipient. Before this * call returns, the recipient must have transferred `amounts` plus `feeAmounts` for each token back to the * Vault, or else the entire flash loan will revert. * * `userData` is the same value passed in the `IVault.flashLoan` call. */ function receiveFlashLoan( IERC20[] memory tokens, uint256[] memory amounts, uint256[] memory feeAmounts, bytes memory userData ) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; /** * @title ICurveAddressProvider interface * @notice Interface for the Curve Address Provider. **/ interface ICurveAddressProvider { function get_address(uint256 id) external view returns (address); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; interface ICurveExchange { function exchange( address _pool, address _from, address _to, uint256 _amount, uint256 _expected, address _receiver ) external payable returns (uint256); function exchange_multiple( address[9] memory _route, uint256[3][4] memory _swap_params, uint256 _amount, uint256 _expected, address[4] memory _pools, address _receiver ) external payable returns (uint256); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; interface ICurvePool { function get_virtual_price() external view returns (uint256 price); function price_oracle() external view returns (uint256); function add_liquidity(uint256[2] memory amounts, uint256 _min_mint_amount) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; interface IBaseLeverage { enum FlashLoanType { AAVE, BALANCER } enum SwapType { NONE, NO_SWAP, UNISWAP, BALANCER, CURVE } struct MultipSwapPath { address[9] routes; uint256[3][4] routeParams; // uniswap/balancer/curve SwapType swapType; uint256 poolCount; address swapFrom; address swapTo; uint256 inAmount; uint256 outAmount; } struct SwapInfo { MultipSwapPath[3] paths; MultipSwapPath[3] reversePaths; uint256 pathLength; } struct FlashLoanParams { bool isEnterPosition; uint256 minCollateralAmount; address user; address collateralAsset; address silo; SwapInfo swapInfo; } struct LeverageParams { address user; uint256 principal; uint256 leverage; address borrowAsset; address collateralAsset; address silo; FlashLoanType flashLoanType; SwapInfo swapInfo; } function enterPositionWithFlashloan( uint256 _principal, uint256 _leverage, address _borrowAsset, address _collateralAsset, address _silo, FlashLoanType _flashLoanType, SwapInfo calldata _swapInfo ) external; function withdrawWithFlashloan( uint256 _repayAmount, uint256 _requiredAmount, address _borrowAsset, address _collateralAsset, address _silo, FlashLoanType _flashLoanType, SwapInfo calldata _swapInfo ) external; }
// SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.21; import { IERC165 } from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; interface IDualOracle is IERC165 { function ORACLE_PRECISION() external view returns (uint256); function BASE_TOKEN_0() external view returns (address); function BASE_TOKEN_0_DECIMALS() external view returns (uint256); function BASE_TOKEN_1() external view returns (address); function BASE_TOKEN_1_DECIMALS() external view returns (uint256); function decimals() external view returns (uint8); function getPricesNormalized() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh); function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh); function name() external view returns (string memory); function NORMALIZATION_0() external view returns (int256); function NORMALIZATION_1() external view returns (int256); function QUOTE_TOKEN_0() external view returns (address); function QUOTE_TOKEN_0_DECIMALS() external view returns (uint256); function QUOTE_TOKEN_1() external view returns (address); function QUOTE_TOKEN_1_DECIMALS() external view returns (uint256); }
// SPDX-License-Identifier: ISC pragma solidity >=0.8.15; import "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; interface IERC4626 is IERC20, IERC20Metadata { event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); event Withdraw( address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); function asset() external view returns (address); function convertToAssets(uint256 shares) external view returns (uint256); function convertToShares(uint256 assets) external view returns (uint256); function maxDeposit(address) external view returns (uint256); function maxMint(address) external view returns (uint256); function maxRedeem(address owner) external view returns (uint256); function maxWithdraw(address owner) external view returns (uint256); function previewDeposit(uint256 assets) external view returns (uint256); function previewMint(uint256 shares) external view returns (uint256); function previewRedeem(uint256 shares) external view returns (uint256); function previewWithdraw(uint256 assets) external view returns (uint256); function totalAssets() external view returns (uint256); function mint(uint256 shares, address receiver) external returns (uint256 assets); function deposit(uint256 assets, address receiver) external returns (uint256 shares); function redeem( uint256 shares, address receiver, address owner ) external returns (uint256 assets); function withdraw( uint256 assets, address receiver, address owner ) external returns (uint256 shares); }
// SPDX-License-Identifier: ISC pragma solidity >=0.8.21; interface IRateCalculator { function name() external pure returns (string memory); function requireValidInitData(bytes calldata _initData) external pure; function getConstants() external pure returns (bytes memory _calldata); function getNewRate(bytes calldata _data, bytes calldata _initData) external pure returns (uint64 _newRatePerSec); }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; interface IRateCalculatorV2 { function name() external view returns (string memory); function version() external view returns (uint256, uint256, uint256); function getNewRate( uint256 _deltaTime, uint256 _utilization, uint64 _maxInterest ) external view returns (uint64 _newRatePerSec, uint64 _newMaxInterest); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; import { VaultAccount } from "../libraries/VaultAccount.sol"; interface ISturdyPair { struct CurrentRateInfo { uint32 lastBlock; uint32 feeToProtocolRate; // Fee amount 1e5 precision uint64 lastTimestamp; uint64 ratePerSec; uint64 fullUtilizationRate; } function CIRCUIT_BREAKER_ADDRESS() external view returns (address); function COMPTROLLER_ADDRESS() external view returns (address); function DEPLOYER_ADDRESS() external view returns (address); function FRAXLEND_WHITELIST_ADDRESS() external view returns (address); function timelockAddress() external view returns (address); function addCollateral(uint256 _collateralAmount, address _borrower) external; function addInterest( bool _returnAccounting ) external returns ( uint256 _interestEarned, uint256 _feesAmount, uint256 _feesShare, CurrentRateInfo memory _currentRateInfo, VaultAccount memory _totalAsset, VaultAccount memory _totalBorrow ); function allowance(address owner, address spender) external view returns (uint256); function approve(address spender, uint256 amount) external returns (bool); function approvedBorrowers(address) external view returns (bool); function approvedLenders(address) external view returns (bool); function approveBorrowDelegation(address _delegatee, uint256 _amount) external; function asset() external view returns (address); function balanceOf(address account) external view returns (uint256); function borrowAsset( uint256 _borrowAmount, uint256 _collateralAmount, address _receiver ) external returns (uint256 _shares); function borrowAssetOnBehalfOf( uint256 _borrowAmount, address _onBehalfOf ) external returns (uint256 _shares); function borrowerWhitelistActive() external view returns (bool); function changeFee(uint32 _newFee) external; function cleanLiquidationFee() external view returns (uint256); function collateralContract() external view returns (address); function currentRateInfo() external view returns ( uint32 lastBlock, uint32 feeToProtocolRate, uint64 lastTimestamp, uint64 ratePerSec, uint64 fullUtilizationRate ); function decimals() external view returns (uint8); function decreaseAllowance(address spender, uint256 subtractedValue) external returns (bool); function deposit(uint256 _amount, address _receiver) external returns (uint256 _sharesReceived); function dirtyLiquidationFee() external view returns (uint256); function exchangeRateInfo() external view returns (address oracle, uint32 maxOracleDeviation, uint184 lastTimestamp, uint256 lowExchangeRate, uint256 highExchangeRate); function getConstants() external pure returns ( uint256 _LTV_PRECISION, uint256 _LIQ_PRECISION, uint256 _UTIL_PREC, uint256 _FEE_PRECISION, uint256 _EXCHANGE_PRECISION, uint256 _DEVIATION_PRECISION, uint256 _RATE_PRECISION, uint256 _MAX_PROTOCOL_FEE ); function getImmutableAddressBool() external view returns ( address _assetContract, address _collateralContract, address _oracleMultiply, address _oracleDivide, address _rateContract, address _DEPLOYER_CONTRACT, address _COMPTROLLER_ADDRESS, address _FRAXLEND_WHITELIST, bool _borrowerWhitelistActive, bool _lenderWhitelistActive ); function getImmutableUint256() external view returns ( uint256 _oracleNormalization, uint256 _maxLTV, uint256 _cleanLiquidationFee, uint256 _maturityDate, uint256 _penaltyRate ); function getPairAccounting() external view returns ( uint128 _totalAssetAmount, uint128 _totalAssetShares, uint128 _totalBorrowAmount, uint128 _totalBorrowShares, uint256 _totalCollateral ); function getUserSnapshot( address _address ) external view returns (uint256 _userAssetShares, uint256 _userBorrowShares, uint256 _userCollateralBalance); function increaseAllowance(address spender, uint256 addedValue) external returns (bool); function lenderWhitelistActive() external view returns (bool); function leveragedPosition( address _swapperAddress, uint256 _borrowAmount, uint256 _initialCollateralAmount, uint256 _amountCollateralOutMin, address[] memory _path ) external returns (uint256 _totalCollateralBalance); function liquidate( uint128 _sharesToLiquidate, uint256 _deadline, address _borrower ) external returns (uint256 _collateralForLiquidator); function maturityDate() external view returns (uint256); function maxLTV() external view returns (uint256); function maxOracleDelay() external view returns (uint256); function name() external view returns (string memory); function oracleDivide() external view returns (address); function oracleMultiply() external view returns (address); function oracleNormalization() external view returns (uint256); function owner() external view returns (address); function pause() external; function paused() external view returns (bool); function penaltyRate() external view returns (uint256); function rateContract() external view returns (address); function redeem(uint256 _shares, address _receiver, address _owner) external returns (uint256 _amountToReturn); function removeCollateral(uint256 _collateralAmount, address _receiver) external; function removeCollateralFrom( uint256 _collateralAmount, address _receiver, address _borrower ) external; function setWhitelistedDelegators(address _delegator, bool _enabled) external; function renounceOwnership() external; function repayAsset(uint256 _shares, address _borrower) external returns (uint256 _amountToRepay); function repayAssetWithCollateral( address _swapperAddress, uint256 _collateralToSwap, uint256 _amountAssetOutMin, address[] memory _path ) external returns (uint256 _amountAssetOut); function setApprovedBorrowers(address[] memory _borrowers, bool _approval) external; function setApprovedLenders(address[] memory _lenders, bool _approval) external; function setMaxOracleDelay(uint256 _newDelay) external; function setSwapper(address _swapper, bool _approval) external; function setTimelock(address _newAddress) external; function swappers(address) external view returns (bool); function symbol() external view returns (string memory); function toAssetAmount( uint256 _shares, bool _roundUp, bool _previewInterest ) external view returns (uint256); function toAssetShares( uint256 _amount, bool _roundUp, bool _previewInterest ) external view returns (uint256); function toBorrowAmount( uint256 _shares, bool _roundUp, bool _previewInterest ) external view returns (uint256 _amount); function toBorrowShares( uint256 _amount, bool _roundUp, bool _previewInterest ) external view returns (uint256 _shares); function totalAsset() external view returns (uint128 amount, uint128 shares); function totalBorrow() external view returns (uint128 amount, uint128 shares); function totalCollateral() external view returns (uint256); function totalSupply() external view returns (uint256); function transfer(address to, uint256 amount) external returns (bool); function transferFrom(address from, address to, uint256 amount) external returns (bool); function transferOwnership(address newOwner) external; function unpause() external; function updateExchangeRate() external returns (bool _isBorrowAllowed, uint256 _lowExchangeRate, uint256 _highExchangeRate); function userBorrowShares(address) external view returns (uint256); function userCollateralBalance(address) external view returns (uint256); function version() external pure returns (uint256 _major, uint256 _minor, uint256 _patch); function withdrawFees(uint128 _shares, address _recipient) external returns (uint256 _amountToTransfer); function isInterestPaused() external view returns (bool); }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; interface ISturdyPairRegistry { event AddPair(address pairAddress); event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); event SetDeployer(address deployer, bool _bool); function acceptOwnership() external; function addPair(address _pairAddress) external; function deployedPairsArray(uint256) external view returns (address); function deployedPairsByName(string memory) external view returns (address); function deployedPairsLength() external view returns (uint256); function deployers(address) external view returns (bool); function getAllPairAddresses() external view returns (address[] memory _deployedPairsArray); function owner() external view returns (address); function pendingOwner() external view returns (address); function renounceOwnership() external; function setDeployers(address[] memory _deployers, bool _bool) external; function transferOwnership(address newOwner) external; }
// SPDX-License-Identifier: ISC pragma solidity >=0.8.21; interface ISturdyWhitelist { event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); event SetSturdyDeployerWhitelist(address indexed _address, bool _bool); function acceptOwnership() external; function sturdyDeployerWhitelist(address) external view returns (bool); function owner() external view returns (address); function pendingOwner() external view returns (address); function renounceOwnership() external; function setSturdyDeployerWhitelist(address[] memory _addresses, bool _bool) external; function transferOwnership(address newOwner) external; }
// SPDX-License-Identifier: MIT pragma solidity >=0.8.21; interface ISwapper { function swapExactTokensForTokens( uint256 amountIn, uint256 amountOutMin, address[] calldata path, address to, uint256 deadline ) external returns (uint256[] memory amounts); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; interface IPoolPositionSlim { /// @notice Amount of pool.tokenA() and pool.tokenB() tokens held by the //PoolPosition // @return reserveA Amount of pool.tokenA() tokens held by the // PoolPosition // @return reserveB Amount of pool.tokenB() tokens held by the // PoolPosition function getReserves() external view returns (uint256 reserveA, uint256 reserveB); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; import {IUniswapV3SwapCallback} from "./IUniswapV3SwapCallback.sol"; /// @title Router token swapping functionality /// @notice Functions for swapping tokens via Uniswap V3 interface ISwapRouter is IUniswapV3SwapCallback { struct ExactInputSingleParams { address tokenIn; address tokenOut; uint24 fee; address recipient; uint256 deadline; uint256 amountIn; uint256 amountOutMinimum; uint160 sqrtPriceLimitX96; } /// @notice Swaps `amountIn` of one token for as much as possible of another token /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata /// @return amountOut The amount of the received token function exactInputSingle( ExactInputSingleParams calldata params ) external payable returns (uint256 amountOut); struct ExactInputParams { bytes path; address recipient; uint256 deadline; uint256 amountIn; uint256 amountOutMinimum; } /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata /// @return amountOut The amount of the received token function exactInput( ExactInputParams calldata params ) external payable returns (uint256 amountOut); struct ExactOutputSingleParams { address tokenIn; address tokenOut; uint24 fee; address recipient; uint256 deadline; uint256 amountOut; uint256 amountInMaximum; uint160 sqrtPriceLimitX96; } /// @notice Swaps as little as possible of one token for `amountOut` of another token /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata /// @return amountIn The amount of the input token function exactOutputSingle( ExactOutputSingleParams calldata params ) external payable returns (uint256 amountIn); struct ExactOutputParams { bytes path; address recipient; uint256 deadline; uint256 amountOut; uint256 amountInMaximum; } /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed) /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata /// @return amountIn The amount of the input token function exactOutput( ExactOutputParams calldata params ) external payable returns (uint256 amountIn); // Taken from https://soliditydeveloper.com/uniswap3 // Manually added to the interface function refundETH() external payable; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; /// @title Callback for IUniswapV3PoolActions#swap /// @notice Any contract that calls IUniswapV3PoolActions#swap must implement this interface interface IUniswapV3SwapCallback { /// @notice Called to `msg.sender` after executing a swap via IUniswapV3Pool#swap. /// @dev In the implementation you must pay the pool tokens owed for the swap. /// The caller of this method must be checked to be a UniswapV3Pool deployed by the canonical UniswapV3Factory. /// amount0Delta and amount1Delta can both be 0 if no tokens were swapped. /// @param amount0Delta The amount of token0 that was sent (negative) or must be received (positive) by the pool by /// the end of the swap. If positive, the callback must send that amount of token0 to the pool. /// @param amount1Delta The amount of token1 that was sent (negative) or must be received (positive) by the pool by /// the end of the swap. If positive, the callback must send that amount of token1 to the pool. /// @param data Any data passed through by the caller via the IUniswapV3PoolActions#swap call function uniswapV3SwapCallback( int256 amount0Delta, int256 amount1Delta, bytes calldata data ) external; }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; interface IYearnVault { function pricePerShare() external view returns (uint256 price); function deposit(uint256 _amount) external returns (uint256); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {ReentrancyGuard} from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import {IBaseLeverage} from "../interfaces/IBaseLeverage.sol"; import {IFlashLoanReceiver} from "../interfaces/Aave/V3/IFlashLoanReceiver.sol"; import {IFlashLoanRecipient} from "../interfaces/Balancer/IFlashLoanRecipient.sol"; import {IPool} from "../interfaces/Aave/V3/IPool.sol"; import {IBalancerVault} from "../interfaces/Balancer/IBalancerVault.sol"; import {BalancerswapAdapter} from "../swappers/BalancerswapAdapter.sol"; import {UniswapAdapter} from "../swappers/UniswapAdapter.sol"; import {CurveswapAdapter} from "../swappers/CurveswapAdapter.sol"; abstract contract BaseLeverage is IFlashLoanReceiver, IFlashLoanRecipient, ReentrancyGuard { using SafeERC20 for IERC20; error LV_INVALID_CONFIGURATION(); error LV_AMOUNT_NOT_GT_0(); error LV_SUPPLY_NOT_ALLOWED(); error LV_SUPPLY_FAILED(); address private constant AAVE_LENDING_POOL_ADDRESS = 0x87870Bca3F3fD6335C3F4ce8392D69350B4fA4E2; address private constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; uint256 private constant PERCENTAGE_FACTOR = 100_00; //1 == not inExec //2 == inExec; //setting default to 1 to save some gas. uint256 private _balancerFlashLoanLock = 1; /** * This function is called after your contract has received the flash loaned amount * overriding executeOperation() in IFlashLoanReceiver */ function executeOperation( address[] calldata assets, uint256[] calldata amounts, uint256[] calldata premiums, address initiator, bytes calldata params ) external override returns (bool) { if (initiator != address(this)) revert LV_INVALID_CONFIGURATION(); if (msg.sender != AAVE_LENDING_POOL_ADDRESS) revert LV_INVALID_CONFIGURATION(); if (assets.length != amounts.length) revert LV_INVALID_CONFIGURATION(); if (assets.length != premiums.length) revert LV_INVALID_CONFIGURATION(); if (amounts[0] == 0) revert LV_INVALID_CONFIGURATION(); if (assets[0] == address(0)) revert LV_INVALID_CONFIGURATION(); _executeOperation(assets[0], amounts[0], premiums[0], params); // approve the Aave LendingPool contract allowance to *pull* the owed amount IERC20(assets[0]).safeApprove(AAVE_LENDING_POOL_ADDRESS, 0); IERC20(assets[0]).safeApprove(AAVE_LENDING_POOL_ADDRESS, amounts[0] + premiums[0]); return true; } /** * This function is called after your contract has received the flash loaned amount * overriding receiveFlashLoan() in IFlashLoanRecipient */ function receiveFlashLoan( IERC20[] memory tokens, uint256[] memory amounts, uint256[] memory feeAmounts, bytes memory userData ) external override { if (msg.sender != BALANCER_VAULT) revert LV_INVALID_CONFIGURATION(); if (_balancerFlashLoanLock != 2) revert LV_INVALID_CONFIGURATION(); if (tokens.length != amounts.length) revert LV_INVALID_CONFIGURATION(); if (tokens.length != feeAmounts.length) revert LV_INVALID_CONFIGURATION(); if (amounts[0] == 0) revert LV_INVALID_CONFIGURATION(); if (address(tokens[0]) == address(0)) revert LV_INVALID_CONFIGURATION(); _balancerFlashLoanLock = 1; _executeOperation(address(tokens[0]), amounts[0], feeAmounts[0], userData); // send tokens to Balancer vault contract IERC20(tokens[0]).safeTransfer(msg.sender, amounts[0] + feeAmounts[0]); } function _executeOperation( address asset, uint256 borrowAmount, uint256 fee, bytes memory params ) internal { // parse params IBaseLeverage.FlashLoanParams memory opsParams = abi.decode( params, (IBaseLeverage.FlashLoanParams) ); if (opsParams.minCollateralAmount == 0) revert LV_INVALID_CONFIGURATION(); if (opsParams.user == address(0)) revert LV_INVALID_CONFIGURATION(); if (opsParams.isEnterPosition) { _enterPositionWithFlashloan(asset, borrowAmount, fee, opsParams); } else { _withdrawWithFlashloan(asset, borrowAmount, opsParams); } } /** * @param _principal - The amount of collateral * @param _leverage - Extra leverage value and must be greater than 0, ex. 300% = 300_00 * _principal + _principal * _leverage should be used as collateral * @param _borrowAsset - The borrowing asset address when leverage works * @param _collateralAsset - The collateral asset address when leverage works * @param _silo - The silo address * @param _flashLoanType - 0 is Aave, 1 is Balancer * @param _swapInfo - The uniswap/balancer swap paths between borrowAsset and collateral */ function enterPositionWithFlashloan( uint256 _principal, uint256 _leverage, address _borrowAsset, address _collateralAsset, address _silo, IBaseLeverage.FlashLoanType _flashLoanType, IBaseLeverage.SwapInfo calldata _swapInfo ) external nonReentrant { if (_principal == 0) revert LV_AMOUNT_NOT_GT_0(); if (_leverage == 0) revert LV_AMOUNT_NOT_GT_0(); if (_leverage >= 900_00) revert LV_INVALID_CONFIGURATION(); if (_borrowAsset == address(0)) revert LV_INVALID_CONFIGURATION(); if (_silo == address(0)) revert LV_INVALID_CONFIGURATION(); if (IERC20(_collateralAsset).balanceOf(msg.sender) < _principal) revert LV_SUPPLY_NOT_ALLOWED(); IERC20(_collateralAsset).safeTransferFrom(msg.sender, address(this), _principal); _leverageWithFlashloan( IBaseLeverage.LeverageParams( msg.sender, _principal, _leverage, _borrowAsset, _collateralAsset, _silo, _flashLoanType, _swapInfo ) ); } /** * @param _repayAmount - The amount of repay * @param _requiredAmount - The amount of collateral * @param _borrowAsset - The borrowing asset address when leverage works * @param _collateralAsset - The collateral asset address when leverage works * @param _silo - The silo address * @param _flashLoanType - 0 is Aave, 1 is Balancer * @param _swapInfo - The uniswap/balancer/curve swap infos between borrowAsset and collateral */ function withdrawWithFlashloan( uint256 _repayAmount, uint256 _requiredAmount, address _borrowAsset, address _collateralAsset, address _silo, IBaseLeverage.FlashLoanType _flashLoanType, IBaseLeverage.SwapInfo calldata _swapInfo ) external nonReentrant { if (_repayAmount == 0) revert LV_AMOUNT_NOT_GT_0(); if (_requiredAmount == 0) revert LV_AMOUNT_NOT_GT_0(); if (_borrowAsset == address(0)) revert LV_INVALID_CONFIGURATION(); if (_collateralAsset == address(0)) revert LV_INVALID_CONFIGURATION(); if (_silo == address(0)) revert LV_INVALID_CONFIGURATION(); uint256[] memory amounts = new uint256[](1); amounts[0] = _repayAmount; bytes memory params = abi.encode( false /*leavePosition*/, _requiredAmount, msg.sender, _collateralAsset, _silo, _swapInfo ); if (_flashLoanType == IBaseLeverage.FlashLoanType.AAVE) { // 0 means revert the transaction if not validated uint256[] memory modes = new uint256[](1); modes[0] = 0; address[] memory assets = new address[](1); assets[0] = _borrowAsset; IPool(AAVE_LENDING_POOL_ADDRESS).flashLoan( address(this), assets, amounts, modes, address(this), params, 0 ); } else { if (_balancerFlashLoanLock != 1) revert LV_INVALID_CONFIGURATION(); IERC20[] memory assets = new IERC20[](1); assets[0] = IERC20(_borrowAsset); _balancerFlashLoanLock = 2; IBalancerVault(BALANCER_VAULT).flashLoan(address(this), assets, amounts, params); } // remained borrow asset -> collateral _swapTo( _borrowAsset, _collateralAsset, IERC20(_borrowAsset).balanceOf(address(this)), _swapInfo.paths, _swapInfo.pathLength, false ); uint256 collateralAmount = IERC20(_collateralAsset).balanceOf(address(this)); if (collateralAmount > _requiredAmount) { _supply(_collateralAsset, _silo, collateralAmount - _requiredAmount, msg.sender); collateralAmount = _requiredAmount; } // finally deliver the collateral to user IERC20(_collateralAsset).safeTransfer(msg.sender, collateralAmount); } function _enterPositionWithFlashloan( address _borrowAsset, uint256 _borrowedAmount, uint256 _fee, IBaseLeverage.FlashLoanParams memory _params ) internal { //swap borrow asset to collateral _swapTo( _borrowAsset, _params.collateralAsset, _borrowedAmount, _params.swapInfo.paths, _params.swapInfo.pathLength, true ); uint256 collateralAmount = IERC20(_params.collateralAsset).balanceOf(address(this)); if (collateralAmount < _params.minCollateralAmount) revert LV_SUPPLY_FAILED(); //deposit collateral _supply(_params.collateralAsset, _params.silo, collateralAmount, _params.user); //borrow _borrow(_borrowAsset, _params.silo, _borrowedAmount + _fee, _params.user); } function _leverageWithFlashloan(IBaseLeverage.LeverageParams memory _params) internal { uint256 minCollateralAmount = _params.principal * (PERCENTAGE_FACTOR + _params.leverage) / PERCENTAGE_FACTOR; bytes memory params = abi.encode( true /*enterPosition*/, minCollateralAmount, _params.user, _params.collateralAsset, _params.silo, _params.swapInfo ); uint256 borrowAssetDecimals = IERC20Metadata(_params.borrowAsset).decimals(); uint256[] memory amounts = new uint256[](1); amounts[0] = _params.swapInfo.paths[0].inAmount; if (_params.flashLoanType == IBaseLeverage.FlashLoanType.AAVE) { // 0 means revert the transaction if not validated uint256[] memory modes = new uint256[](1); address[] memory assets = new address[](1); assets[0] = _params.borrowAsset; IPool(AAVE_LENDING_POOL_ADDRESS).flashLoan( address(this), assets, amounts, modes, address(this), params, 0 ); } else { if (_balancerFlashLoanLock != 1) revert LV_INVALID_CONFIGURATION(); IERC20[] memory assets = new IERC20[](1); assets[0] = IERC20(_params.borrowAsset); _balancerFlashLoanLock = 2; IBalancerVault(BALANCER_VAULT).flashLoan(address(this), assets, amounts, params); _balancerFlashLoanLock = 1; } } function _swapTo( address _borrowingAsset, address _collateralAsset, uint256 _amount, IBaseLeverage.MultipSwapPath[3] memory _paths, uint256 _pathLength, bool _checkOutAmount ) internal returns (uint256) { if (_pathLength == 0) revert LV_INVALID_CONFIGURATION(); if (_paths[0].swapFrom != _borrowingAsset) revert LV_INVALID_CONFIGURATION(); if (_paths[_pathLength - 1].swapTo != _collateralAsset) revert LV_INVALID_CONFIGURATION(); uint256 amount = _amount; if (amount == 0) return 0; for (uint256 i; i < _pathLength; ++i) { if (_paths[i].swapType == IBaseLeverage.SwapType.NONE) continue; amount = _processSwap(amount, _paths[i], false, _checkOutAmount); } return amount; } function _swapFrom( address _borrowingAsset, address _collateralAsset, IBaseLeverage.MultipSwapPath[3] memory _paths, uint256 _pathLength ) internal returns (uint256) { if (_pathLength == 0) revert LV_INVALID_CONFIGURATION(); if (_paths[0].swapFrom != _collateralAsset) revert LV_INVALID_CONFIGURATION(); if (_paths[_pathLength - 1].swapTo != _borrowingAsset) revert LV_INVALID_CONFIGURATION(); uint256 amount = IERC20(_collateralAsset).balanceOf(address(this)); if (amount == 0) return 0; for (uint256 i; i < _pathLength; ++i) { if (_paths[i].swapType == IBaseLeverage.SwapType.NONE) continue; amount = _processSwap(amount, _paths[i], true, true); } return amount; } function _swapByPath( uint256 _fromAmount, IBaseLeverage.MultipSwapPath memory _path, bool _checkOutAmount ) internal returns (uint256) { uint256 poolCount = _path.poolCount; uint256 outAmount = _checkOutAmount ? _path.outAmount : 0; if (poolCount == 0) revert LV_INVALID_CONFIGURATION(); if (_path.swapType == IBaseLeverage.SwapType.BALANCER) { // Balancer Swap BalancerswapAdapter.Path memory path; path.tokens = new address[](poolCount + 1); path.poolIds = new bytes32[](poolCount); for (uint256 i; i < poolCount; ++i) { path.tokens[i] = _path.routes[i * 2]; path.poolIds[i] = bytes32(_path.routeParams[i][0]); } path.tokens[poolCount] = _path.routes[poolCount * 2]; return BalancerswapAdapter.swapExactTokensForTokens( _path.swapFrom, _path.swapTo, _fromAmount, path, outAmount ); } if (_path.swapType == IBaseLeverage.SwapType.UNISWAP) { // UniSwap UniswapAdapter.Path memory path; path.tokens = new address[](poolCount + 1); path.fees = new uint256[](poolCount); for (uint256 i; i < poolCount; ++i) { path.tokens[i] = _path.routes[i * 2]; path.fees[i] = _path.routeParams[i][0]; } path.tokens[poolCount] = _path.routes[poolCount * 2]; return UniswapAdapter.swapExactTokensForTokens( address(0), _path.swapFrom, _path.swapTo, _fromAmount, path, outAmount ); } // Curve Swap return CurveswapAdapter.swapExactTokensForTokens( address(0), _path.swapFrom, _path.swapTo, _fromAmount, CurveswapAdapter.Path(_path.routes, _path.routeParams), outAmount ); } function _withdrawWithFlashloan( address _borrowAsset, uint256 _borrowedAmount, IBaseLeverage.FlashLoanParams memory _params ) internal virtual; function _supply( address _collateralAsset, address _silo, uint256 _amount, address _user ) internal virtual; function _remove( uint256 _amount, address _silo, uint256 _slippage, address _user ) internal virtual; function _borrow( address _borrowAsset, address _silo, uint256 _amount, address borrower ) internal virtual; function _repay( address _borrowAsset, address _silo, uint256 _amount, address borrower ) internal virtual; function _processSwap( uint256 _amount, IBaseLeverage.MultipSwapPath memory _path, bool _isFrom, bool _checkOutAmount ) internal virtual returns (uint256); }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; import {BaseLeverage, IBaseLeverage, IERC20, SafeERC20} from "./BaseLeverage.sol"; import {ISturdyPair} from "../interfaces/ISturdyPair.sol"; contract SturdyLeverage is BaseLeverage { using SafeERC20 for IERC20; error LV_REPAY_FAILED(); function _withdrawWithFlashloan( address _borrowAsset, uint256 _borrowedAmount, IBaseLeverage.FlashLoanParams memory _params ) internal override { // repay _repay(_borrowAsset, _params.silo, _borrowedAmount, _params.user); // withdraw collateral ISturdyPair pair = ISturdyPair(_params.silo); if (_params.collateralAsset != pair.collateralContract()) revert LV_INVALID_CONFIGURATION(); ( uint256 LTV_PRECISION,,,, uint256 EXCHANGE_PRECISION,,,) = ISturdyPair(_params.silo).getConstants(); ISturdyPair(_params.silo).addInterest(false); (,, uint256 exchangeRate) = ISturdyPair(_params.silo).updateExchangeRate(); uint256 borrowShares = pair.userBorrowShares(_params.user); uint256 borrowAmount = ISturdyPair(_params.silo).toBorrowAmount(borrowShares, true, false); uint256 collateralAmount = pair.userCollateralBalance(_params.user); uint256 withdrawalAmount = collateralAmount - (borrowAmount * exchangeRate * LTV_PRECISION / EXCHANGE_PRECISION / pair.maxLTV()); if (withdrawalAmount < _params.minCollateralAmount) revert LV_SUPPLY_NOT_ALLOWED(); _remove(withdrawalAmount, _params.silo, 0, _params.user); // collateral -> borrow asset _swapFrom(_borrowAsset, _params.collateralAsset, _params.swapInfo.reversePaths, _params.swapInfo.pathLength); } function _supply( address _collateralAsset, address _silo, uint256 _amount, address _user ) internal override { IERC20(_collateralAsset).safeApprove(_silo, 0); IERC20(_collateralAsset).safeApprove(_silo, _amount); ISturdyPair(_silo).addCollateral(_amount, _user); } function _remove( uint256 _amount, address _silo, uint256 _slippage, address _user ) internal override { ISturdyPair(_silo).removeCollateralFrom(_amount, address(this), _user); } function _borrow( address _borrowAsset, address _silo, uint256 _amount, address _borrower ) internal override { ISturdyPair(_silo).borrowAssetOnBehalfOf(_amount, _borrower); } function _repay( address _borrowAsset, address _silo, uint256 _amount, address _borrower ) internal override { ISturdyPair(_silo).addInterest(false); uint256 borrowShares = ISturdyPair(_silo).toBorrowShares(_amount, false, false); IERC20(_borrowAsset).safeApprove(_silo, 0); IERC20(_borrowAsset).safeApprove(_silo, _amount); uint256 paybackAmount = ISturdyPair(_silo).repayAsset(borrowShares, _borrower); if (paybackAmount == 0) revert LV_REPAY_FAILED(); } function _processSwap( uint256 _amount, IBaseLeverage.MultipSwapPath memory _path, bool _isFrom, bool _checkOutAmount ) internal override returns (uint256) { return _swapByPath(_amount, _path, _checkOutAmount); } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; library DataTypesV3 { struct ReserveData { //stores the reserve configuration ReserveConfigurationMap configuration; //the liquidity index. Expressed in ray uint128 liquidityIndex; //the current supply rate. Expressed in ray uint128 currentLiquidityRate; //variable borrow index. Expressed in ray uint128 variableBorrowIndex; //the current variable borrow rate. Expressed in ray uint128 currentVariableBorrowRate; //the current stable borrow rate. Expressed in ray uint128 currentStableBorrowRate; //timestamp of last update uint40 lastUpdateTimestamp; //the id of the reserve. Represents the position in the list of the active reserves uint16 id; //aToken address address aTokenAddress; //stableDebtToken address address stableDebtTokenAddress; //variableDebtToken address address variableDebtTokenAddress; //address of the interest rate strategy address interestRateStrategyAddress; //the current treasury balance, scaled uint128 accruedToTreasury; //the outstanding unbacked aTokens minted through the bridging feature uint128 unbacked; //the outstanding debt borrowed against this asset in isolation mode uint128 isolationModeTotalDebt; } struct ReserveConfigurationMap { //bit 0-15: LTV //bit 16-31: Liq. threshold //bit 32-47: Liq. bonus //bit 48-55: Decimals //bit 56: reserve is active //bit 57: reserve is frozen //bit 58: borrowing is enabled //bit 59: stable rate borrowing enabled //bit 60: asset is paused //bit 61: borrowing in isolation mode is enabled //bit 62-63: reserved //bit 64-79: reserve factor //bit 80-115 borrow cap in whole tokens, borrowCap == 0 => no cap //bit 116-151 supply cap in whole tokens, supplyCap == 0 => no cap //bit 152-167 liquidation protocol fee //bit 168-175 eMode category //bit 176-211 unbacked mint cap in whole tokens, unbackedMintCap == 0 => minting disabled //bit 212-251 debt ceiling for isolation mode with (ReserveConfiguration::DEBT_CEILING_DECIMALS) decimals //bit 252-255 unused uint256 data; } struct UserConfigurationMap { /** * @dev Bitmap of the users collaterals and borrows. It is divided in pairs of bits, one pair per asset. * The first bit indicates if an asset is used as collateral by the user, the second whether an * asset is borrowed by the user. */ uint256 data; } struct EModeCategory { // each eMode category has a custom ltv and liquidation threshold uint16 ltv; uint16 liquidationThreshold; uint16 liquidationBonus; // each eMode category may or may not have a custom oracle to override the individual assets price oracles address priceSource; string label; } enum InterestRateMode { NONE, STABLE, VARIABLE } struct ReserveCache { uint256 currScaledVariableDebt; uint256 nextScaledVariableDebt; uint256 currPrincipalStableDebt; uint256 currAvgStableBorrowRate; uint256 currTotalStableDebt; uint256 nextAvgStableBorrowRate; uint256 nextTotalStableDebt; uint256 currLiquidityIndex; uint256 nextLiquidityIndex; uint256 currVariableBorrowIndex; uint256 nextVariableBorrowIndex; uint256 currLiquidityRate; uint256 currVariableBorrowRate; uint256 reserveFactor; ReserveConfigurationMap reserveConfiguration; address aTokenAddress; address stableDebtTokenAddress; address variableDebtTokenAddress; uint40 reserveLastUpdateTimestamp; uint40 stableDebtLastUpdateTimestamp; } struct ExecuteLiquidationCallParams { uint256 reservesCount; uint256 debtToCover; address collateralAsset; address debtAsset; address user; bool receiveAToken; address priceOracle; uint8 userEModeCategory; address priceOracleSentinel; } struct ExecuteSupplyParams { address asset; uint256 amount; address onBehalfOf; uint16 referralCode; } struct ExecuteBorrowParams { address asset; address user; address onBehalfOf; uint256 amount; InterestRateMode interestRateMode; uint16 referralCode; bool releaseUnderlying; uint256 maxStableRateBorrowSizePercent; uint256 reservesCount; address oracle; uint8 userEModeCategory; address priceOracleSentinel; } struct ExecuteRepayParams { address asset; uint256 amount; InterestRateMode interestRateMode; address onBehalfOf; bool useATokens; } struct ExecuteWithdrawParams { address asset; uint256 amount; address to; uint256 reservesCount; address oracle; uint8 userEModeCategory; } struct ExecuteSetUserEModeParams { uint256 reservesCount; address oracle; uint8 categoryId; } struct FinalizeTransferParams { address asset; address from; address to; uint256 amount; uint256 balanceFromBefore; uint256 balanceToBefore; uint256 reservesCount; address oracle; uint8 fromEModeCategory; } struct FlashloanParams { address receiverAddress; address[] assets; uint256[] amounts; uint256[] interestRateModes; address onBehalfOf; bytes params; uint16 referralCode; uint256 flashLoanPremiumToProtocol; uint256 flashLoanPremiumTotal; uint256 maxStableRateBorrowSizePercent; uint256 reservesCount; address addressesProvider; uint8 userEModeCategory; bool isAuthorizedFlashBorrower; } struct FlashloanSimpleParams { address receiverAddress; address asset; uint256 amount; bytes params; uint16 referralCode; uint256 flashLoanPremiumToProtocol; uint256 flashLoanPremiumTotal; } struct FlashLoanRepaymentParams { uint256 amount; uint256 totalPremium; uint256 flashLoanPremiumToProtocol; address asset; address receiverAddress; uint16 referralCode; } struct CalculateUserAccountDataParams { UserConfigurationMap userConfig; uint256 reservesCount; address user; address oracle; uint8 userEModeCategory; } struct ValidateBorrowParams { ReserveCache reserveCache; UserConfigurationMap userConfig; address asset; address userAddress; uint256 amount; InterestRateMode interestRateMode; uint256 maxStableLoanPercent; uint256 reservesCount; address oracle; uint8 userEModeCategory; address priceOracleSentinel; bool isolationModeActive; address isolationModeCollateralAddress; uint256 isolationModeDebtCeiling; } struct ValidateLiquidationCallParams { ReserveCache debtReserveCache; uint256 totalDebt; uint256 healthFactor; address priceOracleSentinel; } struct CalculateInterestRatesParams { uint256 unbacked; uint256 liquidityAdded; uint256 liquidityTaken; uint256 totalStableDebt; uint256 totalVariableDebt; uint256 averageStableBorrowRate; uint256 reserveFactor; address reserve; address aToken; } struct InitReserveParams { address asset; address aTokenAddress; address stableDebtAddress; address variableDebtAddress; address interestRateStrategyAddress; uint16 reservesCount; uint16 maxNumberReserves; } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; import {DataTypesV3} from "./DataTypesV3.sol"; /** * @title ReserveConfiguration library * @author Aave * @notice Implements the bitmap logic to handle the reserve configuration */ library ReserveConfiguration { uint256 internal constant LIQUIDATION_THRESHOLD_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFF; // prettier-ignore uint256 internal constant DECIMALS_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFF; // prettier-ignore uint256 internal constant ACTIVE_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFF; // prettier-ignore uint256 internal constant FROZEN_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFDFFFFFFFFFFFFFF; // prettier-ignore uint256 internal constant BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFBFFFFFFFFFFFFFF; // prettier-ignore uint256 internal constant STABLE_BORROWING_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFFFFFFFFF; // prettier-ignore uint256 internal constant PAUSED_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFF; // prettier-ignore uint256 internal constant RESERVE_FACTOR_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF0000FFFFFFFFFFFFFFFF; // prettier-ignore uint256 internal constant SUPPLY_CAP_MASK = 0xFFFFFFFFFFFFFFFFFFFFFFFFFF000000000FFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore uint256 internal constant EMODE_CATEGORY_MASK = 0xFFFFFFFFFFFFFFFFFFFF00FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF; // prettier-ignore /// @dev For the LTV, the start bit is 0 (up to 15), hence no bitshifting is needed uint256 internal constant LIQUIDATION_THRESHOLD_START_BIT_POSITION = 16; uint256 internal constant RESERVE_DECIMALS_START_BIT_POSITION = 48; uint256 internal constant RESERVE_FACTOR_START_BIT_POSITION = 64; uint256 internal constant SUPPLY_CAP_START_BIT_POSITION = 116; uint256 internal constant EMODE_CATEGORY_START_BIT_POSITION = 168; /** * @notice Gets the configuration flags of the reserve * @param self The reserve configuration * @return The state flag representing active * @return The state flag representing frozen * @return The state flag representing borrowing enabled * @return The state flag representing stableRateBorrowing enabled * @return The state flag representing paused */ function getFlags( DataTypesV3.ReserveConfigurationMap memory self ) internal pure returns (bool, bool, bool, bool, bool) { uint256 dataLocal = self.data; return ( (dataLocal & ~ACTIVE_MASK) != 0, (dataLocal & ~FROZEN_MASK) != 0, (dataLocal & ~BORROWING_MASK) != 0, (dataLocal & ~STABLE_BORROWING_MASK) != 0, (dataLocal & ~PAUSED_MASK) != 0 ); } /** * @notice Gets the supply cap of the reserve * @param self The reserve configuration * @return The supply cap */ function getSupplyCap( DataTypesV3.ReserveConfigurationMap memory self ) internal pure returns (uint256) { return (self.data & ~SUPPLY_CAP_MASK) >> SUPPLY_CAP_START_BIT_POSITION; } /** * @notice Gets the decimals of the underlying asset of the reserve * @param self The reserve configuration * @return The decimals of the asset */ function getDecimals( DataTypesV3.ReserveConfigurationMap memory self ) internal pure returns (uint256) { return (self.data & ~DECIMALS_MASK) >> RESERVE_DECIMALS_START_BIT_POSITION; } /** * @notice Gets the reserve factor of the reserve * @param self The reserve configuration * @return The reserve factor */ function getReserveFactor( DataTypesV3.ReserveConfigurationMap memory self ) internal pure returns (uint256) { return (self.data & ~RESERVE_FACTOR_MASK) >> RESERVE_FACTOR_START_BIT_POSITION; } /** * @notice Gets the liquidation threshold of the reserve * @param self The reserve configuration * @return The liquidation threshold */ function getLiquidationThreshold( DataTypesV3.ReserveConfigurationMap memory self ) internal pure returns (uint256) { return (self.data & ~LIQUIDATION_THRESHOLD_MASK) >> LIQUIDATION_THRESHOLD_START_BIT_POSITION; } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol"; import { SafeERC20 as OZSafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; // solhint-disable avoid-low-level-calls // solhint-disable max-line-length /// @title SafeERC20 provides helper functions for safe transfers as well as safe metadata access /// @author Library originally written by @Boring_Crypto github.com/boring_crypto, modified by Drake Evans (Frax Finance) github.com/drakeevans /// @dev original: https://github.com/boringcrypto/BoringSolidity/blob/fed25c5d43cb7ce20764cd0b838e21a02ea162e9/contracts/libraries/BoringERC20.sol library SafeERC20 { bytes4 private constant SIG_SYMBOL = 0x95d89b41; // symbol() bytes4 private constant SIG_NAME = 0x06fdde03; // name() bytes4 private constant SIG_DECIMALS = 0x313ce567; // decimals() function returnDataToString(bytes memory data) internal pure returns (string memory) { if (data.length >= 64) { return abi.decode(data, (string)); } else if (data.length == 32) { uint8 i = 0; while (i < 32 && data[i] != 0) { i++; } bytes memory bytesArray = new bytes(i); for (i = 0; i < 32 && data[i] != 0; i++) { bytesArray[i] = data[i]; } return string(bytesArray); } else { return "???"; } } /// @notice Provides a safe ERC20.symbol version which returns '???' as fallback string. /// @param token The address of the ERC-20 token contract. /// @return (string) Token symbol. function safeSymbol(IERC20 token) internal view returns (string memory) { (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_SYMBOL)); return success ? returnDataToString(data) : "???"; } /// @notice Provides a safe ERC20.name version which returns '???' as fallback string. /// @param token The address of the ERC-20 token contract. /// @return (string) Token name. function safeName(IERC20 token) internal view returns (string memory) { (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_NAME)); return success ? returnDataToString(data) : "???"; } /// @notice Provides a safe ERC20.decimals version which returns '18' as fallback value. /// @param token The address of the ERC-20 token contract. /// @return (uint8) Token decimals. function safeDecimals(IERC20 token) internal view returns (uint8) { (bool success, bytes memory data) = address(token).staticcall(abi.encodeWithSelector(SIG_DECIMALS)); return success && data.length == 32 ? abi.decode(data, (uint8)) : 18; } function safeTransfer(IERC20 token, address to, uint256 value) internal { OZSafeERC20.safeTransfer(token, to, value); } function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { OZSafeERC20.safeTransferFrom(token, from, to, value); } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; struct VaultAccount { uint128 amount; // Total amount, analogous to market cap uint128 shares; // Total shares, analogous to shares outstanding } /// @title VaultAccount Library /// @author Drake Evans (Frax Finance) github.com/drakeevans, modified from work by @Boring_Crypto github.com/boring_crypto /// @notice Provides a library for use with the VaultAccount struct, provides convenient math implementations /// @dev Uses uint128 to save on storage library VaultAccountingLibrary { /// @notice Calculates the shares value in relationship to `amount` and `total` /// @dev Given an amount, return the appropriate number of shares function toShares(VaultAccount memory total, uint256 amount, bool roundUp) internal pure returns (uint256 shares) { if (total.amount == 0) { shares = amount; } else { shares = (amount * total.shares) / total.amount; if (roundUp && (shares * total.amount) / total.shares < amount) { shares = shares + 1; } } } /// @notice Calculates the amount value in relationship to `shares` and `total` /// @dev Given a number of shares, returns the appropriate amount function toAmount(VaultAccount memory total, uint256 shares, bool roundUp) internal pure returns (uint256 amount) { if (total.shares == 0) { amount = shares; } else { amount = (shares * total.amount) / total.shares; if (roundUp && (amount * total.shares) / total.amount < shares) { amount = amount + 1; } } } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; // ======================= LinearInterestRate ========================= import { IRateCalculator } from "./interfaces/IRateCalculator.sol"; /// @title A formula for calculating interest rates linearly as a function of utilization /// @author Drake Evans github.com/drakeevans contract LinearInterestRate is IRateCalculator { uint256 private constant MIN_INT = 0; // 0.00% annual rate uint256 private constant MAX_INT = 146_248_508_681; // 10,000% annual rate uint256 private constant MAX_VERTEX_UTIL = 1e5; // 100% uint256 private constant UTIL_PREC = 1e5; /// @notice The ```name``` function returns the name of the rate contract /// @return memory name of contract function name() external pure returns (string memory) { return "Linear Interest Rate"; } /// @notice The ```getConstants``` function returns abi encoded constants /// @return _calldata abi.encode(uint256 MIN_INT, uint256 MAX_INT, uint256 MAX_VERTEX_UTIL, uint256 UTIL_PREC) function getConstants() external pure returns (bytes memory _calldata) { return abi.encode(MIN_INT, MAX_INT, MAX_VERTEX_UTIL, UTIL_PREC); } /// @notice The ```requireValidInitData``` function reverts if initialization data fails to be validated /// @param _initData abi.encode(uint256 _minInterest, uint256 _vertexInterest, uint256 _maxInterest, uint256 _vertexUtilization) function requireValidInitData(bytes calldata _initData) public pure { (uint256 _minInterest, uint256 _vertexInterest, uint256 _maxInterest, uint256 _vertexUtilization) = abi.decode( _initData, (uint256, uint256, uint256, uint256) ); require( _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT, "LinearInterestRate: _minInterest < MAX_INT && _minInterest <= _vertexInterest && _minInterest >= MIN_INT" ); require( _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT, "LinearInterestRate: _maxInterest <= MAX_INT && _vertexInterest <= _maxInterest && _maxInterest > MIN_INT" ); require( _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0, "LinearInterestRate: _vertexUtilization < MAX_VERTEX_UTIL && _vertexUtilization > 0" ); } /// @notice Calculates interest rates using two linear functions f(utilization) /// @dev We use calldata to remain un-opinionated about future implementations /// @param _data abi.encode(uint64 _currentRatePerSec, uint256 _deltaTime, uint256 _utilization, uint256 _deltaBlocks) /// @param _initData abi.encode(uint256 _minInterest, uint256 _vertexInterest, uint256 _maxInterest, uint256 _vertexUtilization) /// @return _newRatePerSec The new interest rate per second, 1e18 precision function getNewRate(bytes calldata _data, bytes calldata _initData) external pure returns (uint64 _newRatePerSec) { requireValidInitData(_initData); (, , uint256 _utilization, ) = abi.decode(_data, (uint64, uint256, uint256, uint256)); (uint256 _minInterest, uint256 _vertexInterest, uint256 _maxInterest, uint256 _vertexUtilization) = abi.decode( _initData, (uint256, uint256, uint256, uint256) ); if (_utilization < _vertexUtilization) { uint256 _slope = ((_vertexInterest - _minInterest) * UTIL_PREC) / _vertexUtilization; _newRatePerSec = uint64(_minInterest + ((_utilization * _slope) / UTIL_PREC)); } else if (_utilization > _vertexUtilization) { uint256 _slope = (((_maxInterest - _vertexInterest) * UTIL_PREC) / (UTIL_PREC - _vertexUtilization)); _newRatePerSec = uint64(_vertexInterest + (((_utilization - _vertexUtilization) * _slope) / UTIL_PREC)); } else { _newRatePerSec = uint64(_vertexInterest); } } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; /// @title CrvUSDCRVOracle /// @author Jason (Sturdy) https://github.com/iris112 /// @notice An oracle for CrvUSD/CRV interface ILLAMMA { function price_oracle() external view returns (uint256); } contract CrvUSDCRVOracle { address private constant ETH_CRVUSD_AMM_CONTROLLER = 0x1681195C176239ac5E72d9aeBaCf5b2492E0C4ee; address private constant ETH_USD_CHAINLINK = 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419; address private constant CRV_USD_CHAINLINK = 0xCd627aA160A6fA45Eb793D19Ef54f5062F20f33f; address private constant CRVUSD_USD_CHAINLINK = 0xEEf0C605546958c1f899b6fB336C20671f9cD49F; uint8 public constant DECIMALS = 18; uint256 public immutable MAX_ORACLE_DELAY; uint256 public immutable PRICE_MIN; string public name; error CHAINLINK_BAD_PRICE(); constructor( uint256 _maxOracleDelay, uint256 _priceMin, string memory _name ) { name = _name; MAX_ORACLE_DELAY = _maxOracleDelay; PRICE_MIN = _priceMin; } /// @notice The ```getPrices``` function is intended to return price of ERC4626 token based on the base asset /// @return _isBadData is always false, just sync to other oracle interfaces /// @return _priceLow is the lower of the prices /// @return _priceHigh is the higher of the prices function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh) { uint256 rate = ILLAMMA(ETH_CRVUSD_AMM_CONTROLLER).price_oracle(); // ETH/crvUSD (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(ETH_USD_CHAINLINK) .latestRoundData(); // ETH/USD // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { revert CHAINLINK_BAD_PRICE(); } rate = uint256(_answer) * 1e18 / rate; // crvUSD/USD (, _answer, , _updatedAt, ) = AggregatorV3Interface(CRVUSD_USD_CHAINLINK) .latestRoundData(); // crvUSD/USD // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { revert CHAINLINK_BAD_PRICE(); } rate = Math.min(rate, uint256(_answer)); (, _answer, , _updatedAt, ) = AggregatorV3Interface(CRV_USD_CHAINLINK) .latestRoundData(); // CRV/USD // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { revert CHAINLINK_BAD_PRICE(); } rate = rate * 1e18 / uint256(_answer); // crvUSD/CRV _priceHigh = rate > PRICE_MIN ? rate : PRICE_MIN; _priceLow = _priceHigh; } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import { ICurvePool } from "../interfaces/Curve/ICurvePool.sol"; import { IYearnVault } from "../interfaces/Yearn/IYearnVault.sol"; /// @title CrvUSDYv3CRVCrvUSDOracle /// @author Jason (Sturdy) https://github.com/iris112 /// @notice An oracle for CrvUSD/Yv3CRVCrvUSD interface ILLAMMA { function price_oracle() external view returns (uint256); } contract CrvUSDYv3CRVCrvUSDOracle { address private constant ETH_CRVUSD_AMM_CONTROLLER = 0x1681195C176239ac5E72d9aeBaCf5b2492E0C4ee; address private constant ETH_USD_CHAINLINK = 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419; address private constant CRVUSD_USD_CHAINLINK = 0xEEf0C605546958c1f899b6fB336C20671f9cD49F; uint8 public constant DECIMALS = 18; address public immutable THREECRV_ETH_CHAINLINK; address public immutable CURVE_CRVUSD_3CRV_POOL; address public immutable YEARN_CRVUSD_3CRV_VAULT; uint256 public immutable MAX_ORACLE_DELAY; uint256 public immutable PRICE_MIN; string public name; error CHAINLINK_BAD_PRICE(); constructor( uint256 _maxOracleDelay, uint256 _priceMin, address _ethUnitchainlinkAddress, address _curvePoolAddress, address _yearnVaultAddress, string memory _name ) { THREECRV_ETH_CHAINLINK = _ethUnitchainlinkAddress; CURVE_CRVUSD_3CRV_POOL = _curvePoolAddress; YEARN_CRVUSD_3CRV_VAULT = _yearnVaultAddress; name = _name; MAX_ORACLE_DELAY = _maxOracleDelay; PRICE_MIN = _priceMin; } /// @notice The ```getPrices``` function is intended to return price of ERC4626 token based on the base asset /// @return _isBadData is always false, just sync to other oracle interfaces /// @return _priceLow is the lower of the prices /// @return _priceHigh is the higher of the prices function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh) { uint256 crvUSDPriceInETH = _getCrvUSDPrice(); uint256 yvLPTokenPriceInETH = _getYv3CRVCrvUSDPrice(crvUSDPriceInETH); uint256 rate = crvUSDPriceInETH * 1e18 / yvLPTokenPriceInETH; // crvUSD/yv3CRVCrvUSD _priceHigh = rate > PRICE_MIN ? rate : PRICE_MIN; _priceLow = _priceHigh; } /** * @dev Get price for crvUSD */ function _getCrvUSDPrice() internal view returns (uint256) { // Get crvUSD price from AMM controller uint256 crvUSDPrice; uint256 rate = ILLAMMA(ETH_CRVUSD_AMM_CONTROLLER).price_oracle(); // ETH/crvUSD rate = 1e36 / rate; // crvUSD/ETH // Get crvUSD price from chainlink (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(CRVUSD_USD_CHAINLINK) .latestRoundData(); // crvUSD/USD // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { revert CHAINLINK_BAD_PRICE(); } crvUSDPrice = uint256(_answer); // Get ETH price from chainlink (, _answer, , _updatedAt, ) = AggregatorV3Interface(ETH_USD_CHAINLINK) .latestRoundData(); // ETH/USD // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { revert CHAINLINK_BAD_PRICE(); } crvUSDPrice = crvUSDPrice * 1e26 / uint256(_answer); // crvUSD/ETH return Math.min(rate, crvUSDPrice); } /** * @dev Get price for yearn Curve-(USDT/USDC/DAI/FRAX)-CrvUSD LP Token */ function _getYv3CRVCrvUSDPrice(uint256 _crvUSDPrice) internal view returns (uint256) { // Get (USDT/USDC/DAI/FRAX) price from chainlink (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(THREECRV_ETH_CHAINLINK) .latestRoundData(); // 3CRV/ETH // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { revert CHAINLINK_BAD_PRICE(); } uint256 minStable = Math.min(uint256(_answer), _crvUSDPrice); uint256 curveLPTokenPrice = (ICurvePool(CURVE_CRVUSD_3CRV_POOL).get_virtual_price() * minStable) / 1e18; return curveLPTokenPrice * IYearnVault(YEARN_CRVUSD_3CRV_VAULT).pricePerShare() / 1e18; } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import { ICurvePool } from "../interfaces/Curve/ICurvePool.sol"; import { IYearnVault } from "../interfaces/Yearn/IYearnVault.sol"; /// @title CrvUSDYvMkUSDCrvUSDOracle /// @author Jason (Sturdy) https://github.com/iris112 /// @notice An oracle for CrvUSD/YvMkUSDCrvUSD interface ILLAMMA { function price_oracle() external view returns (uint256); } contract CrvUSDYvMkUSDCrvUSDOracle { address private constant ETH_CRVUSD_AMM_CONTROLLER = 0x1681195C176239ac5E72d9aeBaCf5b2492E0C4ee; address private constant ETH_USD_CHAINLINK = 0x5f4eC3Df9cbd43714FE2740f5E3616155c5b8419; address private constant CRVUSD_USD_CHAINLINK = 0xEEf0C605546958c1f899b6fB336C20671f9cD49F; address public constant CURVE_CRVUSD_MKUSD_POOL = 0x3de254A0f838a844F727fee81040e0FA7884B935; address public constant YEARN_CRVUSD_MKUSD_VAULT = 0xd901DCf4948a29d7D9D7E015AAF61591825AC267; uint8 public constant DECIMALS = 18; uint256 public immutable MAX_ORACLE_DELAY; uint256 public immutable PRICE_MIN; string public name; error CHAINLINK_BAD_PRICE(); constructor( uint256 _maxOracleDelay, uint256 _priceMin, string memory _name ) { name = _name; MAX_ORACLE_DELAY = _maxOracleDelay; PRICE_MIN = _priceMin; } /// @notice The ```getPrices``` function is intended to return price of ERC4626 token based on the base asset /// @return _isBadData is always false, just sync to other oracle interfaces /// @return _priceLow is the lower of the prices /// @return _priceHigh is the higher of the prices function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh) { uint256 crvUSDPriceInETH = _getCrvUSDPrice(); uint256 yvLPTokenPriceInETH = _getYvMkUSDCrvUSDPrice(crvUSDPriceInETH); uint256 rate = crvUSDPriceInETH * 1e18 / yvLPTokenPriceInETH; // crvUSD/yvMkUSDCrvUSD _priceHigh = rate > PRICE_MIN ? rate : PRICE_MIN; _priceLow = _priceHigh; } /** * @dev Get price for crvUSD */ function _getCrvUSDPrice() internal view returns (uint256) { // Get crvUSD price from AMM controller uint256 crvUSDPrice; uint256 rate = ILLAMMA(ETH_CRVUSD_AMM_CONTROLLER).price_oracle(); // ETH/crvUSD rate = 1e36 / rate; // crvUSD/ETH // Get crvUSD price from chainlink (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(CRVUSD_USD_CHAINLINK) .latestRoundData(); // crvUSD/USD // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { revert CHAINLINK_BAD_PRICE(); } crvUSDPrice = uint256(_answer); // Get ETH price from chainlink (, _answer, , _updatedAt, ) = AggregatorV3Interface(ETH_USD_CHAINLINK) .latestRoundData(); // ETH/USD // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { revert CHAINLINK_BAD_PRICE(); } crvUSDPrice = crvUSDPrice * 1e26 / uint256(_answer); // crvUSD/ETH return Math.min(rate, crvUSDPrice); } /** * @dev Get price for yearn Curve-MKUSD-CrvUSD LP Token */ function _getYvMkUSDCrvUSDPrice(uint256 _crvUSDPrice) internal view returns (uint256) { // Get MKUSD price from curve pool uint256 mkUSDRatio = ICurvePool(CURVE_CRVUSD_MKUSD_POOL).price_oracle(); uint256 minStable = Math.min(mkUSDRatio, 1e18) * _crvUSDPrice / 1e18; uint256 curveLPTokenPrice = (ICurvePool(CURVE_CRVUSD_MKUSD_POOL).get_virtual_price() * minStable) / 1e18; return curveLPTokenPrice * IYearnVault(YEARN_CRVUSD_MKUSD_VAULT).pricePerShare() / 1e18; } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; // ==================== DualOracleChainlinkUniV3 ====================== import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import { IStaticOracle } from "@mean-finance/uniswap-v3-oracle/solidity/interfaces/IStaticOracle.sol"; import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { Timelock2Step } from "../../Timelock2Step.sol"; /// @title DualOracleChainlinkUniV3 /// @author Drake Evans (Frax Finance) https://github.com/drakeevans /// @notice An oracle for combining Chainlink & UniV3 Twap prices contract DualOracleChainlinkUniV3 is Timelock2Step { uint128 public constant ORACLE_PRECISION = 1e18; address public immutable BASE_TOKEN; address public immutable QUOTE_TOKEN; // Chainlink Config address public immutable CHAINLINK_MULTIPLY_ADDRESS; address public immutable CHAINLINK_DIVIDE_ADDRESS; uint256 public immutable CHAINLINK_NORMALIZATION; uint256 public maxOracleDelay; // Uni V3 Data address public immutable UNI_V3_PAIR_ADDRESS; uint32 public immutable TWAP_DURATION; // Config Data uint8 internal constant DECIMALS = 18; string public name; uint256 public oracleType = 1; // events /// @notice The ```SetMaxOracleDelay``` event is emitted when the max oracle delay is set /// @param oldMaxOracleDelay The old max oracle delay /// @param newMaxOracleDelay The new max oracle delay event SetMaxOracleDelay(uint256 oldMaxOracleDelay, uint256 newMaxOracleDelay); constructor( address _baseToken, address _quoteToken, address _chainlinkMultiplyAddress, address _chainlinkDivideAddress, uint256 _maxOracleDelay, address _uniV3PairAddress, uint32 _twapDuration, address _timelockAddress, string memory _name ) Timelock2Step() { _setTimelock({ _newTimelock: _timelockAddress }); BASE_TOKEN = _baseToken; QUOTE_TOKEN = _quoteToken; CHAINLINK_MULTIPLY_ADDRESS = _chainlinkMultiplyAddress; CHAINLINK_DIVIDE_ADDRESS = _chainlinkDivideAddress; uint8 _multiplyDecimals = _chainlinkMultiplyAddress != address(0) ? AggregatorV3Interface(_chainlinkMultiplyAddress).decimals() : 0; uint8 _divideDecimals = _chainlinkDivideAddress != address(0) ? AggregatorV3Interface(_chainlinkDivideAddress).decimals() : 0; CHAINLINK_NORMALIZATION = 10 ** (18 + _multiplyDecimals - _divideDecimals + IERC20Metadata(_baseToken).decimals() - IERC20Metadata(_quoteToken).decimals()); maxOracleDelay = _maxOracleDelay; UNI_V3_PAIR_ADDRESS = _uniV3PairAddress; if (_twapDuration == 0) revert("DURATION == 0"); TWAP_DURATION = _twapDuration; name = _name; } /// @notice The ```setMaxOracleDelay``` function sets the max oracle delay to determine if Chainlink data is stale /// @dev Requires msg.sender to be the timelock address /// @param _newMaxOracleDelay The new max oracle delay function setMaxOracleDelay(uint256 _newMaxOracleDelay) external { _requireTimelock(); emit SetMaxOracleDelay(maxOracleDelay, _newMaxOracleDelay); maxOracleDelay = _newMaxOracleDelay; } function _getChainlinkPrice() internal view returns (bool _isBadData, uint256 _price) { _price = uint256(1e36); if (CHAINLINK_MULTIPLY_ADDRESS != address(0)) { (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(CHAINLINK_MULTIPLY_ADDRESS) .latestRoundData(); // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > maxOracleDelay)) { _isBadData = true; return (_isBadData, _price); } _price = _price * uint256(_answer); } if (CHAINLINK_DIVIDE_ADDRESS != address(0)) { (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(CHAINLINK_DIVIDE_ADDRESS) .latestRoundData(); // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > maxOracleDelay)) { _isBadData = true; return (_isBadData, _price); } _price = _price / uint256(_answer); } // return price as ratio of Collateral/Asset including decimal differences // CHAINLINK_NORMALIZATION = 10**(18 + asset.decimals() - collateral.decimals() + multiplyOracle.decimals() - divideOracle.decimals()) _price = _price / CHAINLINK_NORMALIZATION; } /// @notice The ```getPrices``` function is intended to return two prices from different oracles /// @return _isBadData is true when chainlink data is stale or negative /// @return _priceLow is the lower of the two prices /// @return _priceHigh is the higher of the two prices function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh) { uint256 _price1; if (UNI_V3_PAIR_ADDRESS != address(0)) { address[] memory _pools = new address[](1); _pools[0] = UNI_V3_PAIR_ADDRESS; _price1 = IStaticOracle(0xB210CE856631EeEB767eFa666EC7C1C57738d438).quoteSpecificPoolsWithTimePeriod( ORACLE_PRECISION, BASE_TOKEN, QUOTE_TOKEN, _pools, TWAP_DURATION ); } uint256 _price2; (_isBadData, _price2) = _getChainlinkPrice(); // If bad data return price1 for both, else set high to higher price and low to lower price if (UNI_V3_PAIR_ADDRESS != address(0)) { _priceLow = _isBadData || _price1 < _price2 ? _price1 : _price2; _priceHigh = _isBadData || _price1 > _price2 ? _price1 : _price2; } else { _priceLow = _price2; _priceHigh = _price2; } } function decimals() external pure returns (uint8) { return DECIMALS; } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; import { IERC4626 } from "../interfaces/IERC4626.sol"; /// @title ERC4626Oracle /// @author Jason (Sturdy) https://github.com/iris112 /// @notice An oracle for ERC4626 Token contract ERC4626Oracle { uint8 public constant DECIMALS = 18; address public immutable TOKEN; uint8 public immutable TOKEN_DECIMALS; uint256 public immutable PRICE_MIN; string public name; constructor( uint256 _priceMin, address _token, uint8 _decimals, string memory _name ) { PRICE_MIN = _priceMin; TOKEN = _token; TOKEN_DECIMALS = _decimals; name = _name; } /// @notice The ```getPrices``` function is intended to return price of ERC4626 token based on the base asset /// @return _isBadData is always false, just sync to other oracle interfaces /// @return _priceLow is the lower of the prices /// @return _priceHigh is the higher of the prices function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh) { uint256 rate = IERC4626(TOKEN).convertToShares(10 ** TOKEN_DECIMALS); rate = rate * 10 ** DECIMALS / 10 ** TOKEN_DECIMALS; _priceHigh = rate > PRICE_MIN ? rate : PRICE_MIN; _priceLow = _priceHigh; } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; /// @title ETHSWETHOracle /// @author Jason (Sturdy) https://github.com/iris112 /// @notice An oracle for ETH/swETH interface IswETH { function swETHToETHRate() external view returns (uint256); } contract ETHSWETHOracle { address private constant TOKEN = 0xf951E335afb289353dc249e82926178EaC7DEd78; address private constant REDSTONE_SWETH_ETH_PRICE = 0x061bB36F8b67bB922937C102092498dcF4619F86; uint8 public constant DECIMALS = 18; uint256 public immutable MAX_ORACLE_DELAY; uint256 public immutable PRICE_MIN; string public name; error REDSTONE_BAD_PRICE(); constructor( uint256 _maxOracleDelay, uint256 _priceMin, string memory _name ) { name = _name; MAX_ORACLE_DELAY = _maxOracleDelay; PRICE_MIN = _priceMin; } /// @notice The ```getPrices``` function is intended to return price of ERC4626 token based on the base asset /// @return _isBadData is always false, just sync to other oracle interfaces /// @return _priceLow is the lower of the prices /// @return _priceHigh is the higher of the prices function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh) { uint256 rate = IswETH(TOKEN).swETHToETHRate(); (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(REDSTONE_SWETH_ETH_PRICE).latestRoundData(); // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { revert REDSTONE_BAD_PRICE(); } rate = Math.min(uint256(_answer) * 1e10, rate); // redstone price decimal is 8 rate = 1e36 / rate; // ETH/SWETH _priceHigh = rate > PRICE_MIN ? rate : PRICE_MIN; _priceLow = _priceHigh; } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; import "@pendle/core-v2/contracts/oracles/PendleLpOracleLib.sol"; import "@pendle/core-v2/contracts/interfaces/IPMarket.sol"; import "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; /// @title ETHSWETHPendleLPTOracle /// @author Jason (Sturdy) https://github.com/iris112 /// @notice An oracle for ETH/swETH Pendle LPT interface IswETH { function swETHToETHRate() external view returns (uint256); } contract ETHSWETHPendleLPTOracle { using PendleLpOracleLib for IPMarket; address private constant SWETH = 0xf951E335afb289353dc249e82926178EaC7DEd78; address private constant REDSTONE_SWETH_ETH_PRICE = 0x061bB36F8b67bB922937C102092498dcF4619F86; uint8 public constant DECIMALS = 18; address public immutable PENDLE_LPT; uint32 public immutable TWAP_DURATION; uint256 public immutable MAX_ORACLE_DELAY; uint256 public immutable PRICE_MIN; string public name; error REDSTONE_BAD_PRICE(); constructor( address _pendleLPT, uint32 _twapDuration, uint256 _maxOracleDelay, uint256 _priceMin, string memory _name ) { PENDLE_LPT = _pendleLPT; TWAP_DURATION = _twapDuration; name = _name; MAX_ORACLE_DELAY = _maxOracleDelay; PRICE_MIN = _priceMin; } /// @notice The ```getPrices``` function is intended to return price of ERC4626 token based on the base asset /// @return _isBadData is always false, just sync to other oracle interfaces /// @return _priceLow is the lower of the prices /// @return _priceHigh is the higher of the prices function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh) { uint256 swETHRate = IswETH(SWETH).swETHToETHRate(); (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(REDSTONE_SWETH_ETH_PRICE).latestRoundData(); // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { revert REDSTONE_BAD_PRICE(); } swETHRate = Math.min(uint256(_answer) * 1e10, swETHRate); // redstone price decimal is 8 uint256 lpRate = IPMarket(PENDLE_LPT).getLpToAssetRate(TWAP_DURATION); uint256 rate = (swETHRate * lpRate) / 1e18; // LPT/ETH rate = 1e36 / rate; // ETH/LPT _priceHigh = rate > PRICE_MIN ? rate : PRICE_MIN; _priceLow = _priceHigh; } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; /// @title FruxFAssetOracle /// @author Jason (Sturdy) https://github.com/iris112 /// @notice An oracle for CrvUSD/CRV interface IFAsset { function exchangeRateStored() external view returns (uint256); } contract FruxFAssetOracle { uint8 public constant DECIMALS = 18; address public immutable FASSET; address public immutable UNDERLYING_FASSET; address public immutable ASSET; address public immutable CHAINLINK_MULTIPLY_ADDRESS; address public immutable CHAINLINK_DIVIDE_ADDRESS; uint256 public immutable CHAINLINK_NORMALIZATION; uint256 public immutable MAX_ORACLE_DELAY; uint256 public immutable PRICE_MIN; string public name; error CHAINLINK_BAD_PRICE(); constructor( address _fAsset, address _underlyingFAsset, address _asset, address _chainlinkMultiplyAddress, address _chainlinkDivideAddress, uint256 _maxOracleDelay, uint256 _priceMin, string memory _name ) { FASSET = _fAsset; UNDERLYING_FASSET = _underlyingFAsset; ASSET = _asset; CHAINLINK_MULTIPLY_ADDRESS = _chainlinkMultiplyAddress; CHAINLINK_DIVIDE_ADDRESS = _chainlinkDivideAddress; uint8 _multiplyDecimals = _chainlinkMultiplyAddress != address(0) ? AggregatorV3Interface(_chainlinkMultiplyAddress).decimals() : 0; uint8 _divideDecimals = _chainlinkDivideAddress != address(0) ? AggregatorV3Interface(_chainlinkDivideAddress).decimals() : 0; CHAINLINK_NORMALIZATION = 10 ** (18 + _multiplyDecimals - _divideDecimals + IERC20Metadata(_asset).decimals() - IERC20Metadata(_underlyingFAsset).decimals()); name = _name; MAX_ORACLE_DELAY = _maxOracleDelay; PRICE_MIN = _priceMin; } /// @notice The ```getPrices``` function is intended to return price of ERC4626 token based on the base asset /// @return _isBadData is always false, just sync to other oracle interfaces /// @return _priceLow is the lower of the prices /// @return _priceHigh is the higher of the prices function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh) { uint256 price; (_isBadData, price) = _getChainlinkPrice(); // assetAmount * price = underlyingFAssetAmount if (_isBadData) revert CHAINLINK_BAD_PRICE(); uint256 rate = IFAsset(FASSET).exchangeRateStored(); // underlyingFAssetAmount / rate = FAssetAmount, rate decimal is constant 18 rate = price * 1e18 / rate; _priceHigh = rate > PRICE_MIN ? rate : PRICE_MIN; _priceLow = _priceHigh; } function _getChainlinkPrice() internal view returns (bool _isBadData, uint256 _price) { _price = uint256(1e36); if (CHAINLINK_MULTIPLY_ADDRESS != address(0)) { (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(CHAINLINK_MULTIPLY_ADDRESS) .latestRoundData(); // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { _isBadData = true; return (_isBadData, _price); } _price = _price * uint256(_answer); } if (CHAINLINK_DIVIDE_ADDRESS != address(0)) { (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(CHAINLINK_DIVIDE_ADDRESS) .latestRoundData(); // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { _isBadData = true; return (_isBadData, _price); } _price = _price / uint256(_answer); } // return price as ratio of underlyingFAsset/Asset including decimal differences // CHAINLINK_NORMALIZATION = 10**(18 + asset.decimals() - underlyingFAsset.decimals() + multiplyOracle.decimals() - divideOracle.decimals()) _price = _price / CHAINLINK_NORMALIZATION; } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import { ICurvePool } from "../interfaces/Curve/ICurvePool.sol"; /// @title PxETHBalPxETHOracle /// @author Jason (Sturdy) https://github.com/iris112 /// @notice An oracle for PxETH/BalPxETH contract PxETHBalPxETHOracle { uint8 public constant DECIMALS = 18; address public immutable BAL_PXETH_POOL; uint256 public immutable MAX_ORACLE_DELAY; uint256 public immutable PRICE_MIN; string public name; error CHAINLINK_BAD_PRICE(); constructor( uint256 _maxOracleDelay, uint256 _priceMin, address _balPoolAddress, string memory _name ) { BAL_PXETH_POOL = _balPoolAddress; name = _name; MAX_ORACLE_DELAY = _maxOracleDelay; PRICE_MIN = _priceMin; } /// @notice The ```getPrices``` function is intended to return price of ERC4626 token based on the base asset /// @return _isBadData is always false, just sync to other oracle interfaces /// @return _priceLow is the lower of the prices /// @return _priceHigh is the higher of the prices function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh) { // uint256 yvLPTokenPriceInETH = _getYv3CRVCrvUSDPrice(crvUSDPriceInETH); // uint256 rate = crvUSDPriceInETH * 1e18 / yvLPTokenPriceInETH; // crvUSD/yv3CRVCrvUSD uint256 rate = 1e18; _priceHigh = rate > PRICE_MIN ? rate : PRICE_MIN; _priceLow = _priceHigh; } // /** // * @dev Get price for yearn Curve-(USDT/USDC/DAI/FRAX)-CrvUSD LP Token // */ // function _getYv3CRVCrvUSDPrice(uint256 _crvUSDPrice) internal view returns (uint256) { // // Get (USDT/USDC/DAI/FRAX) price from chainlink // (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(THREECRV_ETH_CHAINLINK) // .latestRoundData(); // 3CRV/ETH // // If data is stale or negative, set bad data to true and return // if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { // revert CHAINLINK_BAD_PRICE(); // } // uint256 minStable = Math.min(uint256(_answer), _crvUSDPrice); // uint256 curveLPTokenPrice = (ICurvePool(BAL_PXETH_POOL).get_virtual_price() * minStable) / 1e18; // return curveLPTokenPrice * IYearnVault(YEARN_CRVUSD_3CRV_VAULT).pricePerShare() / 1e18; // } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import { ICurvePool } from "../interfaces/Curve/ICurvePool.sol"; /// @title PxETHCrvPxETHOracle /// @author Jason (Sturdy) https://github.com/iris112 /// @notice An oracle for PxETH/CrvPxETH contract PxETHCrvPxETHOracle { uint8 public constant DECIMALS = 18; address public immutable CURVE_PXETH_POOL; uint256 public immutable MAX_ORACLE_DELAY; uint256 public immutable PRICE_MIN; string public name; error CHAINLINK_BAD_PRICE(); constructor( uint256 _maxOracleDelay, uint256 _priceMin, address _curvePoolAddress, string memory _name ) { CURVE_PXETH_POOL = _curvePoolAddress; name = _name; MAX_ORACLE_DELAY = _maxOracleDelay; PRICE_MIN = _priceMin; } /// @notice The ```getPrices``` function is intended to return price of ERC4626 token based on the base asset /// @return _isBadData is always false, just sync to other oracle interfaces /// @return _priceLow is the lower of the prices /// @return _priceHigh is the higher of the prices function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh) { // uint256 yvLPTokenPriceInETH = _getYv3CRVCrvUSDPrice(crvUSDPriceInETH); // uint256 rate = crvUSDPriceInETH * 1e18 / yvLPTokenPriceInETH; // crvUSD/yv3CRVCrvUSD uint256 rate = 1e18; _priceHigh = rate > PRICE_MIN ? rate : PRICE_MIN; _priceLow = _priceHigh; } // /** // * @dev Get price for yearn Curve-(USDT/USDC/DAI/FRAX)-CrvUSD LP Token // */ // function _getYv3CRVCrvUSDPrice(uint256 _crvUSDPrice) internal view returns (uint256) { // // Get (USDT/USDC/DAI/FRAX) price from chainlink // (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(THREECRV_ETH_CHAINLINK) // .latestRoundData(); // 3CRV/ETH // // If data is stale or negative, set bad data to true and return // if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { // revert CHAINLINK_BAD_PRICE(); // } // uint256 minStable = Math.min(uint256(_answer), _crvUSDPrice); // uint256 curveLPTokenPrice = (ICurvePool(CURVE_PXETH_POOL).get_virtual_price() * minStable) / 1e18; // return curveLPTokenPrice * IYearnVault(YEARN_CRVUSD_3CRV_VAULT).pricePerShare() / 1e18; // } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ICurvePool } from "../interfaces/Curve/ICurvePool.sol"; import { IPoolPositionSlim } from "../interfaces/Maverick/IPoolPositionSlim.sol"; import { IERC4626 } from "../interfaces/IERC4626.sol"; /// @title PxETHSturdyMavPxETHOracle /// @author Jason (Sturdy) https://github.com/iris112 /// @notice An oracle for PxETH/SturdyMavPxETH contract PxETHSturdyMavPxETHOracle { uint8 public constant DECIMALS = 18; address private constant MAV_PXETH_POOL = 0x5263DBD1FBFf32E0ba38C67539821B6D0D0dBf61; address public immutable STURDY_MAV_PXETH_REWARD_COMPOUNDER; uint256 public immutable PRICE_MIN; string public name; constructor( uint256 _priceMin, address _sturdyRewardCompounder, string memory _name ) { STURDY_MAV_PXETH_REWARD_COMPOUNDER = _sturdyRewardCompounder; name = _name; PRICE_MIN = _priceMin; } /// @notice The ```getPrices``` function is intended to return price of ERC4626 token based on the base asset /// @return _isBadData is always false, just sync to other oracle interfaces /// @return _priceLow is the lower of the prices /// @return _priceHigh is the higher of the prices function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh) { uint256 mavLPInETH = _getMavPxETHLPPrice(); uint256 rate = IERC4626(STURDY_MAV_PXETH_REWARD_COMPOUNDER).convertToAssets(1e18); // SturdyMavPxETH/MavPxETH rate = rate * mavLPInETH / 1e18; _priceHigh = rate > PRICE_MIN ? rate : PRICE_MIN; _priceLow = _priceHigh; } /** * @dev Get price for maverick PxETH-ETH boosted position LP Token, 1 pxETH <= 1ETH */ function _getMavPxETHLPPrice() internal view returns (uint256) { (uint256 pxETHAmount, uint256 WETHAmount) = IPoolPositionSlim(MAV_PXETH_POOL).getReserves(); uint256 LPSupply = IERC20(MAV_PXETH_POOL).totalSupply(); return (pxETHAmount + WETHAmount) * 1e18 / LPSupply; } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import {Math} from "@openzeppelin/contracts/utils/math/Math.sol"; import { ICurvePool } from "../interfaces/Curve/ICurvePool.sol"; /// @title PxETHUniV3BunniPxETHOracle /// @author Jason (Sturdy) https://github.com/iris112 /// @notice An oracle for PxETH/UniV3BunniPxETH contract PxETHUniV3BunniPxETHOracle { uint8 public constant DECIMALS = 18; address public immutable UNIV3_BUNNI_PXETH_POOL; uint256 public immutable MAX_ORACLE_DELAY; uint256 public immutable PRICE_MIN; string public name; error CHAINLINK_BAD_PRICE(); constructor( uint256 _maxOracleDelay, uint256 _priceMin, address _univ3BunniPoolAddress, string memory _name ) { UNIV3_BUNNI_PXETH_POOL = _univ3BunniPoolAddress; name = _name; MAX_ORACLE_DELAY = _maxOracleDelay; PRICE_MIN = _priceMin; } /// @notice The ```getPrices``` function is intended to return price of ERC4626 token based on the base asset /// @return _isBadData is always false, just sync to other oracle interfaces /// @return _priceLow is the lower of the prices /// @return _priceHigh is the higher of the prices function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh) { // uint256 yvLPTokenPriceInETH = _getYv3CRVCrvUSDPrice(crvUSDPriceInETH); // uint256 rate = crvUSDPriceInETH * 1e18 / yvLPTokenPriceInETH; // crvUSD/yv3CRVCrvUSD uint256 rate = 1e18; _priceHigh = rate > PRICE_MIN ? rate : PRICE_MIN; _priceLow = _priceHigh; } // /** // * @dev Get price for yearn Curve-(USDT/USDC/DAI/FRAX)-CrvUSD LP Token // */ // function _getYv3CRVCrvUSDPrice(uint256 _crvUSDPrice) internal view returns (uint256) { // // Get (USDT/USDC/DAI/FRAX) price from chainlink // (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(THREECRV_ETH_CHAINLINK) // .latestRoundData(); // 3CRV/ETH // // If data is stale or negative, set bad data to true and return // if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { // revert CHAINLINK_BAD_PRICE(); // } // uint256 minStable = Math.min(uint256(_answer), _crvUSDPrice); // uint256 curveLPTokenPrice = (ICurvePool(UNIV3_BUNNI_PXETH_POOL).get_virtual_price() * minStable) / 1e18; // return curveLPTokenPrice * IYearnVault(YEARN_CRVUSD_3CRV_VAULT).pricePerShare() / 1e18; // } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; import { AggregatorV3Interface } from "@chainlink/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol"; import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { IERC4626 } from "../interfaces/IERC4626.sol"; /// @title SFRAXOracle /// @author Jason (Sturdy) https://github.com/iris112 /// @notice An oracle for CrvUSD/CRV interface IFAsset { function exchangeRateStored() external view returns (uint256); } contract SFRAXOracle { address public constant SFRAX = 0xA663B02CF0a4b149d2aD41910CB81e23e1c41c32; address public constant FRAX = 0x853d955aCEf822Db058eb8505911ED77F175b99e; uint8 public constant DECIMALS = 18; address public immutable ASSET; address public immutable CHAINLINK_MULTIPLY_ADDRESS; address public immutable CHAINLINK_DIVIDE_ADDRESS; uint256 public immutable CHAINLINK_NORMALIZATION; uint256 public immutable MAX_ORACLE_DELAY; uint256 public immutable PRICE_MIN; string public name; error CHAINLINK_BAD_PRICE(); constructor( address _asset, address _chainlinkMultiplyAddress, address _chainlinkDivideAddress, uint256 _maxOracleDelay, uint256 _priceMin, string memory _name ) { ASSET = _asset; CHAINLINK_MULTIPLY_ADDRESS = _chainlinkMultiplyAddress; CHAINLINK_DIVIDE_ADDRESS = _chainlinkDivideAddress; uint8 _multiplyDecimals = _chainlinkMultiplyAddress != address(0) ? AggregatorV3Interface(_chainlinkMultiplyAddress).decimals() : 0; uint8 _divideDecimals = _chainlinkDivideAddress != address(0) ? AggregatorV3Interface(_chainlinkDivideAddress).decimals() : 0; CHAINLINK_NORMALIZATION = 10 ** (18 + _multiplyDecimals - _divideDecimals + IERC20Metadata(_asset).decimals() - IERC20Metadata(FRAX).decimals()); name = _name; MAX_ORACLE_DELAY = _maxOracleDelay; PRICE_MIN = _priceMin; } /// @notice The ```getPrices``` function is intended to return price of ERC4626 token based on the base asset /// @return _isBadData is always false, just sync to other oracle interfaces /// @return _priceLow is the lower of the prices /// @return _priceHigh is the higher of the prices function getPrices() external view returns (bool _isBadData, uint256 _priceLow, uint256 _priceHigh) { uint256 price; (_isBadData, price) = _getChainlinkPrice(); // assetAmount * price = FRAXAmount if (_isBadData) revert CHAINLINK_BAD_PRICE(); uint256 rate = IERC4626(SFRAX).convertToShares(10 ** 18); // FraxAmount * rate = SFRAXAmount, rate decimal is 18 rate = price * rate / 1e18; _priceHigh = rate > PRICE_MIN ? rate : PRICE_MIN; _priceLow = _priceHigh; } function _getChainlinkPrice() internal view returns (bool _isBadData, uint256 _price) { _price = uint256(1e36); if (CHAINLINK_MULTIPLY_ADDRESS != address(0)) { (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(CHAINLINK_MULTIPLY_ADDRESS) .latestRoundData(); // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { _isBadData = true; return (_isBadData, _price); } _price = _price * uint256(_answer); } if (CHAINLINK_DIVIDE_ADDRESS != address(0)) { (, int256 _answer, , uint256 _updatedAt, ) = AggregatorV3Interface(CHAINLINK_DIVIDE_ADDRESS) .latestRoundData(); // If data is stale or negative, set bad data to true and return if (_answer <= 0 || (block.timestamp - _updatedAt > MAX_ORACLE_DELAY)) { _isBadData = true; return (_isBadData, _price); } _price = _price / uint256(_answer); } // return price as ratio of underlyingFAsset/Asset including decimal differences // CHAINLINK_NORMALIZATION = 10**(18 + asset.decimals() - underlyingFAsset.decimals() + multiplyOracle.decimals() - divideOracle.decimals()) _price = _price / CHAINLINK_NORMALIZATION; } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; // ========================== SturdyPair ============================ import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import { SturdyPairCore } from "./SturdyPairCore.sol"; import { SafeERC20 } from "./libraries/SafeERC20.sol"; import { VaultAccount, VaultAccountingLibrary } from "./libraries/VaultAccount.sol"; import { IRateCalculatorV2 } from "./interfaces/IRateCalculatorV2.sol"; /// @title SturdyPair /// @author Drake Evans (Frax Finance) https://github.com/drakeevans /// @notice The SturdyPair is a lending pair that allows users to engage in lending and borrowing activities contract SturdyPair is IERC20Metadata, SturdyPairCore { using VaultAccountingLibrary for VaultAccount; using SafeERC20 for IERC20; using SafeCast for uint256; /// @param _configData abi.encode(address _asset, address _collateral, address _oracle, uint32 _maxOracleDeviation, address _rateContract, uint64 _fullUtilizationRate, uint256 _maxLTV, uint256 _cleanLiquidationFee, uint256 _dirtyLiquidationFee, uint256 _protocolLiquidationFee) /// @param _immutables abi.encode(address _circuitBreakerAddress, address _comptrollerAddress, address _timelockAddress) /// @param _customConfigData abi.encode(string memory _nameOfContract, string memory _symbolOfContract, uint8 _decimalsOfContract) constructor( bytes memory _configData, bytes memory _immutables, bytes memory _customConfigData ) SturdyPairCore(_configData, _immutables, _customConfigData) {} // ============================================================================================ // ERC20 Metadata // ============================================================================================ function name() public view override(ERC20, IERC20Metadata) returns (string memory) { return nameOfContract; } function symbol() public view override(ERC20, IERC20Metadata) returns (string memory) { return symbolOfContract; } function decimals() public view override(ERC20, IERC20Metadata) returns (uint8) { return decimalsOfContract; } // totalSupply for fToken ERC20 compatibility function totalSupply() public view override(ERC20, IERC20) returns (uint256) { return totalAsset.shares; } // ============================================================================================ // Functions: Helpers // ============================================================================================ function asset() external view returns (address) { return address(assetContract); } function getConstants() external pure returns ( uint256 _LTV_PRECISION, uint256 _LIQ_PRECISION, uint256 _UTIL_PREC, uint256 _FEE_PRECISION, uint256 _EXCHANGE_PRECISION, uint256 _DEVIATION_PRECISION, uint256 _RATE_PRECISION, uint256 _MAX_PROTOCOL_FEE ) { _LTV_PRECISION = LTV_PRECISION; _LIQ_PRECISION = LIQ_PRECISION; _UTIL_PREC = UTIL_PREC; _FEE_PRECISION = FEE_PRECISION; _EXCHANGE_PRECISION = EXCHANGE_PRECISION; _DEVIATION_PRECISION = DEVIATION_PRECISION; _RATE_PRECISION = RATE_PRECISION; _MAX_PROTOCOL_FEE = MAX_PROTOCOL_FEE; } /// @notice The ```getUserSnapshot``` function gets user level accounting data /// @param _address The user address /// @return _userAssetShares The user fToken balance /// @return _userBorrowShares The user borrow shares /// @return _userCollateralBalance The user collateral balance function getUserSnapshot( address _address ) external view returns (uint256 _userAssetShares, uint256 _userBorrowShares, uint256 _userCollateralBalance) { _userAssetShares = balanceOf(_address); _userBorrowShares = userBorrowShares[_address]; _userCollateralBalance = userCollateralBalance[_address]; } /// @notice The ```getPairAccounting``` function gets all pair level accounting numbers /// @return _totalAssetAmount Total assets deposited and interest accrued, total claims /// @return _totalAssetShares Total fTokens /// @return _totalBorrowAmount Total borrows /// @return _totalBorrowShares Total borrow shares /// @return _totalCollateral Total collateral function getPairAccounting() external view returns ( uint128 _totalAssetAmount, uint128 _totalAssetShares, uint128 _totalBorrowAmount, uint128 _totalBorrowShares, uint256 _totalCollateral ) { (, , , , VaultAccount memory _totalAsset, VaultAccount memory _totalBorrow) = previewAddInterest(); _totalAssetAmount = _totalAsset.amount; _totalAssetShares = _totalAsset.shares; _totalBorrowAmount = _totalBorrow.amount; _totalBorrowShares = _totalBorrow.shares; _totalCollateral = totalCollateral; } /// @notice The ```toBorrowShares``` function converts a given amount of borrow debt into the number of shares /// @param _amount Amount of borrow /// @param _roundUp Whether to roundup during division /// @param _previewInterest Whether to simulate interest accrual /// @return _shares The number of shares function toBorrowShares( uint256 _amount, bool _roundUp, bool _previewInterest ) external view returns (uint256 _shares) { if (_previewInterest) { (, , , , , VaultAccount memory _totalBorrow) = previewAddInterest(); _shares = _totalBorrow.toShares(_amount, _roundUp); } else { _shares = totalBorrow.toShares(_amount, _roundUp); } } /// @notice The ```toBorrowAmount``` function converts a given amount of borrow debt into the number of shares /// @param _shares Shares of borrow /// @param _roundUp Whether to roundup during division /// @param _previewInterest Whether to simulate interest accrual /// @return _amount The amount of asset function toBorrowAmount( uint256 _shares, bool _roundUp, bool _previewInterest ) external view returns (uint256 _amount) { if (_previewInterest) { (, , , , , VaultAccount memory _totalBorrow) = previewAddInterest(); _amount = _totalBorrow.toAmount(_shares, _roundUp); } else { _amount = totalBorrow.toAmount(_shares, _roundUp); } } /// @notice The ```toAssetAmount``` function converts a given number of shares to an asset amount /// @param _shares Shares of asset (fToken) /// @param _roundUp Whether to round up after division /// @param _previewInterest Whether to preview interest accrual before calculation /// @return _amount The amount of asset function toAssetAmount( uint256 _shares, bool _roundUp, bool _previewInterest ) public view returns (uint256 _amount) { if (_previewInterest) { (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest(); _amount = _totalAsset.toAmount(_shares, _roundUp); } else { _amount = totalAsset.toAmount(_shares, _roundUp); } } /// @notice The ```toAssetShares``` function converts a given asset amount to a number of asset shares (fTokens) /// @param _amount The amount of asset /// @param _roundUp Whether to round up after division /// @param _previewInterest Whether to preview interest accrual before calculation /// @return _shares The number of shares (fTokens) function toAssetShares( uint256 _amount, bool _roundUp, bool _previewInterest ) public view returns (uint256 _shares) { if (_previewInterest) { (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest(); _shares = _totalAsset.toShares(_amount, _roundUp); } else { _shares = totalAsset.toShares(_amount, _roundUp); } } function convertToAssets(uint256 _shares) external view returns (uint256 _assets) { _assets = toAssetAmount(_shares, false, true); } function convertToShares(uint256 _assets) external view returns (uint256 _shares) { _shares = toAssetShares(_assets, false, true); } function pricePerShare() external view returns (uint256 _amount) { _amount = toAssetAmount(1e18, false, true); } function totalAssets() external view returns (uint256) { (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest(); return _totalAsset.amount; } function maxDeposit(address _receiver) public view returns (uint256 _maxAssets) { (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest(); _maxAssets = _totalAsset.amount >= depositLimit ? 0 : depositLimit - _totalAsset.amount; } function maxMint(address _receiver) external view returns (uint256 _maxShares) { (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest(); uint256 _maxDeposit = _totalAsset.amount >= depositLimit ? 0 : depositLimit - _totalAsset.amount; _maxShares = _totalAsset.toShares(_maxDeposit, false); } function maxWithdraw(address _owner) external view returns (uint256 _maxAssets) { if (isWithdrawPaused) return 0; ( , , uint256 _feesShare, , VaultAccount memory _totalAsset, VaultAccount memory _totalBorrow ) = previewAddInterest(); // Get the owner balance and include the fees share if owner is this contract uint256 _ownerBalance = _owner == address(this) ? balanceOf(_owner) + _feesShare : balanceOf(_owner); // Return the lower of total assets in contract or total assets available to _owner uint256 _totalAssetsAvailable = _totalAssetAvailable(_totalAsset, _totalBorrow); uint256 _totalUserWithdraw = _totalAsset.toAmount(_ownerBalance, false); _maxAssets = _totalAssetsAvailable < _totalUserWithdraw ? _totalAssetsAvailable : _totalUserWithdraw; } function maxRedeem(address _owner) external view returns (uint256 _maxShares) { if (isWithdrawPaused) return 0; ( , , uint256 _feesShare, , VaultAccount memory _totalAsset, VaultAccount memory _totalBorrow ) = previewAddInterest(); // Calculate the total shares available uint256 _totalAssetsAvailable = _totalAssetAvailable(_totalAsset, _totalBorrow); uint256 _totalSharesAvailable = _totalAsset.toShares(_totalAssetsAvailable, false); // Get the owner balance and include the fees share if owner is this contract uint256 _ownerBalance = _owner == address(this) ? balanceOf(_owner) + _feesShare : balanceOf(_owner); _maxShares = _totalSharesAvailable < _ownerBalance ? _totalSharesAvailable : _ownerBalance; } // ============================================================================================ // Functions: Configuration // ============================================================================================ /// @notice The ```SetOracleInfo``` event is emitted when the oracle info (address and max deviation) is set /// @param oldOracle The old oracle address /// @param oldMaxOracleDeviation The old max oracle deviation /// @param newOracle The new oracle address /// @param newMaxOracleDeviation The new max oracle deviation event SetOracleInfo( address oldOracle, uint32 oldMaxOracleDeviation, address newOracle, uint32 newMaxOracleDeviation ); /// @notice The ```setOracleInfo``` function sets the oracle data /// @param _newOracle The new oracle address /// @param _newMaxOracleDeviation The new max oracle deviation function setOracle(address _newOracle, uint32 _newMaxOracleDeviation) external { _requireTimelock(); ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo; emit SetOracleInfo( _exchangeRateInfo.oracle, _exchangeRateInfo.maxOracleDeviation, _newOracle, _newMaxOracleDeviation ); _exchangeRateInfo.oracle = _newOracle; _exchangeRateInfo.maxOracleDeviation = _newMaxOracleDeviation; exchangeRateInfo = _exchangeRateInfo; } bool public isMaxLTVSetterRevoked; /// @notice The ```RevokeMaxLTVSetter``` event is emitted when the max LTV setter is revoked event RevokeMaxLTVSetter(); /// @notice The ```revokeMaxLTVSetter``` function revokes the max LTV setter function revokeMaxLTVSetter() external { _requireTimelock(); isMaxLTVSetterRevoked = true; emit RevokeMaxLTVSetter(); } /// @notice The ```SetMaxLTV``` event is emitted when the max LTV is set /// @param oldMaxLTV The old max LTV /// @param newMaxLTV The new max LTV event SetMaxLTV(uint256 oldMaxLTV, uint256 newMaxLTV); /// @notice The ```setMaxLTV``` function sets the max LTV /// @param _newMaxLTV The new max LTV function setMaxLTV(uint256 _newMaxLTV) external { _requireTimelock(); if (isMaxLTVSetterRevoked) revert SetterRevoked(); emit SetMaxLTV(maxLTV, _newMaxLTV); maxLTV = _newMaxLTV; } /// @notice The ```SetRateContract``` event is emitted when the rate contract is set /// @param oldRateContract The old rate contract /// @param newRateContract The new rate contract event SetRateContract(address oldRateContract, address newRateContract); /// @notice The ```setRateContract``` function sets the rate contract address /// @param _newRateContract The new rate contract address function setRateContract(address _newRateContract) external { _requireTimelock(); emit SetRateContract(address(rateContract), _newRateContract); rateContract = IRateCalculatorV2(_newRateContract); } /// @notice The ```SetLiquidationFees``` event is emitted when the liquidation fees are set /// @param oldCleanLiquidationFee The old clean liquidation fee /// @param oldDirtyLiquidationFee The old dirty liquidation fee /// @param oldProtocolLiquidationFee The old protocol liquidation fee /// @param newCleanLiquidationFee The new clean liquidation fee /// @param newDirtyLiquidationFee The new dirty liquidation fee /// @param newProtocolLiquidationFee The new protocol liquidation fee event SetLiquidationFees( uint256 oldCleanLiquidationFee, uint256 oldDirtyLiquidationFee, uint256 oldProtocolLiquidationFee, uint256 newCleanLiquidationFee, uint256 newDirtyLiquidationFee, uint256 newProtocolLiquidationFee ); /// @notice The ```setLiquidationFees``` function sets the liquidation fees /// @param _newCleanLiquidationFee The new clean liquidation fee /// @param _newDirtyLiquidationFee The new dirty liquidation fee function setLiquidationFees( uint256 _newCleanLiquidationFee, uint256 _newDirtyLiquidationFee, uint256 _newProtocolLiquidationFee ) external { _requireTimelock(); emit SetLiquidationFees( cleanLiquidationFee, dirtyLiquidationFee, protocolLiquidationFee, _newCleanLiquidationFee, _newDirtyLiquidationFee, _newProtocolLiquidationFee ); cleanLiquidationFee = _newCleanLiquidationFee; dirtyLiquidationFee = _newDirtyLiquidationFee; protocolLiquidationFee = _newProtocolLiquidationFee; } /// @notice The ```ChangeFee``` event first when the fee is changed /// @param newFee The new fee event ChangeFee(uint32 newFee); /// @notice The ```changeFee``` function changes the protocol fee, max 50% /// @param _newFee The new fee function changeFee(uint32 _newFee) external { _requireTimelock(); if (isInterestPaused) revert InterestPaused(); if (_newFee > MAX_PROTOCOL_FEE) { revert BadProtocolFee(); } _addInterest(); currentRateInfo.feeToProtocolRate = _newFee; emit ChangeFee(_newFee); } /// @notice The ```WithdrawFees``` event fires when the fees are withdrawn /// @param shares Number of shares (fTokens) redeemed /// @param recipient To whom the assets were sent /// @param amountToTransfer The amount of fees redeemed event WithdrawFees(uint128 shares, address recipient, uint256 amountToTransfer, uint256 collateralAmount); /// @notice The ```withdrawFees``` function withdraws fees accumulated /// @param _shares Number of fTokens to redeem /// @param _recipient Address to send the assets /// @return _amountToTransfer Amount of assets sent to recipient function withdrawFees(uint128 _shares, address _recipient) external onlyOwner returns (uint256 _amountToTransfer) { if (_recipient == address(0)) revert InvalidReceiver(); // Grab some data from state to save gas VaultAccount memory _totalAsset = totalAsset; // Take all available if 0 value passed if (_shares == 0) _shares = uint128(balanceOf(address(this))); // We must calculate this before we subtract from _totalAsset or invoke _burn _amountToTransfer = _totalAsset.toAmount(_shares, true); _approve(address(this), msg.sender, _shares); _redeem(_totalAsset, _amountToTransfer.toUint128(), _shares, _recipient, address(this)); uint256 _collateralAmount = userCollateralBalance[address(this)]; _removeCollateral(_collateralAmount, _recipient, address(this)); emit WithdrawFees(_shares, _recipient, _amountToTransfer, _collateralAmount); } /// @notice The ```SetSwapper``` event fires whenever a swapper is black or whitelisted /// @param swapper The swapper address /// @param approval The approval event SetSwapper(address swapper, bool approval); /// @notice The ```setSwapper``` function is called to black or whitelist a given swapper address /// @dev /// @param _swapper The swapper address /// @param _approval The approval function setSwapper(address _swapper, bool _approval) external onlyOwner { swappers[_swapper] = _approval; emit SetSwapper(_swapper, _approval); } // ============================================================================================ // Functions: Access Control // ============================================================================================ /// @notice The ```pause``` function is called to pause all contract functionality function pause() external { _requireProtocolOrOwner(); _setBorrowLimit(0); _setDepositLimit(0); if (!isRepayAccessControlRevoked) _pauseRepay(true); if (!isWithdrawAccessControlRevoked) _pauseWithdraw(true); if (!isLiquidateAccessControlRevoked) _pauseLiquidate(true); _addInterest(); _pauseInterest(true); } /// @notice The ```unpause``` function is called to unpause all contract functionality function unpause() external { _requireTimelockOrOwner(); _setBorrowLimit(type(uint256).max); _setDepositLimit(type(uint256).max); if (!isRepayAccessControlRevoked) _pauseRepay(false); if (!isWithdrawAccessControlRevoked) _pauseWithdraw(false); if (!isLiquidateAccessControlRevoked) _pauseLiquidate(false); _addInterest(); _pauseInterest(false); } /// @notice The ```pauseBorrow``` function sets borrow limit to 0 function pauseBorrow() external { _requireProtocolOrOwner(); _setBorrowLimit(0); } /// @notice The ```setBorrowLimit``` function sets the borrow limit /// @param _limit The new borrow limit function setBorrowLimit(uint256 _limit) external { _requireTimelockOrOwner(); _setBorrowLimit(_limit); } /// @notice The ```pauseDeposit``` function pauses deposit functionality function pauseDeposit() external { _requireProtocolOrOwner(); _setDepositLimit(0); } /// @notice The ```setDepositLimit``` function sets the deposit limit /// @param _limit The new deposit limit function setDepositLimit(uint256 _limit) external { _requireTimelockOrOwner(); _setDepositLimit(_limit); } /// @notice The ```pauseRepay``` function pauses repay functionality /// @param _isPaused The new pause state function pauseRepay(bool _isPaused) external { if (_isPaused) { _requireProtocolOrOwner(); } else { _requireTimelockOrOwner(); } if (isRepayAccessControlRevoked) revert AccessControlRevoked(); _pauseRepay(_isPaused); } /// @notice The ```revokeRepayAccessControl``` function revokes repay access control function revokeRepayAccessControl() external { _requireTimelock(); _revokeRepayAccessControl(); } /// @notice The ```pauseWithdraw``` function pauses withdraw functionality /// @param _isPaused The new pause state function pauseWithdraw(bool _isPaused) external { if (_isPaused) { _requireProtocolOrOwner(); } else { _requireTimelockOrOwner(); } if (isWithdrawAccessControlRevoked) revert AccessControlRevoked(); _pauseWithdraw(_isPaused); } /// @notice The ```revokeWithdrawAccessControl``` function revokes withdraw access control function revokeWithdrawAccessControl() external { _requireTimelock(); _revokeWithdrawAccessControl(); } /// @notice The ```pauseLiquidate``` function pauses liquidate functionality /// @param _isPaused The new pause state function pauseLiquidate(bool _isPaused) external { if (_isPaused) { _requireProtocolOrOwner(); } else { _requireTimelockOrOwner(); } if (isLiquidateAccessControlRevoked) revert AccessControlRevoked(); _pauseLiquidate(_isPaused); } /// @notice The ```revokeLiquidateAccessControl``` function revokes liquidate access control function revokeLiquidateAccessControl() external { _requireTimelock(); _revokeLiquidateAccessControl(); } /// @notice The ```pauseInterest``` function pauses interest functionality /// @param _isPaused The new pause state function pauseInterest(bool _isPaused) external { if (_isPaused) { _requireProtocolOrOwner(); } else { _requireTimelockOrOwner(); } // Resets the lastTimestamp which has the effect of no interest accruing over the pause period _addInterest(); _pauseInterest(_isPaused); } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; // ==================== SturdyPairAccessControl ===================== import { Ownable2Step, Ownable } from "@openzeppelin/contracts/access/Ownable2Step.sol"; import { Timelock2Step } from "./Timelock2Step.sol"; import { SturdyPairAccessControlErrors } from "./SturdyPairAccessControlErrors.sol"; /// @title SturdyPairAccessControl /// @author Drake Evans (Frax Finance) https://github.com/drakeevans /// @notice An abstract contract which contains the access control logic for SturdyPair abstract contract SturdyPairAccessControl is Timelock2Step, Ownable2Step, SturdyPairAccessControlErrors { // Deployer address public immutable DEPLOYER_ADDRESS; // Admin contracts address public circuitBreakerAddress; // access control uint256 public borrowLimit = type(uint256).max; uint256 public depositLimit = type(uint256).max; bool public isRepayPaused; bool public isRepayAccessControlRevoked; bool public isWithdrawPaused; bool public isWithdrawAccessControlRevoked; bool public isLiquidatePaused; bool public isLiquidateAccessControlRevoked; bool public isInterestPaused; /// @param _immutables abi.encode(address _circuitBreakerAddress, address _comptrollerAddress, address _timelockAddress) constructor(bytes memory _immutables) Timelock2Step() Ownable2Step() { // Handle Immutables Configuration (address _circuitBreakerAddress, address _comptrollerAddress, address _timelockAddress) = abi.decode( _immutables, (address, address, address) ); _setTimelock(_timelockAddress); _transferOwnership(_comptrollerAddress); // Deployer contract DEPLOYER_ADDRESS = msg.sender; circuitBreakerAddress = _circuitBreakerAddress; } // ============================================================================================ // Functions: Access Control // ============================================================================================ function _requireProtocolOrOwner() internal view { if ( msg.sender != circuitBreakerAddress && msg.sender != owner() && msg.sender != DEPLOYER_ADDRESS && msg.sender != timelockAddress ) { revert OnlyProtocolOrOwner(); } } function _requireTimelockOrOwner() internal view { if (msg.sender != owner() && msg.sender != timelockAddress) { revert OnlyTimelockOrOwner(); } } /// @notice The ```SetBorrowLimit``` event is emitted when the borrow limit is set /// @param limit The new borrow limit event SetBorrowLimit(uint256 limit); function _setBorrowLimit(uint256 _limit) internal { borrowLimit = _limit; emit SetBorrowLimit(_limit); } /// @notice The ```SetDepositLimit``` event is emitted when the deposit limit is set /// @param limit The new deposit limit event SetDepositLimit(uint256 limit); function _setDepositLimit(uint256 _limit) internal { depositLimit = _limit; emit SetDepositLimit(_limit); } /// @notice The ```RevokeRepayAccessControl``` event is emitted when repay access control is revoked event RevokeRepayAccessControl(); function _revokeRepayAccessControl() internal { isRepayAccessControlRevoked = true; emit RevokeRepayAccessControl(); } /// @notice The ```PauseRepay``` event is emitted when repay is paused or unpaused /// @param isPaused The new paused state event PauseRepay(bool isPaused); function _pauseRepay(bool _isPaused) internal { isRepayPaused = _isPaused; emit PauseRepay(_isPaused); } /// @notice The ```RevokeWithdrawAccessControl``` event is emitted when withdraw access control is revoked event RevokeWithdrawAccessControl(); function _revokeWithdrawAccessControl() internal { isWithdrawAccessControlRevoked = true; emit RevokeWithdrawAccessControl(); } /// @notice The ```PauseWithdraw``` event is emitted when withdraw is paused or unpaused /// @param isPaused The new paused state event PauseWithdraw(bool isPaused); function _pauseWithdraw(bool _isPaused) internal { isWithdrawPaused = _isPaused; emit PauseWithdraw(_isPaused); } /// @notice The ```RevokeLiquidateAccessControl``` event is emitted when liquidate access control is revoked event RevokeLiquidateAccessControl(); function _revokeLiquidateAccessControl() internal { isLiquidateAccessControlRevoked = true; emit RevokeLiquidateAccessControl(); } /// @notice The ```PauseLiquidate``` event is emitted when liquidate is paused or unpaused /// @param isPaused The new paused state event PauseLiquidate(bool isPaused); function _pauseLiquidate(bool _isPaused) internal { isLiquidatePaused = _isPaused; emit PauseLiquidate(_isPaused); } /// @notice The ```PauseInterest``` event is emitted when interest is paused or unpaused /// @param isPaused The new paused state event PauseInterest(bool isPaused); function _pauseInterest(bool _isPaused) internal { isInterestPaused = _isPaused; emit PauseInterest(_isPaused); } /// @notice The ```SetCircuitBreaker``` event is emitted when the circuit breaker address is set /// @param oldCircuitBreaker The old circuit breaker address /// @param newCircuitBreaker The new circuit breaker address event SetCircuitBreaker(address oldCircuitBreaker, address newCircuitBreaker); /// @notice The ```_setCircuitBreaker``` function is called to set the circuit breaker address /// @param _newCircuitBreaker The new circuit breaker address function _setCircuitBreaker(address _newCircuitBreaker) internal { address oldCircuitBreaker = circuitBreakerAddress; circuitBreakerAddress = _newCircuitBreaker; emit SetCircuitBreaker(oldCircuitBreaker, _newCircuitBreaker); } /// @notice The ```setCircuitBreaker``` function is called to set the circuit breaker address /// @param _newCircuitBreaker The new circuit breaker address function setCircuitBreaker(address _newCircuitBreaker) external virtual { _requireTimelock(); _setCircuitBreaker(_newCircuitBreaker); } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; // ================ SturdyPairAccessControlErrors =================== /// @title SturdyPairAccessControlErrors /// @author Drake Evans (Frax Finance) https://github.com/drakeevans /// @notice An abstract contract which contains the errors for the Access Control contract abstract contract SturdyPairAccessControlErrors { error OnlyProtocolOrOwner(); error OnlyTimelockOrOwner(); error ExceedsBorrowLimit(); error AccessControlRevoked(); error RepayPaused(); error ExceedsDepositLimit(); error WithdrawPaused(); error LiquidatePaused(); error InterestPaused(); }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; // ===================== SturdyPairConstants ======================== /// @title SturdyPairConstants /// @author Drake Evans (Frax Finance) https://github.com/drakeevans /// @notice An abstract contract which contains the errors and constants for the SturdyPair contract abstract contract SturdyPairConstants { // ============================================================================================ // Constants // ============================================================================================ // Precision settings uint256 internal constant LTV_PRECISION = 1e5; // 5 decimals uint256 internal constant LIQ_PRECISION = 1e5; uint256 internal constant UTIL_PREC = 1e5; uint256 internal constant FEE_PRECISION = 1e5; uint256 internal constant EXCHANGE_PRECISION = 1e18; uint256 internal constant DEVIATION_PRECISION = 1e5; uint256 internal constant RATE_PRECISION = 1e18; // Protocol Fee uint256 internal constant MAX_PROTOCOL_FEE = 5e4; // 50% 1e5 precision error Insolvent(uint256 _borrow, uint256 _collateral, uint256 _exchangeRate); error BorrowerSolvent(); error InsufficientAssetsInContract(uint256 _assets, uint256 _request); error SlippageTooHigh(uint256 _minOut, uint256 _actual); error BadSwapper(); error InvalidPath(address _expected, address _actual); error BadProtocolFee(); error PastDeadline(uint256 _blockTimestamp, uint256 _deadline); error SetterRevoked(); error ExceedsMaxOracleDeviation(); error InvalidReceiver(); error InvalidOnBehalfOf(); error InvalidApproveBorrowDelegation(); error InvalidBorrower(); error InvalidDelegator(); }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; // ========================= SturdyPairCore ========================= import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ReentrancyGuard } from "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; import { SturdyPairAccessControl } from "./SturdyPairAccessControl.sol"; import { SturdyPairConstants } from "./SturdyPairConstants.sol"; import { VaultAccount, VaultAccountingLibrary } from "./libraries/VaultAccount.sol"; import { SafeERC20 } from "./libraries/SafeERC20.sol"; import { IDualOracle } from "./interfaces/IDualOracle.sol"; import { IRateCalculatorV2 } from "./interfaces/IRateCalculatorV2.sol"; import { ISwapper } from "./interfaces/ISwapper.sol"; /// @title SturdyPairCore /// @author Drake Evans (Frax Finance) https://github.com/drakeevans /// @notice An abstract contract which contains the core logic and storage for the SturdyPair abstract contract SturdyPairCore is SturdyPairAccessControl, SturdyPairConstants, ERC20, ReentrancyGuard { using VaultAccountingLibrary for VaultAccount; using SafeERC20 for IERC20; using SafeCast for uint256; function version() external pure returns (uint256 _major, uint256 _minor, uint256 _patch) { _major = 3; _minor = 0; _patch = 0; } // ============================================================================================ // Settings set by constructor() // ============================================================================================ // Asset and collateral contracts IERC20 internal immutable assetContract; IERC20 public immutable collateralContract; // LTV Settings /// @notice The maximum LTV allowed for this pair /// @dev 1e5 precision uint256 public maxLTV; // Liquidation Fees /// @notice The liquidation fee, given as a % of repayment amount, when all collateral is consumed in liquidation /// @dev 1e5 precision uint256 public cleanLiquidationFee; /// @notice The liquidation fee, given as % of repayment amount, when some collateral remains for borrower /// @dev 1e5 precision uint256 public dirtyLiquidationFee; /// @notice The portion of the liquidation fee given to protocol /// @dev 1e5 precision uint256 public protocolLiquidationFee; // Interest Rate Calculator Contract IRateCalculatorV2 public rateContract; // For complex rate calculations // Swapper mapping(address => bool) public swappers; // approved swapper addresses // ERC20 Metadata string internal nameOfContract; string internal symbolOfContract; uint8 internal immutable decimalsOfContract; // ============================================================================================ // Storage // ============================================================================================ /// @notice Stores information about the current interest rate /// @dev struct is packed to reduce SLOADs. feeToProtocolRate is 1e5 precision, ratePerSec & fullUtilizationRate is 1e18 precision CurrentRateInfo public currentRateInfo; struct CurrentRateInfo { uint32 lastBlock; uint32 feeToProtocolRate; // Fee amount 1e5 precision uint64 lastTimestamp; uint64 ratePerSec; uint64 fullUtilizationRate; } /// @notice Stores information about the current exchange rate. Collateral:Asset ratio /// @dev Struct packed to save SLOADs. Amount of Collateral Token to buy 1e18 Asset Token ExchangeRateInfo public exchangeRateInfo; struct ExchangeRateInfo { address oracle; uint32 maxOracleDeviation; // % of larger number, 1e5 precision uint184 lastTimestamp; uint256 lowExchangeRate; uint256 highExchangeRate; } // Contract Level Accounting VaultAccount public totalAsset; // amount = total amount of assets, shares = total shares outstanding VaultAccount public totalBorrow; // amount = total borrow amount with interest accrued, shares = total shares outstanding uint256 public totalCollateral; // total amount of collateral in contract // User Level Accounting /// @notice Stores the balance of collateral for each user mapping(address => uint256) public userCollateralBalance; // amount of collateral each user is backed /// @notice Stores the balance of borrow shares for each user mapping(address => uint256) public userBorrowShares; // represents the shares held by individuals /// @notice Stores the borrow allowance of the user mapping(address => mapping(address => uint256)) public userBorrowAllowances; // represent the borrowable amount on behalfof user /// @notice Stores the whitelisted delegators who call the removeCollateralFrom(), ex: Leverage contracts mapping(address => bool) public whitelistedDelegators; // represent the leverage contract who call the removeCollateralFrom() // NOTE: user shares of assets are represented as ERC-20 tokens and accessible via balanceOf() // ============================================================================================ // Constructor // ============================================================================================ /// @notice The ```constructor``` function is called on deployment /// @param _configData abi.encode(address _asset, address _collateral, address _oracle, uint32 _maxOracleDeviation, address _rateContract, uint64 _fullUtilizationRate, uint256 _maxLTV, uint256 _cleanLiquidationFee, uint256 _dirtyLiquidationFee, uint256 _protocolLiquidationFee) /// @param _immutables abi.encode(address _circuitBreakerAddress, address _comptrollerAddress, address _timelockAddress) /// @param _customConfigData abi.encode(string memory _nameOfContract, string memory _symbolOfContract, uint8 _decimalsOfContract) constructor( bytes memory _configData, bytes memory _immutables, bytes memory _customConfigData ) SturdyPairAccessControl(_immutables) ERC20("", "") { { ( address _asset, address _collateral, address _oracle, uint32 _maxOracleDeviation, address _rateContract, uint64 _fullUtilizationRate, uint256 _maxLTV, uint256 _liquidationFee, uint256 _protocolLiquidationFee ) = abi.decode( _configData, (address, address, address, uint32, address, uint64, uint256, uint256, uint256) ); // Pair Settings assetContract = IERC20(_asset); collateralContract = IERC20(_collateral); currentRateInfo.feeToProtocolRate = 0; currentRateInfo.fullUtilizationRate = _fullUtilizationRate; currentRateInfo.lastTimestamp = uint64(block.timestamp - 1); currentRateInfo.lastBlock = uint32(block.number - 1); exchangeRateInfo.oracle = _oracle; exchangeRateInfo.maxOracleDeviation = _maxOracleDeviation; rateContract = IRateCalculatorV2(_rateContract); //Liquidation Fee Settings cleanLiquidationFee = _liquidationFee; dirtyLiquidationFee = (_liquidationFee * 90_000) / LIQ_PRECISION; // 90% of clean fee protocolLiquidationFee = _protocolLiquidationFee; // set maxLTV maxLTV = _maxLTV; } { (string memory _nameOfContract, string memory _symbolOfContract, uint8 _decimalsOfContract) = abi.decode( _customConfigData, (string, string, uint8) ); // ERC20 Metadata nameOfContract = _nameOfContract; symbolOfContract = _symbolOfContract; decimalsOfContract = _decimalsOfContract; // Instantiate Interest _addInterest(); // Instantiate Exchange Rate _updateExchangeRate(); } } // ============================================================================================ // Internal Helpers // ============================================================================================ /// @notice The ```_totalAssetAvailable``` function returns the total balance of Asset Tokens in the contract /// @param _totalAsset VaultAccount struct which stores total amount and shares for assets /// @param _totalBorrow VaultAccount struct which stores total amount and shares for borrows /// @return The balance of Asset Tokens held by contract function _totalAssetAvailable( VaultAccount memory _totalAsset, VaultAccount memory _totalBorrow ) internal pure returns (uint256) { return _totalAsset.amount - _totalBorrow.amount; } /// @notice The ```_isSolvent``` function determines if a given borrower is solvent given an exchange rate /// @param _borrower The borrower address to check /// @param _exchangeRate The exchange rate, i.e. the amount of collateral to buy 1e18 asset /// @return Whether borrower is solvent function _isSolvent(address _borrower, uint256 _exchangeRate) internal view returns (bool) { if (maxLTV == 0) return true; uint256 _borrowerAmount = totalBorrow.toAmount(userBorrowShares[_borrower], true); if (_borrowerAmount == 0) return true; uint256 _collateralAmount = userCollateralBalance[_borrower]; if (_collateralAmount == 0) return false; uint256 _ltv = (((_borrowerAmount * _exchangeRate) / EXCHANGE_PRECISION) * LTV_PRECISION) / _collateralAmount; return _ltv <= maxLTV; } // ============================================================================================ // Modifiers // ============================================================================================ /// @notice Checks for solvency AFTER executing contract code /// @param _borrower The borrower whose solvency we will check modifier isSolvent(address _borrower) { _; ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo; if (!_isSolvent(_borrower, exchangeRateInfo.highExchangeRate)) { revert Insolvent( totalBorrow.toAmount(userBorrowShares[_borrower], true), userCollateralBalance[_borrower], exchangeRateInfo.highExchangeRate ); } } // ============================================================================================ // Functions: Interest Accumulation and Adjustment // ============================================================================================ /// @notice The ```AddInterest``` event is emitted when interest is accrued by borrowers /// @param interestEarned The total interest accrued by all borrowers /// @param rate The interest rate used to calculate accrued interest /// @param feesAmount The amount of fees paid to protocol /// @param feesShare The amount of shares distributed to protocol event AddInterest(uint256 interestEarned, uint256 rate, uint256 feesAmount, uint256 feesShare); /// @notice The ```UpdateRate``` event is emitted when the interest rate is updated /// @param oldRatePerSec The old interest rate (per second) /// @param oldFullUtilizationRate The old full utilization rate /// @param newRatePerSec The new interest rate (per second) /// @param newFullUtilizationRate The new full utilization rate event UpdateRate( uint256 oldRatePerSec, uint256 oldFullUtilizationRate, uint256 newRatePerSec, uint256 newFullUtilizationRate ); /// @notice The ```addInterest``` function is a public implementation of _addInterest and allows 3rd parties to trigger interest accrual /// @return _interestEarned The amount of interest accrued by all borrowers /// @return _feesAmount The amount of fees paid to protocol /// @return _feesShare The amount of shares distributed to protocol /// @return _currentRateInfo The new rate info struct /// @return _totalAsset The new total asset struct /// @return _totalBorrow The new total borrow struct function addInterest( bool _returnAccounting ) external nonReentrant returns ( uint256 _interestEarned, uint256 _feesAmount, uint256 _feesShare, CurrentRateInfo memory _currentRateInfo, VaultAccount memory _totalAsset, VaultAccount memory _totalBorrow ) { (, _interestEarned, _feesAmount, _feesShare, _currentRateInfo) = _addInterest(); if (_returnAccounting) { _totalAsset = totalAsset; _totalBorrow = totalBorrow; } } /// @notice The ```previewAddInterest``` function /// @return _interestEarned The amount of interest accrued by all borrowers /// @return _feesAmount The amount of fees paid to protocol /// @return _feesShare The amount of shares distributed to protocol /// @return _newCurrentRateInfo The new rate info struct /// @return _totalAsset The new total asset struct /// @return _totalBorrow The new total borrow struct function previewAddInterest() public view returns ( uint256 _interestEarned, uint256 _feesAmount, uint256 _feesShare, CurrentRateInfo memory _newCurrentRateInfo, VaultAccount memory _totalAsset, VaultAccount memory _totalBorrow ) { _newCurrentRateInfo = currentRateInfo; // Write return values InterestCalculationResults memory _results = _calculateInterest(_newCurrentRateInfo); if (_results.isInterestUpdated) { _interestEarned = _results.interestEarned; _feesAmount = _results.feesAmount; _feesShare = _results.feesShare; _newCurrentRateInfo.ratePerSec = _results.newRate; _newCurrentRateInfo.fullUtilizationRate = _results.newFullUtilizationRate; _totalAsset = _results.totalAsset; _totalBorrow = _results.totalBorrow; } else { _totalAsset = totalAsset; _totalBorrow = totalBorrow; } } struct InterestCalculationResults { bool isInterestUpdated; uint64 newRate; uint64 newFullUtilizationRate; uint256 interestEarned; uint256 feesAmount; uint256 feesShare; VaultAccount totalAsset; VaultAccount totalBorrow; } /// @notice The ```_calculateInterest``` function calculates the interest to be accrued and the new interest rate info /// @param _currentRateInfo The current rate info /// @return _results The results of the interest calculation function _calculateInterest( CurrentRateInfo memory _currentRateInfo ) internal view returns (InterestCalculationResults memory _results) { // Short circuit if interest already calculated this block OR if interest is paused if (_currentRateInfo.lastTimestamp != block.timestamp && !isInterestPaused) { // Indicate that interest is updated and calculated _results.isInterestUpdated = true; // Write return values and use these to save gas _results.totalAsset = totalAsset; _results.totalBorrow = totalBorrow; // Time elapsed since last interest update uint256 _deltaTime = block.timestamp - _currentRateInfo.lastTimestamp; // Get the utilization rate uint256 _utilizationRate = _results.totalAsset.amount == 0 ? 0 : (UTIL_PREC * _results.totalBorrow.amount) / _results.totalAsset.amount; // Request new interest rate and full utilization rate from the rate calculator (_results.newRate, _results.newFullUtilizationRate) = IRateCalculatorV2(rateContract).getNewRate( _deltaTime, _utilizationRate, _currentRateInfo.fullUtilizationRate ); // Calculate interest accrued _results.interestEarned = (_deltaTime * _results.totalBorrow.amount * _results.newRate) / RATE_PRECISION; // Accrue interest (if any) and fees iff no overflow if ( _results.interestEarned > 0 && _results.interestEarned + _results.totalBorrow.amount <= type(uint128).max && _results.interestEarned + _results.totalAsset.amount <= type(uint128).max ) { // Increment totalBorrow and totalAsset by interestEarned _results.totalBorrow.amount += uint128(_results.interestEarned); _results.totalAsset.amount += uint128(_results.interestEarned); if (_currentRateInfo.feeToProtocolRate > 0) { _results.feesAmount = (_results.interestEarned * _currentRateInfo.feeToProtocolRate) / FEE_PRECISION; _results.feesShare = (_results.feesAmount * _results.totalAsset.shares) / (_results.totalAsset.amount - _results.feesAmount); // Effects: Give new shares to this contract, effectively diluting lenders an amount equal to the fees // We can safely cast because _feesShare < _feesAmount < interestEarned which is always less than uint128 _results.totalAsset.shares += uint128(_results.feesShare); } } } } /// @notice The ```_addInterest``` function is invoked prior to every external function and is used to accrue interest and update interest rate /// @dev Can only called once per block /// @return _isInterestUpdated True if interest was calculated /// @return _interestEarned The amount of interest accrued by all borrowers /// @return _feesAmount The amount of fees paid to protocol /// @return _feesShare The amount of shares distributed to protocol /// @return _currentRateInfo The new rate info struct function _addInterest() internal returns ( bool _isInterestUpdated, uint256 _interestEarned, uint256 _feesAmount, uint256 _feesShare, CurrentRateInfo memory _currentRateInfo ) { // Pull from storage and set default return values _currentRateInfo = currentRateInfo; // Calc interest InterestCalculationResults memory _results = _calculateInterest(_currentRateInfo); // Write return values only if interest was updated and calculated if (_results.isInterestUpdated) { _isInterestUpdated = _results.isInterestUpdated; _interestEarned = _results.interestEarned; _feesAmount = _results.feesAmount; _feesShare = _results.feesShare; // emit here so that we have access to the old values emit UpdateRate( _currentRateInfo.ratePerSec, _currentRateInfo.fullUtilizationRate, _results.newRate, _results.newFullUtilizationRate ); emit AddInterest(_interestEarned, _results.newRate, _feesAmount, _feesShare); // overwrite original values _currentRateInfo.ratePerSec = _results.newRate; _currentRateInfo.fullUtilizationRate = _results.newFullUtilizationRate; _currentRateInfo.lastTimestamp = uint64(block.timestamp); _currentRateInfo.lastBlock = uint32(block.number); // Effects: write to state currentRateInfo = _currentRateInfo; totalAsset = _results.totalAsset; totalBorrow = _results.totalBorrow; if (_feesShare > 0) _mint(address(this), _feesShare); } } // ============================================================================================ // Functions: ExchangeRate // ============================================================================================ /// @notice The ```UpdateExchangeRate``` event is emitted when the Collateral:Asset exchange rate is updated /// @param lowExchangeRate The low exchange rate /// @param highExchangeRate The high exchange rate event UpdateExchangeRate(uint256 lowExchangeRate, uint256 highExchangeRate); /// @notice The ```WarnOracleData``` event is emitted when one of the oracles has stale or otherwise problematic data /// @param oracle The oracle address event WarnOracleData(address oracle); /// @notice The ```updateExchangeRate``` function is the external implementation of _updateExchangeRate. /// @dev This function is invoked at most once per block as these queries can be expensive /// @return _isBorrowAllowed True if deviation is within bounds /// @return _lowExchangeRate The low exchange rate /// @return _highExchangeRate The high exchange rate function updateExchangeRate() external nonReentrant returns (bool _isBorrowAllowed, uint256 _lowExchangeRate, uint256 _highExchangeRate) { return _updateExchangeRate(); } /// @notice The ```_updateExchangeRate``` function retrieves the latest exchange rate. i.e how much collateral to buy 1e18 asset. /// @dev This function is invoked at most once per block as these queries can be expensive /// @return _isBorrowAllowed True if deviation is within bounds /// @return _lowExchangeRate The low exchange rate /// @return _highExchangeRate The high exchange rate function _updateExchangeRate() internal returns (bool _isBorrowAllowed, uint256 _lowExchangeRate, uint256 _highExchangeRate) { // Pull from storage to save gas and set default return values ExchangeRateInfo memory _exchangeRateInfo = exchangeRateInfo; // Short circuit if already updated this block if (_exchangeRateInfo.lastTimestamp != block.timestamp) { // Get the latest exchange rate from the dual oracle bool _oneOracleBad; (_oneOracleBad, _lowExchangeRate, _highExchangeRate) = IDualOracle(_exchangeRateInfo.oracle).getPrices(); // If one oracle is bad data, emit an event for off-chain monitoring if (_oneOracleBad) emit WarnOracleData(_exchangeRateInfo.oracle); // Effects: Bookkeeping and write to storage _exchangeRateInfo.lastTimestamp = uint184(block.timestamp); _exchangeRateInfo.lowExchangeRate = _lowExchangeRate; _exchangeRateInfo.highExchangeRate = _highExchangeRate; exchangeRateInfo = _exchangeRateInfo; emit UpdateExchangeRate(_lowExchangeRate, _highExchangeRate); } else { // Use default return values if already updated this block _lowExchangeRate = _exchangeRateInfo.lowExchangeRate; _highExchangeRate = _exchangeRateInfo.highExchangeRate; } uint256 _deviation = (DEVIATION_PRECISION * (_exchangeRateInfo.highExchangeRate - _exchangeRateInfo.lowExchangeRate)) / _exchangeRateInfo.highExchangeRate; if (_deviation <= _exchangeRateInfo.maxOracleDeviation) { _isBorrowAllowed = true; } } // ============================================================================================ // Functions: Lending // ============================================================================================ /// @notice The ```Deposit``` event fires when a user deposits assets to the pair /// @param caller the msg.sender /// @param owner the account the fTokens are sent to /// @param assets the amount of assets deposited /// @param shares the number of fTokens minted event Deposit(address indexed caller, address indexed owner, uint256 assets, uint256 shares); /// @notice The ```_deposit``` function is the internal implementation for lending assets /// @dev Caller must invoke ```ERC20.approve``` on the Asset Token contract prior to calling function /// @param _totalAsset An in memory VaultAccount struct representing the total amounts and shares for the Asset Token /// @param _amount The amount of Asset Token to be transferred /// @param _shares The amount of Asset Shares (fTokens) to be minted /// @param _receiver The address to receive the Asset Shares (fTokens) function _deposit(VaultAccount memory _totalAsset, uint128 _amount, uint128 _shares, address _receiver) internal { // Effects: bookkeeping _totalAsset.amount += _amount; _totalAsset.shares += _shares; // Effects: write back to storage _mint(_receiver, _shares); totalAsset = _totalAsset; // Interactions assetContract.safeTransferFrom(msg.sender, address(this), _amount); emit Deposit(msg.sender, _receiver, _amount, _shares); } function previewDeposit(uint256 _assets) external view returns (uint256 _sharesReceived) { (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest(); _sharesReceived = _totalAsset.toShares(_assets, false); } /// @notice The ```deposit``` function allows a user to Lend Assets by specifying the amount of Asset Tokens to lend /// @dev Caller must invoke ```ERC20.approve``` on the Asset Token contract prior to calling function /// @param _amount The amount of Asset Token to transfer to Pair /// @param _receiver The address to receive the Asset Shares (fTokens) /// @return _sharesReceived The number of fTokens received for the deposit function deposit(uint256 _amount, address _receiver) external nonReentrant returns (uint256 _sharesReceived) { if (_receiver == address(0)) revert InvalidReceiver(); // Accrue interest if necessary _addInterest(); // Pull from storage to save gas VaultAccount memory _totalAsset = totalAsset; // Check if this deposit will violate the deposit limit if (depositLimit < _totalAsset.amount + _amount) revert ExceedsDepositLimit(); // Calculate the number of fTokens to mint _sharesReceived = _totalAsset.toShares(_amount, false); // Execute the deposit effects _deposit(_totalAsset, _amount.toUint128(), _sharesReceived.toUint128(), _receiver); } function previewMint(uint256 _shares) external view returns (uint256 _amount) { (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest(); _amount = _totalAsset.toAmount(_shares, false); } function mint(uint256 _shares, address _receiver) external nonReentrant returns (uint256 _amount) { if (_receiver == address(0)) revert InvalidReceiver(); // Accrue interest if necessary _addInterest(); // Pull from storage to save gas VaultAccount memory _totalAsset = totalAsset; // Calculate the number of assets to transfer based on the shares to mint _amount = _totalAsset.toAmount(_shares, false); // Check if this deposit will violate the deposit limit if (depositLimit < _totalAsset.amount + _amount) revert ExceedsDepositLimit(); // Execute the deposit effects _deposit(_totalAsset, _amount.toUint128(), _shares.toUint128(), _receiver); } /// @notice The ```Withdraw``` event fires when a user redeems their fTokens for the underlying asset /// @param caller the msg.sender /// @param receiver The address to which the underlying asset will be transferred to /// @param owner The owner of the fTokens /// @param assets The assets transferred /// @param shares The number of fTokens burned event Withdraw( address indexed caller, address indexed receiver, address indexed owner, uint256 assets, uint256 shares ); /// @notice The ```_redeem``` function is an internal implementation which allows a Lender to pull their Asset Tokens out of the Pair /// @dev Caller must invoke ```ERC20.approve``` on the Asset Token contract prior to calling function /// @param _totalAsset An in-memory VaultAccount struct which holds the total amount of Asset Tokens and the total number of Asset Shares (fTokens) /// @param _amountToReturn The number of Asset Tokens to return /// @param _shares The number of Asset Shares (fTokens) to burn /// @param _receiver The address to which the Asset Tokens will be transferred /// @param _owner The owner of the Asset Shares (fTokens) function _redeem( VaultAccount memory _totalAsset, uint128 _amountToReturn, uint128 _shares, address _receiver, address _owner ) internal { // Check for sufficient allowance/approval if necessary if (msg.sender != _owner) { uint256 allowed = allowance(_owner, msg.sender); // NOTE: This will revert on underflow ensuring that allowance > shares if (allowed != type(uint256).max) _approve(_owner, msg.sender, allowed - _shares); } // Check for sufficient withdraw liquidity (not strictly necessary because balance will underflow) uint256 _assetsAvailable = _totalAssetAvailable(_totalAsset, totalBorrow); if (_assetsAvailable < _amountToReturn) { revert InsufficientAssetsInContract(_assetsAvailable, _amountToReturn); } // Effects: bookkeeping _totalAsset.amount -= _amountToReturn; _totalAsset.shares -= _shares; // Effects: write to storage totalAsset = _totalAsset; _burn(_owner, _shares); // Interactions assetContract.safeTransfer(_receiver, _amountToReturn); emit Withdraw(msg.sender, _receiver, _owner, _amountToReturn, _shares); } function previewRedeem(uint256 _shares) external view returns (uint256 _assets) { (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest(); _assets = _totalAsset.toAmount(_shares, false); } /// @notice The ```redeem``` function allows the caller to redeem their Asset Shares for Asset Tokens /// @param _shares The number of Asset Shares (fTokens) to burn for Asset Tokens /// @param _receiver The address to which the Asset Tokens will be transferred /// @param _owner The owner of the Asset Shares (fTokens) /// @return _amountToReturn The amount of Asset Tokens to be transferred function redeem( uint256 _shares, address _receiver, address _owner ) external nonReentrant returns (uint256 _amountToReturn) { if (_receiver == address(0)) revert InvalidReceiver(); // Check if withdraw is paused and revert if necessary if (isWithdrawPaused) revert WithdrawPaused(); // Accrue interest if necessary _addInterest(); // Pull from storage to save gas VaultAccount memory _totalAsset = totalAsset; // Calculate the number of assets to transfer based on the shares to burn _amountToReturn = _totalAsset.toAmount(_shares, false); // Execute the withdraw effects _redeem(_totalAsset, _amountToReturn.toUint128(), _shares.toUint128(), _receiver, _owner); } /// @notice The ```previewWithdraw``` function returns the number of Asset Shares (fTokens) that would be burned for a given amount of Asset Tokens /// @param _amount The amount of Asset Tokens to be withdrawn /// @return _sharesToBurn The number of shares that would be burned function previewWithdraw(uint256 _amount) external view returns (uint256 _sharesToBurn) { (, , , , VaultAccount memory _totalAsset, ) = previewAddInterest(); _sharesToBurn = _totalAsset.toShares(_amount, true); } /// @notice The ```withdraw``` function allows the caller to withdraw their Asset Tokens for a given amount of fTokens /// @param _amount The amount to withdraw /// @param _receiver The address to which the Asset Tokens will be transferred /// @param _owner The owner of the Asset Shares (fTokens) /// @return _sharesToBurn The number of shares (fTokens) that were burned function withdraw( uint256 _amount, address _receiver, address _owner ) external nonReentrant returns (uint256 _sharesToBurn) { if (_receiver == address(0)) revert InvalidReceiver(); // Check if withdraw is paused and revert if necessary if (isWithdrawPaused) revert WithdrawPaused(); // Accrue interest if necessary _addInterest(); // Pull from storage to save gas VaultAccount memory _totalAsset = totalAsset; // Calculate the number of shares to burn based on the amount to withdraw _sharesToBurn = _totalAsset.toShares(_amount, true); // Execute the withdraw effects _redeem(_totalAsset, _amount.toUint128(), _sharesToBurn.toUint128(), _receiver, _owner); } // ============================================================================================ // Functions: Borrowing // ============================================================================================ /// @notice The ```BorrowAsset``` event is emitted when a borrower increases their position /// @param _borrower The borrower whose account was debited /// @param _receiver The address to which the Asset Tokens were transferred /// @param _borrowAmount The amount of Asset Tokens transferred /// @param _sharesAdded The number of Borrow Shares the borrower was debited event BorrowAsset( address indexed _borrower, address indexed _receiver, uint256 _borrowAmount, uint256 _sharesAdded ); /// @notice The ```_borrowAsset``` function is the internal implementation for borrowing assets /// @param _borrowAmount The amount of the Asset Token to borrow /// @param _receiver The address to receive the Asset Tokens /// @return _sharesAdded The amount of borrow shares the msg.sender will be debited function _borrowAsset(uint128 _borrowAmount, address _receiver) internal returns (uint256 _sharesAdded) { // Get borrow accounting from storage to save gas VaultAccount memory _totalBorrow = totalBorrow; // Check available capital (not strictly necessary because balance will underflow, but better revert message) uint256 _assetsAvailable = _totalAssetAvailable(totalAsset, _totalBorrow); if (_assetsAvailable < _borrowAmount) { revert InsufficientAssetsInContract(_assetsAvailable, _borrowAmount); } // Calculate the number of shares to add based on the amount to borrow _sharesAdded = _totalBorrow.toShares(_borrowAmount, true); // Effects: Bookkeeping to add shares & amounts to total Borrow accounting _totalBorrow.amount += _borrowAmount; _totalBorrow.shares += uint128(_sharesAdded); // NOTE: we can safely cast here because shares are always less than amount and _borrowAmount is uint128 // Effects: write back to storage totalBorrow = _totalBorrow; userBorrowShares[msg.sender] += _sharesAdded; // Interactions if (_receiver != address(this)) { assetContract.safeTransfer(_receiver, _borrowAmount); } emit BorrowAsset(msg.sender, _receiver, _borrowAmount, _sharesAdded); } /// @notice The ```_borrowAssetOnBehalfOf``` function is the internal implementation for borrowing assets on behalf of borrower /// @dev msg.sender will receive the Asset Tokens /// @param _borrowAmount The amount of the Asset Token to borrow /// @param _onBehalfOf The address which will receive the debt. Should be the address of the borrower itself /// @return _sharesAdded The amount of borrow shares the _onBehalfOf will be debited function _borrowAssetOnBehalfOf(uint128 _borrowAmount, address _onBehalfOf) internal returns (uint256 _sharesAdded) { // Get borrow accounting from storage to save gas VaultAccount memory _totalBorrow = totalBorrow; // Check available capital (not strictly necessary because balance will underflow, but better revert message) uint256 _assetsAvailable = _totalAssetAvailable(totalAsset, _totalBorrow); if (_assetsAvailable < _borrowAmount) { revert InsufficientAssetsInContract(_assetsAvailable, _borrowAmount); } // decrease borrowAllowance (borrowing power) uint256 newAllowance = userBorrowAllowances[_onBehalfOf][msg.sender] - _borrowAmount; userBorrowAllowances[_onBehalfOf][msg.sender] = newAllowance; emit UserBorrowAllowanceDelegated(_onBehalfOf, msg.sender, newAllowance); // Calculate the number of shares to add based on the amount to borrow _sharesAdded = _totalBorrow.toShares(_borrowAmount, true); // Effects: Bookkeeping to add shares & amounts to total Borrow accounting _totalBorrow.amount += _borrowAmount; _totalBorrow.shares += uint128(_sharesAdded); // NOTE: we can safely cast here because shares are always less than amount and _borrowAmount is uint128 // Effects: write back to storage totalBorrow = _totalBorrow; userBorrowShares[_onBehalfOf] += _sharesAdded; // Interactions assetContract.safeTransfer(msg.sender, _borrowAmount); emit BorrowAsset(_onBehalfOf, msg.sender, _borrowAmount, _sharesAdded); } /// @notice The ```borrowAsset``` function allows a user to open/increase a borrow position /// @dev Borrower must call ```ERC20.approve``` on the Collateral Token contract if applicable /// @param _borrowAmount The amount of Asset Token to borrow /// @param _collateralAmount The amount of Collateral Token to transfer to Pair /// @param _receiver The address which will receive the Asset Tokens /// @return _shares The number of borrow Shares the msg.sender will be debited function borrowAsset( uint256 _borrowAmount, uint256 _collateralAmount, address _receiver ) external nonReentrant isSolvent(msg.sender) returns (uint256 _shares) { if (_receiver == address(0)) revert InvalidReceiver(); // Accrue interest if necessary _addInterest(); // Check if borrow will violate the borrow limit and revert if necessary if (borrowLimit < totalBorrow.amount + _borrowAmount) revert ExceedsBorrowLimit(); // Update _exchangeRate and check if borrow is allowed based on deviation (bool _isBorrowAllowed, , ) = _updateExchangeRate(); if (!_isBorrowAllowed) revert ExceedsMaxOracleDeviation(); // Only add collateral if necessary if (_collateralAmount > 0) { _addCollateral(msg.sender, _collateralAmount, msg.sender); } // Effects: Call internal borrow function _shares = _borrowAsset(_borrowAmount.toUint128(), _receiver); } /// @notice The ```borrowAssetOnBehalfOf``` function allows a user to open/increase a borrow position on behalf of borrower /// @dev msg.sender will receive the Asset Tokens /// @param _borrowAmount The amount of Asset Token to borrow /// @param _onBehalfOf The address which will receive the debt. Should be the address of the borrower itself /// @return _shares The number of borrow Shares the _onBehalfOf will be debited function borrowAssetOnBehalfOf( uint256 _borrowAmount, address _onBehalfOf ) external nonReentrant isSolvent(_onBehalfOf) returns (uint256 _shares) { if (_onBehalfOf == address(0) || msg.sender == _onBehalfOf) revert InvalidOnBehalfOf(); // Accrue interest if necessary _addInterest(); // Check if borrow will violate the borrow limit and revert if necessary if (borrowLimit < totalBorrow.amount + _borrowAmount) revert ExceedsBorrowLimit(); // Update _exchangeRate and check if borrow is allowed based on deviation (bool _isBorrowAllowed, , ) = _updateExchangeRate(); if (!_isBorrowAllowed) revert ExceedsMaxOracleDeviation(); // Effects: Call internal borrow function _shares = _borrowAssetOnBehalfOf(_borrowAmount.toUint128(), _onBehalfOf); } /// @notice The ```UserBorrowAllowanceDelegated``` event is emitted when a borrower delegates borrowing power to a user /// @param _fromUser The borrower who delegates borrowing power /// @param _toUser The user who receive the borrowing power from borrower /// @param _amount The max amount being delegated event UserBorrowAllowanceDelegated( address indexed _fromUser, address indexed _toUser, uint256 _amount ); /// @notice The ```approveBorrowDelegation``` function delegates borrowing power to a user /// @param _delegatee the address receiving the delegated borrowing power /// @param _amount the maximum amount being delegated. function approveBorrowDelegation(address _delegatee, uint256 _amount) external { // To change the approve amount you first have to reduce the addresses` // allowance to zero by calling `approveBorrowDelegation(_delegatee, 0)` if it is not // already 0 to mitigate the race condition described here: // https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 if ((_amount != 0) && (userBorrowAllowances[msg.sender][_delegatee] != 0)) { revert InvalidApproveBorrowDelegation(); } userBorrowAllowances[msg.sender][_delegatee] = _amount; emit UserBorrowAllowanceDelegated(msg.sender, _delegatee, _amount); } /// @notice The ```AddCollateral``` event is emitted when a borrower adds collateral to their position /// @param sender The source of funds for the new collateral /// @param borrower The borrower account for which the collateral should be credited /// @param collateralAmount The amount of Collateral Token to be transferred event AddCollateral(address indexed sender, address indexed borrower, uint256 collateralAmount); /// @notice The ```_addCollateral``` function is an internal implementation for adding collateral to a borrowers position /// @param _sender The source of funds for the new collateral /// @param _collateralAmount The amount of Collateral Token to be transferred /// @param _borrower The borrower account for which the collateral should be credited function _addCollateral(address _sender, uint256 _collateralAmount, address _borrower) internal { // Effects: write to state userCollateralBalance[_borrower] += _collateralAmount; totalCollateral += _collateralAmount; // Interactions if (_sender != address(this)) { collateralContract.safeTransferFrom(_sender, address(this), _collateralAmount); } emit AddCollateral(_sender, _borrower, _collateralAmount); } /// @notice The ```addCollateral``` function allows the caller to add Collateral Token to a borrowers position /// @dev msg.sender must call ERC20.approve() on the Collateral Token contract prior to invocation /// @param _collateralAmount The amount of Collateral Token to be added to borrower's position /// @param _borrower The account to be credited function addCollateral(uint256 _collateralAmount, address _borrower) external nonReentrant { if (_borrower == address(0)) revert InvalidReceiver(); _addInterest(); _addCollateral(msg.sender, _collateralAmount, _borrower); } /// @notice The ```RemoveCollateral``` event is emitted when collateral is removed from a borrower's position /// @param _sender The account from which funds are transferred /// @param _collateralAmount The amount of Collateral Token to be transferred /// @param _receiver The address to which Collateral Tokens will be transferred event RemoveCollateral( address indexed _sender, uint256 _collateralAmount, address indexed _receiver, address indexed _borrower ); /// @notice The ```_removeCollateral``` function is the internal implementation for removing collateral from a borrower's position /// @param _collateralAmount The amount of Collateral Token to remove from the borrower's position /// @param _receiver The address to receive the Collateral Token transferred /// @param _borrower The borrower whose account will be debited the Collateral amount function _removeCollateral(uint256 _collateralAmount, address _receiver, address _borrower) internal { // Effects: write to state // NOTE: Following line will revert on underflow if _collateralAmount > userCollateralBalance userCollateralBalance[_borrower] -= _collateralAmount; // NOTE: Following line will revert on underflow if totalCollateral < _collateralAmount totalCollateral -= _collateralAmount; // Interactions if (_receiver != address(this)) { collateralContract.safeTransfer(_receiver, _collateralAmount); } emit RemoveCollateral(msg.sender, _collateralAmount, _receiver, _borrower); } /// @notice The ```removeCollateral``` function is used to remove collateral from msg.sender's borrow position /// @dev msg.sender must be solvent after invocation or transaction will revert /// @param _collateralAmount The amount of Collateral Token to transfer /// @param _receiver The address to receive the transferred funds function removeCollateral( uint256 _collateralAmount, address _receiver ) external nonReentrant isSolvent(msg.sender) { if (_receiver == address(0)) revert InvalidReceiver(); _addInterest(); // Note: exchange rate is irrelevant when borrower has no debt shares if (userBorrowShares[msg.sender] > 0) { (bool _isBorrowAllowed, , ) = _updateExchangeRate(); if (!_isBorrowAllowed) revert ExceedsMaxOracleDeviation(); } _removeCollateral(_collateralAmount, _receiver, msg.sender); } /// @notice The ```SetWhitelistedDelegators``` event is emitted when admin enable/disable delegator who can call removeCollateralFrom() /// @param _delegator the address of contract who can call the removeCollateralFrom() /// @param _enabled the enable/disable flag. event SetWhitelistedDelegators( address indexed _delegator, bool _enabled ); /// @notice The ```setWhitelistedDelegators``` function enable/disable the delegators who can call the removeCollateralFrom() /// @param _delegator the address of contract who can call the removeCollateralFrom() /// @param _enabled the enable/disable flag. function setWhitelistedDelegators(address _delegator, bool _enabled) external payable onlyOwner { whitelistedDelegators[_delegator] = _enabled; emit SetWhitelistedDelegators(_delegator, _enabled); } /// @notice The ```removeCollateralFrom``` function is used to remove collateral from the borrower /// @dev caller must be delegator(ex: leverage contract) and borrower must be solvent after invocation or transaction will revert /// @param _collateralAmount The amount of Collateral Token to transfer /// @param _receiver The address to receive the transferred funds /// @param _borrower The address removing collateral from function removeCollateralFrom( uint256 _collateralAmount, address _receiver, address _borrower ) external payable nonReentrant isSolvent(_borrower) { if(whitelistedDelegators[msg.sender] == false) revert InvalidDelegator(); if (_receiver == address(0)) revert InvalidReceiver(); if (_borrower == address(0)) revert InvalidBorrower(); _addInterest(); // Note: exchange rate is irrelevant when borrower has no debt shares if (userBorrowShares[_borrower] > 0) { (bool _isBorrowAllowed, , ) = _updateExchangeRate(); if (!_isBorrowAllowed) revert ExceedsMaxOracleDeviation(); } _removeCollateral(_collateralAmount, _receiver, _borrower); } /// @notice The ```RepayAsset``` event is emitted whenever a debt position is repaid /// @param payer The address paying for the repayment /// @param borrower The borrower whose account will be credited /// @param amountToRepay The amount of Asset token to be transferred /// @param shares The amount of Borrow Shares which will be debited from the borrower after repayment event RepayAsset(address indexed payer, address indexed borrower, uint256 amountToRepay, uint256 shares); /// @notice The ```_repayAsset``` function is the internal implementation for repaying a borrow position /// @dev The payer must have called ERC20.approve() on the Asset Token contract prior to invocation /// @param _totalBorrow An in memory copy of the totalBorrow VaultAccount struct /// @param _amountToRepay The amount of Asset Token to transfer /// @param _shares The number of Borrow Shares the sender is repaying /// @param _payer The address from which funds will be transferred /// @param _borrower The borrower account which will be credited function _repayAsset( VaultAccount memory _totalBorrow, uint128 _amountToRepay, uint128 _shares, address _payer, address _borrower ) internal { // Effects: Bookkeeping _totalBorrow.amount -= _amountToRepay; _totalBorrow.shares -= _shares; // Effects: write to state userBorrowShares[_borrower] -= _shares; totalBorrow = _totalBorrow; // Interactions if (_payer != address(this)) { assetContract.safeTransferFrom(_payer, address(this), _amountToRepay); } emit RepayAsset(_payer, _borrower, _amountToRepay, _shares); } /// @notice The ```repayAsset``` function allows the caller to pay down the debt for a given borrower. /// @dev Caller must first invoke ```ERC20.approve()``` for the Asset Token contract /// @param _shares The number of Borrow Shares which will be repaid by the call /// @param _borrower The account for which the debt will be reduced /// @return _amountToRepay The amount of Asset Tokens which were transferred in order to repay the Borrow Shares function repayAsset(uint256 _shares, address _borrower) external nonReentrant returns (uint256 _amountToRepay) { if (_borrower == address(0)) revert InvalidReceiver(); // Check if repay is paused revert if necessary if (isRepayPaused) revert RepayPaused(); // Accrue interest if necessary _addInterest(); // Calculate amount to repay based on shares VaultAccount memory _totalBorrow = totalBorrow; _amountToRepay = _totalBorrow.toAmount(_shares, true); // Execute repayment effects _repayAsset(_totalBorrow, _amountToRepay.toUint128(), _shares.toUint128(), msg.sender, _borrower); } // ============================================================================================ // Functions: Liquidations // ============================================================================================ /// @notice The ```Liquidate``` event is emitted when a liquidation occurs /// @param _borrower The borrower account for which the liquidation occurred /// @param _collateralForLiquidator The amount of Collateral Token transferred to the liquidator /// @param _sharesToLiquidate The number of Borrow Shares the liquidator repaid on behalf of the borrower /// @param _sharesToAdjust The number of Borrow Shares that were adjusted on liabilities and assets (a writeoff) event Liquidate( address indexed _borrower, uint256 _collateralForLiquidator, uint256 _sharesToLiquidate, uint256 _amountLiquidatorToRepay, uint256 _feesAmount, uint256 _sharesToAdjust, uint256 _amountToAdjust ); /// @notice The ```liquidate``` function allows a third party to repay a borrower's debt if they have become insolvent /// @dev Caller must invoke ```ERC20.approve``` on the Asset Token contract prior to calling ```Liquidate()``` /// @param _sharesToLiquidate The number of Borrow Shares repaid by the liquidator /// @param _deadline The timestamp after which tx will revert /// @param _borrower The account for which the repayment is credited and from whom collateral will be taken /// @return _collateralForLiquidator The amount of Collateral Token transferred to the liquidator function liquidate( uint128 _sharesToLiquidate, uint256 _deadline, address _borrower ) external nonReentrant returns (uint256 _collateralForLiquidator) { if (_borrower == address(0)) revert InvalidReceiver(); // Check if liquidate is paused revert if necessary if (isLiquidatePaused) revert LiquidatePaused(); // Ensure deadline has not passed if (block.timestamp > _deadline) revert PastDeadline(block.timestamp, _deadline); // accrue interest if necessary _addInterest(); // Update exchange rate and use the lower rate for liquidations (, uint256 _exchangeRate, ) = _updateExchangeRate(); // Check if borrower is solvent, revert if they are if (_isSolvent(_borrower, _exchangeRate)) { revert BorrowerSolvent(); } // Read from state VaultAccount memory _totalBorrow = totalBorrow; uint256 _userCollateralBalance = userCollateralBalance[_borrower]; uint128 _borrowerShares = userBorrowShares[_borrower].toUint128(); // Prevent stack-too-deep int256 _leftoverCollateral; uint256 _feesAmount; { // Checks & Calculations // Determine the liquidation amount in collateral units (i.e. how much debt liquidator is going to repay) uint256 _liquidationAmountInCollateralUnits = ((_totalBorrow.toAmount(_sharesToLiquidate, false) * _exchangeRate) / EXCHANGE_PRECISION); // We first optimistically calculate the amount of collateral to give the liquidator based on the higher clean liquidation fee // This fee only applies if the liquidator does a full liquidation uint256 _optimisticCollateralForLiquidator = (_liquidationAmountInCollateralUnits * (LIQ_PRECISION + cleanLiquidationFee)) / LIQ_PRECISION; // Because interest accrues every block, _liquidationAmountInCollateralUnits from a few lines up is an ever increasing value // This means that leftoverCollateral can occasionally go negative by a few hundred wei (cleanLiqFee premium covers this for liquidator) _leftoverCollateral = (_userCollateralBalance.toInt256() - _optimisticCollateralForLiquidator.toInt256()); // If cleanLiquidation fee results in no leftover collateral, give liquidator all the collateral // This will only be true when there liquidator is cleaning out the position _collateralForLiquidator = _leftoverCollateral <= 0 ? _userCollateralBalance : (_liquidationAmountInCollateralUnits * (LIQ_PRECISION + dirtyLiquidationFee)) / LIQ_PRECISION; if (protocolLiquidationFee > 0) { _feesAmount = (protocolLiquidationFee * _collateralForLiquidator) / LIQ_PRECISION; _collateralForLiquidator = _collateralForLiquidator - _feesAmount; } } // Calculated here for use during repayment, grouped with other calcs before effects start uint128 _amountLiquidatorToRepay = (_totalBorrow.toAmount(_sharesToLiquidate, true)).toUint128(); // Determine if and how much debt to adjust uint128 _sharesToAdjust = 0; { uint128 _amountToAdjust = 0; if (_leftoverCollateral <= 0) { // Determine if we need to adjust any shares _sharesToAdjust = _borrowerShares - _sharesToLiquidate; if (_sharesToAdjust > 0) { // Write off bad debt _amountToAdjust = (_totalBorrow.toAmount(_sharesToAdjust, false)).toUint128(); // Note: Ensure this memory struct will be passed to _repayAsset for write to state _totalBorrow.amount -= _amountToAdjust; // Effects: write to state totalAsset.amount -= _amountToAdjust; } } emit Liquidate( _borrower, _collateralForLiquidator, _sharesToLiquidate, _amountLiquidatorToRepay, _feesAmount, _sharesToAdjust, _amountToAdjust ); } // Effects & Interactions // NOTE: reverts if _shares > userBorrowShares _repayAsset( _totalBorrow, _amountLiquidatorToRepay, _sharesToLiquidate + _sharesToAdjust, msg.sender, _borrower ); // liquidator repays shares on behalf of borrower // NOTE: reverts if _collateralForLiquidator > userCollateralBalance // Collateral is removed on behalf of borrower and sent to liquidator // NOTE: reverts if _collateralForLiquidator > userCollateralBalance _removeCollateral(_collateralForLiquidator, msg.sender, _borrower); // Adjust bookkeeping only (decreases collateral held by borrower) _removeCollateral(_feesAmount, address(this), _borrower); // Adjusts bookkeeping only (increases collateral held by protocol) _addCollateral(address(this), _feesAmount, address(this)); } // ============================================================================================ // Functions: Leverage // ============================================================================================ /// @notice The ```RepayAssetWithCollateral``` event is emitted whenever ```repayAssetWithCollateral()``` is invoked /// @param _borrower The borrower account for which the repayment is taking place /// @param _swapperAddress The address of the whitelisted swapper to use for token swaps /// @param _collateralToSwap The amount of Collateral Token to swap and use for repayment /// @param _amountAssetOut The amount of Asset Token which was repaid /// @param _sharesRepaid The number of Borrow Shares which were repaid event RepayAssetWithCollateral( address indexed _borrower, address _swapperAddress, uint256 _collateralToSwap, uint256 _amountAssetOut, uint256 _sharesRepaid ); /// @notice The ```repayAssetWithCollateral``` function allows a borrower to repay their debt using existing collateral in contract /// @param _swapperAddress The address of the whitelisted swapper to use for token swaps /// @param _collateralToSwap The amount of Collateral Tokens to swap for Asset Tokens /// @param _amountAssetOutMin The minimum amount of Asset Tokens to receive during the swap /// @param _path An array containing the addresses of ERC20 tokens to swap. Adheres to UniV2 style path params. /// @return _amountAssetOut The amount of Asset Tokens received for the Collateral Tokens, the amount the borrowers account was credited function repayAssetWithCollateral( address _swapperAddress, uint256 _collateralToSwap, uint256 _amountAssetOutMin, address[] calldata _path ) external nonReentrant isSolvent(msg.sender) returns (uint256 _amountAssetOut) { // Accrue interest if necessary _addInterest(); // Update exchange rate and check if borrow is allowed, revert if not (bool _isBorrowAllowed, , ) = _updateExchangeRate(); if (!_isBorrowAllowed) revert ExceedsMaxOracleDeviation(); IERC20 _assetContract = assetContract; IERC20 _collateralContract = collateralContract; if (!swappers[_swapperAddress]) { revert BadSwapper(); } if (_path[0] != address(_collateralContract)) { revert InvalidPath(address(_collateralContract), _path[0]); } if (_path[_path.length - 1] != address(_assetContract)) { revert InvalidPath(address(_assetContract), _path[_path.length - 1]); } // Effects: bookkeeping & write to state // Debit users collateral balance in preparation for swap, setting _recipient to address(this) means no transfer occurs _removeCollateral(_collateralToSwap, address(this), msg.sender); // Interactions _collateralContract.approve(_swapperAddress, _collateralToSwap); // Even though swappers are trusted, we verify the balance before and after swap uint256 _initialAssetBalance = _assetContract.balanceOf(address(this)); ISwapper(_swapperAddress).swapExactTokensForTokens( _collateralToSwap, _amountAssetOutMin, _path, address(this), block.timestamp ); uint256 _finalAssetBalance = _assetContract.balanceOf(address(this)); // Note: VIOLATES CHECKS-EFFECTS-INTERACTION pattern, make sure function is NONREENTRANT // Effects: bookkeeping _amountAssetOut = _finalAssetBalance - _initialAssetBalance; if (_amountAssetOut < _amountAssetOutMin) { revert SlippageTooHigh(_amountAssetOutMin, _amountAssetOut); } VaultAccount memory _totalBorrow = totalBorrow; uint256 _sharesToRepay = _totalBorrow.toShares(_amountAssetOut, false); // Effects: write to state // Note: setting _payer to address(this) means no actual transfer will occur. Contract already has funds _repayAsset(_totalBorrow, _amountAssetOut.toUint128(), _sharesToRepay.toUint128(), address(this), msg.sender); emit RepayAssetWithCollateral(msg.sender, _swapperAddress, _collateralToSwap, _amountAssetOut, _sharesToRepay); } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; // ====================== SturdyPairRegistry ======================== import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol"; contract SturdyPairRegistry is Ownable2Step { /// @notice addresses of deployers allowed to add to the registry mapping(address => bool) public deployers; /// @notice List of the addresses of all deployed Pairs address[] public deployedPairsArray; /// @notice name => deployed address mapping(string => address) public deployedPairsByName; constructor(address _ownerAddress, address[] memory _initialDeployers) Ownable2Step() { for (uint256 i = 0; i < _initialDeployers.length; i++) { deployers[_initialDeployers[i]] = true; } _transferOwnership(_ownerAddress); } // ============================================================================================ // Functions: View Functions // ============================================================================================ /// @notice The ```deployedPairsLength``` function returns the length of the deployedPairsArray /// @return length of array function deployedPairsLength() external view returns (uint256) { return deployedPairsArray.length; } /// @notice The ```getAllPairAddresses``` function returns an array of all deployed pairs /// @return _deployedPairsArray The array of pairs deployed function getAllPairAddresses() external view returns (address[] memory _deployedPairsArray) { _deployedPairsArray = deployedPairsArray; } // ============================================================================================ // Functions: Setters // ============================================================================================ /// @notice The ```SetDeployer``` event is called when a deployer is added or removed from the whitelist /// @param deployer The address to be set /// @param _bool The value to set (allow or disallow) event SetDeployer(address deployer, bool _bool); /// @notice The ```setDeployers``` function sets the deployers whitelist /// @param _deployers The deployers to set /// @param _bool The boolean to set function setDeployers(address[] memory _deployers, bool _bool) external onlyOwner { for (uint256 i = 0; i < _deployers.length; i++) { deployers[_deployers[i]] = _bool; emit SetDeployer(_deployers[i], _bool); } } // ============================================================================================ // Functions: External Methods // ============================================================================================ /// @notice The ```AddPair``` event is emitted when a new pair is added to the registry /// @param pairAddress The address of the pair event AddPair(address pairAddress); /// @notice The ```addPair``` function adds a pair to the registry and ensures a unique name /// @param _pairAddress The address of the pair function addPair(address _pairAddress) external { // Ensure caller is on the whitelist if (!deployers[msg.sender]) revert AddressIsNotDeployer(); // Add pair to the global list deployedPairsArray.push(_pairAddress); // Pull name, ensure uniqueness and add to the name mapping string memory _name = IERC20Metadata(_pairAddress).name(); if (deployedPairsByName[_name] != address(0)) revert NameMustBeUnique(); deployedPairsByName[_name] = _pairAddress; emit AddPair(_pairAddress); } // ============================================================================================ // Errors // ============================================================================================ error AddressIsNotDeployer(); error NameMustBeUnique(); }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; // ======================= SturdyWhitelist ========================== import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol"; contract SturdyWhitelist is Ownable2Step { /// @notice Sturdy Deployer Whitelist mapping. mapping(address => bool) public sturdyDeployerWhitelist; constructor() Ownable2Step() {} /// @notice The ```SetSturdyDeployerWhitelist``` event fires whenever a status is set for a given address. /// @param _address address being set. /// @param _bool approval being set. event SetSturdyDeployerWhitelist(address indexed _address, bool _bool); /// @notice The ```setSturdyDeployerWhitelist``` function sets a given address to true/false for use as a custom deployer. /// @param _addresses addresses to set status for. /// @param _bool status of approval. function setSturdyDeployerWhitelist(address[] calldata _addresses, bool _bool) external onlyOwner { for (uint256 i = 0; i < _addresses.length; i++) { sturdyDeployerWhitelist[_addresses[i]] = _bool; emit SetSturdyDeployerWhitelist(_addresses[i], _bool); } } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {IBalancerVault} from "../interfaces/Balancer/IBalancerVault.sol"; library BalancerswapAdapter { using SafeERC20 for IERC20; struct Path { address[] tokens; bytes32[] poolIds; } error SW_PATH_LENGTH_INVALID(); error SW_PATH_TOKEN_INVALID(); error SW_MISMATCH_RETURNED_AMOUNT(); address private constant BALANCER_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; function swapExactTokensForTokens( address assetToSwapFrom, address assetToSwapTo, uint256 amountToSwap, Path calldata path, uint256 minAmountOut ) external returns (uint256) { // Check path is valid uint256 length = path.tokens.length; if (length <= 1 || length - 1 != path.poolIds.length) revert SW_PATH_LENGTH_INVALID(); if (path.tokens[0] != assetToSwapFrom || path.tokens[length - 1] != assetToSwapTo) revert SW_PATH_TOKEN_INVALID(); // Approves the transfer for the swap. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix. IERC20(assetToSwapFrom).safeApprove(address(BALANCER_VAULT), 0); if (IERC20(assetToSwapFrom).allowance(address(this), address(BALANCER_VAULT)) == 0) IERC20(assetToSwapFrom).safeApprove(address(BALANCER_VAULT), amountToSwap); IBalancerVault.BatchSwapStep[] memory swaps = new IBalancerVault.BatchSwapStep[](length - 1); int256[] memory limits = new int256[](length); for (uint256 i; i < length - 1; ++i) { swaps[i] = IBalancerVault.BatchSwapStep({ poolId: path.poolIds[i], assetInIndex: i, assetOutIndex: i + 1, amount: 0, userData: "0" }); } swaps[0].amount = amountToSwap; limits[0] = int256(amountToSwap); unchecked { limits[length - 1] = int256(0 - minAmountOut); } IBalancerVault.FundManagement memory funds = IBalancerVault.FundManagement({ sender: address(this), fromInternalBalance: false, recipient: payable(address(this)), toInternalBalance: false }); int256[] memory receivedAmount = IBalancerVault(BALANCER_VAULT).batchSwap( IBalancerVault.SwapKind.GIVEN_IN, swaps, path.tokens, funds, limits, block.timestamp ); uint256 receivedPositveAmount; unchecked { receivedPositveAmount = uint256(0 - receivedAmount[length - 1]); } if (receivedPositveAmount == 0) revert SW_MISMATCH_RETURNED_AMOUNT(); if (IERC20(assetToSwapTo).balanceOf(address(this)) < receivedPositveAmount) revert SW_MISMATCH_RETURNED_AMOUNT(); return receivedPositveAmount; } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {ICurveAddressProvider} from "../interfaces/Curve/ICurveAddressProvider.sol"; import {ICurveExchange} from "../interfaces/Curve/ICurveExchange.sol"; library CurveswapAdapter { using SafeERC20 for IERC20; error SW_MISMATCH_RETURNED_AMOUNT(); address private constant curveAddressProvider = 0x0000000022D53366457F9d5E68Ec105046FC4383; struct Path { address[9] routes; uint256[3][4] swapParams; } address constant ETH = 0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE; function swapExactTokensForTokens( address addressesProvider, address assetToSwapFrom, address assetToSwapTo, uint256 amountToSwap, Path calldata path, uint256 minAmountOut ) external returns (uint256) { // Approves the transfer for the swap. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix. address curveExchange = ICurveAddressProvider(curveAddressProvider).get_address(2); IERC20(assetToSwapFrom).safeApprove(address(curveExchange), 0); IERC20(assetToSwapFrom).safeApprove(address(curveExchange), amountToSwap); address[4] memory pools; uint256 receivedAmount = ICurveExchange(curveExchange).exchange_multiple( path.routes, path.swapParams, amountToSwap, minAmountOut, pools, address(this) ); if (receivedAmount == 0) revert SW_MISMATCH_RETURNED_AMOUNT(); uint256 balanceOfAsset; if (assetToSwapTo == ETH) { balanceOfAsset = address(this).balance; } else { balanceOfAsset = IERC20(assetToSwapTo).balanceOf(address(this)); } if (balanceOfAsset < receivedAmount) revert SW_MISMATCH_RETURNED_AMOUNT(); return receivedAmount; } }
// SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.21; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {SafeERC20} from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import {ISwapRouter} from "../interfaces/Uniswap/V3/ISwapRouter.sol"; library UniswapAdapter { using SafeERC20 for IERC20; error SW_PATH_LENGTH_INVALID(); error SW_PATH_TOKEN_INVALID(); error SW_MISMATCH_RETURNED_AMOUNT(); address private constant UNISWAP_ROUTER = 0xE592427A0AEce92De3Edee1F18E0157C05861564; struct Path { address[] tokens; uint256[] fees; } function swapExactTokensForTokens( address addressesProvider, address assetToSwapFrom, address assetToSwapTo, uint256 amountToSwap, Path calldata path, uint256 minAmountOut ) external returns (uint256) { // Check path is valid uint256 length = path.tokens.length; if (length <= 1 || length - 1 != path.fees.length) revert SW_PATH_LENGTH_INVALID(); if (path.tokens[0] != assetToSwapFrom || path.tokens[length - 1] != assetToSwapTo) revert SW_PATH_TOKEN_INVALID(); // Approves the transfer for the swap. Approves for 0 first to comply with tokens that implement the anti frontrunning approval fix. IERC20(assetToSwapFrom).safeApprove(address(UNISWAP_ROUTER), 0); IERC20(assetToSwapFrom).safeApprove(address(UNISWAP_ROUTER), amountToSwap); uint256 receivedAmount; if (length > 2) { bytes memory _path; for (uint256 i; i < length - 1; ++i) { _path = abi.encodePacked(_path, path.tokens[i], uint24(path.fees[i])); } _path = abi.encodePacked(_path, assetToSwapTo); ISwapRouter.ExactInputParams memory params = ISwapRouter.ExactInputParams({ path: _path, recipient: address(this), deadline: block.timestamp, amountIn: amountToSwap, amountOutMinimum: minAmountOut }); // Executes the swap. receivedAmount = ISwapRouter(UNISWAP_ROUTER).exactInput(params); } else { ISwapRouter.ExactInputSingleParams memory params = ISwapRouter.ExactInputSingleParams({ tokenIn: assetToSwapFrom, tokenOut: assetToSwapTo, fee: uint24(path.fees[0]), recipient: address(this), deadline: block.timestamp, amountIn: amountToSwap, amountOutMinimum: minAmountOut, sqrtPriceLimitX96: 0 }); // Executes the swap. receivedAmount = ISwapRouter(UNISWAP_ROUTER).exactInputSingle(params); } if (receivedAmount == 0) revert SW_MISMATCH_RETURNED_AMOUNT(); if (IERC20(assetToSwapTo).balanceOf(address(this)) < receivedAmount) revert SW_MISMATCH_RETURNED_AMOUNT(); return receivedAmount; } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; // ========================== Timelock2Step =========================== /// @title Timelock2Step /// @author Drake Evans (Frax Finance) https://github.com/drakeevans /// @dev Inspired by the OpenZeppelin's Ownable2Step contract /// @notice An abstract contract which contains 2-step transfer and renounce logic for a timelock address abstract contract Timelock2Step { /// @notice The pending timelock address address public pendingTimelockAddress; /// @notice The current timelock address address public timelockAddress; constructor() { timelockAddress = msg.sender; } /// @notice Emitted when timelock is transferred error OnlyTimelock(); /// @notice Emitted when pending timelock is transferred error OnlyPendingTimelock(); /// @notice The ```TimelockTransferStarted``` event is emitted when the timelock transfer is initiated /// @param previousTimelock The address of the previous timelock /// @param newTimelock The address of the new timelock event TimelockTransferStarted(address indexed previousTimelock, address indexed newTimelock); /// @notice The ```TimelockTransferred``` event is emitted when the timelock transfer is completed /// @param previousTimelock The address of the previous timelock /// @param newTimelock The address of the new timelock event TimelockTransferred(address indexed previousTimelock, address indexed newTimelock); /// @notice The ```_isSenderTimelock``` function checks if msg.sender is current timelock address /// @return Whether or not msg.sender is current timelock address function _isSenderTimelock() internal view returns (bool) { return msg.sender == timelockAddress; } /// @notice The ```_requireTimelock``` function reverts if msg.sender is not current timelock address function _requireTimelock() internal view { if (msg.sender != timelockAddress) revert OnlyTimelock(); } /// @notice The ```_isSenderPendingTimelock``` function checks if msg.sender is pending timelock address /// @return Whether or not msg.sender is pending timelock address function _isSenderPendingTimelock() internal view returns (bool) { return msg.sender == pendingTimelockAddress; } /// @notice The ```_requirePendingTimelock``` function reverts if msg.sender is not pending timelock address function _requirePendingTimelock() internal view { if (msg.sender != pendingTimelockAddress) revert OnlyPendingTimelock(); } /// @notice The ```_transferTimelock``` function initiates the timelock transfer /// @dev This function is to be implemented by a public function /// @param _newTimelock The address of the nominated (pending) timelock function _transferTimelock(address _newTimelock) internal { pendingTimelockAddress = _newTimelock; emit TimelockTransferStarted(timelockAddress, _newTimelock); } /// @notice The ```_acceptTransferTimelock``` function completes the timelock transfer /// @dev This function is to be implemented by a public function function _acceptTransferTimelock() internal { pendingTimelockAddress = address(0); _setTimelock(msg.sender); } /// @notice The ```_setTimelock``` function sets the timelock address /// @dev This function is to be implemented by a public function /// @param _newTimelock The address of the new timelock function _setTimelock(address _newTimelock) internal { emit TimelockTransferred(timelockAddress, _newTimelock); timelockAddress = _newTimelock; } /// @notice The ```transferTimelock``` function initiates the timelock transfer /// @dev Must be called by the current timelock /// @param _newTimelock The address of the nominated (pending) timelock function transferTimelock(address _newTimelock) external virtual { _requireTimelock(); _transferTimelock(_newTimelock); } /// @notice The ```acceptTransferTimelock``` function completes the timelock transfer /// @dev Must be called by the pending timelock function acceptTransferTimelock() external virtual { _requirePendingTimelock(); _acceptTransferTimelock(); } /// @notice The ```renounceTimelock``` function renounces the timelock after setting pending timelock to current timelock /// @dev Pending timelock must be set to current timelock before renouncing, creating a 2-step renounce process function renounceTimelock() external virtual { _requireTimelock(); _requirePendingTimelock(); _transferTimelock(address(0)); _setTimelock(address(0)); } }
// SPDX-License-Identifier: ISC pragma solidity ^0.8.21; // ====================== VariableInterestRate ======================== import { Strings } from "@openzeppelin/contracts/utils/Strings.sol"; import { IRateCalculatorV2 } from "./interfaces/IRateCalculatorV2.sol"; /// @title A formula for calculating interest rates as a function of utilization and time /// @author Drake Evans github.com/drakeevans /// @notice A Contract for calculating interest rates as a function of utilization and time contract VariableInterestRate is IRateCalculatorV2 { using Strings for uint256; /// @notice The name suffix for the interest rate calculator string public suffix; // Utilization Settings /// @notice The minimum utilization wherein no adjustment to full utilization and vertex rates occurs uint256 public immutable MIN_TARGET_UTIL; /// @notice The maximum utilization wherein no adjustment to full utilization and vertex rates occurs uint256 public immutable MAX_TARGET_UTIL; /// @notice The utilization at which the slope increases uint256 public immutable VERTEX_UTILIZATION; /// @notice precision of utilization calculations uint256 public constant UTIL_PREC = 1e5; // 5 decimals // Interest Rate Settings (all rates are per second), 365.24 days per year /// @notice The minimum interest rate (per second) when utilization is 100% uint256 public immutable MIN_FULL_UTIL_RATE; // 18 decimals /// @notice The maximum interest rate (per second) when utilization is 100% uint256 public immutable MAX_FULL_UTIL_RATE; // 18 decimals /// @notice The interest rate (per second) when utilization is 0% uint256 public immutable ZERO_UTIL_RATE; // 18 decimals /// @notice The interest rate half life in seconds, determines rate of adjustments to rate curve uint256 public immutable RATE_HALF_LIFE; // 1 decimals /// @notice The percent of the delta between max and min uint256 public immutable VERTEX_RATE_PERCENT; // 18 decimals /// @notice The precision of interest rate calculations uint256 public constant RATE_PREC = 1e18; // 18 decimals /// @notice The ```constructor``` function /// @param _suffix The suffix of the contract name /// @param _vertexUtilization The utilization at which the slope increases /// @param _vertexRatePercentOfDelta The percent of the delta between max and min, defines vertex rate /// @param _minUtil The minimum utilization wherein no adjustment to full utilization and vertex rates occurs /// @param _maxUtil The maximum utilization wherein no adjustment to full utilization and vertex rates occurs /// @param _zeroUtilizationRate The interest rate (per second) when utilization is 0% /// @param _minFullUtilizationRate The minimum interest rate at 100% utilization /// @param _maxFullUtilizationRate The maximum interest rate at 100% utilization /// @param _rateHalfLife The half life parameter for interest rate adjustments constructor( string memory _suffix, uint256 _vertexUtilization, uint256 _vertexRatePercentOfDelta, uint256 _minUtil, uint256 _maxUtil, uint256 _zeroUtilizationRate, uint256 _minFullUtilizationRate, uint256 _maxFullUtilizationRate, uint256 _rateHalfLife ) { suffix = _suffix; MIN_TARGET_UTIL = _minUtil; MAX_TARGET_UTIL = _maxUtil; VERTEX_UTILIZATION = _vertexUtilization; ZERO_UTIL_RATE = _zeroUtilizationRate; MIN_FULL_UTIL_RATE = _minFullUtilizationRate; MAX_FULL_UTIL_RATE = _maxFullUtilizationRate; RATE_HALF_LIFE = _rateHalfLife; VERTEX_RATE_PERCENT = _vertexRatePercentOfDelta; } /// @notice The ```name``` function returns the name of the rate contract /// @return memory name of contract function name() external view returns (string memory) { return string(abi.encodePacked("Variable Rate V2 ", suffix)); } /// @notice The ```version``` function returns the semantic version of the rate contract /// @dev Follows semantic versioning /// @return _major Major version /// @return _minor Minor version /// @return _patch Patch version function version() external pure returns (uint256 _major, uint256 _minor, uint256 _patch) { _major = 2; _minor = 0; _patch = 0; } /// @notice The ```getFullUtilizationInterest``` function calculate the new maximum interest rate, i.e. rate when utilization is 100% /// @dev Given in interest per second /// @param _deltaTime The elapsed time since last update given in seconds /// @param _utilization The utilization %, given with 5 decimals of precision /// @param _fullUtilizationInterest The interest value when utilization is 100%, given with 18 decimals of precision /// @return _newFullUtilizationInterest The new maximum interest rate function getFullUtilizationInterest( uint256 _deltaTime, uint256 _utilization, uint64 _fullUtilizationInterest ) internal view returns (uint64 _newFullUtilizationInterest) { if (_utilization < MIN_TARGET_UTIL) { // 18 decimals uint256 _deltaUtilization = ((MIN_TARGET_UTIL - _utilization) * 1e18) / MIN_TARGET_UTIL; // 36 decimals uint256 _decayGrowth = (RATE_HALF_LIFE * 1e36) + (_deltaUtilization * _deltaUtilization * _deltaTime); // 18 decimals _newFullUtilizationInterest = uint64((_fullUtilizationInterest * (RATE_HALF_LIFE * 1e36)) / _decayGrowth); } else if (_utilization > MAX_TARGET_UTIL) { // 18 decimals uint256 _deltaUtilization = ((_utilization - MAX_TARGET_UTIL) * 1e18) / (UTIL_PREC - MAX_TARGET_UTIL); // 36 decimals uint256 _decayGrowth = (RATE_HALF_LIFE * 1e36) + (_deltaUtilization * _deltaUtilization * _deltaTime); // 18 decimals _newFullUtilizationInterest = uint64((_fullUtilizationInterest * _decayGrowth) / (RATE_HALF_LIFE * 1e36)); } else { _newFullUtilizationInterest = _fullUtilizationInterest; } if (_newFullUtilizationInterest > MAX_FULL_UTIL_RATE) { _newFullUtilizationInterest = uint64(MAX_FULL_UTIL_RATE); } else if (_newFullUtilizationInterest < MIN_FULL_UTIL_RATE) { _newFullUtilizationInterest = uint64(MIN_FULL_UTIL_RATE); } } /// @notice The ```getNewRate``` function calculates interest rates using two linear functions f(utilization) /// @param _deltaTime The elapsed time since last update, given in seconds /// @param _utilization The utilization %, given with 5 decimals of precision /// @param _oldFullUtilizationInterest The interest value when utilization is 100%, given with 18 decimals of precision /// @return _newRatePerSec The new interest rate, 18 decimals of precision /// @return _newFullUtilizationInterest The new max interest rate, 18 decimals of precision function getNewRate( uint256 _deltaTime, uint256 _utilization, uint64 _oldFullUtilizationInterest ) external view returns (uint64 _newRatePerSec, uint64 _newFullUtilizationInterest) { _newFullUtilizationInterest = getFullUtilizationInterest(_deltaTime, _utilization, _oldFullUtilizationInterest); // _vertexInterest is calculated as the percentage of the delta between min and max interest uint256 _vertexInterest = (((_newFullUtilizationInterest - ZERO_UTIL_RATE) * VERTEX_RATE_PERCENT) / RATE_PREC) + ZERO_UTIL_RATE; if (_utilization < VERTEX_UTILIZATION) { // For readability, the following formula is equivalent to: // uint256 _slope = ((_vertexInterest - ZERO_UTIL_RATE) * UTIL_PREC) / VERTEX_UTILIZATION; // _newRatePerSec = uint64(ZERO_UTIL_RATE + ((_utilization * _slope) / UTIL_PREC)); // 18 decimals _newRatePerSec = uint64( ZERO_UTIL_RATE + (_utilization * (_vertexInterest - ZERO_UTIL_RATE)) / VERTEX_UTILIZATION ); } else { // For readability, the following formula is equivalent to: // uint256 _slope = (((_newFullUtilizationInterest - _vertexInterest) * UTIL_PREC) / (UTIL_PREC - VERTEX_UTILIZATION)); // _newRatePerSec = uint64(_vertexInterest + (((_utilization - VERTEX_UTILIZATION) * _slope) / UTIL_PREC)); // 18 decimals _newRatePerSec = uint64( _vertexInterest + ((_utilization - VERTEX_UTILIZATION) * (_newFullUtilizationInterest - _vertexInterest)) / (UTIL_PREC - VERTEX_UTILIZATION) ); } } }
// SPDX-License-Identifier: Unlicense /* * @title Solidity Bytes Arrays Utils * @author Gonçalo Sá <[email protected]> * * @dev Bytes tightly packed arrays utility library for ethereum contracts written in Solidity. * The library lets you concatenate, slice and type cast bytes arrays both in memory and storage. */ pragma solidity >=0.8.0 <0.9.0; library BytesLib { function concat( bytes memory _preBytes, bytes memory _postBytes ) internal pure returns (bytes memory) { bytes memory tempBytes; assembly { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // Store the length of the first bytes array at the beginning of // the memory for tempBytes. let length := mload(_preBytes) mstore(tempBytes, length) // Maintain a memory counter for the current write location in the // temp bytes array by adding the 32 bytes for the array length to // the starting location. let mc := add(tempBytes, 0x20) // Stop copying when the memory counter reaches the length of the // first bytes array. let end := add(mc, length) for { // Initialize a copy counter to the start of the _preBytes data, // 32 bytes into its memory. let cc := add(_preBytes, 0x20) } lt(mc, end) { // Increase both counters by 32 bytes each iteration. mc := add(mc, 0x20) cc := add(cc, 0x20) } { // Write the _preBytes data into the tempBytes memory 32 bytes // at a time. mstore(mc, mload(cc)) } // Add the length of _postBytes to the current length of tempBytes // and store it as the new length in the first 32 bytes of the // tempBytes memory. length := mload(_postBytes) mstore(tempBytes, add(length, mload(tempBytes))) // Move the memory counter back from a multiple of 0x20 to the // actual end of the _preBytes data. mc := end // Stop copying when the memory counter reaches the new combined // length of the arrays. end := add(mc, length) for { let cc := add(_postBytes, 0x20) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } // Update the free-memory pointer by padding our last write location // to 32 bytes: add 31 bytes to the end of tempBytes to move to the // next 32 byte block, then round down to the nearest multiple of // 32. If the sum of the length of the two arrays is zero then add // one before rounding down to leave a blank 32 bytes (the length block with 0). mstore(0x40, and( add(add(end, iszero(add(length, mload(_preBytes)))), 31), not(31) // Round down to the nearest 32 bytes. )) } return tempBytes; } function concatStorage(bytes storage _preBytes, bytes memory _postBytes) internal { assembly { // Read the first 32 bytes of _preBytes storage, which is the length // of the array. (We don't need to use the offset into the slot // because arrays use the entire slot.) let fslot := sload(_preBytes.slot) // Arrays of 31 bytes or less have an even value in their slot, // while longer arrays have an odd value. The actual length is // the slot divided by two for odd values, and the lowest order // byte divided by two for even values. // If the slot is even, bitwise and the slot with 255 and divide by // two to get the length. If the slot is odd, bitwise and the slot // with -1 and divide by two. let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) let newlength := add(slength, mlength) // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage switch add(lt(slength, 32), lt(newlength, 32)) case 2 { // Since the new array still fits in the slot, we just need to // update the contents of the slot. // uint256(bytes_storage) = uint256(bytes_storage) + uint256(bytes_memory) + new_length sstore( _preBytes.slot, // all the modifications to the slot are inside this // next block add( // we can just add to the slot contents because the // bytes we want to change are the LSBs fslot, add( mul( div( // load the bytes from memory mload(add(_postBytes, 0x20)), // zero all bytes to the right exp(0x100, sub(32, mlength)) ), // and now shift left the number of bytes to // leave space for the length in the slot exp(0x100, sub(32, newlength)) ), // increase length by the double of the memory // bytes length mul(mlength, 2) ) ) ) } case 1 { // The stored value fits in the slot, but the combined value // will exceed it. // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // The contents of the _postBytes array start 32 bytes into // the structure. Our first read should obtain the `submod` // bytes that can fit into the unused space in the last word // of the stored array. To get this, we read 32 bytes starting // from `submod`, so the data we read overlaps with the array // contents by `submod` bytes. Masking the lowest-order // `submod` bytes allows us to add that value directly to the // stored value. let submod := sub(32, slength) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore( sc, add( and( fslot, 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff00 ), and(mload(mc), mask) ) ) for { mc := add(mc, 0x20) sc := add(sc, 1) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } default { // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) // Start copying to the last used word of the stored array. let sc := add(keccak256(0x0, 0x20), div(slength, 32)) // save new length sstore(_preBytes.slot, add(mul(newlength, 2), 1)) // Copy over the first `submod` bytes of the new data as in // case 1 above. let slengthmod := mod(slength, 32) let mlengthmod := mod(mlength, 32) let submod := sub(32, slengthmod) let mc := add(_postBytes, submod) let end := add(_postBytes, mlength) let mask := sub(exp(0x100, submod), 1) sstore(sc, add(sload(sc), and(mload(mc), mask))) for { sc := add(sc, 1) mc := add(mc, 0x20) } lt(mc, end) { sc := add(sc, 1) mc := add(mc, 0x20) } { sstore(sc, mload(mc)) } mask := exp(0x100, sub(mc, end)) sstore(sc, mul(div(mload(mc), mask), mask)) } } } function slice( bytes memory _bytes, uint256 _start, uint256 _length ) internal pure returns (bytes memory) { require(_length + 31 >= _length, "slice_overflow"); require(_bytes.length >= _start + _length, "slice_outOfBounds"); bytes memory tempBytes; assembly { switch iszero(_length) case 0 { // Get a location of some free memory and store it in tempBytes as // Solidity does for memory variables. tempBytes := mload(0x40) // The first word of the slice result is potentially a partial // word read from the original array. To read it, we calculate // the length of that partial word and start copying that many // bytes into the array. The first word we copy will start with // data we don't care about, but the last `lengthmod` bytes will // land at the beginning of the contents of the new array. When // we're done copying, we overwrite the full first word with // the actual length of the slice. let lengthmod := and(_length, 31) // The multiplication in the next line is necessary // because when slicing multiples of 32 bytes (lengthmod == 0) // the following copy loop was copying the origin's length // and then ending prematurely not copying everything it should. let mc := add(add(tempBytes, lengthmod), mul(0x20, iszero(lengthmod))) let end := add(mc, _length) for { // The multiplication in the next line has the same exact purpose // as the one above. let cc := add(add(add(_bytes, lengthmod), mul(0x20, iszero(lengthmod))), _start) } lt(mc, end) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { mstore(mc, mload(cc)) } mstore(tempBytes, _length) //update free-memory pointer //allocating the array padded to 32 bytes like the compiler does now mstore(0x40, and(add(mc, 31), not(31))) } //if we want a zero-length slice let's just return a zero-length array default { tempBytes := mload(0x40) //zero out the 32 bytes slice we are about to return //we need to do it because Solidity does not garbage collect mstore(tempBytes, 0) mstore(0x40, add(tempBytes, 0x20)) } } return tempBytes; } function toAddress(bytes memory _bytes, uint256 _start) internal pure returns (address) { require(_bytes.length >= _start + 20, "toAddress_outOfBounds"); address tempAddress; assembly { tempAddress := div(mload(add(add(_bytes, 0x20), _start)), 0x1000000000000000000000000) } return tempAddress; } function toUint8(bytes memory _bytes, uint256 _start) internal pure returns (uint8) { require(_bytes.length >= _start + 1 , "toUint8_outOfBounds"); uint8 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x1), _start)) } return tempUint; } function toUint16(bytes memory _bytes, uint256 _start) internal pure returns (uint16) { require(_bytes.length >= _start + 2, "toUint16_outOfBounds"); uint16 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x2), _start)) } return tempUint; } function toUint32(bytes memory _bytes, uint256 _start) internal pure returns (uint32) { require(_bytes.length >= _start + 4, "toUint32_outOfBounds"); uint32 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x4), _start)) } return tempUint; } function toUint64(bytes memory _bytes, uint256 _start) internal pure returns (uint64) { require(_bytes.length >= _start + 8, "toUint64_outOfBounds"); uint64 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x8), _start)) } return tempUint; } function toUint96(bytes memory _bytes, uint256 _start) internal pure returns (uint96) { require(_bytes.length >= _start + 12, "toUint96_outOfBounds"); uint96 tempUint; assembly { tempUint := mload(add(add(_bytes, 0xc), _start)) } return tempUint; } function toUint128(bytes memory _bytes, uint256 _start) internal pure returns (uint128) { require(_bytes.length >= _start + 16, "toUint128_outOfBounds"); uint128 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x10), _start)) } return tempUint; } function toUint256(bytes memory _bytes, uint256 _start) internal pure returns (uint256) { require(_bytes.length >= _start + 32, "toUint256_outOfBounds"); uint256 tempUint; assembly { tempUint := mload(add(add(_bytes, 0x20), _start)) } return tempUint; } function toBytes32(bytes memory _bytes, uint256 _start) internal pure returns (bytes32) { require(_bytes.length >= _start + 32, "toBytes32_outOfBounds"); bytes32 tempBytes32; assembly { tempBytes32 := mload(add(add(_bytes, 0x20), _start)) } return tempBytes32; } function equal(bytes memory _preBytes, bytes memory _postBytes) internal pure returns (bool) { bool success = true; assembly { let length := mload(_preBytes) // if lengths don't match the arrays are not equal switch eq(length, mload(_postBytes)) case 1 { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 let mc := add(_preBytes, 0x20) let end := add(mc, length) for { let cc := add(_postBytes, 0x20) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) } eq(add(lt(mc, end), cb), 2) { mc := add(mc, 0x20) cc := add(cc, 0x20) } { // if any of these checks fails then arrays are not equal if iszero(eq(mload(mc), mload(cc))) { // unsuccess: success := 0 cb := 0 } } } default { // unsuccess: success := 0 } } return success; } function equalStorage( bytes storage _preBytes, bytes memory _postBytes ) internal view returns (bool) { bool success = true; assembly { // we know _preBytes_offset is 0 let fslot := sload(_preBytes.slot) // Decode the length of the stored array like in concatStorage(). let slength := div(and(fslot, sub(mul(0x100, iszero(and(fslot, 1))), 1)), 2) let mlength := mload(_postBytes) // if lengths don't match the arrays are not equal switch eq(slength, mlength) case 1 { // slength can contain both the length and contents of the array // if length < 32 bytes so let's prepare for that // v. http://solidity.readthedocs.io/en/latest/miscellaneous.html#layout-of-state-variables-in-storage if iszero(iszero(slength)) { switch lt(slength, 32) case 1 { // blank the last byte which is the length fslot := mul(div(fslot, 0x100), 0x100) if iszero(eq(fslot, mload(add(_postBytes, 0x20)))) { // unsuccess: success := 0 } } default { // cb is a circuit breaker in the for loop since there's // no said feature for inline assembly loops // cb = 1 - don't breaker // cb = 0 - break let cb := 1 // get the keccak hash to get the contents of the array mstore(0x0, _preBytes.slot) let sc := keccak256(0x0, 0x20) let mc := add(_postBytes, 0x20) let end := add(mc, mlength) // the next line is the loop condition: // while(uint256(mc < end) + cb == 2) for {} eq(add(lt(mc, end), cb), 2) { sc := add(sc, 1) mc := add(mc, 0x20) } { if iszero(eq(sload(sc), mload(mc))) { // unsuccess: success := 0 cb := 0 } } } } } default { // unsuccess: success := 0 } } return success; } }
{ "viaIR": true, "optimizer": { "enabled": true, "runs": 1660 }, "evmVersion": "paris", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } }, "metadata": { "useLiteralContent": true }, "libraries": { "": { "__CACHE_BREAKER__": "0x0000000000000031373033363134373438323536" } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"components":[{"internalType":"address","name":"circuitBreaker","type":"address"},{"internalType":"address","name":"comptroller","type":"address"},{"internalType":"address","name":"timelock","type":"address"},{"internalType":"address","name":"sturdyWhitelist","type":"address"},{"internalType":"address","name":"sturdyPairRegistry","type":"address"}],"internalType":"struct ConstructorParams","name":"_params","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"CircuitBreakerOnly","type":"error"},{"inputs":[],"name":"Create2Failed","type":"error"},{"inputs":[],"name":"WhitelistedDeployersOnly","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"address_","type":"address"},{"indexed":true,"internalType":"address","name":"asset","type":"address"},{"indexed":true,"internalType":"address","name":"collateral","type":"address"},{"indexed":false,"internalType":"string","name":"name","type":"string"},{"indexed":false,"internalType":"bytes","name":"configData","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"immutables","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"customConfigData","type":"bytes"}],"name":"LogDeploy","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"SetCircuitBreaker","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"SetComptroller","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"SetRegistry","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"SetTimelock","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":false,"internalType":"address","name":"newAddress","type":"address"}],"name":"SetWhitelist","type":"event"},{"inputs":[],"name":"circuitBreakerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"comptrollerAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contractAddress1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"contractAddress2","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"defaultSwappers","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"_configData","type":"bytes"}],"name":"deploy","outputs":[{"internalType":"address","name":"_pairAddress","type":"address"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"deployedPairsArray","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"deployedPairsLength","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAllPairAddresses","outputs":[{"internalType":"address[]","name":"_deployedPairs","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"address","name":"_collateral","type":"address"}],"name":"getNextNameSymbol","outputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"_addresses","type":"address[]"}],"name":"globalPause","outputs":[{"internalType":"address[]","name":"_updatedAddresses","type":"address[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newAddress","type":"address"}],"name":"setCircuitBreaker","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newAddress","type":"address"}],"name":"setComptroller","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes","name":"_creationCode","type":"bytes"}],"name":"setCreationCode","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_swappers","type":"address[]"}],"name":"setDefaultSwappers","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newAddress","type":"address"}],"name":"setRegistry","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newAddress","type":"address"}],"name":"setTimelock","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newAddress","type":"address"}],"name":"setWhitelist","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sturdyPairRegistryAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sturdyWhitelistAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"timelockAddress","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"uint256","name":"_major","type":"uint256"},{"internalType":"uint256","name":"_minor","type":"uint256"},{"internalType":"uint256","name":"_patch","type":"uint256"}],"stateMutability":"pure","type":"function"}]
Contract Creation Code
6080346200015e57601f6200216a38819003918201601f19168301916001600160401b03918284118585101762000148578160a092869260409687528339810103126200015e5781519060a0820190811182821017620001485782526080620000688462000163565b938483526200007a6020820162000163565b908160208501526200008e85820162000163565b938486820152620000b584620000a76060850162000163565b938460608501520162000163565b93849101526000549560018060a01b039485809481809460018060a01b03199a8c8c33911617600055519b823391167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0600080a31689600354161760035516876004541617600455168560055416176005551683600754161760075516906006541617600655611ff19081620001798239f35b634e487b7160e01b600052604160045260246000fd5b600080fd5b51906001600160a01b03821682036200015e5756fe6080604052600436101561001257600080fd5b60003560e01c8062774360146101a657806306c75b6a146101a157806331c315df1461019c57806336683100146101975780634793221d14610192578063492924271461018d5780634bc66f32146101885780635399212a1461018357806354fd4d501461017e578063607b6d161461017957806368bde41f14610174578063692857271461016f5780636c191eee1461016a578063715018a61461016557806371ad727c146101605780637bc028061461015b5780637ec9e1561461015657806382beee8914610151578063854cff2f1461014c5780638bad38dd146101475780638da5cb5b14610142578063a1f31aa41461013d578063a91ee0dc14610138578063bdacb303146101335763f2fde38b1461012e57600080fd5b610fea565b610f6a565b610eea565b610ec3565b610e9c565b610e1c565b610d9c565b610d1c565b610cf5565b610cce565b610ca7565b610c4a565b610b62565b610b0e565b610ae7565b610a57565b610a2d565b6109d0565b610961565b61093a565b610811565b6106fd565b61069e565b610557565b610270565b634e487b7160e01b600052604160045260246000fd5b67ffffffffffffffff81116101d557604052565b6101ab565b6040810190811067ffffffffffffffff8211176101d557604052565b90601f8019910116810190811067ffffffffffffffff8211176101d557604052565b67ffffffffffffffff81116101d557601f01601f191660200190565b92919261024082610218565b9161024e60405193846101f6565b82948184528183011161026b578281602093846000960137010152565b600080fd5b3461026b5760208060031936011261026b576004803567ffffffffffffffff811161026b573660238201121561026b576102b39036906024818501359101610234565b906102d86102cc6102cc6007546001600160a01b031690565b6001600160a01b031690565b9060409184835180927fdb8c056a0000000000000000000000000000000000000000000000000000000082528180610322338883019190916001600160a01b036020820193169052565b03915afa9081156104fe5760009161052a575b501561050357825161034d90840185018486016116d5565b50505050505050906001600160a01b038080921692169261036e848461120a565b969092600354610384906001600160a01b031690565b988354610397906001600160a01b031690565b6005546001600160a01b031689516001600160a01b039c8d16848201908152928d1660208401529b1660408201528a906060010390601f19918281018c526103df908c6101f6565b856103e988611d8a565b8a519b8c938401926103fa93611758565b03908101895261040a90896101f6565b610415888a84611898565b97600654610429906001600160a01b031690565b6001600160a01b0316803b1561026b5788517fc2b7bbb60000000000000000000000000000000000000000000000000000000081526001600160a01b038b1695810195865294600091869182908490829060200103925af19081156104fe576104e19a7f9303649990c462969a3c46d4e2c758166e92f5a4b18c67f26d3e58d2b0660e67956104c6936104e5575b5089519485948c16978561178b565b0390a4516001600160a01b0390911681529081906020820190565b0390f35b806104f26104f8926101c1565b806106f2565b386104b7565b6111a5565b90517f93afd589000000000000000000000000000000000000000000000000000000008152fd5b61054a9150853d8711610550575b61054281836101f6565b8101906116bd565b38610335565b503d610538565b3461026b5760208060031936011261026b5760043567ffffffffffffffff80821161026b573660238301121561026b57816004013590811161026b576024820191602482369201011161026b5782906105ae6110c6565b6105b9368285610234565b926132c8926105cb8486511015611ee4565b60405190600882019560086132d084019101905b80881061066557505050839461060b948252601f80199101166040526001600160a01b039384916115db565b16936001600160a01b031994856001541617600155821161062857005b6132c71982018281116106605761064761064c92610651943691610234565b611f2f565b6115db565b16906002541617600255600080f35b6111b1565b8151885296820196908201906105df565b602090600319011261026b5760043590565b634e487b7160e01b600052603260045260246000fd5b3461026b576106ac36610676565b60095481101561026b576001600160a01b0360209160096000527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af015416604051908152f35b600091031261026b57565b3461026b57600036600319011261026b576020600954604051908152f35b67ffffffffffffffff81116101d55760051b60200190565b6001600160a01b0381160361026b57565b60208060031983011261026b576004359167ffffffffffffffff831161026b578060238401121561026b57826004013561077d8161071b565b9361078b60405195866101f6565b81855260248486019260051b82010192831161026b57602401905b8282106107b4575050505090565b83809183356107c281610733565b8152019101906107a6565b6020908160408183019282815285518094520193019160005b8281106107f4575050505090565b83516001600160a01b0316855293810193928101926001016107e6565b3461026b5761081f36610744565b6108346102cc6003546001600160a01b031690565b330361091057805161084581611a86565b9060005b81811061085e57604051806104e185826107cd565b6001600160a01b036108806108738387611884565b516001600160a01b031690565b1690813b1561026b576000806001936004604051809481937f8456cb590000000000000000000000000000000000000000000000000000000083525af190816108fd575b506108d0575b01610849565b6108f86108e06108738388611884565b6108ea8387611884565b906001600160a01b03169052565b6108ca565b806104f261090a926101c1565b386108c4565b60046040517fd8ebffc4000000000000000000000000000000000000000000000000000000008152fd5b3461026b57600036600319011261026b5760206001600160a01b0360035416604051908152f35b3461026b57600036600319011261026b5760206001600160a01b0360055416604051908152f35b60005b83811061099b5750506000910152565b818101518382015260200161098b565b906020916109c481518092818552858086019101610988565b601f01601f1916010190565b3461026b57604036600319011261026b57610a1f6104e1610a086004356109f681610733565b60243590610a0382610733565b61120a565b6040929192519384936040855260408501906109ab565b9083820360208501526109ab565b3461026b57600036600319011261026b576060604051600481526001602082015260006040820152f35b3461026b57600036600319011261026b57604051806009549182815260208091019260096000527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af916000905b828210610ac7576104e185610abb818903826101f6565b604051918291826107cd565b83546001600160a01b031686529485019460019384019390910190610aa4565b3461026b57600036600319011261026b5760206001600160a01b0360045416604051908152f35b3461026b57610b1c36610676565b60085481101561026b576001600160a01b0360209160086000527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3015416604051908152f35b3461026b57610b7036610744565b610b786110c6565b805167ffffffffffffffff81116101d5576801000000000000000081116101d55760085481600855808210610c03575b50600860005260209182017ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee39260005b838110610be157005b60019082610bf685516001600160a01b031690565b9401938187015501610bd8565b600060088152827ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee392830192015b828110610c3f575050610ba8565b818155600101610c31565b3461026b57600080600319360112610ca457610c646110c6565b806001600160a01b0381546001600160a01b031981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b80fd5b3461026b57600036600319011261026b5760206001600160a01b0360065416604051908152f35b3461026b57600036600319011261026b5760206001600160a01b0360015416604051908152f35b3461026b57600036600319011261026b5760206001600160a01b0360025416604051908152f35b3461026b57602036600319011261026b57600435610d3981610733565b610d416110c6565b600354604080516001600160a01b038084168252848116602083015292936001600160a01b0319939290917f4cb8c9e37efb94c6cdbd2a80fe36cee1957b5584d1a1986fa2bae115180af59a9190a116911617600355600080f35b3461026b57602036600319011261026b57600435610db981610733565b610dc16110c6565b600754604080516001600160a01b038084168252848116602083015292936001600160a01b0319939290917fe8664b925e623f88e598288ed83ff0a0c9b17d50f56ec07db74f075ca4c1d57b9190a116911617600755600080f35b3461026b57602036600319011261026b57600435610e3981610733565b610e416110c6565b600454604080516001600160a01b038084168252848116602083015292936001600160a01b0319939290917ff45d882a72fce9d8d7a7e2e196a338d4d9d4057510b4b9ddf91a7066104d2eaf9190a116911617600455600080f35b3461026b57600036600319011261026b5760206001600160a01b0360005416604051908152f35b3461026b57600036600319011261026b5760206001600160a01b0360075416604051908152f35b3461026b57602036600319011261026b57600435610f0781610733565b610f0f6110c6565b600654604080516001600160a01b038084168252848116602083015292936001600160a01b0319939290917fa6cdf06494ab3c79fae6cca5316f6324ff80979c2a51d8f239aee07a4aecd35b9190a116911617600655600080f35b3461026b57602036600319011261026b57600435610f8781610733565b610f8f6110c6565b600554604080516001600160a01b038084168252848116602083015292936001600160a01b0319939290917f91aa98337922135c1d3ae8654f8d0b938c01a35c402eb21e568af3755e4dcd799190a116911617600555600080f35b3461026b57602036600319011261026b5760043561100781610733565b61100f6110c6565b6001600160a01b03809116801561105c576000918254826001600160a01b03198216178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b608460405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b6001600160a01b036000541633036110da57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b60405190600854808352826020918282019060086000527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3936000905b82821061117357505050611171925003836101f6565b565b85546001600160a01b03168452600195860195889550938101939091019061115b565b9081602091031261026b575190565b6040513d6000823e3d90fd5b634e487b7160e01b600052601160045260246000fd5b906001820180921161066057565b90601f820180921161066057565b906132c891820180921161066057565b9061120660209282815194859201610988565b0190565b919060049060206112296102cc6102cc6006546001600160a01b031690565b604051938480927f366831000000000000000000000000000000000000000000000000000000000082525afa9182156104fe57600092611429575b506001600160a01b038094169361127a85611b17565b911661128581611d46565b9161128f846111c7565b6112989061148b565b6040517f53747572647920496e7465726573742042656172696e672000000000000000006020820152938492603884016112d1916111f3565b7f20280000000000000000000000000000000000000000000000000000000000008152600201611300916111f3565b602960f81b81526001017f202d2000000000000000000000000000000000000000000000000000000000008152600301611339916111f3565b0391601f1992838101825261134e90826101f6565b9461135890611b17565b9061136290611b17565b9261136c906111c7565b6113759061148b565b6040517f66000000000000000000000000000000000000000000000000000000000000006020820152938492602184016113ae916111f3565b7f280000000000000000000000000000000000000000000000000000000000000081526001016113dd916111f3565b602960f81b81526001017f2d000000000000000000000000000000000000000000000000000000000000008152600101611416916111f3565b03908101825261142690826101f6565b90565b61144b91925060203d8111611452575b61144381836101f6565b810190611196565b9038611264565b503d611439565b9061146382610218565b61147060405191826101f6565b8281528092611481601f1991610218565b0190602036910137565b806000917a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000808210156115cd575b506d04ee2d6d415b85acef8100000000808310156115be575b50662386f26fc10000808310156115af575b506305f5e100808310156115a0575b5061271080831015611591575b506064821015611581575b600a80921015611577575b600190816021611522828701611459565b95860101905b611534575b5050505090565b600019019083907f30313233343536373839616263646566000000000000000000000000000000008282061a83530491821561157257919082611528565b61152d565b9160010191611511565b9190606460029104910191611506565b600491939204910191386114fb565b600891939204910191386114ee565b601091939204910191386114df565b602091939204910191386114cd565b6040935081049150386114b4565b604051906116116021836020810193600085526116018151809260208686019101610988565b81010360018101855201836101f6565b611662602b604051809361165260208301967f600b5981380380925939f3000000000000000000000000000000000000000000885251809285850190610988565b810103600b8101845201826101f6565b51906000f0906001600160a01b0382161561167957565b606460405162461bcd60e51b815260206004820152601160248201527f4445504c4f594d454e545f4641494c45440000000000000000000000000000006044820152fd5b9081602091031261026b5751801515810361026b5790565b908161012091031261026b5780516116ec81610733565b9160208201516116fb81610733565b91604081015161170a81610733565b91606082015163ffffffff8116810361026b5791608081015161172c81610733565b9160a082015167ffffffffffffffff8116810361026b579160c08101519161010060e083015192015190565b9161178460ff916117766040949796976060875260608701906109ab565b9085820360208701526109ab565b9416910152565b926117b761142695936117a96117c5946080885260808801906109ab565b9086820360208801526109ab565b9084820360408601526109ab565b9160608184039101526109ab565b916117fc906117ee61142695936060865260608601906109ab565b9084820360208601526109ab565b9160408184039101526109ab565b600954680100000000000000008110156101d5576001810180600955811015611870576001600160a01b039060096000527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af0191166001600160a01b0319825416179055565b610688565b60001981146106605760010190565b80518210156118705760209160051b010190565b9190916119296001916119236119546118de6118c36118be87546001600160a01b031690565b611a56565b6118d86118be6002546001600160a01b031690565b90611e20565b95604097611948895192611935846118fb83868b602085016117d3565b039461190f601f19968781018352826101f6565b8c51998a91611923602084019e8f906111f3565b906111f3565b038581018a52896101f6565b8a5195869361192360208601809a6111f3565b039081018352826101f6565b519020905160009384f5926001600160a01b0384168015611a2d576119788561180a565b61198061111e565b84845b611991575b50505050505090565b8151811015611a28576119a76108738284611884565b833b15611a245784517f3f2617cb0000000000000000000000000000000000000000000000000000000081526001600160a01b039190911660048201526001602482015290868260448183885af19182156104fe57611a0b92611a11575b50611875565b84611983565b806104f2611a1e926101c1565b38611a05565b8680fd5b611988565b600482517f04a5b3ee000000000000000000000000000000000000000000000000000000008152fd5b803b906000198201908282116106605760019060405193603e601f19910116840160405282845260208401903c90565b90611a908261071b565b611a9d60405191826101f6565b8281528092611481601f199161071b565b3d15611ad9573d90611abf82610218565b91611acd60405193846101f6565b82523d6000602084013e565b606090565b60405190611aeb826101da565b600382527f3f3f3f00000000000000000000000000000000000000000000000000000000006020830152565b600080916040516001600160a01b0360208201917f95d89b4100000000000000000000000000000000000000000000000000000000835260048152611b5b816101da565b5192165afa611b68611aae565b9015611b775761142690611c01565b50611426611ade565b908151811015611870570160200190565b60ff1660ff81146106605760010190565b60208183031261026b5780519067ffffffffffffffff821161026b570181601f8201121561026b578051611bd581610218565b92611be360405194856101f6565b8184526020828401011161026b576114269160208085019101610988565b805160408110611c1f57508060208061142693518301019101611ba2565b602092908303611d3b576000805b60ff81168581109081611d02575b5015611c4f57611c4a90611b91565b611c2d565b92611c5d60ff809516611459565b92825b85811687811080611ccb575b15611cc0579081611cb5611cac611c86611cbb9588611b80565b517fff000000000000000000000000000000000000000000000000000000000000001690565b871a9188611b80565b53611b91565b611c60565b505094505050905090565b507fff00000000000000000000000000000000000000000000000000000000000000611cfa611c868387611b80565b161515611c6c565b7fff000000000000000000000000000000000000000000000000000000000000009150611c86611d329187611b80565b16151538611c3b565b915050611426611ade565b600080916040516001600160a01b0360208201917f06fdde0300000000000000000000000000000000000000000000000000000000835260048152611b5b816101da565b6001600160a01b0360405160208101927f313ce56700000000000000000000000000000000000000000000000000000000845260048252611dca826101da565b600093849384935192165afa611dde611aae565b9080611e15575b15611e0e57602081805181010312611e0a57602001519060ff82168203610ca4575090565b5080fd5b5050601290565b506020815114611de5565b6040519181518084526020808501918501928184019282808701915b858110611e895750505080518093875182018852940193828086019201905b828110611e7a575050505090603f91601f199351011501011660405290565b81518152908301908301611e5b565b8251815291810191849101611e3c565b15611ea057565b606460405162461bcd60e51b815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152fd5b15611eeb57565b606460405162461bcd60e51b815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152fd5b611f4382611f3c816111d5565b1015611e99565b611f588151611f51846111e3565b1115611ee4565b81611f70575050604051600081526020810160405290565b60405191601f8116916132c8831560051b80858701019484860193010101905b808410611fa85750508252601f01601f191660405290565b9092835181526020809101930190611f9056fea2646970667358221220d0f9f659a5c6121c4992559e7e9f442d617dd2f691782bc93ca88172ffbda4d564736f6c634300081500330000000000000000000000002532c3d363306fa6d625e4cbad996bcf534e81540000000000000000000000002532c3d363306fa6d625e4cbad996bcf534e81540000000000000000000000002532c3d363306fa6d625e4cbad996bcf534e8154000000000000000000000000f0382a9eca5276d7b4bbcc503e4159c046c120ec000000000000000000000000d577429db653cd20effcd4977b2b41a6fd794a3b
Deployed Bytecode
0x6080604052600436101561001257600080fd5b60003560e01c8062774360146101a657806306c75b6a146101a157806331c315df1461019c57806336683100146101975780634793221d14610192578063492924271461018d5780634bc66f32146101885780635399212a1461018357806354fd4d501461017e578063607b6d161461017957806368bde41f14610174578063692857271461016f5780636c191eee1461016a578063715018a61461016557806371ad727c146101605780637bc028061461015b5780637ec9e1561461015657806382beee8914610151578063854cff2f1461014c5780638bad38dd146101475780638da5cb5b14610142578063a1f31aa41461013d578063a91ee0dc14610138578063bdacb303146101335763f2fde38b1461012e57600080fd5b610fea565b610f6a565b610eea565b610ec3565b610e9c565b610e1c565b610d9c565b610d1c565b610cf5565b610cce565b610ca7565b610c4a565b610b62565b610b0e565b610ae7565b610a57565b610a2d565b6109d0565b610961565b61093a565b610811565b6106fd565b61069e565b610557565b610270565b634e487b7160e01b600052604160045260246000fd5b67ffffffffffffffff81116101d557604052565b6101ab565b6040810190811067ffffffffffffffff8211176101d557604052565b90601f8019910116810190811067ffffffffffffffff8211176101d557604052565b67ffffffffffffffff81116101d557601f01601f191660200190565b92919261024082610218565b9161024e60405193846101f6565b82948184528183011161026b578281602093846000960137010152565b600080fd5b3461026b5760208060031936011261026b576004803567ffffffffffffffff811161026b573660238201121561026b576102b39036906024818501359101610234565b906102d86102cc6102cc6007546001600160a01b031690565b6001600160a01b031690565b9060409184835180927fdb8c056a0000000000000000000000000000000000000000000000000000000082528180610322338883019190916001600160a01b036020820193169052565b03915afa9081156104fe5760009161052a575b501561050357825161034d90840185018486016116d5565b50505050505050906001600160a01b038080921692169261036e848461120a565b969092600354610384906001600160a01b031690565b988354610397906001600160a01b031690565b6005546001600160a01b031689516001600160a01b039c8d16848201908152928d1660208401529b1660408201528a906060010390601f19918281018c526103df908c6101f6565b856103e988611d8a565b8a519b8c938401926103fa93611758565b03908101895261040a90896101f6565b610415888a84611898565b97600654610429906001600160a01b031690565b6001600160a01b0316803b1561026b5788517fc2b7bbb60000000000000000000000000000000000000000000000000000000081526001600160a01b038b1695810195865294600091869182908490829060200103925af19081156104fe576104e19a7f9303649990c462969a3c46d4e2c758166e92f5a4b18c67f26d3e58d2b0660e67956104c6936104e5575b5089519485948c16978561178b565b0390a4516001600160a01b0390911681529081906020820190565b0390f35b806104f26104f8926101c1565b806106f2565b386104b7565b6111a5565b90517f93afd589000000000000000000000000000000000000000000000000000000008152fd5b61054a9150853d8711610550575b61054281836101f6565b8101906116bd565b38610335565b503d610538565b3461026b5760208060031936011261026b5760043567ffffffffffffffff80821161026b573660238301121561026b57816004013590811161026b576024820191602482369201011161026b5782906105ae6110c6565b6105b9368285610234565b926132c8926105cb8486511015611ee4565b60405190600882019560086132d084019101905b80881061066557505050839461060b948252601f80199101166040526001600160a01b039384916115db565b16936001600160a01b031994856001541617600155821161062857005b6132c71982018281116106605761064761064c92610651943691610234565b611f2f565b6115db565b16906002541617600255600080f35b6111b1565b8151885296820196908201906105df565b602090600319011261026b5760043590565b634e487b7160e01b600052603260045260246000fd5b3461026b576106ac36610676565b60095481101561026b576001600160a01b0360209160096000527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af015416604051908152f35b600091031261026b57565b3461026b57600036600319011261026b576020600954604051908152f35b67ffffffffffffffff81116101d55760051b60200190565b6001600160a01b0381160361026b57565b60208060031983011261026b576004359167ffffffffffffffff831161026b578060238401121561026b57826004013561077d8161071b565b9361078b60405195866101f6565b81855260248486019260051b82010192831161026b57602401905b8282106107b4575050505090565b83809183356107c281610733565b8152019101906107a6565b6020908160408183019282815285518094520193019160005b8281106107f4575050505090565b83516001600160a01b0316855293810193928101926001016107e6565b3461026b5761081f36610744565b6108346102cc6003546001600160a01b031690565b330361091057805161084581611a86565b9060005b81811061085e57604051806104e185826107cd565b6001600160a01b036108806108738387611884565b516001600160a01b031690565b1690813b1561026b576000806001936004604051809481937f8456cb590000000000000000000000000000000000000000000000000000000083525af190816108fd575b506108d0575b01610849565b6108f86108e06108738388611884565b6108ea8387611884565b906001600160a01b03169052565b6108ca565b806104f261090a926101c1565b386108c4565b60046040517fd8ebffc4000000000000000000000000000000000000000000000000000000008152fd5b3461026b57600036600319011261026b5760206001600160a01b0360035416604051908152f35b3461026b57600036600319011261026b5760206001600160a01b0360055416604051908152f35b60005b83811061099b5750506000910152565b818101518382015260200161098b565b906020916109c481518092818552858086019101610988565b601f01601f1916010190565b3461026b57604036600319011261026b57610a1f6104e1610a086004356109f681610733565b60243590610a0382610733565b61120a565b6040929192519384936040855260408501906109ab565b9083820360208501526109ab565b3461026b57600036600319011261026b576060604051600481526001602082015260006040820152f35b3461026b57600036600319011261026b57604051806009549182815260208091019260096000527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af916000905b828210610ac7576104e185610abb818903826101f6565b604051918291826107cd565b83546001600160a01b031686529485019460019384019390910190610aa4565b3461026b57600036600319011261026b5760206001600160a01b0360045416604051908152f35b3461026b57610b1c36610676565b60085481101561026b576001600160a01b0360209160086000527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3015416604051908152f35b3461026b57610b7036610744565b610b786110c6565b805167ffffffffffffffff81116101d5576801000000000000000081116101d55760085481600855808210610c03575b50600860005260209182017ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee39260005b838110610be157005b60019082610bf685516001600160a01b031690565b9401938187015501610bd8565b600060088152827ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee392830192015b828110610c3f575050610ba8565b818155600101610c31565b3461026b57600080600319360112610ca457610c646110c6565b806001600160a01b0381546001600160a01b031981168355167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08280a380f35b80fd5b3461026b57600036600319011261026b5760206001600160a01b0360065416604051908152f35b3461026b57600036600319011261026b5760206001600160a01b0360015416604051908152f35b3461026b57600036600319011261026b5760206001600160a01b0360025416604051908152f35b3461026b57602036600319011261026b57600435610d3981610733565b610d416110c6565b600354604080516001600160a01b038084168252848116602083015292936001600160a01b0319939290917f4cb8c9e37efb94c6cdbd2a80fe36cee1957b5584d1a1986fa2bae115180af59a9190a116911617600355600080f35b3461026b57602036600319011261026b57600435610db981610733565b610dc16110c6565b600754604080516001600160a01b038084168252848116602083015292936001600160a01b0319939290917fe8664b925e623f88e598288ed83ff0a0c9b17d50f56ec07db74f075ca4c1d57b9190a116911617600755600080f35b3461026b57602036600319011261026b57600435610e3981610733565b610e416110c6565b600454604080516001600160a01b038084168252848116602083015292936001600160a01b0319939290917ff45d882a72fce9d8d7a7e2e196a338d4d9d4057510b4b9ddf91a7066104d2eaf9190a116911617600455600080f35b3461026b57600036600319011261026b5760206001600160a01b0360005416604051908152f35b3461026b57600036600319011261026b5760206001600160a01b0360075416604051908152f35b3461026b57602036600319011261026b57600435610f0781610733565b610f0f6110c6565b600654604080516001600160a01b038084168252848116602083015292936001600160a01b0319939290917fa6cdf06494ab3c79fae6cca5316f6324ff80979c2a51d8f239aee07a4aecd35b9190a116911617600655600080f35b3461026b57602036600319011261026b57600435610f8781610733565b610f8f6110c6565b600554604080516001600160a01b038084168252848116602083015292936001600160a01b0319939290917f91aa98337922135c1d3ae8654f8d0b938c01a35c402eb21e568af3755e4dcd799190a116911617600555600080f35b3461026b57602036600319011261026b5760043561100781610733565b61100f6110c6565b6001600160a01b03809116801561105c576000918254826001600160a01b03198216178455167f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e08380a380f35b608460405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201527f64647265737300000000000000000000000000000000000000000000000000006064820152fd5b6001600160a01b036000541633036110da57565b606460405162461bcd60e51b815260206004820152602060248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152fd5b60405190600854808352826020918282019060086000527ff3f7a9fe364faab93b216da50a3214154f22a0a2b415b23a84c8169e8b636ee3936000905b82821061117357505050611171925003836101f6565b565b85546001600160a01b03168452600195860195889550938101939091019061115b565b9081602091031261026b575190565b6040513d6000823e3d90fd5b634e487b7160e01b600052601160045260246000fd5b906001820180921161066057565b90601f820180921161066057565b906132c891820180921161066057565b9061120660209282815194859201610988565b0190565b919060049060206112296102cc6102cc6006546001600160a01b031690565b604051938480927f366831000000000000000000000000000000000000000000000000000000000082525afa9182156104fe57600092611429575b506001600160a01b038094169361127a85611b17565b911661128581611d46565b9161128f846111c7565b6112989061148b565b6040517f53747572647920496e7465726573742042656172696e672000000000000000006020820152938492603884016112d1916111f3565b7f20280000000000000000000000000000000000000000000000000000000000008152600201611300916111f3565b602960f81b81526001017f202d2000000000000000000000000000000000000000000000000000000000008152600301611339916111f3565b0391601f1992838101825261134e90826101f6565b9461135890611b17565b9061136290611b17565b9261136c906111c7565b6113759061148b565b6040517f66000000000000000000000000000000000000000000000000000000000000006020820152938492602184016113ae916111f3565b7f280000000000000000000000000000000000000000000000000000000000000081526001016113dd916111f3565b602960f81b81526001017f2d000000000000000000000000000000000000000000000000000000000000008152600101611416916111f3565b03908101825261142690826101f6565b90565b61144b91925060203d8111611452575b61144381836101f6565b810190611196565b9038611264565b503d611439565b9061146382610218565b61147060405191826101f6565b8281528092611481601f1991610218565b0190602036910137565b806000917a184f03e93ff9f4daa797ed6e38ed64bf6a1f010000000000000000808210156115cd575b506d04ee2d6d415b85acef8100000000808310156115be575b50662386f26fc10000808310156115af575b506305f5e100808310156115a0575b5061271080831015611591575b506064821015611581575b600a80921015611577575b600190816021611522828701611459565b95860101905b611534575b5050505090565b600019019083907f30313233343536373839616263646566000000000000000000000000000000008282061a83530491821561157257919082611528565b61152d565b9160010191611511565b9190606460029104910191611506565b600491939204910191386114fb565b600891939204910191386114ee565b601091939204910191386114df565b602091939204910191386114cd565b6040935081049150386114b4565b604051906116116021836020810193600085526116018151809260208686019101610988565b81010360018101855201836101f6565b611662602b604051809361165260208301967f600b5981380380925939f3000000000000000000000000000000000000000000885251809285850190610988565b810103600b8101845201826101f6565b51906000f0906001600160a01b0382161561167957565b606460405162461bcd60e51b815260206004820152601160248201527f4445504c4f594d454e545f4641494c45440000000000000000000000000000006044820152fd5b9081602091031261026b5751801515810361026b5790565b908161012091031261026b5780516116ec81610733565b9160208201516116fb81610733565b91604081015161170a81610733565b91606082015163ffffffff8116810361026b5791608081015161172c81610733565b9160a082015167ffffffffffffffff8116810361026b579160c08101519161010060e083015192015190565b9161178460ff916117766040949796976060875260608701906109ab565b9085820360208701526109ab565b9416910152565b926117b761142695936117a96117c5946080885260808801906109ab565b9086820360208801526109ab565b9084820360408601526109ab565b9160608184039101526109ab565b916117fc906117ee61142695936060865260608601906109ab565b9084820360208601526109ab565b9160408184039101526109ab565b600954680100000000000000008110156101d5576001810180600955811015611870576001600160a01b039060096000527f6e1540171b6c0c960b71a7020d9f60077f6af931a8bbf590da0223dacf75c7af0191166001600160a01b0319825416179055565b610688565b60001981146106605760010190565b80518210156118705760209160051b010190565b9190916119296001916119236119546118de6118c36118be87546001600160a01b031690565b611a56565b6118d86118be6002546001600160a01b031690565b90611e20565b95604097611948895192611935846118fb83868b602085016117d3565b039461190f601f19968781018352826101f6565b8c51998a91611923602084019e8f906111f3565b906111f3565b038581018a52896101f6565b8a5195869361192360208601809a6111f3565b039081018352826101f6565b519020905160009384f5926001600160a01b0384168015611a2d576119788561180a565b61198061111e565b84845b611991575b50505050505090565b8151811015611a28576119a76108738284611884565b833b15611a245784517f3f2617cb0000000000000000000000000000000000000000000000000000000081526001600160a01b039190911660048201526001602482015290868260448183885af19182156104fe57611a0b92611a11575b50611875565b84611983565b806104f2611a1e926101c1565b38611a05565b8680fd5b611988565b600482517f04a5b3ee000000000000000000000000000000000000000000000000000000008152fd5b803b906000198201908282116106605760019060405193603e601f19910116840160405282845260208401903c90565b90611a908261071b565b611a9d60405191826101f6565b8281528092611481601f199161071b565b3d15611ad9573d90611abf82610218565b91611acd60405193846101f6565b82523d6000602084013e565b606090565b60405190611aeb826101da565b600382527f3f3f3f00000000000000000000000000000000000000000000000000000000006020830152565b600080916040516001600160a01b0360208201917f95d89b4100000000000000000000000000000000000000000000000000000000835260048152611b5b816101da565b5192165afa611b68611aae565b9015611b775761142690611c01565b50611426611ade565b908151811015611870570160200190565b60ff1660ff81146106605760010190565b60208183031261026b5780519067ffffffffffffffff821161026b570181601f8201121561026b578051611bd581610218565b92611be360405194856101f6565b8184526020828401011161026b576114269160208085019101610988565b805160408110611c1f57508060208061142693518301019101611ba2565b602092908303611d3b576000805b60ff81168581109081611d02575b5015611c4f57611c4a90611b91565b611c2d565b92611c5d60ff809516611459565b92825b85811687811080611ccb575b15611cc0579081611cb5611cac611c86611cbb9588611b80565b517fff000000000000000000000000000000000000000000000000000000000000001690565b871a9188611b80565b53611b91565b611c60565b505094505050905090565b507fff00000000000000000000000000000000000000000000000000000000000000611cfa611c868387611b80565b161515611c6c565b7fff000000000000000000000000000000000000000000000000000000000000009150611c86611d329187611b80565b16151538611c3b565b915050611426611ade565b600080916040516001600160a01b0360208201917f06fdde0300000000000000000000000000000000000000000000000000000000835260048152611b5b816101da565b6001600160a01b0360405160208101927f313ce56700000000000000000000000000000000000000000000000000000000845260048252611dca826101da565b600093849384935192165afa611dde611aae565b9080611e15575b15611e0e57602081805181010312611e0a57602001519060ff82168203610ca4575090565b5080fd5b5050601290565b506020815114611de5565b6040519181518084526020808501918501928184019282808701915b858110611e895750505080518093875182018852940193828086019201905b828110611e7a575050505090603f91601f199351011501011660405290565b81518152908301908301611e5b565b8251815291810191849101611e3c565b15611ea057565b606460405162461bcd60e51b815260206004820152600e60248201527f736c6963655f6f766572666c6f770000000000000000000000000000000000006044820152fd5b15611eeb57565b606460405162461bcd60e51b815260206004820152601160248201527f736c6963655f6f75744f66426f756e64730000000000000000000000000000006044820152fd5b611f4382611f3c816111d5565b1015611e99565b611f588151611f51846111e3565b1115611ee4565b81611f70575050604051600081526020810160405290565b60405191601f8116916132c8831560051b80858701019484860193010101905b808410611fa85750508252601f01601f191660405290565b9092835181526020809101930190611f9056fea2646970667358221220d0f9f659a5c6121c4992559e7e9f442d617dd2f691782bc93ca88172ffbda4d564736f6c63430008150033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
0000000000000000000000002532c3d363306fa6d625e4cbad996bcf534e81540000000000000000000000002532c3d363306fa6d625e4cbad996bcf534e81540000000000000000000000002532c3d363306fa6d625e4cbad996bcf534e8154000000000000000000000000f0382a9eca5276d7b4bbcc503e4159c046c120ec000000000000000000000000d577429db653cd20effcd4977b2b41a6fd794a3b
-----Decoded View---------------
Arg [0] : _params (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]
-----Encoded View---------------
5 Constructor Arguments found :
Arg [0] : 0000000000000000000000002532c3d363306fa6d625e4cbad996bcf534e8154
Arg [1] : 0000000000000000000000002532c3d363306fa6d625e4cbad996bcf534e8154
Arg [2] : 0000000000000000000000002532c3d363306fa6d625e4cbad996bcf534e8154
Arg [3] : 000000000000000000000000f0382a9eca5276d7b4bbcc503e4159c046c120ec
Arg [4] : 000000000000000000000000d577429db653cd20effcd4977b2b41a6fd794a3b
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 26 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
[ Download: CSV Export ]
[ Download: CSV Export ]
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.