Overview
ETH Balance
0 ETH
Eth Value
$0.00More Info
Private Name Tags
ContractCreator
View more zero value Internal Transactions in Advanced View mode
Advanced mode:
Loading...
Loading
Contract Source Code Verified (Exact Match)
Contract Name:
FxUSDRegeneracy
Compiler Version
v0.8.26+commit.8a97fa7a
Optimization Enabled:
Yes with 200 runs
Other Settings:
cancun EvmVersion
Contract Source Code (Solidity Standard Json-Input format)
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable-v4/access/AccessControlUpgradeable.sol"; import { ERC20PermitUpgradeable } from "@openzeppelin/contracts-upgradeable-v4/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable-v4/token/ERC20/utils/SafeERC20Upgradeable.sol"; import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable-v4/token/ERC20/IERC20Upgradeable.sol"; import { EnumerableSetUpgradeable } from "@openzeppelin/contracts-upgradeable-v4/utils/structs/EnumerableSetUpgradeable.sol"; import { IFxFractionalTokenV2 } from "../v2/interfaces/IFxFractionalTokenV2.sol"; import { IFxMarketV2 } from "../v2/interfaces/IFxMarketV2.sol"; import { IFxTreasuryV2 } from "../v2/interfaces/IFxTreasuryV2.sol"; import { IFxUSD } from "../v2/interfaces/IFxUSD.sol"; import { IFxShareableRebalancePool } from "../v2/interfaces/IFxShareableRebalancePool.sol"; import { IFxUSDRegeneracy } from "../interfaces/IFxUSDRegeneracy.sol"; import { IPegKeeper } from "../interfaces/IPegKeeper.sol"; import { Math } from "../libraries/Math.sol"; /// @dev It has the same storage layout with `https://github.com/AladdinDAO/aladdin-v3-contracts/contracts/f(x)/v2/FxUSD.sol`. contract FxUSDRegeneracy is AccessControlUpgradeable, ERC20PermitUpgradeable, IFxUSD, IFxUSDRegeneracy { using SafeERC20Upgradeable for IERC20Upgradeable; using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; /********** * Errors * **********/ error ErrorCallerNotPoolManager(); error ErrorCallerNotPegKeeper(); error ErrorExceedStableReserve(); error ErrorInsufficientOutput(); error ErrorInsufficientBuyBack(); /************* * Constants * *************/ /// @notice The role for migrator. bytes32 public constant MIGRATOR_ROLE = keccak256("MIGRATOR_ROLE"); /// @dev The precision used to compute nav. uint256 private constant PRECISION = 1e18; /*********** * Structs * ***********/ /// @param fToken The address of Fractional Token. /// @param treasury The address of treasury contract. /// @param market The address of market contract. /// @param mintCap The maximum amount of fToken can be minted. /// @param managed The amount of fToken managed in this contract. struct FxMarketStruct { address fToken; address treasury; address market; uint256 mintCap; uint256 managed; } /// @dev The struct for stable token reserve. /// @param owned The number of stable coins owned in this contract. /// @param managed The amount of fxUSD managed under this stable coin. /// @param enabled Whether this stable coin is enabled, currently always true /// @param decimals The decimal for the stable coin. /// @param reserved Reserved slots for future usage. struct StableReserveStruct { uint96 owned; uint96 managed; uint8 decimals; } /*********************** * Immutable Variables * ***********************/ /// @inheritdoc IFxUSDRegeneracy address public immutable poolManager; /// @inheritdoc IFxUSDRegeneracy address public immutable stableToken; /// @inheritdoc IFxUSDRegeneracy address public immutable pegKeeper; /********************* * Storage Variables * *********************/ /// @notice Mapping from base token address to metadata. mapping(address => FxMarketStruct) public markets; /// @dev The list of supported base tokens. EnumerableSetUpgradeable.AddressSet private supportedTokens; /// @dev The list of supported rebalance pools. EnumerableSetUpgradeable.AddressSet private supportedPools; /// @notice The total supply for legacy 2.0 pools. uint256 public legacyTotalSupply; /// @notice The reserve struct for stable token. StableReserveStruct public stableReserve; /************* * Modifiers * *************/ modifier onlySupportedMarket(address _baseToken) { _checkBaseToken(_baseToken); _; } modifier onlySupportedPool(address _pool) { if (!supportedPools.contains(_pool)) revert ErrorUnsupportedRebalancePool(); _; } modifier onlyMintableMarket(address _baseToken, bool isMint) { _checkMarketMintable(_baseToken, isMint); _; } modifier onlyPoolManager() { if (_msgSender() != poolManager) revert ErrorCallerNotPoolManager(); _; } modifier onlyPegKeeper() { if (_msgSender() != pegKeeper) revert ErrorCallerNotPegKeeper(); _; } /*************** * Constructor * ***************/ constructor(address _poolManager, address _stableToken, address _pegKeeper) { poolManager = _poolManager; stableToken = _stableToken; pegKeeper = _pegKeeper; } function initialize(string memory _name, string memory _symbol) external initializer { __Context_init(); __ERC165_init(); __AccessControl_init(); __ERC20_init(_name, _symbol); __ERC20Permit_init(_name); _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); } function initializeV2() external reinitializer(2) { stableReserve.decimals = FxUSDRegeneracy(stableToken).decimals(); legacyTotalSupply = totalSupply(); } /************************* * Public View Functions * *************************/ /// @inheritdoc IFxUSD function getMarkets() external view override returns (address[] memory _tokens) { uint256 _numMarkets = supportedTokens.length(); _tokens = new address[](_numMarkets); for (uint256 i = 0; i < _numMarkets; ++i) { _tokens[i] = supportedTokens.at(i); } } /// @inheritdoc IFxUSD function getRebalancePools() external view override returns (address[] memory _pools) { uint256 _numPools = supportedPools.length(); _pools = new address[](_numPools); for (uint256 i = 0; i < _numPools; ++i) { _pools[i] = supportedPools.at(i); } } /// @inheritdoc IFxUSD function nav() external view override returns (uint256 _nav) { uint256 _numMarkets = supportedTokens.length(); uint256 _supply = legacyTotalSupply; if (_supply == 0) return PRECISION; for (uint256 i = 0; i < _numMarkets; i++) { address _baseToken = supportedTokens.at(i); address _fToken = markets[_baseToken].fToken; uint256 _fnav = IFxFractionalTokenV2(_fToken).nav(); _nav += _fnav * markets[_baseToken].managed; } _nav /= _supply; } /// @inheritdoc IFxUSD function isUnderCollateral() public view override returns (bool) { uint256 _numMarkets = supportedTokens.length(); for (uint256 i = 0; i < _numMarkets; i++) { address _baseToken = supportedTokens.at(i); address _treasury = markets[_baseToken].treasury; if (IFxTreasuryV2(_treasury).isUnderCollateral()) return true; } return false; } /**************************** * Public Mutated Functions * ****************************/ /// @inheritdoc IFxUSD function wrap( address _baseToken, uint256 _amount, address _receiver ) external override onlySupportedMarket(_baseToken) onlyMintableMarket(_baseToken, false) { if (isUnderCollateral()) revert ErrorUnderCollateral(); address _fToken = markets[_baseToken].fToken; IERC20Upgradeable(_fToken).safeTransferFrom(_msgSender(), address(this), _amount); _mintShares(_baseToken, _receiver, _amount); emit Wrap(_baseToken, _msgSender(), _receiver, _amount); } /// @inheritdoc IFxUSD function unwrap( address _baseToken, uint256 _amount, address _receiver ) external onlyRole(MIGRATOR_ROLE) onlySupportedMarket(_baseToken) { if (isUnderCollateral()) revert ErrorUnderCollateral(); _burnShares(_baseToken, _msgSender(), _amount); address _fToken = markets[_baseToken].fToken; IERC20Upgradeable(_fToken).safeTransfer(_receiver, _amount); emit Unwrap(_baseToken, _msgSender(), _receiver, _amount); } /// @inheritdoc IFxUSD function wrapFrom(address _pool, uint256 _amount, address _receiver) external override onlySupportedPool(_pool) { if (isUnderCollateral()) revert ErrorUnderCollateral(); address _baseToken = IFxShareableRebalancePool(_pool).baseToken(); _checkBaseToken(_baseToken); _checkMarketMintable(_baseToken, false); IFxShareableRebalancePool(_pool).withdrawFrom(_msgSender(), _amount, address(this)); _mintShares(_baseToken, _receiver, _amount); emit Wrap(_baseToken, _msgSender(), _receiver, _amount); } /// @inheritdoc IFxUSD function mint(address, uint256, address, uint256) external virtual override returns (uint256) { revert("mint paused"); } /// @inheritdoc IFxUSD function earn(address, uint256, address) external virtual override { revert("earn paused"); } /// @inheritdoc IFxUSD function mintAndEarn(address, uint256, address, uint256) external virtual override returns (uint256) { revert("mint and earn paused"); } /// @inheritdoc IFxUSD function redeem( address _baseToken, uint256 _amountIn, address _receiver, uint256 _minOut ) external override onlySupportedMarket(_baseToken) returns (uint256 _amountOut, uint256 _bonusOut) { if (isUnderCollateral()) revert ErrorUnderCollateral(); address _market = markets[_baseToken].market; address _fToken = markets[_baseToken].fToken; uint256 _balance = IERC20Upgradeable(_fToken).balanceOf(address(this)); (_amountOut, _bonusOut) = IFxMarketV2(_market).redeemFToken(_amountIn, _receiver, _minOut); // the real amount of fToken redeemed _amountIn = _balance - IERC20Upgradeable(_fToken).balanceOf(address(this)); _burnShares(_baseToken, _msgSender(), _amountIn); emit Unwrap(_baseToken, _msgSender(), _receiver, _amountIn); } /// @inheritdoc IFxUSD function redeemFrom( address _pool, uint256 _amountIn, address _receiver, uint256 _minOut ) external override onlySupportedPool(_pool) returns (uint256 _amountOut, uint256 _bonusOut) { address _baseToken = IFxShareableRebalancePool(_pool).baseToken(); address _market = markets[_baseToken].market; address _fToken = markets[_baseToken].fToken; // calculate the actual amount of fToken withdrawn from rebalance pool. _amountOut = IERC20Upgradeable(_fToken).balanceOf(address(this)); IFxShareableRebalancePool(_pool).withdrawFrom(_msgSender(), _amountIn, address(this)); _amountOut = IERC20Upgradeable(_fToken).balanceOf(address(this)) - _amountOut; // redeem fToken as base token // assume all fToken will be redeem for simplicity (_amountOut, _bonusOut) = IFxMarketV2(_market).redeemFToken(_amountOut, _receiver, _minOut); } /// @inheritdoc IFxUSD function autoRedeem( uint256 _amountIn, address _receiver, uint256[] memory _minOuts ) external override returns (address[] memory _baseTokens, uint256[] memory _amountOuts, uint256[] memory _bonusOuts) { uint256 _numMarkets = supportedTokens.length(); if (_minOuts.length != _numMarkets) revert ErrorLengthMismatch(); _baseTokens = new address[](_numMarkets); _amountOuts = new uint256[](_numMarkets); _bonusOuts = new uint256[](_numMarkets); uint256[] memory _supplies = new uint256[](_numMarkets); bool _isUnderCollateral = false; for (uint256 i = 0; i < _numMarkets; i++) { _baseTokens[i] = supportedTokens.at(i); _supplies[i] = markets[_baseTokens[i]].managed; address _treasury = markets[_baseTokens[i]].treasury; if (IFxTreasuryV2(_treasury).isUnderCollateral()) _isUnderCollateral = true; } uint256 _supply = legacyTotalSupply; if (_amountIn > _supply) revert("redeem exceed supply"); unchecked { legacyTotalSupply = _supply - _amountIn; } _burn(_msgSender(), _amountIn); if (_isUnderCollateral) { // redeem proportionally for (uint256 i = 0; i < _numMarkets; i++) { _amountOuts[i] = (_supplies[i] * _amountIn) / _supply; } } else { // redeem by sorted fToken amounts while (_amountIn > 0) { unchecked { uint256 maxSupply = _supplies[0]; uint256 maxIndex = 0; for (uint256 i = 1; i < _numMarkets; i++) { if (_supplies[i] > maxSupply) { maxSupply = _supplies[i]; maxIndex = i; } } if (_amountIn > maxSupply) _amountOuts[maxIndex] = maxSupply; else _amountOuts[maxIndex] = _amountIn; _supplies[maxIndex] -= _amountOuts[maxIndex]; _amountIn -= _amountOuts[maxIndex]; } } } for (uint256 i = 0; i < _numMarkets; i++) { if (_amountOuts[i] == 0) continue; emit Unwrap(_baseTokens[i], _msgSender(), _receiver, _amountOuts[i]); markets[_baseTokens[i]].managed -= _amountOuts[i]; address _market = markets[_baseTokens[i]].market; (_amountOuts[i], _bonusOuts[i]) = IFxMarketV2(_market).redeemFToken(_amountOuts[i], _receiver, _minOuts[i]); } } /// @inheritdoc IFxUSDRegeneracy function mint(address to, uint256 amount) external onlyPoolManager { _mint(to, amount); } /// @inheritdoc IFxUSDRegeneracy function burn(address from, uint256 amount) external onlyPoolManager { _burn(from, amount); } /// @inheritdoc IFxUSDRegeneracy function onRebalanceWithStable(uint256 amountStableToken, uint256 amountFxUSD) external onlyPoolManager { stableReserve.owned += uint96(amountStableToken); stableReserve.managed += uint96(amountFxUSD); emit RebalanceWithStable(amountStableToken, amountFxUSD); } /// @inheritdoc IFxUSDRegeneracy function buyback( uint256 amountIn, address receiver, bytes calldata data ) external onlyPegKeeper returns (uint256 amountOut, uint256 bonusOut) { StableReserveStruct memory cachedStableReserve = stableReserve; if (amountIn > cachedStableReserve.owned) revert ErrorExceedStableReserve(); // rounding up uint256 expectedFxUSD = Math.mulDivUp(amountIn, cachedStableReserve.managed, cachedStableReserve.owned); // convert USDC to fxUSD IERC20Upgradeable(stableToken).safeTransfer(pegKeeper, amountIn); uint256 actualOut = balanceOf(address(this)); amountOut = IPegKeeper(pegKeeper).onSwap(stableToken, address(this), amountIn, data); actualOut = balanceOf(address(this)) - actualOut; // check actual fxUSD swapped in case peg keeper is hacked. if (amountOut > actualOut) revert ErrorInsufficientOutput(); // check fxUSD swapped can cover debts if (amountOut < expectedFxUSD) revert ErrorInsufficientBuyBack(); bonusOut = amountOut - expectedFxUSD; _burn(address(this), expectedFxUSD); unchecked { cachedStableReserve.owned -= uint96(amountIn); if (cachedStableReserve.managed > expectedFxUSD) { cachedStableReserve.managed -= uint96(expectedFxUSD); } else { cachedStableReserve.managed = 0; } stableReserve = cachedStableReserve; } if (bonusOut > 0) { _transfer(address(this), receiver, bonusOut); } emit Buyback(amountIn, amountOut, bonusOut); } /********************** * Internal Functions * **********************/ /// @dev Internal function to check base token. /// @param _baseToken The address of the base token. function _checkBaseToken(address _baseToken) private view { if (!supportedTokens.contains(_baseToken)) revert ErrorUnsupportedMarket(); } /// @dev Internal function to check market. /// @param _baseToken The address of the base token. /// @param _checkCollateralRatio Whether to check collateral ratio. function _checkMarketMintable(address _baseToken, bool _checkCollateralRatio) private view { address _treasury = markets[_baseToken].treasury; if (_checkCollateralRatio) { uint256 _collateralRatio = IFxTreasuryV2(_treasury).collateralRatio(); uint256 _stabilityRatio = IFxMarketV2(markets[_baseToken].market).stabilityRatio(); // not allow to mint when collateral ratio <= stability ratio if (_collateralRatio <= _stabilityRatio) revert ErrorMarketInStabilityMode(); } // not allow to mint when price is invalid if (!IFxTreasuryV2(_treasury).isBaseTokenPriceValid()) revert ErrorMarketWithInvalidPrice(); } /// @dev Internal function to mint fxUSD. /// @param _baseToken The address of the base token. /// @param _receiver The address of fxUSD recipient. /// @param _amount The amount of fxUSD to mint. function _mintShares(address _baseToken, address _receiver, uint256 _amount) private { unchecked { markets[_baseToken].managed += _amount; legacyTotalSupply += _amount; } _mint(_receiver, _amount); } /// @dev Internal function to burn fxUSD. /// @param _baseToken The address of the base token. /// @param _owner The address of fxUSD owner. /// @param _amount The amount of fxUSD to burn. function _burnShares(address _baseToken, address _owner, uint256 _amount) private { uint256 _managed = markets[_baseToken].managed; if (_amount > _managed) revert ErrorInsufficientLiquidity(); unchecked { markets[_baseToken].managed -= _amount; legacyTotalSupply -= _amount; } _burn(_owner, _amount); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol) pragma solidity ^0.8.0; import "./IAccessControlUpgradeable.sol"; import "../utils/ContextUpgradeable.sol"; import "../utils/StringsUpgradeable.sol"; import "../utils/introspection/ERC165Upgradeable.sol"; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ```solidity * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ```solidity * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} * to enforce additional security measures for this role. */ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControlUpgradeable, ERC165Upgradeable { struct RoleData { mapping(address => bool) members; bytes32 adminRole; } mapping(bytes32 => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with a standardized message including the required role. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ * * _Available since v4.1._ */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } function __AccessControl_init() internal onlyInitializing { } function __AccessControl_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControlUpgradeable).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual override returns (bool) { return _roles[role].members[account]; } /** * @dev Revert with a standard message if `_msgSender()` is missing `role`. * Overriding this function changes the behavior of the {onlyRole} modifier. * * Format of the revert message is described in {_checkRole}. * * _Available since v4.6._ */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Revert with a standard message if `account` is missing `role`. * * The format of the revert reason is given by the following regular expression: * * /^AccessControl: account (0x[0-9a-f]{40}) is missing role (0x[0-9a-f]{64})$/ */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert( string( abi.encodePacked( "AccessControl: account ", StringsUpgradeable.toHexString(account), " is missing role ", StringsUpgradeable.toHexString(uint256(role), 32) ) ) ); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual override returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleGranted} event. */ function grantRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleRevoked} event. */ function revokeRole(bytes32 role, address account) public virtual override onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. * * May emit a {RoleRevoked} event. */ function renounceRole(bytes32 role, address account) public virtual override { require(account == _msgSender(), "AccessControl: can only renounce roles for self"); _revokeRole(role, account); } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. Note that unlike {grantRole}, this function doesn't perform any * checks on the calling account. * * May emit a {RoleGranted} event. * * [WARNING] * ==== * This function should only be called from the constructor when setting * up the initial roles for the system. * * Using this function in any other way is effectively circumventing the admin * system imposed by {AccessControl}. * ==== * * NOTE: This function is deprecated in favor of {_grantRole}. */ function _setupRole(bytes32 role, address account) internal virtual { _grantRole(role, account); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Grants `role` to `account`. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual { if (!hasRole(role, account)) { _roles[role].members[account] = true; emit RoleGranted(role, account, _msgSender()); } } /** * @dev Revokes `role` from `account`. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual { if (hasRole(role, account)) { _roles[role].members[account] = false; emit RoleRevoked(role, account, _msgSender()); } } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (access/IAccessControl.sol) pragma solidity ^0.8.0; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControlUpgradeable { /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. * * _Available since v3.1._ */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `account`. */ function renounceRole(bytes32 role, address account) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC5267.sol) pragma solidity ^0.8.0; interface IERC5267Upgradeable { /** * @dev MAY be emitted to signal that the domain could have changed. */ event EIP712DomainChanged(); /** * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712 * signature. */ function eip712Domain() external view returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.2; import "../../utils/AddressUpgradeable.sol"; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Indicates that the contract has been initialized. * @custom:oz-retyped-from bool */ uint8 private _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool private _initializing; /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint8 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a * constructor. * * Emits an {Initialized} event. */ modifier initializer() { bool isTopLevelCall = !_initializing; require( (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1), "Initializable: contract is already initialized" ); _initialized = 1; if (isTopLevelCall) { _initializing = true; } _; if (isTopLevelCall) { _initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: setting the version to 255 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint8 version) { require(!_initializing && _initialized < version, "Initializable: contract is already initialized"); _initialized = version; _initializing = true; _; _initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { require(_initializing, "Initializable: contract is not initializing"); _; } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { require(!_initializing, "Initializable: contract is initializing"); if (_initialized != type(uint8).max) { _initialized = type(uint8).max; emit Initialized(type(uint8).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint8) { return _initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _initializing; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol) pragma solidity ^0.8.0; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @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 ReentrancyGuardUpgradeable is Initializable { // 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; function __ReentrancyGuard_init() internal onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { _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; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.0; import "./IERC20Upgradeable.sol"; import "./extensions/IERC20MetadataUpgradeable.sol"; import "../../utils/ContextUpgradeable.sol"; import {Initializable} from "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * 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 ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20Upgradeable, IERC20MetadataUpgradeable { 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. */ function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing { __ERC20_init_unchained(name_, symbol_); } function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing { _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 {} /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[45] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/ERC20Permit.sol) pragma solidity ^0.8.0; import "./IERC20PermitUpgradeable.sol"; import "../ERC20Upgradeable.sol"; import "../../../utils/cryptography/ECDSAUpgradeable.sol"; import "../../../utils/cryptography/EIP712Upgradeable.sol"; import "../../../utils/CountersUpgradeable.sol"; import {Initializable} from "../../../proxy/utils/Initializable.sol"; /** * @dev Implementation 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. * * _Available since v3.4._ * * @custom:storage-size 51 */ abstract contract ERC20PermitUpgradeable is Initializable, ERC20Upgradeable, IERC20PermitUpgradeable, EIP712Upgradeable { using CountersUpgradeable for CountersUpgradeable.Counter; mapping(address => CountersUpgradeable.Counter) private _nonces; // solhint-disable-next-line var-name-mixedcase bytes32 private constant _PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); /** * @dev In previous versions `_PERMIT_TYPEHASH` was declared as `immutable`. * However, to ensure consistency with the upgradeable transpiler, we will continue * to reserve a slot. * @custom:oz-renamed-from _PERMIT_TYPEHASH */ // solhint-disable-next-line var-name-mixedcase bytes32 private _PERMIT_TYPEHASH_DEPRECATED_SLOT; /** * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`. * * It's a good idea to use the same `name` that is defined as the ERC20 token name. */ function __ERC20Permit_init(string memory name) internal onlyInitializing { __EIP712_init_unchained(name, "1"); } function __ERC20Permit_init_unchained(string memory) internal onlyInitializing {} /** * @inheritdoc IERC20PermitUpgradeable */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual override { require(block.timestamp <= deadline, "ERC20Permit: expired deadline"); bytes32 structHash = keccak256(abi.encode(_PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline)); bytes32 hash = _hashTypedDataV4(structHash); address signer = ECDSAUpgradeable.recover(hash, v, r, s); require(signer == owner, "ERC20Permit: invalid signature"); _approve(owner, spender, value); } /** * @inheritdoc IERC20PermitUpgradeable */ function nonces(address owner) public view virtual override returns (uint256) { return _nonces[owner].current(); } /** * @inheritdoc IERC20PermitUpgradeable */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view override returns (bytes32) { return _domainSeparatorV4(); } /** * @dev "Consume a nonce": return the current value and increment. * * _Available since v4.1._ */ function _useNonce(address owner) internal virtual returns (uint256 current) { CountersUpgradeable.Counter storage nonce = _nonces[owner]; current = nonce.current(); nonce.increment(); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[49] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.0; import "../IERC20Upgradeable.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. * * _Available since v4.1._ */ interface IERC20MetadataUpgradeable is IERC20Upgradeable { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.4) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20PermitUpgradeable { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.0; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20Upgradeable { /** * @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 "../IERC20Upgradeable.sol"; import "../extensions/IERC20PermitUpgradeable.sol"; import "../../../utils/AddressUpgradeable.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 SafeERC20Upgradeable { using AddressUpgradeable 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(IERC20Upgradeable 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(IERC20Upgradeable 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(IERC20Upgradeable 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(IERC20Upgradeable 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(IERC20Upgradeable 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(IERC20Upgradeable 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( IERC20PermitUpgradeable 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(IERC20Upgradeable 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(IERC20Upgradeable 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))) && AddressUpgradeable.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 AddressUpgradeable { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol) pragma solidity ^0.8.0; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/Counters.sol) pragma solidity ^0.8.0; /** * @title Counters * @author Matt Condon (@shrugs) * @dev Provides counters that can only be incremented, decremented or reset. This can be used e.g. to track the number * of elements in a mapping, issuing ERC721 ids, or counting request ids. * * Include with `using Counters for Counters.Counter;` */ library CountersUpgradeable { struct Counter { // This variable should never be directly accessed by users of the library: interactions must be restricted to // the library's function. As of Solidity v0.5.2, this cannot be enforced, though there is a proposal to add // this feature: see https://github.com/ethereum/solidity/issues/4637 uint256 _value; // default: 0 } function current(Counter storage counter) internal view returns (uint256) { return counter._value; } function increment(Counter storage counter) internal { unchecked { counter._value += 1; } } function decrement(Counter storage counter) internal { uint256 value = counter._value; require(value > 0, "Counter: decrement overflow"); unchecked { counter._value = value - 1; } } function reset(Counter storage counter) internal { counter._value = 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.0; import "../StringsUpgradeable.sol"; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSAUpgradeable { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS, InvalidSignatureV // Deprecated in v4.8 } function _throwError(RecoverError error) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert("ECDSA: invalid signature"); } else if (error == RecoverError.InvalidSignatureLength) { revert("ECDSA: invalid signature length"); } else if (error == RecoverError.InvalidSignatureS) { revert("ECDSA: invalid signature 's' value"); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature` or error string. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM opcode allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, signature); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] * * _Available since v4.3._ */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError) { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. * * _Available since v4.2._ */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, r, vs); _throwError(error); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. * * _Available since v4.3._ */ function tryRecover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address, RecoverError) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature); } return (signer, RecoverError.NoError); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { (address recovered, RecoverError error) = tryRecover(hash, v, r, s); _throwError(error); return recovered; } /** * @dev Returns an Ethereum Signed Message, created from a `hash`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes32 hash) internal pure returns (bytes32 message) { // 32 is the length in bytes of hash, // enforced by the type signature above /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") mstore(0x1c, hash) message := keccak256(0x00, 0x3c) } } /** * @dev Returns an Ethereum Signed Message, created from `s`. This * produces hash corresponding to the one signed with the * https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] * JSON-RPC method as part of EIP-191. * * See {recover}. */ function toEthSignedMessageHash(bytes memory s) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19Ethereum Signed Message:\n", StringsUpgradeable.toString(s.length), s)); } /** * @dev Returns an Ethereum Signed Typed Data, created from a * `domainSeparator` and a `structHash`. This produces hash corresponding * to the one signed with the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] * JSON-RPC method as part of EIP-712. * * See {recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 data) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, "\x19\x01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) data := keccak256(ptr, 0x42) } } /** * @dev Returns an Ethereum Signed Data with intended validator, created from a * `validator` and `data` according to the version 0 of EIP-191. * * See {recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked("\x19\x00", validator, data)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/cryptography/EIP712.sol) pragma solidity ^0.8.8; import "./ECDSAUpgradeable.sol"; import "../../interfaces/IERC5267Upgradeable.sol"; import {Initializable} from "../../proxy/utils/Initializable.sol"; /** * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. * * The encoding specified in the EIP is very generic, and such a generic implementation in Solidity is not feasible, * thus this contract does not implement the encoding itself. Protocols need to implement the type-specific encoding * they need in their contracts using a combination of `abi.encode` and `keccak256`. * * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA * ({_hashTypedDataV4}). * * The implementation of the domain separator was designed to be as efficient as possible while still properly updating * the chain id to protect against replay attacks on an eventual fork of the chain. * * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. * * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain * separator of the implementation contract. This will cause the `_domainSeparatorV4` function to always rebuild the * separator from the immutable values, which is cheaper than accessing a cached version in cold storage. * * _Available since v3.4._ * * @custom:storage-size 52 */ abstract contract EIP712Upgradeable is Initializable, IERC5267Upgradeable { bytes32 private constant _TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); /// @custom:oz-renamed-from _HASHED_NAME bytes32 private _hashedName; /// @custom:oz-renamed-from _HASHED_VERSION bytes32 private _hashedVersion; string private _name; string private _version; /** * @dev Initializes the domain separator and parameter caches. * * The meaning of `name` and `version` is specified in * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: * * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. * - `version`: the current major version of the signing domain. * * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart * contract upgrade]. */ function __EIP712_init(string memory name, string memory version) internal onlyInitializing { __EIP712_init_unchained(name, version); } function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing { _name = name; _version = version; // Reset prior values in storage if upgrading _hashedName = 0; _hashedVersion = 0; } /** * @dev Returns the domain separator for the current chain. */ function _domainSeparatorV4() internal view returns (bytes32) { return _buildDomainSeparator(); } function _buildDomainSeparator() private view returns (bytes32) { return keccak256(abi.encode(_TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), block.chainid, address(this))); } /** * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this * function returns the hash of the fully encoded EIP712 message for this domain. * * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: * * ```solidity * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( * keccak256("Mail(address to,string contents)"), * mailTo, * keccak256(bytes(mailContents)) * ))); * address signer = ECDSA.recover(digest, signature); * ``` */ function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { return ECDSAUpgradeable.toTypedDataHash(_domainSeparatorV4(), structHash); } /** * @dev See {EIP-5267}. * * _Available since v4.9._ */ function eip712Domain() public view virtual override returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ) { // If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized // and the EIP712 domain is not reliable, as it will be missing name and version. require(_hashedName == 0 && _hashedVersion == 0, "EIP712: Uninitialized"); return ( hex"0f", // 01111 _EIP712Name(), _EIP712Version(), block.chainid, address(this), bytes32(0), new uint256[](0) ); } /** * @dev The name parameter for the EIP712 domain. * * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs * are a concern. */ function _EIP712Name() internal virtual view returns (string memory) { return _name; } /** * @dev The version parameter for the EIP712 domain. * * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs * are a concern. */ function _EIP712Version() internal virtual view returns (string memory) { return _version; } /** * @dev The hash of the name parameter for the EIP712 domain. * * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead. */ function _EIP712NameHash() internal view returns (bytes32) { string memory name = _EIP712Name(); if (bytes(name).length > 0) { return keccak256(bytes(name)); } else { // If the name is empty, the contract may have been upgraded without initializing the new storage. // We return the name hash in storage if non-zero, otherwise we assume the name is empty by design. bytes32 hashedName = _hashedName; if (hashedName != 0) { return hashedName; } else { return keccak256(""); } } } /** * @dev The hash of the version parameter for the EIP712 domain. * * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead. */ function _EIP712VersionHash() internal view returns (bytes32) { string memory version = _EIP712Version(); if (bytes(version).length > 0) { return keccak256(bytes(version)); } else { // If the version is empty, the contract may have been upgraded without initializing the new storage. // We return the version hash in storage if non-zero, otherwise we assume the version is empty by design. bytes32 hashedVersion = _hashedVersion; if (hashedVersion != 0) { return hashedVersion; } else { return keccak256(""); } } } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[48] private __gap; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol) pragma solidity ^0.8.0; import "./IERC165Upgradeable.sol"; import {Initializable} from "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` * * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation. */ abstract contract ERC165Upgradeable is Initializable, IERC165Upgradeable { function __ERC165_init() internal onlyInitializing { } function __ERC165_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IERC165Upgradeable).interfaceId; } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps */ uint256[50] private __gap; }
// 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 IERC165Upgradeable { /** * @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 MathUpgradeable { 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/SignedMath.sol) pragma solidity ^0.8.0; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMathUpgradeable { /** * @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/MathUpgradeable.sol"; import "./math/SignedMathUpgradeable.sol"; /** * @dev String operations. */ library StringsUpgradeable { 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 = MathUpgradeable.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(SignedMathUpgradeable.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, MathUpgradeable.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: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.0; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ```solidity * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSetUpgradeable { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position of the value in the `values` array, plus 1 because index 0 // means a value is not in the set. mapping(bytes32 => uint256) _indexes; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._indexes[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We read and store the value's index to prevent multiple reads from the same storage slot uint256 valueIndex = set._indexes[value]; if (valueIndex != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 toDeleteIndex = valueIndex - 1; uint256 lastIndex = set._values.length - 1; if (lastIndex != toDeleteIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the last value to the index where the value to delete is set._values[toDeleteIndex] = lastValue; // Update the index for the moved value set._indexes[lastValue] = valueIndex; // Replace lastValue's index to valueIndex } // Delete the slot where the moved value was stored set._values.pop(); // Delete the index for the deleted slot delete set._indexes[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._indexes[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol) pragma solidity ^0.8.20; import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol"; import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol"; import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol"; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ```solidity * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ```solidity * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} * to enforce additional security measures for this role. */ abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable { struct RoleData { mapping(address account => bool) hasRole; bytes32 adminRole; } bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl struct AccessControlStorage { mapping(bytes32 role => RoleData) _roles; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800; function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) { assembly { $.slot := AccessControlStorageLocation } } /** * @dev Modifier that checks that an account has a specific role. Reverts * with an {AccessControlUnauthorizedAccount} error including the required role. */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } function __AccessControl_init() internal onlyInitializing { } function __AccessControl_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual returns (bool) { AccessControlStorage storage $ = _getAccessControlStorage(); return $._roles[role].hasRole[account]; } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()` * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier. */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account` * is missing `role`. */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert AccessControlUnauthorizedAccount(account, role); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) { AccessControlStorage storage $ = _getAccessControlStorage(); return $._roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleGranted} event. */ function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleRevoked} event. */ function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `callerConfirmation`. * * May emit a {RoleRevoked} event. */ function renounceRole(bytes32 role, address callerConfirmation) public virtual { if (callerConfirmation != _msgSender()) { revert AccessControlBadConfirmation(); } _revokeRole(role, callerConfirmation); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { AccessControlStorage storage $ = _getAccessControlStorage(); bytes32 previousAdminRole = getRoleAdmin(role); $._roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual returns (bool) { AccessControlStorage storage $ = _getAccessControlStorage(); if (!hasRole(role, account)) { $._roles[role].hasRole[account] = true; emit RoleGranted(role, account, _msgSender()); return true; } else { return false; } } /** * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual returns (bool) { AccessControlStorage storage $ = _getAccessControlStorage(); if (hasRole(role, account)) { $._roles[role].hasRole[account] = false; emit RoleRevoked(role, account, _msgSender()); return true; } else { return false; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (proxy/utils/Initializable.sol) pragma solidity ^0.8.20; /** * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect. * * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in * case an upgrade adds a module that needs to be initialized. * * For example: * * [.hljs-theme-light.nopadding] * ```solidity * contract MyToken is ERC20Upgradeable { * function initialize() initializer public { * __ERC20_init("MyToken", "MTK"); * } * } * * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable { * function initializeV2() reinitializer(2) public { * __ERC20Permit_init("MyToken"); * } * } * ``` * * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}. * * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity. * * [CAUTION] * ==== * Avoid leaving a contract uninitialized. * * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed: * * [.hljs-theme-light.nopadding] * ``` * /// @custom:oz-upgrades-unsafe-allow constructor * constructor() { * _disableInitializers(); * } * ``` * ==== */ abstract contract Initializable { /** * @dev Storage of the initializable contract. * * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions * when using with upgradeable contracts. * * @custom:storage-location erc7201:openzeppelin.storage.Initializable */ struct InitializableStorage { /** * @dev Indicates that the contract has been initialized. */ uint64 _initialized; /** * @dev Indicates that the contract is in the process of being initialized. */ bool _initializing; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00; /** * @dev The contract is already initialized. */ error InvalidInitialization(); /** * @dev The contract is not initializing. */ error NotInitializing(); /** * @dev Triggered when the contract has been initialized or reinitialized. */ event Initialized(uint64 version); /** * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope, * `onlyInitializing` functions can be used to initialize parent contracts. * * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in * production. * * Emits an {Initialized} event. */ modifier initializer() { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); // Cache values to avoid duplicated sloads bool isTopLevelCall = !$._initializing; uint64 initialized = $._initialized; // Allowed calls: // - initialSetup: the contract is not in the initializing state and no previous version was // initialized // - construction: the contract is initialized at version 1 (no reininitialization) and the // current contract is just being deployed bool initialSetup = initialized == 0 && isTopLevelCall; bool construction = initialized == 1 && address(this).code.length == 0; if (!initialSetup && !construction) { revert InvalidInitialization(); } $._initialized = 1; if (isTopLevelCall) { $._initializing = true; } _; if (isTopLevelCall) { $._initializing = false; emit Initialized(1); } } /** * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be * used to initialize parent contracts. * * A reinitializer may be used after the original initialization step. This is essential to configure modules that * are added through upgrades and that require initialization. * * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer` * cannot be nested. If one is invoked in the context of another, execution will revert. * * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in * a contract, executing them in the right order is up to the developer or operator. * * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization. * * Emits an {Initialized} event. */ modifier reinitializer(uint64 version) { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing || $._initialized >= version) { revert InvalidInitialization(); } $._initialized = version; $._initializing = true; _; $._initializing = false; emit Initialized(version); } /** * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the * {initializer} and {reinitializer} modifiers, directly or indirectly. */ modifier onlyInitializing() { _checkInitializing(); _; } /** * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}. */ function _checkInitializing() internal view virtual { if (!_isInitializing()) { revert NotInitializing(); } } /** * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call. * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized * to any version. It is recommended to use this to lock implementation contracts that are designed to be called * through proxies. * * Emits an {Initialized} event the first time it is successfully executed. */ function _disableInitializers() internal virtual { // solhint-disable-next-line var-name-mixedcase InitializableStorage storage $ = _getInitializableStorage(); if ($._initializing) { revert InvalidInitialization(); } if ($._initialized != type(uint64).max) { $._initialized = type(uint64).max; emit Initialized(type(uint64).max); } } /** * @dev Returns the highest version that has been initialized. See {reinitializer}. */ function _getInitializedVersion() internal view returns (uint64) { return _getInitializableStorage()._initialized; } /** * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}. */ function _isInitializing() internal view returns (bool) { return _getInitializableStorage()._initializing; } /** * @dev Returns a pointer to the storage namespace. */ // solhint-disable-next-line var-name-mixedcase function _getInitializableStorage() private pure returns (InitializableStorage storage $) { assembly { $.slot := INITIALIZABLE_STORAGE } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import {IERC20Metadata} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol"; import {IERC20Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; import {Initializable} from "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. */ abstract contract ERC20Upgradeable is Initializable, ContextUpgradeable, IERC20, IERC20Metadata, IERC20Errors { /// @custom:storage-location erc7201:openzeppelin.storage.ERC20 struct ERC20Storage { mapping(address account => uint256) _balances; mapping(address account => mapping(address spender => uint256)) _allowances; uint256 _totalSupply; string _name; string _symbol; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC20")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant ERC20StorageLocation = 0x52c63247e1f47db19d5ce0460030c497f067ca4cebf71ba98eeadabe20bace00; function _getERC20Storage() private pure returns (ERC20Storage storage $) { assembly { $.slot := ERC20StorageLocation } } /** * @dev Sets the values for {name} and {symbol}. * * All two of these values are immutable: they can only be set once during * construction. */ function __ERC20_init(string memory name_, string memory symbol_) internal onlyInitializing { __ERC20_init_unchained(name_, symbol_); } function __ERC20_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing { ERC20Storage storage $ = _getERC20Storage(); $._name = name_; $._symbol = symbol_; } /** * @dev Returns the name of the token. */ function name() public view virtual returns (string memory) { ERC20Storage storage $ = _getERC20Storage(); return $._name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual returns (string memory) { ERC20Storage storage $ = _getERC20Storage(); return $._symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the default value returned by this function, unless * it's overridden. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual returns (uint256) { ERC20Storage storage $ = _getERC20Storage(); return $._totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual returns (uint256) { ERC20Storage storage $ = _getERC20Storage(); return $._balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `value`. */ function transfer(address to, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _transfer(owner, to, value); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual returns (uint256) { ERC20Storage storage $ = _getERC20Storage(); return $._allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, value); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `value`. * - the caller must have allowance for ``from``'s tokens of at least * `value`. */ function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, value); _transfer(from, to, value); return true; } /** * @dev Moves a `value` amount of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _transfer(address from, address to, uint256 value) internal { if (from == address(0)) { revert ERC20InvalidSender(address(0)); } if (to == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(from, to, value); } /** * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding * this function. * * Emits a {Transfer} event. */ function _update(address from, address to, uint256 value) internal virtual { ERC20Storage storage $ = _getERC20Storage(); if (from == address(0)) { // Overflow check required: The rest of the code assumes that totalSupply never overflows $._totalSupply += value; } else { uint256 fromBalance = $._balances[from]; if (fromBalance < value) { revert ERC20InsufficientBalance(from, fromBalance, value); } unchecked { // Overflow not possible: value <= fromBalance <= totalSupply. $._balances[from] = fromBalance - value; } } if (to == address(0)) { unchecked { // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. $._totalSupply -= value; } } else { unchecked { // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256. $._balances[to] += value; } } emit Transfer(from, to, value); } /** * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). * Relies on the `_update` mechanism * * Emits a {Transfer} event with `from` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _mint(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(address(0), account, value); } /** * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. * Relies on the `_update` mechanism. * * Emits a {Transfer} event with `to` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead */ function _burn(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidSender(address(0)); } _update(account, address(0), value); } /** * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. * * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. */ function _approve(address owner, address spender, uint256 value) internal { _approve(owner, spender, value, true); } /** * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event. * * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any * `Approval` event during `transferFrom` operations. * * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to * true using the following override: * ``` * function _approve(address owner, address spender, uint256 value, bool) internal virtual override { * super._approve(owner, spender, value, true); * } * ``` * * Requirements are the same as {_approve}. */ function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { ERC20Storage storage $ = _getERC20Storage(); if (owner == address(0)) { revert ERC20InvalidApprover(address(0)); } if (spender == address(0)) { revert ERC20InvalidSpender(address(0)); } $._allowances[owner][spender] = value; if (emitEvent) { emit Approval(owner, spender, value); } } /** * @dev Updates `owner` s allowance for `spender` based on spent `value`. * * Does not update the allowance value in case of infinite allowance. * Revert if not enough allowance is available. * * Does not emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 value) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { if (currentAllowance < value) { revert ERC20InsufficientAllowance(spender, currentAllowance, value); } unchecked { _approve(owner, spender, currentAllowance - value, false); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/ERC20Permit.sol) pragma solidity ^0.8.20; import {IERC20Permit} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol"; import {ERC20Upgradeable} from "../ERC20Upgradeable.sol"; import {ECDSA} from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol"; import {EIP712Upgradeable} from "../../../utils/cryptography/EIP712Upgradeable.sol"; import {NoncesUpgradeable} from "../../../utils/NoncesUpgradeable.sol"; import {Initializable} from "../../../proxy/utils/Initializable.sol"; /** * @dev Implementation 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. */ abstract contract ERC20PermitUpgradeable is Initializable, ERC20Upgradeable, IERC20Permit, EIP712Upgradeable, NoncesUpgradeable { bytes32 private constant PERMIT_TYPEHASH = keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)"); /** * @dev Permit deadline has expired. */ error ERC2612ExpiredSignature(uint256 deadline); /** * @dev Mismatched signature. */ error ERC2612InvalidSigner(address signer, address owner); /** * @dev Initializes the {EIP712} domain separator using the `name` parameter, and setting `version` to `"1"`. * * It's a good idea to use the same `name` that is defined as the ERC20 token name. */ function __ERC20Permit_init(string memory name) internal onlyInitializing { __EIP712_init_unchained(name, "1"); } function __ERC20Permit_init_unchained(string memory) internal onlyInitializing {} /** * @inheritdoc IERC20Permit */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) public virtual { if (block.timestamp > deadline) { revert ERC2612ExpiredSignature(deadline); } bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, _useNonce(owner), deadline)); bytes32 hash = _hashTypedDataV4(structHash); address signer = ECDSA.recover(hash, v, r, s); if (signer != owner) { revert ERC2612InvalidSigner(signer, owner); } _approve(owner, spender, value); } /** * @inheritdoc IERC20Permit */ function nonces(address owner) public view virtual override(IERC20Permit, NoncesUpgradeable) returns (uint256) { return super.nonces(owner); } /** * @inheritdoc IERC20Permit */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view virtual returns (bytes32) { return _domainSeparatorV4(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/ERC721.sol) pragma solidity ^0.8.20; import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import {IERC721Receiver} from "@openzeppelin/contracts/token/ERC721/IERC721Receiver.sol"; import {IERC721Metadata} from "@openzeppelin/contracts/token/ERC721/extensions/IERC721Metadata.sol"; import {ContextUpgradeable} from "../../utils/ContextUpgradeable.sol"; import {Strings} from "@openzeppelin/contracts/utils/Strings.sol"; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {ERC165Upgradeable} from "../../utils/introspection/ERC165Upgradeable.sol"; import {IERC721Errors} from "@openzeppelin/contracts/interfaces/draft-IERC6093.sol"; import {Initializable} from "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of https://eips.ethereum.org/EIPS/eip-721[ERC721] Non-Fungible Token Standard, including * the Metadata extension, but not including the Enumerable extension, which is available separately as * {ERC721Enumerable}. */ abstract contract ERC721Upgradeable is Initializable, ContextUpgradeable, ERC165Upgradeable, IERC721, IERC721Metadata, IERC721Errors { using Strings for uint256; /// @custom:storage-location erc7201:openzeppelin.storage.ERC721 struct ERC721Storage { // Token name string _name; // Token symbol string _symbol; mapping(uint256 tokenId => address) _owners; mapping(address owner => uint256) _balances; mapping(uint256 tokenId => address) _tokenApprovals; mapping(address owner => mapping(address operator => bool)) _operatorApprovals; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ERC721")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant ERC721StorageLocation = 0x80bb2b638cc20bc4d0a60d66940f3ab4a00c1d7b313497ca82fb0b4ab0079300; function _getERC721Storage() private pure returns (ERC721Storage storage $) { assembly { $.slot := ERC721StorageLocation } } /** * @dev Initializes the contract by setting a `name` and a `symbol` to the token collection. */ function __ERC721_init(string memory name_, string memory symbol_) internal onlyInitializing { __ERC721_init_unchained(name_, symbol_); } function __ERC721_init_unchained(string memory name_, string memory symbol_) internal onlyInitializing { ERC721Storage storage $ = _getERC721Storage(); $._name = name_; $._symbol = symbol_; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override(ERC165Upgradeable, IERC165) returns (bool) { return interfaceId == type(IERC721).interfaceId || interfaceId == type(IERC721Metadata).interfaceId || super.supportsInterface(interfaceId); } /** * @dev See {IERC721-balanceOf}. */ function balanceOf(address owner) public view virtual returns (uint256) { ERC721Storage storage $ = _getERC721Storage(); if (owner == address(0)) { revert ERC721InvalidOwner(address(0)); } return $._balances[owner]; } /** * @dev See {IERC721-ownerOf}. */ function ownerOf(uint256 tokenId) public view virtual returns (address) { return _requireOwned(tokenId); } /** * @dev See {IERC721Metadata-name}. */ function name() public view virtual returns (string memory) { ERC721Storage storage $ = _getERC721Storage(); return $._name; } /** * @dev See {IERC721Metadata-symbol}. */ function symbol() public view virtual returns (string memory) { ERC721Storage storage $ = _getERC721Storage(); return $._symbol; } /** * @dev See {IERC721Metadata-tokenURI}. */ function tokenURI(uint256 tokenId) public view virtual returns (string memory) { _requireOwned(tokenId); string memory baseURI = _baseURI(); return bytes(baseURI).length > 0 ? string.concat(baseURI, tokenId.toString()) : ""; } /** * @dev Base URI for computing {tokenURI}. If set, the resulting URI for each * token will be the concatenation of the `baseURI` and the `tokenId`. Empty * by default, can be overridden in child contracts. */ function _baseURI() internal view virtual returns (string memory) { return ""; } /** * @dev See {IERC721-approve}. */ function approve(address to, uint256 tokenId) public virtual { _approve(to, tokenId, _msgSender()); } /** * @dev See {IERC721-getApproved}. */ function getApproved(uint256 tokenId) public view virtual returns (address) { _requireOwned(tokenId); return _getApproved(tokenId); } /** * @dev See {IERC721-setApprovalForAll}. */ function setApprovalForAll(address operator, bool approved) public virtual { _setApprovalForAll(_msgSender(), operator, approved); } /** * @dev See {IERC721-isApprovedForAll}. */ function isApprovedForAll(address owner, address operator) public view virtual returns (bool) { ERC721Storage storage $ = _getERC721Storage(); return $._operatorApprovals[owner][operator]; } /** * @dev See {IERC721-transferFrom}. */ function transferFrom(address from, address to, uint256 tokenId) public virtual { if (to == address(0)) { revert ERC721InvalidReceiver(address(0)); } // Setting an "auth" arguments enables the `_isAuthorized` check which verifies that the token exists // (from != 0). Therefore, it is not needed to verify that the return value is not 0 here. address previousOwner = _update(to, tokenId, _msgSender()); if (previousOwner != from) { revert ERC721IncorrectOwner(from, tokenId, previousOwner); } } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom(address from, address to, uint256 tokenId) public { safeTransferFrom(from, to, tokenId, ""); } /** * @dev See {IERC721-safeTransferFrom}. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory data) public virtual { transferFrom(from, to, tokenId); _checkOnERC721Received(from, to, tokenId, data); } /** * @dev Returns the owner of the `tokenId`. Does NOT revert if token doesn't exist * * IMPORTANT: Any overrides to this function that add ownership of tokens not tracked by the * core ERC721 logic MUST be matched with the use of {_increaseBalance} to keep balances * consistent with ownership. The invariant to preserve is that for any address `a` the value returned by * `balanceOf(a)` must be equal to the number of tokens such that `_ownerOf(tokenId)` is `a`. */ function _ownerOf(uint256 tokenId) internal view virtual returns (address) { ERC721Storage storage $ = _getERC721Storage(); return $._owners[tokenId]; } /** * @dev Returns the approved address for `tokenId`. Returns 0 if `tokenId` is not minted. */ function _getApproved(uint256 tokenId) internal view virtual returns (address) { ERC721Storage storage $ = _getERC721Storage(); return $._tokenApprovals[tokenId]; } /** * @dev Returns whether `spender` is allowed to manage `owner`'s tokens, or `tokenId` in * particular (ignoring whether it is owned by `owner`). * * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this * assumption. */ function _isAuthorized(address owner, address spender, uint256 tokenId) internal view virtual returns (bool) { return spender != address(0) && (owner == spender || isApprovedForAll(owner, spender) || _getApproved(tokenId) == spender); } /** * @dev Checks if `spender` can operate on `tokenId`, assuming the provided `owner` is the actual owner. * Reverts if `spender` does not have approval from the provided `owner` for the given token or for all its assets * the `spender` for the specific `tokenId`. * * WARNING: This function assumes that `owner` is the actual owner of `tokenId` and does not verify this * assumption. */ function _checkAuthorized(address owner, address spender, uint256 tokenId) internal view virtual { if (!_isAuthorized(owner, spender, tokenId)) { if (owner == address(0)) { revert ERC721NonexistentToken(tokenId); } else { revert ERC721InsufficientApproval(spender, tokenId); } } } /** * @dev Unsafe write access to the balances, used by extensions that "mint" tokens using an {ownerOf} override. * * NOTE: the value is limited to type(uint128).max. This protect against _balance overflow. It is unrealistic that * a uint256 would ever overflow from increments when these increments are bounded to uint128 values. * * WARNING: Increasing an account's balance using this function tends to be paired with an override of the * {_ownerOf} function to resolve the ownership of the corresponding tokens so that balances and ownership * remain consistent with one another. */ function _increaseBalance(address account, uint128 value) internal virtual { ERC721Storage storage $ = _getERC721Storage(); unchecked { $._balances[account] += value; } } /** * @dev Transfers `tokenId` from its current owner to `to`, or alternatively mints (or burns) if the current owner * (or `to`) is the zero address. Returns the owner of the `tokenId` before the update. * * The `auth` argument is optional. If the value passed is non 0, then this function will check that * `auth` is either the owner of the token, or approved to operate on the token (by the owner). * * Emits a {Transfer} event. * * NOTE: If overriding this function in a way that tracks balances, see also {_increaseBalance}. */ function _update(address to, uint256 tokenId, address auth) internal virtual returns (address) { ERC721Storage storage $ = _getERC721Storage(); address from = _ownerOf(tokenId); // Perform (optional) operator check if (auth != address(0)) { _checkAuthorized(from, auth, tokenId); } // Execute the update if (from != address(0)) { // Clear approval. No need to re-authorize or emit the Approval event _approve(address(0), tokenId, address(0), false); unchecked { $._balances[from] -= 1; } } if (to != address(0)) { unchecked { $._balances[to] += 1; } } $._owners[tokenId] = to; emit Transfer(from, to, tokenId); return from; } /** * @dev Mints `tokenId` and transfers it to `to`. * * WARNING: Usage of this method is discouraged, use {_safeMint} whenever possible * * Requirements: * * - `tokenId` must not exist. * - `to` cannot be the zero address. * * Emits a {Transfer} event. */ function _mint(address to, uint256 tokenId) internal { if (to == address(0)) { revert ERC721InvalidReceiver(address(0)); } address previousOwner = _update(to, tokenId, address(0)); if (previousOwner != address(0)) { revert ERC721InvalidSender(address(0)); } } /** * @dev Mints `tokenId`, transfers it to `to` and checks for `to` acceptance. * * Requirements: * * - `tokenId` must not exist. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeMint(address to, uint256 tokenId) internal { _safeMint(to, tokenId, ""); } /** * @dev Same as {xref-ERC721-_safeMint-address-uint256-}[`_safeMint`], with an additional `data` parameter which is * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. */ function _safeMint(address to, uint256 tokenId, bytes memory data) internal virtual { _mint(to, tokenId); _checkOnERC721Received(address(0), to, tokenId, data); } /** * @dev Destroys `tokenId`. * The approval is cleared when the token is burned. * This is an internal function that does not check if the sender is authorized to operate on the token. * * Requirements: * * - `tokenId` must exist. * * Emits a {Transfer} event. */ function _burn(uint256 tokenId) internal { address previousOwner = _update(address(0), tokenId, address(0)); if (previousOwner == address(0)) { revert ERC721NonexistentToken(tokenId); } } /** * @dev Transfers `tokenId` from `from` to `to`. * As opposed to {transferFrom}, this imposes no restrictions on msg.sender. * * Requirements: * * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * * Emits a {Transfer} event. */ function _transfer(address from, address to, uint256 tokenId) internal { if (to == address(0)) { revert ERC721InvalidReceiver(address(0)); } address previousOwner = _update(to, tokenId, address(0)); if (previousOwner == address(0)) { revert ERC721NonexistentToken(tokenId); } else if (previousOwner != from) { revert ERC721IncorrectOwner(from, tokenId, previousOwner); } } /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking that contract recipients * are aware of the ERC721 standard to prevent tokens from being forever locked. * * `data` is additional data, it has no specified format and it is sent in call to `to`. * * This internal function is like {safeTransferFrom} in the sense that it invokes * {IERC721Receiver-onERC721Received} on the receiver, and can be used to e.g. * implement alternative mechanisms to perform token transfer, such as signature-based. * * Requirements: * * - `tokenId` token must exist and be owned by `from`. * - `to` cannot be the zero address. * - `from` cannot be the zero address. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon a safe transfer. * * Emits a {Transfer} event. */ function _safeTransfer(address from, address to, uint256 tokenId) internal { _safeTransfer(from, to, tokenId, ""); } /** * @dev Same as {xref-ERC721-_safeTransfer-address-address-uint256-}[`_safeTransfer`], with an additional `data` parameter which is * forwarded in {IERC721Receiver-onERC721Received} to contract recipients. */ function _safeTransfer(address from, address to, uint256 tokenId, bytes memory data) internal virtual { _transfer(from, to, tokenId); _checkOnERC721Received(from, to, tokenId, data); } /** * @dev Approve `to` to operate on `tokenId` * * The `auth` argument is optional. If the value passed is non 0, then this function will check that `auth` is * either the owner of the token, or approved to operate on all tokens held by this owner. * * Emits an {Approval} event. * * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. */ function _approve(address to, uint256 tokenId, address auth) internal { _approve(to, tokenId, auth, true); } /** * @dev Variant of `_approve` with an optional flag to enable or disable the {Approval} event. The event is not * emitted in the context of transfers. */ function _approve(address to, uint256 tokenId, address auth, bool emitEvent) internal virtual { ERC721Storage storage $ = _getERC721Storage(); // Avoid reading the owner unless necessary if (emitEvent || auth != address(0)) { address owner = _requireOwned(tokenId); // We do not use _isAuthorized because single-token approvals should not be able to call approve if (auth != address(0) && owner != auth && !isApprovedForAll(owner, auth)) { revert ERC721InvalidApprover(auth); } if (emitEvent) { emit Approval(owner, to, tokenId); } } $._tokenApprovals[tokenId] = to; } /** * @dev Approve `operator` to operate on all of `owner` tokens * * Requirements: * - operator can't be the address zero. * * Emits an {ApprovalForAll} event. */ function _setApprovalForAll(address owner, address operator, bool approved) internal virtual { ERC721Storage storage $ = _getERC721Storage(); if (operator == address(0)) { revert ERC721InvalidOperator(operator); } $._operatorApprovals[owner][operator] = approved; emit ApprovalForAll(owner, operator, approved); } /** * @dev Reverts if the `tokenId` doesn't have a current owner (it hasn't been minted, or it has been burned). * Returns the owner. * * Overrides to ownership logic should be done to {_ownerOf}. */ function _requireOwned(uint256 tokenId) internal view returns (address) { address owner = _ownerOf(tokenId); if (owner == address(0)) { revert ERC721NonexistentToken(tokenId); } return owner; } /** * @dev Private function to invoke {IERC721Receiver-onERC721Received} on a target address. This will revert if the * recipient doesn't accept the token transfer. The call is not executed if the target address is not a contract. * * @param from address representing the previous owner of the given token ID * @param to target address that will receive the tokens * @param tokenId uint256 ID of the token to be transferred * @param data bytes optional data to send along with the call */ function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private { if (to.code.length > 0) { try IERC721Receiver(to).onERC721Received(_msgSender(), from, tokenId, data) returns (bytes4 retval) { if (retval != IERC721Receiver.onERC721Received.selector) { revert ERC721InvalidReceiver(to); } } catch (bytes memory reason) { if (reason.length == 0) { revert ERC721InvalidReceiver(to); } else { /// @solidity memory-safe-assembly assembly { revert(add(32, reason), mload(reason)) } } } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract ContextUpgradeable is Initializable { function __Context_init() internal onlyInitializing { } function __Context_init_unchained() internal onlyInitializing { } function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol) pragma solidity ^0.8.20; import {MessageHashUtils} from "@openzeppelin/contracts/utils/cryptography/MessageHashUtils.sol"; import {IERC5267} from "@openzeppelin/contracts/interfaces/IERC5267.sol"; import {Initializable} from "../../proxy/utils/Initializable.sol"; /** * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data. * * The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose * encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract * does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to * produce the hash of their typed data using a combination of `abi.encode` and `keccak256`. * * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA * ({_hashTypedDataV4}). * * The implementation of the domain separator was designed to be as efficient as possible while still properly updating * the chain id to protect against replay attacks on an eventual fork of the chain. * * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask]. * * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain * separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the * separator from the immutable values, which is cheaper than accessing a cached version in cold storage. */ abstract contract EIP712Upgradeable is Initializable, IERC5267 { bytes32 private constant TYPE_HASH = keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"); /// @custom:storage-location erc7201:openzeppelin.storage.EIP712 struct EIP712Storage { /// @custom:oz-renamed-from _HASHED_NAME bytes32 _hashedName; /// @custom:oz-renamed-from _HASHED_VERSION bytes32 _hashedVersion; string _name; string _version; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.EIP712")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant EIP712StorageLocation = 0xa16a46d94261c7517cc8ff89f61c0ce93598e3c849801011dee649a6a557d100; function _getEIP712Storage() private pure returns (EIP712Storage storage $) { assembly { $.slot := EIP712StorageLocation } } /** * @dev Initializes the domain separator and parameter caches. * * The meaning of `name` and `version` is specified in * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]: * * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol. * - `version`: the current major version of the signing domain. * * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart * contract upgrade]. */ function __EIP712_init(string memory name, string memory version) internal onlyInitializing { __EIP712_init_unchained(name, version); } function __EIP712_init_unchained(string memory name, string memory version) internal onlyInitializing { EIP712Storage storage $ = _getEIP712Storage(); $._name = name; $._version = version; // Reset prior values in storage if upgrading $._hashedName = 0; $._hashedVersion = 0; } /** * @dev Returns the domain separator for the current chain. */ function _domainSeparatorV4() internal view returns (bytes32) { return _buildDomainSeparator(); } function _buildDomainSeparator() private view returns (bytes32) { return keccak256(abi.encode(TYPE_HASH, _EIP712NameHash(), _EIP712VersionHash(), block.chainid, address(this))); } /** * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this * function returns the hash of the fully encoded EIP712 message for this domain. * * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example: * * ```solidity * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode( * keccak256("Mail(address to,string contents)"), * mailTo, * keccak256(bytes(mailContents)) * ))); * address signer = ECDSA.recover(digest, signature); * ``` */ function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) { return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash); } /** * @dev See {IERC-5267}. */ function eip712Domain() public view virtual returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ) { EIP712Storage storage $ = _getEIP712Storage(); // If the hashed name and version in storage are non-zero, the contract hasn't been properly initialized // and the EIP712 domain is not reliable, as it will be missing name and version. require($._hashedName == 0 && $._hashedVersion == 0, "EIP712: Uninitialized"); return ( hex"0f", // 01111 _EIP712Name(), _EIP712Version(), block.chainid, address(this), bytes32(0), new uint256[](0) ); } /** * @dev The name parameter for the EIP712 domain. * * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs * are a concern. */ function _EIP712Name() internal view virtual returns (string memory) { EIP712Storage storage $ = _getEIP712Storage(); return $._name; } /** * @dev The version parameter for the EIP712 domain. * * NOTE: This function reads from storage by default, but can be redefined to return a constant value if gas costs * are a concern. */ function _EIP712Version() internal view virtual returns (string memory) { EIP712Storage storage $ = _getEIP712Storage(); return $._version; } /** * @dev The hash of the name parameter for the EIP712 domain. * * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Name` instead. */ function _EIP712NameHash() internal view returns (bytes32) { EIP712Storage storage $ = _getEIP712Storage(); string memory name = _EIP712Name(); if (bytes(name).length > 0) { return keccak256(bytes(name)); } else { // If the name is empty, the contract may have been upgraded without initializing the new storage. // We return the name hash in storage if non-zero, otherwise we assume the name is empty by design. bytes32 hashedName = $._hashedName; if (hashedName != 0) { return hashedName; } else { return keccak256(""); } } } /** * @dev The hash of the version parameter for the EIP712 domain. * * NOTE: In previous versions this function was virtual. In this version you should override `_EIP712Version` instead. */ function _EIP712VersionHash() internal view returns (bytes32) { EIP712Storage storage $ = _getEIP712Storage(); string memory version = _EIP712Version(); if (bytes(version).length > 0) { return keccak256(bytes(version)); } else { // If the version is empty, the contract may have been upgraded without initializing the new storage. // We return the version hash in storage if non-zero, otherwise we assume the version is empty by design. bytes32 hashedVersion = $._hashedVersion; if (hashedVersion != 0) { return hashedVersion; } else { return keccak256(""); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol"; import {Initializable} from "../../proxy/utils/Initializable.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` */ abstract contract ERC165Upgradeable is Initializable, IERC165 { function __ERC165_init() internal onlyInitializing { } function __ERC165_init_unchained() internal onlyInitializing { } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Nonces.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @dev Provides tracking nonces for addresses. Nonces will only increment. */ abstract contract NoncesUpgradeable is Initializable { /** * @dev The nonce used for an `account` is not the expected current nonce. */ error InvalidAccountNonce(address account, uint256 currentNonce); /// @custom:storage-location erc7201:openzeppelin.storage.Nonces struct NoncesStorage { mapping(address account => uint256) _nonces; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Nonces")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant NoncesStorageLocation = 0x5ab42ced628888259c08ac98db1eb0cf702fc1501344311d8b100cd1bfe4bb00; function _getNoncesStorage() private pure returns (NoncesStorage storage $) { assembly { $.slot := NoncesStorageLocation } } function __Nonces_init() internal onlyInitializing { } function __Nonces_init_unchained() internal onlyInitializing { } /** * @dev Returns the next unused nonce for an address. */ function nonces(address owner) public view virtual returns (uint256) { NoncesStorage storage $ = _getNoncesStorage(); return $._nonces[owner]; } /** * @dev Consumes a nonce. * * Returns the current value and increments nonce. */ function _useNonce(address owner) internal virtual returns (uint256) { NoncesStorage storage $ = _getNoncesStorage(); // For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be // decremented or reset. This guarantees that the nonce never overflows. unchecked { // It is important to do x++ and not ++x here. return $._nonces[owner]++; } } /** * @dev Same as {_useNonce} but checking that `nonce` is the next valid for `owner`. */ function _useCheckedNonce(address owner, uint256 nonce) internal virtual { uint256 current = _useNonce(owner); if (nonce != current) { revert InvalidAccountNonce(owner, current); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol) pragma solidity ^0.8.20; import {Initializable} from "../proxy/utils/Initializable.sol"; /** * @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 ReentrancyGuardUpgradeable is Initializable { // 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; /// @custom:storage-location erc7201:openzeppelin.storage.ReentrancyGuard struct ReentrancyGuardStorage { uint256 _status; } // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.ReentrancyGuard")) - 1)) & ~bytes32(uint256(0xff)) bytes32 private constant ReentrancyGuardStorageLocation = 0x9b779b17422d0df92223018b32b4d1fa46e071723d6817e2486d003becc55f00; function _getReentrancyGuardStorage() private pure returns (ReentrancyGuardStorage storage $) { assembly { $.slot := ReentrancyGuardStorageLocation } } /** * @dev Unauthorized reentrant call. */ error ReentrancyGuardReentrantCall(); function __ReentrancyGuard_init() internal onlyInitializing { __ReentrancyGuard_init_unchained(); } function __ReentrancyGuard_init_unchained() internal onlyInitializing { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); $._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 { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); // On the first call to nonReentrant, _status will be NOT_ENTERED if ($._status == ENTERED) { revert ReentrancyGuardReentrantCall(); } // Any calls to nonReentrant after this point will fail $._status = ENTERED; } function _nonReentrantAfter() private { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); // 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) { ReentrancyGuardStorage storage $ = _getReentrancyGuardStorage(); return $._status == ENTERED; } }
// 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.5.0) (interfaces/draft-IERC1822.sol) pragma solidity ^0.8.0; /** * @dev ERC1822: Universal Upgradeable Proxy Standard (UUPS) documents a method for upgradeability through a simplified * proxy whose upgrades are fully controlled by the current implementation. */ interface IERC1822Proxiable { /** * @dev Returns the storage slot that the proxiable contract assumes is being used to store the implementation * address. * * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this * function revert if invoked through a proxy. */ function proxiableUUID() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (interfaces/IERC1967.sol) pragma solidity ^0.8.0; /** * @dev ERC-1967: Proxy Storage Slots. This interface contains the events defined in the ERC. * * _Available since v4.8.3._ */ interface IERC1967 { /** * @dev Emitted when the implementation is upgraded. */ event Upgraded(address indexed implementation); /** * @dev Emitted when the admin account has changed. */ event AdminChanged(address previousAdmin, address newAdmin); /** * @dev Emitted when the beacon is changed. */ event BeaconUpgraded(address indexed beacon); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts v4.4.1 (proxy/beacon/IBeacon.sol) pragma solidity ^0.8.0; /** * @dev This is the interface that {BeaconProxy} expects of its beacon. */ interface IBeacon { /** * @dev Must return an address that can be used as a delegate call target. * * {BeaconProxy} will check that this address is a contract. */ function implementation() external view returns (address); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.7.0) (proxy/ERC1967/ERC1967Proxy.sol) pragma solidity ^0.8.0; import "../Proxy.sol"; import "./ERC1967Upgrade.sol"; /** * @dev This contract implements an upgradeable proxy. It is upgradeable because calls are delegated to an * implementation address that can be changed. This address is stored in storage in the location specified by * https://eips.ethereum.org/EIPS/eip-1967[EIP1967], so that it doesn't conflict with the storage layout of the * implementation behind the proxy. */ contract ERC1967Proxy is Proxy, ERC1967Upgrade { /** * @dev Initializes the upgradeable proxy with an initial implementation specified by `_logic`. * * If `_data` is nonempty, it's used as data in a delegate call to `_logic`. This will typically be an encoded * function call, and allows initializing the storage of the proxy like a Solidity constructor. */ constructor(address _logic, bytes memory _data) payable { _upgradeToAndCall(_logic, _data, false); } /** * @dev Returns the current implementation address. */ function _implementation() internal view virtual override returns (address impl) { return ERC1967Upgrade._getImplementation(); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (proxy/ERC1967/ERC1967Upgrade.sol) pragma solidity ^0.8.2; import "../beacon/IBeacon.sol"; import "../../interfaces/IERC1967.sol"; import "../../interfaces/draft-IERC1822.sol"; import "../../utils/Address.sol"; import "../../utils/StorageSlot.sol"; /** * @dev This abstract contract provides getters and event emitting update functions for * https://eips.ethereum.org/EIPS/eip-1967[EIP1967] slots. * * _Available since v4.1._ */ abstract contract ERC1967Upgrade is IERC1967 { // This is the keccak-256 hash of "eip1967.proxy.rollback" subtracted by 1 bytes32 private constant _ROLLBACK_SLOT = 0x4910fdfa16fed3260ed0e7147f7cc6da11a60208b5b9406d12a635614ffd9143; /** * @dev Storage slot with the address of the current implementation. * This is the keccak-256 hash of "eip1967.proxy.implementation" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; /** * @dev Returns the current implementation address. */ function _getImplementation() internal view returns (address) { return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; } /** * @dev Stores a new address in the EIP1967 implementation slot. */ function _setImplementation(address newImplementation) private { require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; } /** * @dev Perform implementation upgrade * * Emits an {Upgraded} event. */ function _upgradeTo(address newImplementation) internal { _setImplementation(newImplementation); emit Upgraded(newImplementation); } /** * @dev Perform implementation upgrade with additional setup call. * * Emits an {Upgraded} event. */ function _upgradeToAndCall(address newImplementation, bytes memory data, bool forceCall) internal { _upgradeTo(newImplementation); if (data.length > 0 || forceCall) { Address.functionDelegateCall(newImplementation, data); } } /** * @dev Perform implementation upgrade with security checks for UUPS proxies, and additional setup call. * * Emits an {Upgraded} event. */ function _upgradeToAndCallUUPS(address newImplementation, bytes memory data, bool forceCall) internal { // Upgrades from old implementations will perform a rollback test. This test requires the new // implementation to upgrade back to the old, non-ERC1822 compliant, implementation. Removing // this special case will break upgrade paths from old UUPS implementation to new ones. if (StorageSlot.getBooleanSlot(_ROLLBACK_SLOT).value) { _setImplementation(newImplementation); } else { try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) { require(slot == _IMPLEMENTATION_SLOT, "ERC1967Upgrade: unsupported proxiableUUID"); } catch { revert("ERC1967Upgrade: new implementation is not UUPS"); } _upgradeToAndCall(newImplementation, data, forceCall); } } /** * @dev Storage slot with the admin of the contract. * This is the keccak-256 hash of "eip1967.proxy.admin" subtracted by 1, and is * validated in the constructor. */ bytes32 internal constant _ADMIN_SLOT = 0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103; /** * @dev Returns the current admin. */ function _getAdmin() internal view returns (address) { return StorageSlot.getAddressSlot(_ADMIN_SLOT).value; } /** * @dev Stores a new address in the EIP1967 admin slot. */ function _setAdmin(address newAdmin) private { require(newAdmin != address(0), "ERC1967: new admin is the zero address"); StorageSlot.getAddressSlot(_ADMIN_SLOT).value = newAdmin; } /** * @dev Changes the admin of the proxy. * * Emits an {AdminChanged} event. */ function _changeAdmin(address newAdmin) internal { emit AdminChanged(_getAdmin(), newAdmin); _setAdmin(newAdmin); } /** * @dev The storage slot of the UpgradeableBeacon contract which defines the implementation for this proxy. * This is bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) and is validated in the constructor. */ bytes32 internal constant _BEACON_SLOT = 0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50; /** * @dev Returns the current beacon. */ function _getBeacon() internal view returns (address) { return StorageSlot.getAddressSlot(_BEACON_SLOT).value; } /** * @dev Stores a new beacon in the EIP1967 beacon slot. */ function _setBeacon(address newBeacon) private { require(Address.isContract(newBeacon), "ERC1967: new beacon is not a contract"); require( Address.isContract(IBeacon(newBeacon).implementation()), "ERC1967: beacon implementation is not a contract" ); StorageSlot.getAddressSlot(_BEACON_SLOT).value = newBeacon; } /** * @dev Perform beacon upgrade with additional setup call. Note: This upgrades the address of the beacon, it does * not upgrade the implementation contained in the beacon (see {UpgradeableBeacon-_setImplementation} for that). * * Emits a {BeaconUpgraded} event. */ function _upgradeBeaconToAndCall(address newBeacon, bytes memory data, bool forceCall) internal { _setBeacon(newBeacon); emit BeaconUpgraded(newBeacon); if (data.length > 0 || forceCall) { Address.functionDelegateCall(IBeacon(newBeacon).implementation(), data); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.6.0) (proxy/Proxy.sol) pragma solidity ^0.8.0; /** * @dev This abstract contract provides a fallback function that delegates all calls to another contract using the EVM * instruction `delegatecall`. We refer to the second contract as the _implementation_ behind the proxy, and it has to * be specified by overriding the virtual {_implementation} function. * * Additionally, delegation to the implementation can be triggered manually through the {_fallback} function, or to a * different contract through the {_delegate} function. * * The success and return data of the delegated call will be returned back to the caller of the proxy. */ abstract contract Proxy { /** * @dev Delegates the current call to `implementation`. * * This function does not return to its internal call site, it will return directly to the external caller. */ function _delegate(address implementation) internal virtual { assembly { // Copy msg.data. We take full control of memory in this inline assembly // block because it will not return to Solidity code. We overwrite the // Solidity scratch pad at memory position 0. calldatacopy(0, 0, calldatasize()) // Call the implementation. // out and outsize are 0 because we don't know the size yet. let result := delegatecall(gas(), implementation, 0, calldatasize(), 0, 0) // Copy the returned data. returndatacopy(0, 0, returndatasize()) switch result // delegatecall returns 0 on error. case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } /** * @dev This is a virtual function that should be overridden so it returns the address to which the fallback function * and {_fallback} should delegate. */ function _implementation() internal view virtual returns (address); /** * @dev Delegates the current call to the address returned by `_implementation()`. * * This function does not return to its internal call site, it will return directly to the external caller. */ function _fallback() internal virtual { _beforeFallback(); _delegate(_implementation()); } /** * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if no other * function in the contract matches the call data. */ fallback() external payable virtual { _fallback(); } /** * @dev Fallback function that delegates calls to the address returned by `_implementation()`. Will run if call data * is empty. */ receive() external payable virtual { _fallback(); } /** * @dev Hook that is called before falling back to the implementation. Can happen as part of a manual `_fallback` * call, or as part of the Solidity `fallback` or `receive` functions. * * If overridden should call `super._beforeFallback()`. */ function _beforeFallback() internal virtual {} }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.8.3) (proxy/transparent/ProxyAdmin.sol) pragma solidity ^0.8.0; import "./TransparentUpgradeableProxy.sol"; import "../../access/Ownable.sol"; /** * @dev This is an auxiliary contract meant to be assigned as the admin of a {TransparentUpgradeableProxy}. For an * explanation of why you would want to use this see the documentation for {TransparentUpgradeableProxy}. */ contract ProxyAdmin is Ownable { /** * @dev Returns the current implementation of `proxy`. * * Requirements: * * - This contract must be the admin of `proxy`. */ function getProxyImplementation(ITransparentUpgradeableProxy proxy) public view virtual returns (address) { // We need to manually run the static call since the getter cannot be flagged as view // bytes4(keccak256("implementation()")) == 0x5c60da1b (bool success, bytes memory returndata) = address(proxy).staticcall(hex"5c60da1b"); require(success); return abi.decode(returndata, (address)); } /** * @dev Returns the current admin of `proxy`. * * Requirements: * * - This contract must be the admin of `proxy`. */ function getProxyAdmin(ITransparentUpgradeableProxy proxy) public view virtual returns (address) { // We need to manually run the static call since the getter cannot be flagged as view // bytes4(keccak256("admin()")) == 0xf851a440 (bool success, bytes memory returndata) = address(proxy).staticcall(hex"f851a440"); require(success); return abi.decode(returndata, (address)); } /** * @dev Changes the admin of `proxy` to `newAdmin`. * * Requirements: * * - This contract must be the current admin of `proxy`. */ function changeProxyAdmin(ITransparentUpgradeableProxy proxy, address newAdmin) public virtual onlyOwner { proxy.changeAdmin(newAdmin); } /** * @dev Upgrades `proxy` to `implementation`. See {TransparentUpgradeableProxy-upgradeTo}. * * Requirements: * * - This contract must be the admin of `proxy`. */ function upgrade(ITransparentUpgradeableProxy proxy, address implementation) public virtual onlyOwner { proxy.upgradeTo(implementation); } /** * @dev Upgrades `proxy` to `implementation` and calls a function on the new implementation. See * {TransparentUpgradeableProxy-upgradeToAndCall}. * * Requirements: * * - This contract must be the admin of `proxy`. */ function upgradeAndCall( ITransparentUpgradeableProxy proxy, address implementation, bytes memory data ) public payable virtual onlyOwner { proxy.upgradeToAndCall{value: msg.value}(implementation, data); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (proxy/transparent/TransparentUpgradeableProxy.sol) pragma solidity ^0.8.0; import "../ERC1967/ERC1967Proxy.sol"; /** * @dev Interface for {TransparentUpgradeableProxy}. In order to implement transparency, {TransparentUpgradeableProxy} * does not implement this interface directly, and some of its functions are implemented by an internal dispatch * mechanism. The compiler is unaware that these functions are implemented by {TransparentUpgradeableProxy} and will not * include them in the ABI so this interface must be used to interact with it. */ interface ITransparentUpgradeableProxy is IERC1967 { function admin() external view returns (address); function implementation() external view returns (address); function changeAdmin(address) external; function upgradeTo(address) external; function upgradeToAndCall(address, bytes memory) external payable; } /** * @dev This contract implements a proxy that is upgradeable by an admin. * * To avoid https://medium.com/nomic-labs-blog/malicious-backdoors-in-ethereum-proxies-62629adf3357[proxy selector * clashing], which can potentially be used in an attack, this contract uses the * https://blog.openzeppelin.com/the-transparent-proxy-pattern/[transparent proxy pattern]. This pattern implies two * things that go hand in hand: * * 1. If any account other than the admin calls the proxy, the call will be forwarded to the implementation, even if * that call matches one of the admin functions exposed by the proxy itself. * 2. If the admin calls the proxy, it can access the admin functions, but its calls will never be forwarded to the * implementation. If the admin tries to call a function on the implementation it will fail with an error that says * "admin cannot fallback to proxy target". * * These properties mean that the admin account can only be used for admin actions like upgrading the proxy or changing * the admin, so it's best if it's a dedicated account that is not used for anything else. This will avoid headaches due * to sudden errors when trying to call a function from the proxy implementation. * * Our recommendation is for the dedicated account to be an instance of the {ProxyAdmin} contract. If set up this way, * you should think of the `ProxyAdmin` instance as the real administrative interface of your proxy. * * NOTE: The real interface of this proxy is that defined in `ITransparentUpgradeableProxy`. This contract does not * inherit from that interface, and instead the admin functions are implicitly implemented using a custom dispatch * mechanism in `_fallback`. Consequently, the compiler will not produce an ABI for this contract. This is necessary to * fully implement transparency without decoding reverts caused by selector clashes between the proxy and the * implementation. * * WARNING: It is not recommended to extend this contract to add additional external functions. If you do so, the compiler * will not check that there are no selector conflicts, due to the note above. A selector clash between any new function * and the functions declared in {ITransparentUpgradeableProxy} will be resolved in favor of the new one. This could * render the admin operations inaccessible, which could prevent upgradeability. Transparency may also be compromised. */ contract TransparentUpgradeableProxy is ERC1967Proxy { /** * @dev Initializes an upgradeable proxy managed by `_admin`, backed by the implementation at `_logic`, and * optionally initialized with `_data` as explained in {ERC1967Proxy-constructor}. */ constructor(address _logic, address admin_, bytes memory _data) payable ERC1967Proxy(_logic, _data) { _changeAdmin(admin_); } /** * @dev Modifier used internally that will delegate the call to the implementation unless the sender is the admin. * * CAUTION: This modifier is deprecated, as it could cause issues if the modified function has arguments, and the * implementation provides a function with the same selector. */ modifier ifAdmin() { if (msg.sender == _getAdmin()) { _; } else { _fallback(); } } /** * @dev If caller is the admin process the call internally, otherwise transparently fallback to the proxy behavior */ function _fallback() internal virtual override { if (msg.sender == _getAdmin()) { bytes memory ret; bytes4 selector = msg.sig; if (selector == ITransparentUpgradeableProxy.upgradeTo.selector) { ret = _dispatchUpgradeTo(); } else if (selector == ITransparentUpgradeableProxy.upgradeToAndCall.selector) { ret = _dispatchUpgradeToAndCall(); } else if (selector == ITransparentUpgradeableProxy.changeAdmin.selector) { ret = _dispatchChangeAdmin(); } else if (selector == ITransparentUpgradeableProxy.admin.selector) { ret = _dispatchAdmin(); } else if (selector == ITransparentUpgradeableProxy.implementation.selector) { ret = _dispatchImplementation(); } else { revert("TransparentUpgradeableProxy: admin cannot fallback to proxy target"); } assembly { return(add(ret, 0x20), mload(ret)) } } else { super._fallback(); } } /** * @dev Returns the current admin. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103` */ function _dispatchAdmin() private returns (bytes memory) { _requireZeroValue(); address admin = _getAdmin(); return abi.encode(admin); } /** * @dev Returns the current implementation. * * TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) using the * https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. * `0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc` */ function _dispatchImplementation() private returns (bytes memory) { _requireZeroValue(); address implementation = _implementation(); return abi.encode(implementation); } /** * @dev Changes the admin of the proxy. * * Emits an {AdminChanged} event. */ function _dispatchChangeAdmin() private returns (bytes memory) { _requireZeroValue(); address newAdmin = abi.decode(msg.data[4:], (address)); _changeAdmin(newAdmin); return ""; } /** * @dev Upgrade the implementation of the proxy. */ function _dispatchUpgradeTo() private returns (bytes memory) { _requireZeroValue(); address newImplementation = abi.decode(msg.data[4:], (address)); _upgradeToAndCall(newImplementation, bytes(""), false); return ""; } /** * @dev Upgrade the implementation of the proxy, and then call a function from the new implementation as specified * by `data`, which should be an encoded function call. This is useful to initialize new storage variables in the * proxied contract. */ function _dispatchUpgradeToAndCall() private returns (bytes memory) { (address newImplementation, bytes memory data) = abi.decode(msg.data[4:], (address, bytes)); _upgradeToAndCall(newImplementation, data, true); return ""; } /** * @dev Returns the current admin. * * CAUTION: This function is deprecated. Use {ERC1967Upgrade-_getAdmin} instead. */ function _admin() internal view virtual returns (address) { return _getAdmin(); } /** * @dev To keep this contract fully transparent, all `ifAdmin` functions must be payable. This helper is here to * emulate some proxy functions being non-payable while still allowing value to pass through. */ function _requireZeroValue() private { require(msg.value == 0); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/Address.sol) pragma solidity ^0.8.1; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev Returns true if `account` is a contract. * * [IMPORTANT] * ==== * It is unsafe to assume that an address for which this function returns * false is an externally-owned account (EOA) and not a contract. * * Among others, `isContract` will return false for the following * types of addresses: * * - an externally-owned account * - a contract in construction * - an address where a contract will be created * - an address where a contract lived, but was destroyed * * Furthermore, `isContract` will also return true if the target contract within * the same transaction is already scheduled for destruction by `SELFDESTRUCT`, * which only has an effect at the end of a transaction. * ==== * * [IMPORTANT] * ==== * You shouldn't rely on `isContract` to protect against flash loan attacks! * * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract * constructor. * ==== */ function isContract(address account) internal view returns (bool) { // This method relies on extcodesize/address.code.length, which returns 0 // for contracts in construction, since the code is only stored at the end // of the constructor execution. return account.code.length > 0; } /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { require(address(this).balance >= amount, "Address: insufficient balance"); (bool success, ) = recipient.call{value: amount}(""); require(success, "Address: unable to send value, recipient may have reverted"); } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason, it is bubbled up by this * function (like regular Solidity function calls). * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. * * _Available since v3.1._ */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, "Address: low-level call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with * `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { return functionCallWithValue(target, data, 0, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. * * _Available since v3.1._ */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { return functionCallWithValue(target, data, value, "Address: low-level call with value failed"); } /** * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but * with `errorMessage` as a fallback revert reason when `target` reverts. * * _Available since v3.1._ */ function functionCallWithValue( address target, bytes memory data, uint256 value, string memory errorMessage ) internal returns (bytes memory) { require(address(this).balance >= value, "Address: insufficient balance for call"); (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { return functionStaticCall(target, data, "Address: low-level static call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a static call. * * _Available since v3.3._ */ function functionStaticCall( address target, bytes memory data, string memory errorMessage ) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { return functionDelegateCall(target, data, "Address: low-level delegate call failed"); } /** * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`], * but performing a delegate call. * * _Available since v3.4._ */ function functionDelegateCall( address target, bytes memory data, string memory errorMessage ) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata, errorMessage); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract. * * _Available since v4.8._ */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata, string memory errorMessage ) internal view returns (bytes memory) { if (success) { if (returndata.length == 0) { // only check isContract if the call was successful and the return data is empty // otherwise we already know that it was a contract require(isContract(target), "Address: call to non-contract"); } return returndata; } else { _revert(returndata, errorMessage); } } /** * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the * revert reason or using the provided one. * * _Available since v4.3._ */ function verifyCallResult( bool success, bytes memory returndata, string memory errorMessage ) internal pure returns (bytes memory) { if (success) { return returndata; } else { _revert(returndata, errorMessage); } } function _revert(bytes memory returndata, string memory errorMessage) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert(errorMessage); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.4) (utils/Context.sol) pragma solidity ^0.8.0; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v4.9.0) (utils/StorageSlot.sol) // This file was procedurally generated from scripts/generate/templates/StorageSlot.js. pragma solidity ^0.8.0; /** * @dev Library for reading and writing primitive types to specific storage slots. * * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts. * This library helps with reading and writing to such slots without the need for inline assembly. * * The functions in this library return Slot structs that contain a `value` member that can be used to read or write. * * Example usage to set ERC1967 implementation slot: * ```solidity * contract ERC1967 { * bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc; * * function _getImplementation() internal view returns (address) { * return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value; * } * * function _setImplementation(address newImplementation) internal { * require(Address.isContract(newImplementation), "ERC1967: new implementation is not a contract"); * StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation; * } * } * ``` * * _Available since v4.1 for `address`, `bool`, `bytes32`, `uint256`._ * _Available since v4.9 for `string`, `bytes`._ */ library StorageSlot { struct AddressSlot { address value; } struct BooleanSlot { bool value; } struct Bytes32Slot { bytes32 value; } struct Uint256Slot { uint256 value; } struct StringSlot { string value; } struct BytesSlot { bytes value; } /** * @dev Returns an `AddressSlot` with member `value` located at `slot`. */ function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `BooleanSlot` with member `value` located at `slot`. */ function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `Bytes32Slot` with member `value` located at `slot`. */ function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `Uint256Slot` with member `value` located at `slot`. */ function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `StringSlot` with member `value` located at `slot`. */ function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `StringSlot` representation of the string storage pointer `store`. */ function getStringSlot(string storage store) internal pure returns (StringSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := store.slot } } /** * @dev Returns an `BytesSlot` with member `value` located at `slot`. */ function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := slot } } /** * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`. */ function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) { /// @solidity memory-safe-assembly assembly { r.slot := store.slot } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/AccessControl.sol) pragma solidity ^0.8.20; import {IAccessControl} from "./IAccessControl.sol"; import {Context} from "../utils/Context.sol"; import {ERC165} from "../utils/introspection/ERC165.sol"; /** * @dev Contract module that allows children to implement role-based access * control mechanisms. This is a lightweight version that doesn't allow enumerating role * members except through off-chain means by accessing the contract event logs. Some * applications may benefit from on-chain enumerability, for those cases see * {AccessControlEnumerable}. * * Roles are referred to by their `bytes32` identifier. These should be exposed * in the external API and be unique. The best way to achieve this is by * using `public constant` hash digests: * * ```solidity * bytes32 public constant MY_ROLE = keccak256("MY_ROLE"); * ``` * * Roles can be used to represent a set of permissions. To restrict access to a * function call, use {hasRole}: * * ```solidity * function foo() public { * require(hasRole(MY_ROLE, msg.sender)); * ... * } * ``` * * Roles can be granted and revoked dynamically via the {grantRole} and * {revokeRole} functions. Each role has an associated admin role, and only * accounts that have a role's admin role can call {grantRole} and {revokeRole}. * * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means * that only accounts with this role will be able to grant or revoke other * roles. More complex role relationships can be created by using * {_setRoleAdmin}. * * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to * grant and revoke this role. Extra precautions should be taken to secure * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules} * to enforce additional security measures for this role. */ abstract contract AccessControl is Context, IAccessControl, ERC165 { struct RoleData { mapping(address account => bool) hasRole; bytes32 adminRole; } mapping(bytes32 role => RoleData) private _roles; bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00; /** * @dev Modifier that checks that an account has a specific role. Reverts * with an {AccessControlUnauthorizedAccount} error including the required role. */ modifier onlyRole(bytes32 role) { _checkRole(role); _; } /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId); } /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) public view virtual returns (bool) { return _roles[role].hasRole[account]; } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()` * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier. */ function _checkRole(bytes32 role) internal view virtual { _checkRole(role, _msgSender()); } /** * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account` * is missing `role`. */ function _checkRole(bytes32 role, address account) internal view virtual { if (!hasRole(role, account)) { revert AccessControlUnauthorizedAccount(account, role); } } /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) { return _roles[role].adminRole; } /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleGranted} event. */ function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _grantRole(role, account); } /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. * * May emit a {RoleRevoked} event. */ function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) { _revokeRole(role, account); } /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been revoked `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `callerConfirmation`. * * May emit a {RoleRevoked} event. */ function renounceRole(bytes32 role, address callerConfirmation) public virtual { if (callerConfirmation != _msgSender()) { revert AccessControlBadConfirmation(); } _revokeRole(role, callerConfirmation); } /** * @dev Sets `adminRole` as ``role``'s admin role. * * Emits a {RoleAdminChanged} event. */ function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual { bytes32 previousAdminRole = getRoleAdmin(role); _roles[role].adminRole = adminRole; emit RoleAdminChanged(role, previousAdminRole, adminRole); } /** * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted. * * Internal function without access restriction. * * May emit a {RoleGranted} event. */ function _grantRole(bytes32 role, address account) internal virtual returns (bool) { if (!hasRole(role, account)) { _roles[role].hasRole[account] = true; emit RoleGranted(role, account, _msgSender()); return true; } else { return false; } } /** * @dev Attempts to revoke `role` to `account` and returns a boolean indicating if `role` was revoked. * * Internal function without access restriction. * * May emit a {RoleRevoked} event. */ function _revokeRole(bytes32 role, address account) internal virtual returns (bool) { if (hasRole(role, account)) { _roles[role].hasRole[account] = false; emit RoleRevoked(role, account, _msgSender()); return true; } else { return false; } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/IAccessControl.sol) pragma solidity ^0.8.20; /** * @dev External interface of AccessControl declared to support ERC165 detection. */ interface IAccessControl { /** * @dev The `account` is missing a role. */ error AccessControlUnauthorizedAccount(address account, bytes32 neededRole); /** * @dev The caller of a function is not the expected one. * * NOTE: Don't confuse with {AccessControlUnauthorizedAccount}. */ error AccessControlBadConfirmation(); /** * @dev Emitted when `newAdminRole` is set as ``role``'s admin role, replacing `previousAdminRole` * * `DEFAULT_ADMIN_ROLE` is the starting admin for all roles, despite * {RoleAdminChanged} not being emitted signaling this. */ event RoleAdminChanged(bytes32 indexed role, bytes32 indexed previousAdminRole, bytes32 indexed newAdminRole); /** * @dev Emitted when `account` is granted `role`. * * `sender` is the account that originated the contract call, an admin role * bearer except when using {AccessControl-_setupRole}. */ event RoleGranted(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Emitted when `account` is revoked `role`. * * `sender` is the account that originated the contract call: * - if using `revokeRole`, it is the admin role bearer * - if using `renounceRole`, it is the role bearer (i.e. `account`) */ event RoleRevoked(bytes32 indexed role, address indexed account, address indexed sender); /** * @dev Returns `true` if `account` has been granted `role`. */ function hasRole(bytes32 role, address account) external view returns (bool); /** * @dev Returns the admin role that controls `role`. See {grantRole} and * {revokeRole}. * * To change a role's admin, use {AccessControl-_setRoleAdmin}. */ function getRoleAdmin(bytes32 role) external view returns (bytes32); /** * @dev Grants `role` to `account`. * * If `account` had not been already granted `role`, emits a {RoleGranted} * event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function grantRole(bytes32 role, address account) external; /** * @dev Revokes `role` from `account`. * * If `account` had been granted `role`, emits a {RoleRevoked} event. * * Requirements: * * - the caller must have ``role``'s admin role. */ function revokeRole(bytes32 role, address account) external; /** * @dev Revokes `role` from the calling account. * * Roles are often managed via {grantRole} and {revokeRole}: this function's * purpose is to provide a mechanism for accounts to lose their privileges * if they are compromised (such as when a trusted device is misplaced). * * If the calling account had been granted `role`, emits a {RoleRevoked} * event. * * Requirements: * * - the caller must be `callerConfirmation`. */ function renounceRole(bytes32 role, address callerConfirmation) external; }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (access/Ownable.sol) pragma solidity ^0.8.20; import {Context} from "../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. * * The initial owner is set to the address provided by the deployer. 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; /** * @dev The caller account is not authorized to perform an operation. */ error OwnableUnauthorizedAccount(address account); /** * @dev The owner is not a valid owner account. (eg. `address(0)`) */ error OwnableInvalidOwner(address owner); event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /** * @dev Initializes the contract setting the address provided by the deployer as the initial owner. */ constructor(address initialOwner) { if (initialOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _transferOwnership(initialOwner); } /** * @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 { if (owner() != _msgSender()) { revert OwnableUnauthorizedAccount(_msgSender()); } } /** * @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 { if (newOwner == address(0)) { revert OwnableInvalidOwner(address(0)); } _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 v5.0.0) (access/Ownable2Step.sol) pragma solidity ^0.8.20; import {Ownable} from "./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. * * The initial owner is specified at deployment time in the constructor for `Ownable`. 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(); if (pendingOwner() != sender) { revert OwnableUnauthorizedAccount(sender); } _transferOwnership(sender); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/draft-IERC6093.sol) pragma solidity ^0.8.20; /** * @dev Standard ERC20 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC20 tokens. */ interface IERC20Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientBalance(address sender, uint256 balance, uint256 needed); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC20InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC20InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `spender`’s `allowance`. Used in transfers. * @param spender Address that may be allowed to operate on tokens without being their owner. * @param allowance Amount of tokens a `spender` is allowed to operate with. * @param needed Minimum amount required to perform a transfer. */ error ERC20InsufficientAllowance(address spender, uint256 allowance, uint256 needed); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC20InvalidApprover(address approver); /** * @dev Indicates a failure with the `spender` to be approved. Used in approvals. * @param spender Address that may be allowed to operate on tokens without being their owner. */ error ERC20InvalidSpender(address spender); } /** * @dev Standard ERC721 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC721 tokens. */ interface IERC721Errors { /** * @dev Indicates that an address can't be an owner. For example, `address(0)` is a forbidden owner in EIP-20. * Used in balance queries. * @param owner Address of the current owner of a token. */ error ERC721InvalidOwner(address owner); /** * @dev Indicates a `tokenId` whose `owner` is the zero address. * @param tokenId Identifier number of a token. */ error ERC721NonexistentToken(uint256 tokenId); /** * @dev Indicates an error related to the ownership over a particular token. Used in transfers. * @param sender Address whose tokens are being transferred. * @param tokenId Identifier number of a token. * @param owner Address of the current owner of a token. */ error ERC721IncorrectOwner(address sender, uint256 tokenId, address owner); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC721InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC721InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param tokenId Identifier number of a token. */ error ERC721InsufficientApproval(address operator, uint256 tokenId); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC721InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC721InvalidOperator(address operator); } /** * @dev Standard ERC1155 Errors * Interface of the https://eips.ethereum.org/EIPS/eip-6093[ERC-6093] custom errors for ERC1155 tokens. */ interface IERC1155Errors { /** * @dev Indicates an error related to the current `balance` of a `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. * @param balance Current balance for the interacting account. * @param needed Minimum amount required to perform a transfer. * @param tokenId Identifier number of a token. */ error ERC1155InsufficientBalance(address sender, uint256 balance, uint256 needed, uint256 tokenId); /** * @dev Indicates a failure with the token `sender`. Used in transfers. * @param sender Address whose tokens are being transferred. */ error ERC1155InvalidSender(address sender); /** * @dev Indicates a failure with the token `receiver`. Used in transfers. * @param receiver Address to which tokens are being transferred. */ error ERC1155InvalidReceiver(address receiver); /** * @dev Indicates a failure with the `operator`’s approval. Used in transfers. * @param operator Address that may be allowed to operate on tokens without being their owner. * @param owner Address of the current owner of a token. */ error ERC1155MissingApprovalForAll(address operator, address owner); /** * @dev Indicates a failure with the `approver` of a token to be approved. Used in approvals. * @param approver Address initiating an approval operation. */ error ERC1155InvalidApprover(address approver); /** * @dev Indicates a failure with the `operator` to be approved. Used in approvals. * @param operator Address that may be allowed to operate on tokens without being their owner. */ error ERC1155InvalidOperator(address operator); /** * @dev Indicates an array length mismatch between ids and values in a safeBatchTransferFrom operation. * Used in batch transfers. * @param idsLength Length of the array of token identifiers * @param valuesLength Length of the array of token amounts */ error ERC1155InvalidArrayLength(uint256 idsLength, uint256 valuesLength); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol) pragma solidity ^0.8.20; interface IERC5267 { /** * @dev MAY be emitted to signal that the domain could have changed. */ event EIP712DomainChanged(); /** * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712 * signature. */ function eip712Domain() external view returns ( bytes1 fields, string memory name, string memory version, uint256 chainId, address verifyingContract, bytes32 salt, uint256[] memory extensions ); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/ERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "./IERC20.sol"; import {IERC20Metadata} from "./extensions/IERC20Metadata.sol"; import {Context} from "../../utils/Context.sol"; import {IERC20Errors} from "../../interfaces/draft-IERC6093.sol"; /** * @dev Implementation of the {IERC20} interface. * * This implementation is agnostic to the way tokens are created. This means * that a supply mechanism has to be added in a derived contract using {_mint}. * * TIP: For a detailed writeup see our guide * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How * to implement supply mechanisms]. * * The default value of {decimals} is 18. To change this, you should override * this function so it returns a different value. * * We have followed general OpenZeppelin Contracts guidelines: functions revert * instead returning `false` on failure. This behavior is nonetheless * conventional and does not conflict with the expectations of ERC20 * applications. * * Additionally, an {Approval} event is emitted on calls to {transferFrom}. * This allows applications to reconstruct the allowance for all accounts just * by listening to said events. Other implementations of the EIP may not emit * these events, as it isn't required by the specification. */ abstract contract ERC20 is Context, IERC20, IERC20Metadata, IERC20Errors { mapping(address account => uint256) private _balances; mapping(address account => mapping(address spender => uint256)) private _allowances; uint256 private _totalSupply; string private _name; string private _symbol; /** * @dev Sets the values for {name} and {symbol}. * * 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 returns (string memory) { return _name; } /** * @dev Returns the symbol of the token, usually a shorter version of the * name. */ function symbol() public view virtual returns (string memory) { return _symbol; } /** * @dev Returns the number of decimals used to get its user representation. * For example, if `decimals` equals `2`, a balance of `505` tokens should * be displayed to a user as `5.05` (`505 / 10 ** 2`). * * Tokens usually opt for a value of 18, imitating the relationship between * Ether and Wei. This is the default value returned by this function, unless * it's overridden. * * NOTE: This information is only used for _display_ purposes: it in * no way affects any of the arithmetic of the contract, including * {IERC20-balanceOf} and {IERC20-transfer}. */ function decimals() public view virtual returns (uint8) { return 18; } /** * @dev See {IERC20-totalSupply}. */ function totalSupply() public view virtual returns (uint256) { return _totalSupply; } /** * @dev See {IERC20-balanceOf}. */ function balanceOf(address account) public view virtual returns (uint256) { return _balances[account]; } /** * @dev See {IERC20-transfer}. * * Requirements: * * - `to` cannot be the zero address. * - the caller must have a balance of at least `value`. */ function transfer(address to, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _transfer(owner, to, value); return true; } /** * @dev See {IERC20-allowance}. */ function allowance(address owner, address spender) public view virtual returns (uint256) { return _allowances[owner][spender]; } /** * @dev See {IERC20-approve}. * * NOTE: If `value` is the maximum `uint256`, the allowance is not updated on * `transferFrom`. This is semantically equivalent to an infinite approval. * * Requirements: * * - `spender` cannot be the zero address. */ function approve(address spender, uint256 value) public virtual returns (bool) { address owner = _msgSender(); _approve(owner, spender, value); return true; } /** * @dev See {IERC20-transferFrom}. * * Emits an {Approval} event indicating the updated allowance. This is not * required by the EIP. See the note at the beginning of {ERC20}. * * NOTE: Does not update the allowance if the current allowance * is the maximum `uint256`. * * Requirements: * * - `from` and `to` cannot be the zero address. * - `from` must have a balance of at least `value`. * - the caller must have allowance for ``from``'s tokens of at least * `value`. */ function transferFrom(address from, address to, uint256 value) public virtual returns (bool) { address spender = _msgSender(); _spendAllowance(from, spender, value); _transfer(from, to, value); return true; } /** * @dev Moves a `value` amount of tokens from `from` to `to`. * * This internal function is equivalent to {transfer}, and can be used to * e.g. implement automatic token fees, slashing mechanisms, etc. * * Emits a {Transfer} event. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _transfer(address from, address to, uint256 value) internal { if (from == address(0)) { revert ERC20InvalidSender(address(0)); } if (to == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(from, to, value); } /** * @dev Transfers a `value` amount of tokens from `from` to `to`, or alternatively mints (or burns) if `from` * (or `to`) is the zero address. All customizations to transfers, mints, and burns should be done by overriding * this function. * * Emits a {Transfer} event. */ function _update(address from, address to, uint256 value) internal virtual { if (from == address(0)) { // Overflow check required: The rest of the code assumes that totalSupply never overflows _totalSupply += value; } else { uint256 fromBalance = _balances[from]; if (fromBalance < value) { revert ERC20InsufficientBalance(from, fromBalance, value); } unchecked { // Overflow not possible: value <= fromBalance <= totalSupply. _balances[from] = fromBalance - value; } } if (to == address(0)) { unchecked { // Overflow not possible: value <= totalSupply or value <= fromBalance <= totalSupply. _totalSupply -= value; } } else { unchecked { // Overflow not possible: balance + value is at most totalSupply, which we know fits into a uint256. _balances[to] += value; } } emit Transfer(from, to, value); } /** * @dev Creates a `value` amount of tokens and assigns them to `account`, by transferring it from address(0). * Relies on the `_update` mechanism * * Emits a {Transfer} event with `from` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead. */ function _mint(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(address(0), account, value); } /** * @dev Destroys a `value` amount of tokens from `account`, lowering the total supply. * Relies on the `_update` mechanism. * * Emits a {Transfer} event with `to` set to the zero address. * * NOTE: This function is not virtual, {_update} should be overridden instead */ function _burn(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidSender(address(0)); } _update(account, address(0), value); } /** * @dev Sets `value` as the allowance of `spender` over the `owner` s tokens. * * This internal function is equivalent to `approve`, and can be used to * e.g. set automatic allowances for certain subsystems, etc. * * Emits an {Approval} event. * * Requirements: * * - `owner` cannot be the zero address. * - `spender` cannot be the zero address. * * Overrides to this logic should be done to the variant with an additional `bool emitEvent` argument. */ function _approve(address owner, address spender, uint256 value) internal { _approve(owner, spender, value, true); } /** * @dev Variant of {_approve} with an optional flag to enable or disable the {Approval} event. * * By default (when calling {_approve}) the flag is set to true. On the other hand, approval changes made by * `_spendAllowance` during the `transferFrom` operation set the flag to false. This saves gas by not emitting any * `Approval` event during `transferFrom` operations. * * Anyone who wishes to continue emitting `Approval` events on the`transferFrom` operation can force the flag to * true using the following override: * ``` * function _approve(address owner, address spender, uint256 value, bool) internal virtual override { * super._approve(owner, spender, value, true); * } * ``` * * Requirements are the same as {_approve}. */ function _approve(address owner, address spender, uint256 value, bool emitEvent) internal virtual { if (owner == address(0)) { revert ERC20InvalidApprover(address(0)); } if (spender == address(0)) { revert ERC20InvalidSpender(address(0)); } _allowances[owner][spender] = value; if (emitEvent) { emit Approval(owner, spender, value); } } /** * @dev Updates `owner` s allowance for `spender` based on spent `value`. * * Does not update the allowance value in case of infinite allowance. * Revert if not enough allowance is available. * * Does not emit an {Approval} event. */ function _spendAllowance(address owner, address spender, uint256 value) internal virtual { uint256 currentAllowance = allowance(owner, spender); if (currentAllowance != type(uint256).max) { if (currentAllowance < value) { revert ERC20InsufficientAllowance(spender, currentAllowance, value); } unchecked { _approve(owner, spender, currentAllowance - value, false); } } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; /** * @dev Interface for the optional metadata functions from the ERC20 standard. */ interface IERC20Metadata is IERC20 { /** * @dev Returns the name of the token. */ function name() external view returns (string memory); /** * @dev Returns the symbol of the token. */ function symbol() external view returns (string memory); /** * @dev Returns the decimals places of the token. */ function decimals() external view returns (uint8); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612]. * * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't * need to send a transaction, and thus is not required to hold Ether at all. * * ==== Security Considerations * * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be * considered as an intention to spend the allowance in any specific way. The second is that because permits have * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be * generally recommended is: * * ```solidity * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public { * try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {} * doThing(..., value); * } * * function doThing(..., uint256 value) public { * token.safeTransferFrom(msg.sender, address(this), value); * ... * } * ``` * * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also * {SafeERC20-safeTransferFrom}). * * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so * contracts should have entry points that don't rely on permit. */ interface IERC20Permit { /** * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens, * given ``owner``'s signed approval. * * IMPORTANT: The same issues {IERC20-approve} has related to transaction * ordering also apply here. * * Emits an {Approval} event. * * Requirements: * * - `spender` cannot be the zero address. * - `deadline` must be a timestamp in the future. * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner` * over the EIP712-formatted function arguments. * - the signature must use ``owner``'s current nonce (see {nonces}). * * For more information on the signature format, see the * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP * section]. * * CAUTION: See Security Considerations above. */ function permit( address owner, address spender, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s ) external; /** * @dev Returns the current nonce for `owner`. This value must be * included whenever a signature is generated for {permit}. * * Every successful call to {permit} increases ``owner``'s nonce by one. This * prevents a signature from being used multiple times. */ function nonces(address owner) external view returns (uint256); /** * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}. */ // solhint-disable-next-line func-name-mixedcase function DOMAIN_SEPARATOR() external view returns (bytes32); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/IERC20.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC20 standard as defined in the EIP. */ interface IERC20 { /** * @dev Emitted when `value` tokens are moved from one account (`from`) to * another (`to`). * * Note that `value` may be zero. */ event Transfer(address indexed from, address indexed to, uint256 value); /** * @dev Emitted when the allowance of a `spender` for an `owner` is set by * a call to {approve}. `value` is the new allowance. */ event Approval(address indexed owner, address indexed spender, uint256 value); /** * @dev Returns the value of tokens in existence. */ function totalSupply() external view returns (uint256); /** * @dev Returns the value of tokens owned by `account`. */ function balanceOf(address account) external view returns (uint256); /** * @dev Moves a `value` amount of tokens from the caller's account to `to`. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transfer(address to, uint256 value) external returns (bool); /** * @dev Returns the remaining number of tokens that `spender` will be * allowed to spend on behalf of `owner` through {transferFrom}. This is * zero by default. * * This value changes when {approve} or {transferFrom} are called. */ function allowance(address owner, address spender) external view returns (uint256); /** * @dev Sets a `value` amount of tokens as the allowance of `spender` over the * caller's tokens. * * Returns a boolean value indicating whether the operation succeeded. * * IMPORTANT: Beware that changing an allowance with this method brings the risk * that someone may use both the old and the new allowance by unfortunate * transaction ordering. One possible solution to mitigate this race * condition is to first reduce the spender's allowance to 0 and set the * desired value afterwards: * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729 * * Emits an {Approval} event. */ function approve(address spender, uint256 value) external returns (bool); /** * @dev Moves a `value` amount of tokens from `from` to `to` using the * allowance mechanism. `value` is then deducted from the caller's * allowance. * * Returns a boolean value indicating whether the operation succeeded. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 value) external returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/utils/SafeERC20.sol) pragma solidity ^0.8.20; import {IERC20} from "../IERC20.sol"; import {IERC20Permit} from "../extensions/IERC20Permit.sol"; import {Address} from "../../../utils/Address.sol"; /** * @title SafeERC20 * @dev Wrappers around ERC20 operations that throw on failure (when the token * contract returns false). Tokens that return no value (and instead revert or * throw on failure) are also supported, non-reverting calls are assumed to be * successful. * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract, * which allows you to call the safe operations as `token.safeTransfer(...)`, etc. */ library SafeERC20 { using Address for address; /** * @dev An operation with an ERC20 token failed. */ error SafeERC20FailedOperation(address token); /** * @dev Indicates a failed `decreaseAllowance` request. */ error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease); /** * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeTransfer(IERC20 token, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transfer, (to, value))); } /** * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful. */ function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal { _callOptionalReturn(token, abi.encodeCall(token.transferFrom, (from, to, value))); } /** * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. */ function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal { uint256 oldAllowance = token.allowance(address(this), spender); forceApprove(token, spender, oldAllowance + value); } /** * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no * value, non-reverting calls are assumed to be successful. */ function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal { unchecked { uint256 currentAllowance = token.allowance(address(this), spender); if (currentAllowance < requestedDecrease) { revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease); } forceApprove(token, spender, currentAllowance - requestedDecrease); } } /** * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value, * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval * to be set to zero before setting it to a non-zero value, such as USDT. */ function forceApprove(IERC20 token, address spender, uint256 value) internal { bytes memory approvalCall = abi.encodeCall(token.approve, (spender, value)); if (!_callOptionalReturnBool(token, approvalCall)) { _callOptionalReturn(token, abi.encodeCall(token.approve, (spender, 0))); _callOptionalReturn(token, approvalCall); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). */ function _callOptionalReturn(IERC20 token, bytes memory data) private { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that // the target address contains contract code and also asserts for success in the low-level call. bytes memory returndata = address(token).functionCall(data); if (returndata.length != 0 && !abi.decode(returndata, (bool))) { revert SafeERC20FailedOperation(address(token)); } } /** * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement * on the return value: the return value is optional (but if data is returned, it must not be false). * @param token The token targeted by the call. * @param data The call data (encoded using abi.encode or one of its variants). * * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead. */ function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) { // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false // and not revert is the subcall reverts. (bool success, bytes memory returndata) = address(token).call(data); return success && (returndata.length == 0 || abi.decode(returndata, (bool))) && address(token).code.length > 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/extensions/IERC721Metadata.sol) pragma solidity ^0.8.20; import {IERC721} from "../IERC721.sol"; /** * @title ERC-721 Non-Fungible Token Standard, optional metadata extension * @dev See https://eips.ethereum.org/EIPS/eip-721 */ interface IERC721Metadata is IERC721 { /** * @dev Returns the token collection name. */ function name() external view returns (string memory); /** * @dev Returns the token collection symbol. */ function symbol() external view returns (string memory); /** * @dev Returns the Uniform Resource Identifier (URI) for `tokenId` token. */ function tokenURI(uint256 tokenId) external view returns (string memory); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721.sol) pragma solidity ^0.8.20; import {IERC165} from "../../utils/introspection/IERC165.sol"; /** * @dev Required interface of an ERC721 compliant contract. */ interface IERC721 is IERC165 { /** * @dev Emitted when `tokenId` token is transferred from `from` to `to`. */ event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables `approved` to manage the `tokenId` token. */ event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); /** * @dev Emitted when `owner` enables or disables (`approved`) `operator` to manage all of its assets. */ event ApprovalForAll(address indexed owner, address indexed operator, bool approved); /** * @dev Returns the number of tokens in ``owner``'s account. */ function balanceOf(address owner) external view returns (uint256 balance); /** * @dev Returns the owner of the `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function ownerOf(uint256 tokenId) external view returns (address owner); /** * @dev Safely transfers `tokenId` token from `from` to `to`. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon * a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external; /** * @dev Safely transfers `tokenId` token from `from` to `to`, checking first that contract recipients * are aware of the ERC721 protocol to prevent tokens from being forever locked. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must exist and be owned by `from`. * - If the caller is not `from`, it must have been allowed to move this token by either {approve} or * {setApprovalForAll}. * - If `to` refers to a smart contract, it must implement {IERC721Receiver-onERC721Received}, which is called upon * a safe transfer. * * Emits a {Transfer} event. */ function safeTransferFrom(address from, address to, uint256 tokenId) external; /** * @dev Transfers `tokenId` token from `from` to `to`. * * WARNING: Note that the caller is responsible to confirm that the recipient is capable of receiving ERC721 * or else they may be permanently lost. Usage of {safeTransferFrom} prevents loss, though the caller must * understand this adds an external call which potentially creates a reentrancy vulnerability. * * Requirements: * * - `from` cannot be the zero address. * - `to` cannot be the zero address. * - `tokenId` token must be owned by `from`. * - If the caller is not `from`, it must be approved to move this token by either {approve} or {setApprovalForAll}. * * Emits a {Transfer} event. */ function transferFrom(address from, address to, uint256 tokenId) external; /** * @dev Gives permission to `to` to transfer `tokenId` token to another account. * The approval is cleared when the token is transferred. * * Only a single account can be approved at a time, so approving the zero address clears previous approvals. * * Requirements: * * - The caller must own the token or be an approved operator. * - `tokenId` must exist. * * Emits an {Approval} event. */ function approve(address to, uint256 tokenId) external; /** * @dev Approve or remove `operator` as an operator for the caller. * Operators can call {transferFrom} or {safeTransferFrom} for any token owned by the caller. * * Requirements: * * - The `operator` cannot be the address zero. * * Emits an {ApprovalForAll} event. */ function setApprovalForAll(address operator, bool approved) external; /** * @dev Returns the account approved for `tokenId` token. * * Requirements: * * - `tokenId` must exist. */ function getApproved(uint256 tokenId) external view returns (address operator); /** * @dev Returns if the `operator` is allowed to manage all of the assets of `owner`. * * See {setApprovalForAll} */ function isApprovedForAll(address owner, address operator) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (token/ERC721/IERC721Receiver.sol) pragma solidity ^0.8.20; /** * @title ERC721 token receiver interface * @dev Interface for any contract that wants to support safeTransfers * from ERC721 asset contracts. */ interface IERC721Receiver { /** * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom} * by `operator` from `from`, this function is called. * * It must return its Solidity selector to confirm the token transfer. * If any other value is returned or the interface is not implemented by the recipient, the transfer will be * reverted. * * The selector can be obtained in Solidity with `IERC721Receiver.onERC721Received.selector`. */ function onERC721Received( address operator, address from, uint256 tokenId, bytes calldata data ) external returns (bytes4); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Address.sol) pragma solidity ^0.8.20; /** * @dev Collection of functions related to the address type */ library Address { /** * @dev The ETH balance of the account is not enough to perform the operation. */ error AddressInsufficientBalance(address account); /** * @dev There's no code at `target` (it is not a contract). */ error AddressEmptyCode(address target); /** * @dev A call to an address target failed. The target may have reverted. */ error FailedInnerCall(); /** * @dev Replacement for Solidity's `transfer`: sends `amount` wei to * `recipient`, forwarding all available gas and reverting on errors. * * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost * of certain opcodes, possibly making contracts go over the 2300 gas limit * imposed by `transfer`, making them unable to receive funds via * `transfer`. {sendValue} removes this limitation. * * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more]. * * IMPORTANT: because control is transferred to `recipient`, care must be * taken to not create reentrancy vulnerabilities. Consider using * {ReentrancyGuard} or the * https://solidity.readthedocs.io/en/v0.8.20/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern]. */ function sendValue(address payable recipient, uint256 amount) internal { if (address(this).balance < amount) { revert AddressInsufficientBalance(address(this)); } (bool success, ) = recipient.call{value: amount}(""); if (!success) { revert FailedInnerCall(); } } /** * @dev Performs a Solidity function call using a low level `call`. A * plain `call` is an unsafe replacement for a function call: use this * function instead. * * If `target` reverts with a revert reason or custom error, it is bubbled * up by this function (like regular Solidity function calls). However, if * the call reverted with no returned reason, this function reverts with a * {FailedInnerCall} error. * * Returns the raw returned data. To convert to the expected return value, * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`]. * * Requirements: * * - `target` must be a contract. * - calling `target` with `data` must not revert. */ function functionCall(address target, bytes memory data) internal returns (bytes memory) { return functionCallWithValue(target, data, 0); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but also transferring `value` wei to `target`. * * Requirements: * * - the calling contract must have an ETH balance of at least `value`. * - the called Solidity function must be `payable`. */ function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) { if (address(this).balance < value) { revert AddressInsufficientBalance(address(this)); } (bool success, bytes memory returndata) = target.call{value: value}(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a static call. */ function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) { (bool success, bytes memory returndata) = target.staticcall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], * but performing a delegate call. */ function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) { (bool success, bytes memory returndata) = target.delegatecall(data); return verifyCallResultFromTarget(target, success, returndata); } /** * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an * unsuccessful call. */ function verifyCallResultFromTarget( address target, bool success, bytes memory returndata ) internal view returns (bytes memory) { if (!success) { _revert(returndata); } else { // only check if target is a contract if the call was successful and the return data is empty // otherwise we already know that it was a contract if (returndata.length == 0 && target.code.length == 0) { revert AddressEmptyCode(target); } return returndata; } } /** * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the * revert reason or with a default {FailedInnerCall} error. */ function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) { if (!success) { _revert(returndata); } else { return returndata; } } /** * @dev Reverts with returndata if present. Otherwise reverts with {FailedInnerCall}. */ function _revert(bytes memory returndata) private pure { // Look for revert reason and bubble it up if present if (returndata.length > 0) { // The easiest way to bubble the revert reason is using memory via assembly /// @solidity memory-safe-assembly assembly { let returndata_size := mload(returndata) revert(add(32, returndata), returndata_size) } } else { revert FailedInnerCall(); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol) pragma solidity ^0.8.20; /** * @dev Provides information about the current execution context, including the * sender of the transaction and its data. While these are generally available * via msg.sender and msg.data, they should not be accessed in such a direct * manner, since when dealing with meta-transactions the account sending and * paying for execution may not be the actual sender (as far as an application * is concerned). * * This contract is only required for intermediate, library-like contracts. */ abstract contract Context { function _msgSender() internal view virtual returns (address) { return msg.sender; } function _msgData() internal view virtual returns (bytes calldata) { return msg.data; } function _contextSuffixLength() internal view virtual returns (uint256) { return 0; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol) pragma solidity ^0.8.20; /** * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations. * * These functions can be used to verify that a message was signed by the holder * of the private keys of a given address. */ library ECDSA { enum RecoverError { NoError, InvalidSignature, InvalidSignatureLength, InvalidSignatureS } /** * @dev The signature derives the `address(0)`. */ error ECDSAInvalidSignature(); /** * @dev The signature has an invalid length. */ error ECDSAInvalidSignatureLength(uint256 length); /** * @dev The signature has an S value that is in the upper half order. */ error ECDSAInvalidSignatureS(bytes32 s); /** * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not * return address(0) without also returning an error description. Errors are documented using an enum (error type) * and a bytes32 providing additional information about the error. * * If no error is returned, then the address can be used for verification purposes. * * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it. * * Documentation for signature generation: * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js] * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers] */ function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) { if (signature.length == 65) { bytes32 r; bytes32 s; uint8 v; // ecrecover takes the signature parameters, and the only way to get them // currently is to use assembly. /// @solidity memory-safe-assembly assembly { r := mload(add(signature, 0x20)) s := mload(add(signature, 0x40)) v := byte(0, mload(add(signature, 0x60))) } return tryRecover(hash, v, r, s); } else { return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length)); } } /** * @dev Returns the address that signed a hashed message (`hash`) with * `signature`. This address can then be used for verification purposes. * * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures: * this function rejects them by requiring the `s` value to be in the lower * half order, and the `v` value to be either 27 or 28. * * IMPORTANT: `hash` _must_ be the result of a hash operation for the * verification to be secure: it is possible to craft signatures that * recover to arbitrary addresses for non-hashed data. A safe way to ensure * this is by receiving a hash of the original message (which may otherwise * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it. */ function recover(bytes32 hash, bytes memory signature) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature); _throwError(error, errorArg); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately. * * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures] */ function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) { unchecked { bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff); // We do not check for an overflow here since the shift operation results in 0 or 1. uint8 v = uint8((uint256(vs) >> 255) + 27); return tryRecover(hash, v, r, s); } } /** * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately. */ function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs); _throwError(error, errorArg); return recovered; } /** * @dev Overload of {ECDSA-tryRecover} that receives the `v`, * `r` and `s` signature fields separately. */ function tryRecover( bytes32 hash, uint8 v, bytes32 r, bytes32 s ) internal pure returns (address, RecoverError, bytes32) { // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most // signatures from current libraries generate a unique signature with an s-value in the lower half order. // // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept // these malleable signatures as well. if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) { return (address(0), RecoverError.InvalidSignatureS, s); } // If the signature is valid (and not malleable), return the signer address address signer = ecrecover(hash, v, r, s); if (signer == address(0)) { return (address(0), RecoverError.InvalidSignature, bytes32(0)); } return (signer, RecoverError.NoError, bytes32(0)); } /** * @dev Overload of {ECDSA-recover} that receives the `v`, * `r` and `s` signature fields separately. */ function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) { (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s); _throwError(error, errorArg); return recovered; } /** * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided. */ function _throwError(RecoverError error, bytes32 errorArg) private pure { if (error == RecoverError.NoError) { return; // no error: do nothing } else if (error == RecoverError.InvalidSignature) { revert ECDSAInvalidSignature(); } else if (error == RecoverError.InvalidSignatureLength) { revert ECDSAInvalidSignatureLength(uint256(errorArg)); } else if (error == RecoverError.InvalidSignatureS) { revert ECDSAInvalidSignatureS(errorArg); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol) pragma solidity ^0.8.20; import {Strings} from "../Strings.sol"; /** * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing. * * The library provides methods for generating a hash of a message that conforms to the * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712] * specifications. */ library MessageHashUtils { /** * @dev Returns the keccak256 digest of an EIP-191 signed data with version * `0x45` (`personal_sign` messages). * * The digest is calculated by prefixing a bytes32 `messageHash` with * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. * * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with * keccak256, although any bytes32 value can be safely used because the final digest will * be re-hashed. * * See {ECDSA-recover}. */ function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) { /// @solidity memory-safe-assembly assembly { mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20) } } /** * @dev Returns the keccak256 digest of an EIP-191 signed data with version * `0x45` (`personal_sign` messages). * * The digest is calculated by prefixing an arbitrary `message` with * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method. * * See {ECDSA-recover}. */ function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) { return keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message)); } /** * @dev Returns the keccak256 digest of an EIP-191 signed data with version * `0x00` (data with intended validator). * * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended * `validator` address. Then hashing the result. * * See {ECDSA-recover}. */ function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) { return keccak256(abi.encodePacked(hex"19_00", validator, data)); } /** * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`). * * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with * `\x19\x01` and hashing the result. It corresponds to the hash signed by the * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712. * * See {ECDSA-recover}. */ function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) { /// @solidity memory-safe-assembly assembly { let ptr := mload(0x40) mstore(ptr, hex"19_01") mstore(add(ptr, 0x02), domainSeparator) mstore(add(ptr, 0x22), structHash) digest := keccak256(ptr, 0x42) } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol) pragma solidity ^0.8.20; import {IERC165} from "./IERC165.sol"; /** * @dev Implementation of the {IERC165} interface. * * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check * for the additional interface id that will be supported. For example: * * ```solidity * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) { * return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId); * } * ``` */ abstract contract ERC165 is IERC165 { /** * @dev See {IERC165-supportsInterface}. */ function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) { return interfaceId == type(IERC165).interfaceId; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol) pragma solidity ^0.8.20; /** * @dev Interface of the ERC165 standard, as defined in the * https://eips.ethereum.org/EIPS/eip-165[EIP]. * * Implementers can declare support of contract interfaces, which can then be * queried by others ({ERC165Checker}). * * For an implementation, see {ERC165}. */ interface IERC165 { /** * @dev Returns true if this contract implements the interface defined by * `interfaceId`. See the corresponding * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section] * to learn more about how these ids are created. * * This function call must use less than 30 000 gas. */ function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/Math.sol) pragma solidity ^0.8.20; /** * @dev Standard math utilities missing in the Solidity language. */ library Math { /** * @dev Muldiv operation overflow. */ error MathOverflowedMulDiv(); enum Rounding { Floor, // Toward negative infinity Ceil, // Toward positive infinity Trunc, // Toward zero Expand // Away from zero } /** * @dev Returns the addition of two unsigned integers, with an overflow flag. */ function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { uint256 c = a + b; if (c < a) return (false, 0); return (true, c); } } /** * @dev Returns the subtraction of two unsigned integers, with an overflow flag. */ function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b > a) return (false, 0); return (true, a - b); } } /** * @dev Returns the multiplication of two unsigned integers, with an overflow flag. */ function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { // Gas optimization: this is cheaper than requiring 'a' not being zero, but the // benefit is lost if 'b' is also tested. // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522 if (a == 0) return (true, 0); uint256 c = a * b; if (c / a != b) return (false, 0); return (true, c); } } /** * @dev Returns the division of two unsigned integers, with a division by zero flag. */ function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a / b); } } /** * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag. */ function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) { unchecked { if (b == 0) return (false, 0); return (true, a % b); } } /** * @dev Returns the largest of two numbers. */ function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /** * @dev Returns the smallest of two numbers. */ function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /** * @dev Returns the average of two numbers. The result is rounded towards * zero. */ function average(uint256 a, uint256 b) internal pure returns (uint256) { // (a + b) / 2 can overflow. return (a & b) + (a ^ b) / 2; } /** * @dev Returns the ceiling of the division of two numbers. * * This differs from standard division with `/` in that it rounds towards infinity instead * of rounding towards zero. */ function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) { if (b == 0) { // Guarantee the same behavior as in a regular Solidity division. return a / b; } // (a + b - 1) / b can overflow on addition, so we distribute. return a == 0 ? 0 : (a - 1) / b + 1; } /** * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or * denominator == 0. * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv) with further edits by * Uniswap Labs also under MIT license. */ function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) { unchecked { // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256 // variables such that product = prod1 * 2^256 + prod0. uint256 prod0 = x * y; // Least significant 256 bits of the product uint256 prod1; // Most significant 256 bits of the product assembly { let mm := mulmod(x, y, not(0)) prod1 := sub(sub(mm, prod0), lt(mm, prod0)) } // Handle non-overflow cases, 256 by 256 division. if (prod1 == 0) { // Solidity will revert if denominator == 0, unlike the div opcode on its own. // The surrounding unchecked block does not change this fact. // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic. return prod0 / denominator; } // Make sure the result is less than 2^256. Also prevents denominator == 0. if (denominator <= prod1) { revert MathOverflowedMulDiv(); } /////////////////////////////////////////////// // 512 by 256 division. /////////////////////////////////////////////// // Make division exact by subtracting the remainder from [prod1 prod0]. uint256 remainder; assembly { // Compute remainder using mulmod. remainder := mulmod(x, y, denominator) // Subtract 256 bit number from 512 bit number. prod1 := sub(prod1, gt(remainder, prod0)) prod0 := sub(prod0, remainder) } // Factor powers of two out of denominator and compute largest power of two divisor of denominator. // Always >= 1. See https://cs.stackexchange.com/q/138556/92363. uint256 twos = denominator & (0 - denominator); assembly { // Divide denominator by twos. denominator := div(denominator, twos) // Divide [prod1 prod0] by twos. prod0 := div(prod0, twos) // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one. twos := add(div(sub(0, twos), twos), 1) } // Shift in bits from prod1 into prod0. prod0 |= prod1 * twos; // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for // four bits. That is, denominator * inv = 1 mod 2^4. uint256 inverse = (3 * denominator) ^ 2; // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also // works in modular arithmetic, doubling the correct bits in each step. inverse *= 2 - denominator * inverse; // inverse mod 2^8 inverse *= 2 - denominator * inverse; // inverse mod 2^16 inverse *= 2 - denominator * inverse; // inverse mod 2^32 inverse *= 2 - denominator * inverse; // inverse mod 2^64 inverse *= 2 - denominator * inverse; // inverse mod 2^128 inverse *= 2 - denominator * inverse; // inverse mod 2^256 // Because the division is now exact we can divide by multiplying with the modular inverse of denominator. // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1 // is no longer required. result = prod0 * inverse; return result; } } /** * @notice Calculates x * y / denominator with full precision, following the selected rounding direction. */ function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) { uint256 result = mulDiv(x, y, denominator); if (unsignedRoundsUp(rounding) && mulmod(x, y, denominator) > 0) { result += 1; } return result; } /** * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded * towards zero. * * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11). */ function sqrt(uint256 a) internal pure returns (uint256) { if (a == 0) { return 0; } // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target. // // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`. // // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)` // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))` // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)` // // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit. uint256 result = 1 << (log2(a) >> 1); // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128, // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision // into the expected uint128 result. unchecked { result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; result = (result + a / result) >> 1; return min(result, a / result); } } /** * @notice Calculates sqrt(a), following the selected rounding direction. */ function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = sqrt(a); return result + (unsignedRoundsUp(rounding) && result * result < a ? 1 : 0); } } /** * @dev Return the log in base 2 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log2(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 128; } if (value >> 64 > 0) { value >>= 64; result += 64; } if (value >> 32 > 0) { value >>= 32; result += 32; } if (value >> 16 > 0) { value >>= 16; result += 16; } if (value >> 8 > 0) { value >>= 8; result += 8; } if (value >> 4 > 0) { value >>= 4; result += 4; } if (value >> 2 > 0) { value >>= 2; result += 2; } if (value >> 1 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 2, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log2(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log2(value); return result + (unsignedRoundsUp(rounding) && 1 << result < value ? 1 : 0); } } /** * @dev Return the log in base 10 of a positive value rounded towards zero. * Returns 0 if given 0. */ function log10(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >= 10 ** 64) { value /= 10 ** 64; result += 64; } if (value >= 10 ** 32) { value /= 10 ** 32; result += 32; } if (value >= 10 ** 16) { value /= 10 ** 16; result += 16; } if (value >= 10 ** 8) { value /= 10 ** 8; result += 8; } if (value >= 10 ** 4) { value /= 10 ** 4; result += 4; } if (value >= 10 ** 2) { value /= 10 ** 2; result += 2; } if (value >= 10 ** 1) { result += 1; } } return result; } /** * @dev Return the log in base 10, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log10(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log10(value); return result + (unsignedRoundsUp(rounding) && 10 ** result < value ? 1 : 0); } } /** * @dev Return the log in base 256 of a positive value rounded towards zero. * Returns 0 if given 0. * * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string. */ function log256(uint256 value) internal pure returns (uint256) { uint256 result = 0; unchecked { if (value >> 128 > 0) { value >>= 128; result += 16; } if (value >> 64 > 0) { value >>= 64; result += 8; } if (value >> 32 > 0) { value >>= 32; result += 4; } if (value >> 16 > 0) { value >>= 16; result += 2; } if (value >> 8 > 0) { result += 1; } } return result; } /** * @dev Return the log in base 256, following the selected rounding direction, of a positive value. * Returns 0 if given 0. */ function log256(uint256 value, Rounding rounding) internal pure returns (uint256) { unchecked { uint256 result = log256(value); return result + (unsignedRoundsUp(rounding) && 1 << (result << 3) < value ? 1 : 0); } } /** * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers. */ function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) { return uint8(rounding) % 2 == 1; } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SafeCast.sol) // This file was procedurally generated from scripts/generate/templates/SafeCast.js. pragma solidity ^0.8.20; /** * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow * checks. * * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can * easily result in undesired exploitation or bugs, since developers usually * assume that overflows raise errors. `SafeCast` restores this intuition by * reverting the transaction when such an operation overflows. * * Using this library instead of the unchecked operations eliminates an entire * class of bugs, so it's recommended to use it always. */ library SafeCast { /** * @dev Value doesn't fit in an uint of `bits` size. */ error SafeCastOverflowedUintDowncast(uint8 bits, uint256 value); /** * @dev An int value doesn't fit in an uint of `bits` size. */ error SafeCastOverflowedIntToUint(int256 value); /** * @dev Value doesn't fit in an int of `bits` size. */ error SafeCastOverflowedIntDowncast(uint8 bits, int256 value); /** * @dev An uint value doesn't fit in an int of `bits` size. */ error SafeCastOverflowedUintToInt(uint256 value); /** * @dev Returns the downcasted uint248 from uint256, reverting on * overflow (when the input is greater than largest uint248). * * Counterpart to Solidity's `uint248` operator. * * Requirements: * * - input must fit into 248 bits */ function toUint248(uint256 value) internal pure returns (uint248) { if (value > type(uint248).max) { revert SafeCastOverflowedUintDowncast(248, value); } return uint248(value); } /** * @dev Returns the downcasted uint240 from uint256, reverting on * overflow (when the input is greater than largest uint240). * * Counterpart to Solidity's `uint240` operator. * * Requirements: * * - input must fit into 240 bits */ function toUint240(uint256 value) internal pure returns (uint240) { if (value > type(uint240).max) { revert SafeCastOverflowedUintDowncast(240, value); } return uint240(value); } /** * @dev Returns the downcasted uint232 from uint256, reverting on * overflow (when the input is greater than largest uint232). * * Counterpart to Solidity's `uint232` operator. * * Requirements: * * - input must fit into 232 bits */ function toUint232(uint256 value) internal pure returns (uint232) { if (value > type(uint232).max) { revert SafeCastOverflowedUintDowncast(232, value); } return uint232(value); } /** * @dev Returns the downcasted uint224 from uint256, reverting on * overflow (when the input is greater than largest uint224). * * Counterpart to Solidity's `uint224` operator. * * Requirements: * * - input must fit into 224 bits */ function toUint224(uint256 value) internal pure returns (uint224) { if (value > type(uint224).max) { revert SafeCastOverflowedUintDowncast(224, value); } return uint224(value); } /** * @dev Returns the downcasted uint216 from uint256, reverting on * overflow (when the input is greater than largest uint216). * * Counterpart to Solidity's `uint216` operator. * * Requirements: * * - input must fit into 216 bits */ function toUint216(uint256 value) internal pure returns (uint216) { if (value > type(uint216).max) { revert SafeCastOverflowedUintDowncast(216, value); } return uint216(value); } /** * @dev Returns the downcasted uint208 from uint256, reverting on * overflow (when the input is greater than largest uint208). * * Counterpart to Solidity's `uint208` operator. * * Requirements: * * - input must fit into 208 bits */ function toUint208(uint256 value) internal pure returns (uint208) { if (value > type(uint208).max) { revert SafeCastOverflowedUintDowncast(208, value); } return uint208(value); } /** * @dev Returns the downcasted uint200 from uint256, reverting on * overflow (when the input is greater than largest uint200). * * Counterpart to Solidity's `uint200` operator. * * Requirements: * * - input must fit into 200 bits */ function toUint200(uint256 value) internal pure returns (uint200) { if (value > type(uint200).max) { revert SafeCastOverflowedUintDowncast(200, value); } return uint200(value); } /** * @dev Returns the downcasted uint192 from uint256, reverting on * overflow (when the input is greater than largest uint192). * * Counterpart to Solidity's `uint192` operator. * * Requirements: * * - input must fit into 192 bits */ function toUint192(uint256 value) internal pure returns (uint192) { if (value > type(uint192).max) { revert SafeCastOverflowedUintDowncast(192, value); } return uint192(value); } /** * @dev Returns the downcasted uint184 from uint256, reverting on * overflow (when the input is greater than largest uint184). * * Counterpart to Solidity's `uint184` operator. * * Requirements: * * - input must fit into 184 bits */ function toUint184(uint256 value) internal pure returns (uint184) { if (value > type(uint184).max) { revert SafeCastOverflowedUintDowncast(184, value); } return uint184(value); } /** * @dev Returns the downcasted uint176 from uint256, reverting on * overflow (when the input is greater than largest uint176). * * Counterpart to Solidity's `uint176` operator. * * Requirements: * * - input must fit into 176 bits */ function toUint176(uint256 value) internal pure returns (uint176) { if (value > type(uint176).max) { revert SafeCastOverflowedUintDowncast(176, value); } return uint176(value); } /** * @dev Returns the downcasted uint168 from uint256, reverting on * overflow (when the input is greater than largest uint168). * * Counterpart to Solidity's `uint168` operator. * * Requirements: * * - input must fit into 168 bits */ function toUint168(uint256 value) internal pure returns (uint168) { if (value > type(uint168).max) { revert SafeCastOverflowedUintDowncast(168, value); } return uint168(value); } /** * @dev Returns the downcasted uint160 from uint256, reverting on * overflow (when the input is greater than largest uint160). * * Counterpart to Solidity's `uint160` operator. * * Requirements: * * - input must fit into 160 bits */ function toUint160(uint256 value) internal pure returns (uint160) { if (value > type(uint160).max) { revert SafeCastOverflowedUintDowncast(160, value); } return uint160(value); } /** * @dev Returns the downcasted uint152 from uint256, reverting on * overflow (when the input is greater than largest uint152). * * Counterpart to Solidity's `uint152` operator. * * Requirements: * * - input must fit into 152 bits */ function toUint152(uint256 value) internal pure returns (uint152) { if (value > type(uint152).max) { revert SafeCastOverflowedUintDowncast(152, value); } return uint152(value); } /** * @dev Returns the downcasted uint144 from uint256, reverting on * overflow (when the input is greater than largest uint144). * * Counterpart to Solidity's `uint144` operator. * * Requirements: * * - input must fit into 144 bits */ function toUint144(uint256 value) internal pure returns (uint144) { if (value > type(uint144).max) { revert SafeCastOverflowedUintDowncast(144, value); } return uint144(value); } /** * @dev Returns the downcasted uint136 from uint256, reverting on * overflow (when the input is greater than largest uint136). * * Counterpart to Solidity's `uint136` operator. * * Requirements: * * - input must fit into 136 bits */ function toUint136(uint256 value) internal pure returns (uint136) { if (value > type(uint136).max) { revert SafeCastOverflowedUintDowncast(136, value); } return uint136(value); } /** * @dev Returns the downcasted uint128 from uint256, reverting on * overflow (when the input is greater than largest uint128). * * Counterpart to Solidity's `uint128` operator. * * Requirements: * * - input must fit into 128 bits */ function toUint128(uint256 value) internal pure returns (uint128) { if (value > type(uint128).max) { revert SafeCastOverflowedUintDowncast(128, value); } return uint128(value); } /** * @dev Returns the downcasted uint120 from uint256, reverting on * overflow (when the input is greater than largest uint120). * * Counterpart to Solidity's `uint120` operator. * * Requirements: * * - input must fit into 120 bits */ function toUint120(uint256 value) internal pure returns (uint120) { if (value > type(uint120).max) { revert SafeCastOverflowedUintDowncast(120, value); } return uint120(value); } /** * @dev Returns the downcasted uint112 from uint256, reverting on * overflow (when the input is greater than largest uint112). * * Counterpart to Solidity's `uint112` operator. * * Requirements: * * - input must fit into 112 bits */ function toUint112(uint256 value) internal pure returns (uint112) { if (value > type(uint112).max) { revert SafeCastOverflowedUintDowncast(112, value); } return uint112(value); } /** * @dev Returns the downcasted uint104 from uint256, reverting on * overflow (when the input is greater than largest uint104). * * Counterpart to Solidity's `uint104` operator. * * Requirements: * * - input must fit into 104 bits */ function toUint104(uint256 value) internal pure returns (uint104) { if (value > type(uint104).max) { revert SafeCastOverflowedUintDowncast(104, value); } return uint104(value); } /** * @dev Returns the downcasted uint96 from uint256, reverting on * overflow (when the input is greater than largest uint96). * * Counterpart to Solidity's `uint96` operator. * * Requirements: * * - input must fit into 96 bits */ function toUint96(uint256 value) internal pure returns (uint96) { if (value > type(uint96).max) { revert SafeCastOverflowedUintDowncast(96, value); } return uint96(value); } /** * @dev Returns the downcasted uint88 from uint256, reverting on * overflow (when the input is greater than largest uint88). * * Counterpart to Solidity's `uint88` operator. * * Requirements: * * - input must fit into 88 bits */ function toUint88(uint256 value) internal pure returns (uint88) { if (value > type(uint88).max) { revert SafeCastOverflowedUintDowncast(88, value); } return uint88(value); } /** * @dev Returns the downcasted uint80 from uint256, reverting on * overflow (when the input is greater than largest uint80). * * Counterpart to Solidity's `uint80` operator. * * Requirements: * * - input must fit into 80 bits */ function toUint80(uint256 value) internal pure returns (uint80) { if (value > type(uint80).max) { revert SafeCastOverflowedUintDowncast(80, value); } return uint80(value); } /** * @dev Returns the downcasted uint72 from uint256, reverting on * overflow (when the input is greater than largest uint72). * * Counterpart to Solidity's `uint72` operator. * * Requirements: * * - input must fit into 72 bits */ function toUint72(uint256 value) internal pure returns (uint72) { if (value > type(uint72).max) { revert SafeCastOverflowedUintDowncast(72, value); } return uint72(value); } /** * @dev Returns the downcasted uint64 from uint256, reverting on * overflow (when the input is greater than largest uint64). * * Counterpart to Solidity's `uint64` operator. * * Requirements: * * - input must fit into 64 bits */ function toUint64(uint256 value) internal pure returns (uint64) { if (value > type(uint64).max) { revert SafeCastOverflowedUintDowncast(64, value); } return uint64(value); } /** * @dev Returns the downcasted uint56 from uint256, reverting on * overflow (when the input is greater than largest uint56). * * Counterpart to Solidity's `uint56` operator. * * Requirements: * * - input must fit into 56 bits */ function toUint56(uint256 value) internal pure returns (uint56) { if (value > type(uint56).max) { revert SafeCastOverflowedUintDowncast(56, value); } return uint56(value); } /** * @dev Returns the downcasted uint48 from uint256, reverting on * overflow (when the input is greater than largest uint48). * * Counterpart to Solidity's `uint48` operator. * * Requirements: * * - input must fit into 48 bits */ function toUint48(uint256 value) internal pure returns (uint48) { if (value > type(uint48).max) { revert SafeCastOverflowedUintDowncast(48, value); } return uint48(value); } /** * @dev Returns the downcasted uint40 from uint256, reverting on * overflow (when the input is greater than largest uint40). * * Counterpart to Solidity's `uint40` operator. * * Requirements: * * - input must fit into 40 bits */ function toUint40(uint256 value) internal pure returns (uint40) { if (value > type(uint40).max) { revert SafeCastOverflowedUintDowncast(40, value); } return uint40(value); } /** * @dev Returns the downcasted uint32 from uint256, reverting on * overflow (when the input is greater than largest uint32). * * Counterpart to Solidity's `uint32` operator. * * Requirements: * * - input must fit into 32 bits */ function toUint32(uint256 value) internal pure returns (uint32) { if (value > type(uint32).max) { revert SafeCastOverflowedUintDowncast(32, value); } return uint32(value); } /** * @dev Returns the downcasted uint24 from uint256, reverting on * overflow (when the input is greater than largest uint24). * * Counterpart to Solidity's `uint24` operator. * * Requirements: * * - input must fit into 24 bits */ function toUint24(uint256 value) internal pure returns (uint24) { if (value > type(uint24).max) { revert SafeCastOverflowedUintDowncast(24, value); } return uint24(value); } /** * @dev Returns the downcasted uint16 from uint256, reverting on * overflow (when the input is greater than largest uint16). * * Counterpart to Solidity's `uint16` operator. * * Requirements: * * - input must fit into 16 bits */ function toUint16(uint256 value) internal pure returns (uint16) { if (value > type(uint16).max) { revert SafeCastOverflowedUintDowncast(16, value); } return uint16(value); } /** * @dev Returns the downcasted uint8 from uint256, reverting on * overflow (when the input is greater than largest uint8). * * Counterpart to Solidity's `uint8` operator. * * Requirements: * * - input must fit into 8 bits */ function toUint8(uint256 value) internal pure returns (uint8) { if (value > type(uint8).max) { revert SafeCastOverflowedUintDowncast(8, value); } return uint8(value); } /** * @dev Converts a signed int256 into an unsigned uint256. * * Requirements: * * - input must be greater than or equal to 0. */ function toUint256(int256 value) internal pure returns (uint256) { if (value < 0) { revert SafeCastOverflowedIntToUint(value); } return uint256(value); } /** * @dev Returns the downcasted int248 from int256, reverting on * overflow (when the input is less than smallest int248 or * greater than largest int248). * * Counterpart to Solidity's `int248` operator. * * Requirements: * * - input must fit into 248 bits */ function toInt248(int256 value) internal pure returns (int248 downcasted) { downcasted = int248(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(248, value); } } /** * @dev Returns the downcasted int240 from int256, reverting on * overflow (when the input is less than smallest int240 or * greater than largest int240). * * Counterpart to Solidity's `int240` operator. * * Requirements: * * - input must fit into 240 bits */ function toInt240(int256 value) internal pure returns (int240 downcasted) { downcasted = int240(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(240, value); } } /** * @dev Returns the downcasted int232 from int256, reverting on * overflow (when the input is less than smallest int232 or * greater than largest int232). * * Counterpart to Solidity's `int232` operator. * * Requirements: * * - input must fit into 232 bits */ function toInt232(int256 value) internal pure returns (int232 downcasted) { downcasted = int232(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(232, value); } } /** * @dev Returns the downcasted int224 from int256, reverting on * overflow (when the input is less than smallest int224 or * greater than largest int224). * * Counterpart to Solidity's `int224` operator. * * Requirements: * * - input must fit into 224 bits */ function toInt224(int256 value) internal pure returns (int224 downcasted) { downcasted = int224(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(224, value); } } /** * @dev Returns the downcasted int216 from int256, reverting on * overflow (when the input is less than smallest int216 or * greater than largest int216). * * Counterpart to Solidity's `int216` operator. * * Requirements: * * - input must fit into 216 bits */ function toInt216(int256 value) internal pure returns (int216 downcasted) { downcasted = int216(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(216, value); } } /** * @dev Returns the downcasted int208 from int256, reverting on * overflow (when the input is less than smallest int208 or * greater than largest int208). * * Counterpart to Solidity's `int208` operator. * * Requirements: * * - input must fit into 208 bits */ function toInt208(int256 value) internal pure returns (int208 downcasted) { downcasted = int208(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(208, value); } } /** * @dev Returns the downcasted int200 from int256, reverting on * overflow (when the input is less than smallest int200 or * greater than largest int200). * * Counterpart to Solidity's `int200` operator. * * Requirements: * * - input must fit into 200 bits */ function toInt200(int256 value) internal pure returns (int200 downcasted) { downcasted = int200(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(200, value); } } /** * @dev Returns the downcasted int192 from int256, reverting on * overflow (when the input is less than smallest int192 or * greater than largest int192). * * Counterpart to Solidity's `int192` operator. * * Requirements: * * - input must fit into 192 bits */ function toInt192(int256 value) internal pure returns (int192 downcasted) { downcasted = int192(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(192, value); } } /** * @dev Returns the downcasted int184 from int256, reverting on * overflow (when the input is less than smallest int184 or * greater than largest int184). * * Counterpart to Solidity's `int184` operator. * * Requirements: * * - input must fit into 184 bits */ function toInt184(int256 value) internal pure returns (int184 downcasted) { downcasted = int184(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(184, value); } } /** * @dev Returns the downcasted int176 from int256, reverting on * overflow (when the input is less than smallest int176 or * greater than largest int176). * * Counterpart to Solidity's `int176` operator. * * Requirements: * * - input must fit into 176 bits */ function toInt176(int256 value) internal pure returns (int176 downcasted) { downcasted = int176(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(176, value); } } /** * @dev Returns the downcasted int168 from int256, reverting on * overflow (when the input is less than smallest int168 or * greater than largest int168). * * Counterpart to Solidity's `int168` operator. * * Requirements: * * - input must fit into 168 bits */ function toInt168(int256 value) internal pure returns (int168 downcasted) { downcasted = int168(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(168, value); } } /** * @dev Returns the downcasted int160 from int256, reverting on * overflow (when the input is less than smallest int160 or * greater than largest int160). * * Counterpart to Solidity's `int160` operator. * * Requirements: * * - input must fit into 160 bits */ function toInt160(int256 value) internal pure returns (int160 downcasted) { downcasted = int160(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(160, value); } } /** * @dev Returns the downcasted int152 from int256, reverting on * overflow (when the input is less than smallest int152 or * greater than largest int152). * * Counterpart to Solidity's `int152` operator. * * Requirements: * * - input must fit into 152 bits */ function toInt152(int256 value) internal pure returns (int152 downcasted) { downcasted = int152(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(152, value); } } /** * @dev Returns the downcasted int144 from int256, reverting on * overflow (when the input is less than smallest int144 or * greater than largest int144). * * Counterpart to Solidity's `int144` operator. * * Requirements: * * - input must fit into 144 bits */ function toInt144(int256 value) internal pure returns (int144 downcasted) { downcasted = int144(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(144, value); } } /** * @dev Returns the downcasted int136 from int256, reverting on * overflow (when the input is less than smallest int136 or * greater than largest int136). * * Counterpart to Solidity's `int136` operator. * * Requirements: * * - input must fit into 136 bits */ function toInt136(int256 value) internal pure returns (int136 downcasted) { downcasted = int136(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(136, value); } } /** * @dev Returns the downcasted int128 from int256, reverting on * overflow (when the input is less than smallest int128 or * greater than largest int128). * * Counterpart to Solidity's `int128` operator. * * Requirements: * * - input must fit into 128 bits */ function toInt128(int256 value) internal pure returns (int128 downcasted) { downcasted = int128(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(128, value); } } /** * @dev Returns the downcasted int120 from int256, reverting on * overflow (when the input is less than smallest int120 or * greater than largest int120). * * Counterpart to Solidity's `int120` operator. * * Requirements: * * - input must fit into 120 bits */ function toInt120(int256 value) internal pure returns (int120 downcasted) { downcasted = int120(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(120, value); } } /** * @dev Returns the downcasted int112 from int256, reverting on * overflow (when the input is less than smallest int112 or * greater than largest int112). * * Counterpart to Solidity's `int112` operator. * * Requirements: * * - input must fit into 112 bits */ function toInt112(int256 value) internal pure returns (int112 downcasted) { downcasted = int112(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(112, value); } } /** * @dev Returns the downcasted int104 from int256, reverting on * overflow (when the input is less than smallest int104 or * greater than largest int104). * * Counterpart to Solidity's `int104` operator. * * Requirements: * * - input must fit into 104 bits */ function toInt104(int256 value) internal pure returns (int104 downcasted) { downcasted = int104(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(104, value); } } /** * @dev Returns the downcasted int96 from int256, reverting on * overflow (when the input is less than smallest int96 or * greater than largest int96). * * Counterpart to Solidity's `int96` operator. * * Requirements: * * - input must fit into 96 bits */ function toInt96(int256 value) internal pure returns (int96 downcasted) { downcasted = int96(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(96, value); } } /** * @dev Returns the downcasted int88 from int256, reverting on * overflow (when the input is less than smallest int88 or * greater than largest int88). * * Counterpart to Solidity's `int88` operator. * * Requirements: * * - input must fit into 88 bits */ function toInt88(int256 value) internal pure returns (int88 downcasted) { downcasted = int88(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(88, value); } } /** * @dev Returns the downcasted int80 from int256, reverting on * overflow (when the input is less than smallest int80 or * greater than largest int80). * * Counterpart to Solidity's `int80` operator. * * Requirements: * * - input must fit into 80 bits */ function toInt80(int256 value) internal pure returns (int80 downcasted) { downcasted = int80(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(80, value); } } /** * @dev Returns the downcasted int72 from int256, reverting on * overflow (when the input is less than smallest int72 or * greater than largest int72). * * Counterpart to Solidity's `int72` operator. * * Requirements: * * - input must fit into 72 bits */ function toInt72(int256 value) internal pure returns (int72 downcasted) { downcasted = int72(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(72, value); } } /** * @dev Returns the downcasted int64 from int256, reverting on * overflow (when the input is less than smallest int64 or * greater than largest int64). * * Counterpart to Solidity's `int64` operator. * * Requirements: * * - input must fit into 64 bits */ function toInt64(int256 value) internal pure returns (int64 downcasted) { downcasted = int64(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(64, value); } } /** * @dev Returns the downcasted int56 from int256, reverting on * overflow (when the input is less than smallest int56 or * greater than largest int56). * * Counterpart to Solidity's `int56` operator. * * Requirements: * * - input must fit into 56 bits */ function toInt56(int256 value) internal pure returns (int56 downcasted) { downcasted = int56(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(56, value); } } /** * @dev Returns the downcasted int48 from int256, reverting on * overflow (when the input is less than smallest int48 or * greater than largest int48). * * Counterpart to Solidity's `int48` operator. * * Requirements: * * - input must fit into 48 bits */ function toInt48(int256 value) internal pure returns (int48 downcasted) { downcasted = int48(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(48, value); } } /** * @dev Returns the downcasted int40 from int256, reverting on * overflow (when the input is less than smallest int40 or * greater than largest int40). * * Counterpart to Solidity's `int40` operator. * * Requirements: * * - input must fit into 40 bits */ function toInt40(int256 value) internal pure returns (int40 downcasted) { downcasted = int40(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(40, value); } } /** * @dev Returns the downcasted int32 from int256, reverting on * overflow (when the input is less than smallest int32 or * greater than largest int32). * * Counterpart to Solidity's `int32` operator. * * Requirements: * * - input must fit into 32 bits */ function toInt32(int256 value) internal pure returns (int32 downcasted) { downcasted = int32(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(32, value); } } /** * @dev Returns the downcasted int24 from int256, reverting on * overflow (when the input is less than smallest int24 or * greater than largest int24). * * Counterpart to Solidity's `int24` operator. * * Requirements: * * - input must fit into 24 bits */ function toInt24(int256 value) internal pure returns (int24 downcasted) { downcasted = int24(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(24, value); } } /** * @dev Returns the downcasted int16 from int256, reverting on * overflow (when the input is less than smallest int16 or * greater than largest int16). * * Counterpart to Solidity's `int16` operator. * * Requirements: * * - input must fit into 16 bits */ function toInt16(int256 value) internal pure returns (int16 downcasted) { downcasted = int16(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(16, value); } } /** * @dev Returns the downcasted int8 from int256, reverting on * overflow (when the input is less than smallest int8 or * greater than largest int8). * * Counterpart to Solidity's `int8` operator. * * Requirements: * * - input must fit into 8 bits */ function toInt8(int256 value) internal pure returns (int8 downcasted) { downcasted = int8(value); if (downcasted != value) { revert SafeCastOverflowedIntDowncast(8, value); } } /** * @dev Converts an unsigned uint256 into a signed int256. * * Requirements: * * - input must be less than or equal to maxInt256. */ function toInt256(uint256 value) internal pure returns (int256) { // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive if (value > uint256(type(int256).max)) { revert SafeCastOverflowedUintToInt(value); } return int256(value); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/math/SignedMath.sol) pragma solidity ^0.8.20; /** * @dev Standard signed math utilities missing in the Solidity language. */ library SignedMath { /** * @dev Returns the largest of two signed numbers. */ function max(int256 a, int256 b) internal pure returns (int256) { return a > b ? a : b; } /** * @dev Returns the smallest of two signed numbers. */ function min(int256 a, int256 b) internal pure returns (int256) { return a < b ? a : b; } /** * @dev Returns the average of two signed numbers without overflow. * The result is rounded towards zero. */ function average(int256 a, int256 b) internal pure returns (int256) { // Formula from the book "Hacker's Delight" int256 x = (a & b) + ((a ^ b) >> 1); return x + (int256(uint256(x) >> 255) & (a ^ b)); } /** * @dev Returns the absolute unsigned value of a signed value. */ function abs(int256 n) internal pure returns (uint256) { unchecked { // must be unchecked in order to support `n = type(int256).min` return uint256(n >= 0 ? n : -n); } } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol) pragma solidity ^0.8.20; import {Math} from "./math/Math.sol"; import {SignedMath} from "./math/SignedMath.sol"; /** * @dev String operations. */ library Strings { bytes16 private constant HEX_DIGITS = "0123456789abcdef"; uint8 private constant ADDRESS_LENGTH = 20; /** * @dev The `value` string doesn't fit in the specified `length`. */ error StringsInsufficientHexLength(uint256 value, uint256 length); /** * @dev Converts a `uint256` to its ASCII `string` decimal representation. */ function toString(uint256 value) internal pure returns (string memory) { unchecked { uint256 length = Math.log10(value) + 1; string memory buffer = new string(length); uint256 ptr; /// @solidity memory-safe-assembly assembly { ptr := add(buffer, add(32, length)) } while (true) { ptr--; /// @solidity memory-safe-assembly assembly { mstore8(ptr, byte(mod(value, 10), HEX_DIGITS)) } value /= 10; if (value == 0) break; } return buffer; } } /** * @dev Converts a `int256` to its ASCII `string` decimal representation. */ function toStringSigned(int256 value) internal pure returns (string memory) { return string.concat(value < 0 ? "-" : "", toString(SignedMath.abs(value))); } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation. */ function toHexString(uint256 value) internal pure returns (string memory) { unchecked { return toHexString(value, Math.log256(value) + 1); } } /** * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length. */ function toHexString(uint256 value, uint256 length) internal pure returns (string memory) { uint256 localValue = value; bytes memory buffer = new bytes(2 * length + 2); buffer[0] = "0"; buffer[1] = "x"; for (uint256 i = 2 * length + 1; i > 1; --i) { buffer[i] = HEX_DIGITS[localValue & 0xf]; localValue >>= 4; } if (localValue != 0) { revert StringsInsufficientHexLength(value, length); } return string(buffer); } /** * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal * representation. */ function toHexString(address addr) internal pure returns (string memory) { return toHexString(uint256(uint160(addr)), ADDRESS_LENGTH); } /** * @dev Returns true if the two strings are equal. */ function equal(string memory a, string memory b) internal pure returns (bool) { return bytes(a).length == bytes(b).length && keccak256(bytes(a)) == keccak256(bytes(b)); } }
// SPDX-License-Identifier: MIT // OpenZeppelin Contracts (last updated v5.0.0) (utils/structs/EnumerableSet.sol) // This file was procedurally generated from scripts/generate/templates/EnumerableSet.js. pragma solidity ^0.8.20; /** * @dev Library for managing * https://en.wikipedia.org/wiki/Set_(abstract_data_type)[sets] of primitive * types. * * Sets have the following properties: * * - Elements are added, removed, and checked for existence in constant time * (O(1)). * - Elements are enumerated in O(n). No guarantees are made on the ordering. * * ```solidity * contract Example { * // Add the library methods * using EnumerableSet for EnumerableSet.AddressSet; * * // Declare a set state variable * EnumerableSet.AddressSet private mySet; * } * ``` * * As of v3.3.0, sets of type `bytes32` (`Bytes32Set`), `address` (`AddressSet`) * and `uint256` (`UintSet`) are supported. * * [WARNING] * ==== * Trying to delete such a structure from storage will likely result in data corruption, rendering the structure * unusable. * See https://github.com/ethereum/solidity/pull/11843[ethereum/solidity#11843] for more info. * * In order to clean an EnumerableSet, you can either remove all elements one by one or create a fresh instance using an * array of EnumerableSet. * ==== */ library EnumerableSet { // To implement this library for multiple types with as little code // repetition as possible, we write it in terms of a generic Set type with // bytes32 values. // The Set implementation uses private functions, and user-facing // implementations (such as AddressSet) are just wrappers around the // underlying Set. // This means that we can only create new EnumerableSets for types that fit // in bytes32. struct Set { // Storage of set values bytes32[] _values; // Position is the index of the value in the `values` array plus 1. // Position 0 is used to mean a value is not in the set. mapping(bytes32 value => uint256) _positions; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function _add(Set storage set, bytes32 value) private returns (bool) { if (!_contains(set, value)) { set._values.push(value); // The value is stored at length-1, but we add 1 to all indexes // and use 0 as a sentinel value set._positions[value] = set._values.length; return true; } else { return false; } } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function _remove(Set storage set, bytes32 value) private returns (bool) { // We cache the value's position to prevent multiple reads from the same storage slot uint256 position = set._positions[value]; if (position != 0) { // Equivalent to contains(set, value) // To delete an element from the _values array in O(1), we swap the element to delete with the last one in // the array, and then remove the last element (sometimes called as 'swap and pop'). // This modifies the order of the array, as noted in {at}. uint256 valueIndex = position - 1; uint256 lastIndex = set._values.length - 1; if (valueIndex != lastIndex) { bytes32 lastValue = set._values[lastIndex]; // Move the lastValue to the index where the value to delete is set._values[valueIndex] = lastValue; // Update the tracked position of the lastValue (that was just moved) set._positions[lastValue] = position; } // Delete the slot where the moved value was stored set._values.pop(); // Delete the tracked position for the deleted slot delete set._positions[value]; return true; } else { return false; } } /** * @dev Returns true if the value is in the set. O(1). */ function _contains(Set storage set, bytes32 value) private view returns (bool) { return set._positions[value] != 0; } /** * @dev Returns the number of values on the set. O(1). */ function _length(Set storage set) private view returns (uint256) { return set._values.length; } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function _at(Set storage set, uint256 index) private view returns (bytes32) { return set._values[index]; } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function _values(Set storage set) private view returns (bytes32[] memory) { return set._values; } // Bytes32Set struct Bytes32Set { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _add(set._inner, value); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(Bytes32Set storage set, bytes32 value) internal returns (bool) { return _remove(set._inner, value); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(Bytes32Set storage set, bytes32 value) internal view returns (bool) { return _contains(set._inner, value); } /** * @dev Returns the number of values in the set. O(1). */ function length(Bytes32Set storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(Bytes32Set storage set, uint256 index) internal view returns (bytes32) { return _at(set._inner, index); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(Bytes32Set storage set) internal view returns (bytes32[] memory) { bytes32[] memory store = _values(set._inner); bytes32[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // AddressSet struct AddressSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(AddressSet storage set, address value) internal returns (bool) { return _add(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(AddressSet storage set, address value) internal returns (bool) { return _remove(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(AddressSet storage set, address value) internal view returns (bool) { return _contains(set._inner, bytes32(uint256(uint160(value)))); } /** * @dev Returns the number of values in the set. O(1). */ function length(AddressSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(AddressSet storage set, uint256 index) internal view returns (address) { return address(uint160(uint256(_at(set._inner, index)))); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(AddressSet storage set) internal view returns (address[] memory) { bytes32[] memory store = _values(set._inner); address[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } // UintSet struct UintSet { Set _inner; } /** * @dev Add a value to a set. O(1). * * Returns true if the value was added to the set, that is if it was not * already present. */ function add(UintSet storage set, uint256 value) internal returns (bool) { return _add(set._inner, bytes32(value)); } /** * @dev Removes a value from a set. O(1). * * Returns true if the value was removed from the set, that is if it was * present. */ function remove(UintSet storage set, uint256 value) internal returns (bool) { return _remove(set._inner, bytes32(value)); } /** * @dev Returns true if the value is in the set. O(1). */ function contains(UintSet storage set, uint256 value) internal view returns (bool) { return _contains(set._inner, bytes32(value)); } /** * @dev Returns the number of values in the set. O(1). */ function length(UintSet storage set) internal view returns (uint256) { return _length(set._inner); } /** * @dev Returns the value stored at position `index` in the set. O(1). * * Note that there are no guarantees on the ordering of values inside the * array, and it may change when more values are added or removed. * * Requirements: * * - `index` must be strictly less than {length}. */ function at(UintSet storage set, uint256 index) internal view returns (uint256) { return uint256(_at(set._inner, index)); } /** * @dev Return the entire set in an array * * WARNING: This operation will copy the entire storage to memory, which can be quite expensive. This is designed * to mostly be used by view accessors that are queried without any gas fees. Developers should keep in mind that * this function has an unbounded cost, and using it as part of a state-changing function may render the function * uncallable if the set grows to a point where copying to memory consumes too much gas to fit in a block. */ function values(UintSet storage set) internal view returns (uint256[] memory) { bytes32[] memory store = _values(set._inner); uint256[] memory result; /// @solidity memory-safe-assembly assembly { result := store } return result; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // solhint-disable no-inline-assembly /// @dev A subset copied from the following contracts: /// /// + `balancer-labs/v2-solidity-utils/contracts/helpers/WordCodec.sol` /// + `balancer-labs/v2-solidity-utils/contracts/helpers/WordCodecHelpers.sol` library WordCodec { /// @dev Inserts an unsigned integer of bitLength, shifted by an offset, into a 256 bit word, /// replacing the old value. Returns the new word. function insertUint( bytes32 word, uint256 value, uint256 offset, uint256 bitLength ) internal pure returns (bytes32 result) { // Equivalent to: // uint256 mask = (1 << bitLength) - 1; // bytes32 clearedWord = bytes32(uint256(word) & ~(mask << offset)); // result = clearedWord | bytes32(value << offset); assembly { let mask := sub(shl(bitLength, 1), 1) let clearedWord := and(word, not(shl(offset, mask))) result := or(clearedWord, shl(offset, value)) } } /// @dev Decodes and returns an unsigned integer with `bitLength` bits, shifted by an offset, from a 256 bit word. function decodeUint( bytes32 word, uint256 offset, uint256 bitLength ) internal pure returns (uint256 result) { // Equivalent to: // result = uint256(word >> offset) & ((1 << bitLength) - 1); assembly { result := and(shr(offset, word), sub(shl(bitLength, 1), 1)) } } /// @dev Inserts a signed integer shifted by an offset into a 256 bit word, replacing the old value. Returns /// the new word. /// /// Assumes `value` can be represented using `bitLength` bits. function insertInt( bytes32 word, int256 value, uint256 offset, uint256 bitLength ) internal pure returns (bytes32) { unchecked { uint256 mask = (1 << bitLength) - 1; bytes32 clearedWord = bytes32(uint256(word) & ~(mask << offset)); // Integer values need masking to remove the upper bits of negative values. return clearedWord | bytes32((uint256(value) & mask) << offset); } } /// @dev Decodes and returns a signed integer with `bitLength` bits, shifted by an offset, from a 256 bit word. function decodeInt( bytes32 word, uint256 offset, uint256 bitLength ) internal pure returns (int256 result) { unchecked { int256 maxInt = int256((1 << (bitLength - 1)) - 1); uint256 mask = (1 << bitLength) - 1; int256 value = int256(uint256(word >> offset) & mask); // In case the decoded value is greater than the max positive integer that can be represented with bitLength // bits, we know it was originally a negative integer. Therefore, we mask it to restore the sign in the 256 bit // representation. // // Equivalent to: // result = value > maxInt ? (value | int256(~mask)) : value; assembly { result := or(mul(gt(value, maxInt), not(mask)), value) } } } /// @dev Decodes and returns a boolean shifted by an offset from a 256 bit word. function decodeBool(bytes32 word, uint256 offset) internal pure returns (bool result) { // Equivalent to: // result = (uint256(word >> offset) & 1) == 1; assembly { result := and(shr(offset, word), 1) } } /// @dev Inserts a boolean value shifted by an offset into a 256 bit word, replacing the old value. Returns the new /// word. function insertBool( bytes32 word, bool value, uint256 offset ) internal pure returns (bytes32 result) { // Equivalent to: // bytes32 clearedWord = bytes32(uint256(word) & ~(1 << offset)); // bytes32 referenceInsertBool = clearedWord | bytes32(uint256(value ? 1 : 0) << offset); assembly { let clearedWord := and(word, not(shl(offset, 1))) result := or(clearedWord, shl(offset, value)) } } function clearWordAtPosition( bytes32 word, uint256 offset, uint256 bitLength ) internal pure returns (bytes32 clearedWord) { unchecked { uint256 mask = (1 << bitLength) - 1; clearedWord = bytes32(uint256(word) & ~(mask << offset)); } } }
// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; /******************************************************************************\ * Author: Nick Mudge <[email protected]>, Twitter/Github: @mudgen * EIP-2535 Diamonds * * Implementation of a diamond. /******************************************************************************/ import { LibDiamond } from "./libraries/LibDiamond.sol"; import { IDiamondCut } from "./interfaces/IDiamondCut.sol"; import { IDiamondLoupe } from "./interfaces/IDiamondLoupe.sol"; import { IERC173 } from "./interfaces/IERC173.sol"; import { IERC165 } from "./interfaces/IERC165.sol"; // solhint-disable no-complex-fallback // solhint-disable no-inline-assembly // solhint-disable no-empty-blocks // When no function exists for function called error FunctionNotFound(bytes4 _functionSelector); // This is used in diamond constructor // more arguments are added to this struct // this avoids stack too deep errors struct DiamondArgs { address owner; address init; bytes initCalldata; } contract Diamond { constructor(IDiamondCut.FacetCut[] memory _diamondCut, DiamondArgs memory _args) payable { LibDiamond.setContractOwner(_args.owner); LibDiamond.diamondCut(_diamondCut, _args.init, _args.initCalldata); // Code can be added here to perform actions and set state variables. } // Find facet for function that is called and execute the // function if a facet is found and return any value. fallback() external payable { LibDiamond.DiamondStorage storage ds; bytes32 position = LibDiamond.DIAMOND_STORAGE_POSITION; // get diamond storage assembly { ds.slot := position } // get facet from function selector address facet = ds.facetAddressAndSelectorPosition[msg.sig].facetAddress; if (facet == address(0)) { revert FunctionNotFound(msg.sig); } // Execute external function from facet using delegatecall and return any value. assembly { // copy function selector and any arguments calldatacopy(0, 0, calldatasize()) // execute function call using the facet let result := delegatecall(gas(), facet, 0, calldatasize(), 0, 0) // get any return value returndatacopy(0, 0, returndatasize()) // return any return value or error back to the caller switch result case 0 { revert(0, returndatasize()) } default { return(0, returndatasize()) } } } receive() external payable {} }
// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; /******************************************************************************\ * Author: Nick Mudge <[email protected]>, Twitter/Github: @mudgen * EIP-2535 Diamonds /******************************************************************************/ import { IDiamondCut } from "../interfaces/IDiamondCut.sol"; import { LibDiamond } from "../libraries/LibDiamond.sol"; // Remember to add the loupe functions from DiamondLoupeFacet to the diamond. // The loupe functions are required by the EIP2535 Diamonds standard contract DiamondCutFacet is IDiamondCut { /// @notice Add/replace/remove any number of functions and optionally execute /// a function with delegatecall /// @param _diamondCut Contains the facet addresses and function selectors /// @param _init The address of the contract or facet to execute _calldata /// @param _calldata A function call, including function selector and arguments /// _calldata is executed with delegatecall on _init function diamondCut( FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata ) external override { LibDiamond.enforceIsContractOwner(); LibDiamond.diamondCut(_diamondCut, _init, _calldata); } }
// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; /******************************************************************************\ * Author: Nick Mudge <[email protected]>, Twitter/Github: @mudgen * EIP-2535 Diamonds /******************************************************************************/ // The functions in DiamondLoupeFacet MUST be added to a diamond. // The EIP-2535 Diamond standard requires these functions. import { LibDiamond } from "../libraries/LibDiamond.sol"; import { IDiamondLoupe } from "../interfaces/IDiamondLoupe.sol"; import { IERC165 } from "../interfaces/IERC165.sol"; // solhint-disable no-inline-assembly contract DiamondLoupeFacet is IDiamondLoupe, IERC165 { // Diamond Loupe Functions //////////////////////////////////////////////////////////////////// /// These functions are expected to be called frequently by tools. // // struct Facet { // address facetAddress; // bytes4[] functionSelectors; // } /// @notice Gets all facets and their selectors. /// @return facets_ Facet function facets() external view override returns (Facet[] memory facets_) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); uint256 selectorCount = ds.selectors.length; // create an array set to the maximum size possible facets_ = new Facet[](selectorCount); // create an array for counting the number of selectors for each facet uint16[] memory numFacetSelectors = new uint16[](selectorCount); // total number of facets uint256 numFacets; // loop through function selectors for (uint256 selectorIndex; selectorIndex < selectorCount; selectorIndex++) { bytes4 selector = ds.selectors[selectorIndex]; address facetAddress_ = ds.facetAddressAndSelectorPosition[selector].facetAddress; bool continueLoop = false; // find the functionSelectors array for selector and add selector to it for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) { if (facets_[facetIndex].facetAddress == facetAddress_) { facets_[facetIndex].functionSelectors[numFacetSelectors[facetIndex]] = selector; numFacetSelectors[facetIndex]++; continueLoop = true; break; } } // if functionSelectors array exists for selector then continue loop if (continueLoop) { continueLoop = false; continue; } // create a new functionSelectors array for selector facets_[numFacets].facetAddress = facetAddress_; facets_[numFacets].functionSelectors = new bytes4[](selectorCount); facets_[numFacets].functionSelectors[0] = selector; numFacetSelectors[numFacets] = 1; numFacets++; } for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) { uint256 numSelectors = numFacetSelectors[facetIndex]; bytes4[] memory selectors = facets_[facetIndex].functionSelectors; // setting the number of selectors assembly { mstore(selectors, numSelectors) } } // setting the number of facets assembly { mstore(facets_, numFacets) } } /// @notice Gets all the function selectors supported by a specific facet. /// @param _facet The facet address. /// @return _facetFunctionSelectors The selectors associated with a facet address. function facetFunctionSelectors(address _facet) external view override returns (bytes4[] memory _facetFunctionSelectors) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); uint256 selectorCount = ds.selectors.length; uint256 numSelectors; _facetFunctionSelectors = new bytes4[](selectorCount); // loop through function selectors for (uint256 selectorIndex; selectorIndex < selectorCount; selectorIndex++) { bytes4 selector = ds.selectors[selectorIndex]; address facetAddress_ = ds.facetAddressAndSelectorPosition[selector].facetAddress; if (_facet == facetAddress_) { _facetFunctionSelectors[numSelectors] = selector; numSelectors++; } } // Set the number of selectors in the array assembly { mstore(_facetFunctionSelectors, numSelectors) } } /// @notice Get all the facet addresses used by a diamond. /// @return facetAddresses_ function facetAddresses() external view override returns (address[] memory facetAddresses_) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); uint256 selectorCount = ds.selectors.length; // create an array set to the maximum size possible facetAddresses_ = new address[](selectorCount); uint256 numFacets; // loop through function selectors for (uint256 selectorIndex; selectorIndex < selectorCount; selectorIndex++) { bytes4 selector = ds.selectors[selectorIndex]; address facetAddress_ = ds.facetAddressAndSelectorPosition[selector].facetAddress; bool continueLoop = false; // see if we have collected the address already and break out of loop if we have for (uint256 facetIndex; facetIndex < numFacets; facetIndex++) { if (facetAddress_ == facetAddresses_[facetIndex]) { continueLoop = true; break; } } // continue loop if we already have the address if (continueLoop) { continueLoop = false; continue; } // include address facetAddresses_[numFacets] = facetAddress_; numFacets++; } // Set the number of facet addresses in the array assembly { mstore(facetAddresses_, numFacets) } } /// @notice Gets the facet address that supports the given selector. /// @dev If facet is not found return address(0). /// @param _functionSelector The function selector. /// @return facetAddress_ The facet address. function facetAddress(bytes4 _functionSelector) external view override returns (address facetAddress_) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); facetAddress_ = ds.facetAddressAndSelectorPosition[_functionSelector].facetAddress; } // This implements ERC-165. function supportsInterface(bytes4 _interfaceId) external view override returns (bool) { LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); return ds.supportedInterfaces[_interfaceId]; } }
// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; /******************************************************************************\ * Author: Nick Mudge <[email protected]>, Twitter/Github: @mudgen * EIP-2535 Diamonds /******************************************************************************/ import { LibDiamond } from "../libraries/LibDiamond.sol"; import { IERC173 } from "../interfaces/IERC173.sol"; contract OwnershipFacet is IERC173 { function transferOwnership(address _newOwner) external override { LibDiamond.enforceIsContractOwner(); LibDiamond.setContractOwner(_newOwner); } function owner() external view override returns (address owner_) { owner_ = LibDiamond.contractOwner(); } }
// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; /******************************************************************************\ * Author: Nick Mudge <[email protected]>, Twitter/Github: @mudgen * EIP-2535 Diamonds /******************************************************************************/ interface IDiamond { enum FacetCutAction { Add, Replace, Remove } // Add=0, Replace=1, Remove=2 struct FacetCut { address facetAddress; FacetCutAction action; bytes4[] functionSelectors; } event DiamondCut(FacetCut[] _diamondCut, address _init, bytes _calldata); }
// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; /******************************************************************************\ * Author: Nick Mudge <[email protected]>, Twitter/Github: @mudgen * EIP-2535 Diamonds /******************************************************************************/ import { IDiamond } from "./IDiamond.sol"; interface IDiamondCut is IDiamond { /// @notice Add/replace/remove any number of functions and optionally execute /// a function with delegatecall /// @param _diamondCut Contains the facet addresses and function selectors /// @param _init The address of the contract or facet to execute _calldata /// @param _calldata A function call, including function selector and arguments /// _calldata is executed with delegatecall on _init function diamondCut( FacetCut[] calldata _diamondCut, address _init, bytes calldata _calldata ) external; }
// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; /******************************************************************************\ * Author: Nick Mudge <[email protected]>, Twitter/Github: @mudgen * EIP-2535 Diamonds /******************************************************************************/ // A loupe is a small magnifying glass used to look at diamonds. // These functions look at diamonds interface IDiamondLoupe { /// These functions are expected to be called frequently /// by tools. struct Facet { address facetAddress; bytes4[] functionSelectors; } /// @notice Gets all facet addresses and their four byte function selectors. /// @return facets_ Facet function facets() external view returns (Facet[] memory facets_); /// @notice Gets all the function selectors supported by a specific facet. /// @param _facet The facet address. /// @return facetFunctionSelectors_ function facetFunctionSelectors(address _facet) external view returns (bytes4[] memory facetFunctionSelectors_); /// @notice Get all the facet addresses used by a diamond. /// @return facetAddresses_ function facetAddresses() external view returns (address[] memory facetAddresses_); /// @notice Gets the facet that supports the given selector. /// @dev If facet is not found return address(0). /// @param _functionSelector The function selector. /// @return facetAddress_ The facet address. function facetAddress(bytes4 _functionSelector) external view returns (address facetAddress_); }
// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; interface IERC165 { /// @notice Query if a contract implements an interface /// @param interfaceId The interface identifier, as specified in ERC-165 /// @dev Interface identification is specified in ERC-165. This function /// uses less than 30,000 gas. /// @return `true` if the contract implements `interfaceID` and /// `interfaceID` is not 0xffffffff, `false` otherwise function supportsInterface(bytes4 interfaceId) external view returns (bool); }
// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; /// @title ERC-173 Contract Ownership Standard /// Note: the ERC-165 identifier for this interface is 0x7f5828d0 /* is ERC165 */ interface IERC173 { /// @dev This emits when ownership of a contract changes. event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); /// @notice Get the address of the owner /// @return owner_ The address of the owner. function owner() external view returns (address owner_); /// @notice Set the address of the new owner of the contract /// @dev Set _newOwner to address(0) to renounce any ownership. /// @param _newOwner The address of the new owner of the contract function transferOwnership(address _newOwner) external; }
// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; /******************************************************************************\ * Author: Nick Mudge <[email protected]>, Twitter/Github: @mudgen * EIP-2535 Diamonds /******************************************************************************/ import { IDiamond } from "../interfaces/IDiamond.sol"; import { IDiamondCut } from "../interfaces/IDiamondCut.sol"; // solhint-disable avoid-low-level-calls // solhint-disable no-inline-assembly // Remember to add the loupe functions from DiamondLoupeFacet to the diamond. // The loupe functions are required by the EIP2535 Diamonds standard error NoSelectorsGivenToAdd(); error NotContractOwner(address _user, address _contractOwner); error NoSelectorsProvidedForFacetForCut(address _facetAddress); error CannotAddSelectorsToZeroAddress(bytes4[] _selectors); error NoBytecodeAtAddress(address _contractAddress, string _message); error IncorrectFacetCutAction(uint8 _action); error CannotAddFunctionToDiamondThatAlreadyExists(bytes4 _selector); error CannotReplaceFunctionsFromFacetWithZeroAddress(bytes4[] _selectors); error CannotReplaceImmutableFunction(bytes4 _selector); error CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(bytes4 _selector); error CannotReplaceFunctionThatDoesNotExists(bytes4 _selector); error RemoveFacetAddressMustBeZeroAddress(address _facetAddress); error CannotRemoveFunctionThatDoesNotExist(bytes4 _selector); error CannotRemoveImmutableFunction(bytes4 _selector); error InitializationFunctionReverted(address _initializationContractAddress, bytes _calldata); library LibDiamond { bytes32 internal constant DIAMOND_STORAGE_POSITION = keccak256("diamond.standard.diamond.storage"); struct FacetAddressAndSelectorPosition { address facetAddress; uint16 selectorPosition; } struct DiamondStorage { // function selector => facet address and selector position in selectors array mapping(bytes4 => FacetAddressAndSelectorPosition) facetAddressAndSelectorPosition; bytes4[] selectors; mapping(bytes4 => bool) supportedInterfaces; // owner of the contract address contractOwner; } function diamondStorage() internal pure returns (DiamondStorage storage ds) { bytes32 position = DIAMOND_STORAGE_POSITION; assembly { ds.slot := position } } event OwnershipTransferred(address indexed previousOwner, address indexed newOwner); function setContractOwner(address _newOwner) internal { DiamondStorage storage ds = diamondStorage(); address previousOwner = ds.contractOwner; ds.contractOwner = _newOwner; emit OwnershipTransferred(previousOwner, _newOwner); } function contractOwner() internal view returns (address contractOwner_) { contractOwner_ = diamondStorage().contractOwner; } function enforceIsContractOwner() internal view { if (msg.sender != diamondStorage().contractOwner) { revert NotContractOwner(msg.sender, diamondStorage().contractOwner); } } event DiamondCut(IDiamondCut.FacetCut[] _diamondCut, address _init, bytes _calldata); // Internal function version of diamondCut function diamondCut( IDiamondCut.FacetCut[] memory _diamondCut, address _init, bytes memory _calldata ) internal { for (uint256 facetIndex; facetIndex < _diamondCut.length; facetIndex++) { bytes4[] memory functionSelectors = _diamondCut[facetIndex].functionSelectors; address facetAddress = _diamondCut[facetIndex].facetAddress; if (functionSelectors.length == 0) { revert NoSelectorsProvidedForFacetForCut(facetAddress); } IDiamondCut.FacetCutAction action = _diamondCut[facetIndex].action; if (action == IDiamond.FacetCutAction.Add) { addFunctions(facetAddress, functionSelectors); } else if (action == IDiamond.FacetCutAction.Replace) { replaceFunctions(facetAddress, functionSelectors); } else if (action == IDiamond.FacetCutAction.Remove) { removeFunctions(facetAddress, functionSelectors); } else { revert IncorrectFacetCutAction(uint8(action)); } } emit DiamondCut(_diamondCut, _init, _calldata); initializeDiamondCut(_init, _calldata); } function addFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { if (_facetAddress == address(0)) { revert CannotAddSelectorsToZeroAddress(_functionSelectors); } DiamondStorage storage ds = diamondStorage(); uint16 selectorCount = uint16(ds.selectors.length); enforceHasContractCode(_facetAddress, "LibDiamondCut: Add facet has no code"); for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.facetAddressAndSelectorPosition[selector].facetAddress; if (oldFacetAddress != address(0)) { revert CannotAddFunctionToDiamondThatAlreadyExists(selector); } ds.facetAddressAndSelectorPosition[selector] = FacetAddressAndSelectorPosition(_facetAddress, selectorCount); ds.selectors.push(selector); selectorCount++; } } function replaceFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { DiamondStorage storage ds = diamondStorage(); if (_facetAddress == address(0)) { revert CannotReplaceFunctionsFromFacetWithZeroAddress(_functionSelectors); } enforceHasContractCode(_facetAddress, "LibDiamondCut: Replace facet has no code"); for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; address oldFacetAddress = ds.facetAddressAndSelectorPosition[selector].facetAddress; // can't replace immutable functions -- functions defined directly in the diamond in this case if (oldFacetAddress == address(this)) { revert CannotReplaceImmutableFunction(selector); } if (oldFacetAddress == _facetAddress) { revert CannotReplaceFunctionWithTheSameFunctionFromTheSameFacet(selector); } if (oldFacetAddress == address(0)) { revert CannotReplaceFunctionThatDoesNotExists(selector); } // replace old facet address ds.facetAddressAndSelectorPosition[selector].facetAddress = _facetAddress; } } function removeFunctions(address _facetAddress, bytes4[] memory _functionSelectors) internal { DiamondStorage storage ds = diamondStorage(); uint256 selectorCount = ds.selectors.length; if (_facetAddress != address(0)) { revert RemoveFacetAddressMustBeZeroAddress(_facetAddress); } for (uint256 selectorIndex; selectorIndex < _functionSelectors.length; selectorIndex++) { bytes4 selector = _functionSelectors[selectorIndex]; FacetAddressAndSelectorPosition memory oldFacetAddressAndSelectorPosition = ds.facetAddressAndSelectorPosition[ selector ]; if (oldFacetAddressAndSelectorPosition.facetAddress == address(0)) { revert CannotRemoveFunctionThatDoesNotExist(selector); } // can't remove immutable functions -- functions defined directly in the diamond if (oldFacetAddressAndSelectorPosition.facetAddress == address(this)) { revert CannotRemoveImmutableFunction(selector); } // replace selector with last selector selectorCount--; if (oldFacetAddressAndSelectorPosition.selectorPosition != selectorCount) { bytes4 lastSelector = ds.selectors[selectorCount]; ds.selectors[oldFacetAddressAndSelectorPosition.selectorPosition] = lastSelector; ds.facetAddressAndSelectorPosition[lastSelector].selectorPosition = oldFacetAddressAndSelectorPosition .selectorPosition; } // delete last selector ds.selectors.pop(); delete ds.facetAddressAndSelectorPosition[selector]; } } function initializeDiamondCut(address _init, bytes memory _calldata) internal { if (_init == address(0)) { return; } enforceHasContractCode(_init, "LibDiamondCut: _init address has no code"); (bool success, bytes memory error) = _init.delegatecall(_calldata); if (!success) { if (error.length > 0) { // bubble up error /// @solidity memory-safe-assembly assembly { let returndata_size := mload(error) revert(add(32, error), returndata_size) } } else { revert InitializationFunctionReverted(_init, _calldata); } } } function enforceHasContractCode(address _contract, string memory _errorMessage) internal view { uint256 contractSize; assembly { contractSize := extcodesize(_contract) } if (contractSize == 0) { revert NoBytecodeAtAddress(_contract, _errorMessage); } } }
// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; /******************************************************************************\ * Author: Nick Mudge <[email protected]>, Twitter/Github: @mudgen * EIP-2535 Diamonds * * Contract used to initialize state variables during deployment or upgrade /******************************************************************************/ import { LibDiamond } from "../libraries/LibDiamond.sol"; import { IDiamondLoupe } from "../interfaces/IDiamondLoupe.sol"; import { IDiamondCut } from "../interfaces/IDiamondCut.sol"; import { IERC173 } from "../interfaces/IERC173.sol"; import { IERC165 } from "../interfaces/IERC165.sol"; // It is expected that this contract is customized if you want to deploy your diamond // with data from a deployment script. Use the init function to initialize state variables // of your diamond. Add parameters to the init funciton if you need to. // Adding parameters to the `init` or other functions you add here can make a single deployed // DiamondInit contract reusable accross upgrades, and can be used for multiple diamonds. contract DiamondInit { // You can add parameters to this function in order to pass in // data to set your own state variables function init() external { // adding ERC165 data LibDiamond.DiamondStorage storage ds = LibDiamond.diamondStorage(); ds.supportedInterfaces[type(IERC165).interfaceId] = true; ds.supportedInterfaces[type(IDiamondCut).interfaceId] = true; ds.supportedInterfaces[type(IDiamondLoupe).interfaceId] = true; ds.supportedInterfaces[type(IERC173).interfaceId] = true; // add your own state variables // EIP-2535 specifies that the `diamondCut` function takes two optional // arguments: address _init and bytes calldata _calldata // These arguments are used to execute an arbitrary function using delegatecall // in order to set state variables in the diamond during deployment or an upgrade // More info here: https://eips.ethereum.org/EIPS/eip-2535#diamond-interface } }
// SPDX-License-Identifier: CC0-1.0 pragma solidity ^0.8.0; /******************************************************************************\ * Author: Nick Mudge <[email protected]>, Twitter/Github: @mudgen * EIP-2535 Diamonds * * Contract used to initialize state variables during deployment or upgrade /******************************************************************************/ import { LibDiamond } from "../libraries/LibDiamond.sol"; error AddressAndCalldataLengthDoNotMatch(uint256 _addressesLength, uint256 _calldataLength); contract DiamondMultiInit { // This function is provided in the third parameter of the `diamondCut` function. // The `diamondCut` function executes this function to execute multiple initializer functions for a single upgrade. function multiInit(address[] calldata _addresses, bytes[] calldata _calldata) external { if (_addresses.length != _calldata.length) { revert AddressAndCalldataLengthDoNotMatch(_addresses.length, _calldata.length); } for (uint256 i; i < _addresses.length; i++) { LibDiamond.initializeDiamondCut(_addresses[i], _calldata[i]); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IERC3156FlashBorrower { /** * @dev Receive a flash loan. * @param initiator The initiator of the loan. * @param token The loan currency. * @param amount The amount of tokens lent. * @param fee The additional amount of tokens to repay. * @param data Arbitrary data structure, intended to contain user-defined parameters. * @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan" */ function onFlashLoan( address initiator, address token, uint256 amount, uint256 fee, bytes calldata data ) external returns (bytes32); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IERC3156FlashBorrower } from "./IERC3156FlashBorrower.sol"; interface IERC3156FlashLender { /** * @dev The amount of currency available to be lent. * @param token The loan currency. * @return The amount of `token` that can be borrowed. */ function maxFlashLoan(address token) external view returns (uint256); /** * @dev The fee to be charged for a given loan. * @param token The loan currency. * @param amount The amount of tokens lent. * @return The amount of `token` to be charged for the loan, on top of the returned principal. */ function flashFee(address token, uint256 amount) external view returns (uint256); /** * @dev Initiate a flash loan. * @param receiver The receiver of the tokens in the loan, and the receiver of the callback. * @param token The loan currency. * @param amount The amount of tokens lent. * @param data Arbitrary data structure, intended to contain user-defined parameters. */ function flashLoan( IERC3156FlashBorrower receiver, address token, uint256 amount, bytes calldata data ) external returns (bool); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IMultipleRewardDistributor { /********** * Events * **********/ /// @notice Emitted when new reward token is registered. /// /// @param token The address of reward token. /// @param distributor The address of reward distributor. event RegisterRewardToken(address indexed token, address indexed distributor); /// @notice Emitted when the reward distributor is updated. /// /// @param token The address of reward token. /// @param oldDistributor The address of previous reward distributor. /// @param newDistributor The address of current reward distributor. event UpdateRewardDistributor(address indexed token, address indexed oldDistributor, address indexed newDistributor); /// @notice Emitted when a reward token is unregistered. /// /// @param token The address of reward token. event UnregisterRewardToken(address indexed token); /// @notice Emitted when a reward token is deposited. /// /// @param token The address of reward token. /// @param amount The amount of reward token deposited. event DepositReward(address indexed token, uint256 amount); /********** * Errors * **********/ /// @dev Thrown when caller access an unactive reward token. error NotActiveRewardToken(); /// @dev Thrown when the address of reward distributor is `address(0)`. error RewardDistributorIsZero(); /// @dev Thrown when caller is not reward distributor. error NotRewardDistributor(); /// @dev Thrown when caller try to register an existing reward token. error DuplicatedRewardToken(); /// @dev Thrown when caller try to unregister a reward with pending rewards. error RewardDistributionNotFinished(); /************************* * Public View Functions * *************************/ /// @notice Return the address of reward distributor. /// /// @param token The address of reward token. function distributors(address token) external view returns (address); /// @notice Return the list of active reward tokens. function getActiveRewardTokens() external view returns (address[] memory); /// @notice Return the list of historical reward tokens. function getHistoricalRewardTokens() external view returns (address[] memory); /// @notice Return the amount of pending distributed rewards in current period. /// /// @param token The address of reward token. /// @return distributable The amount of reward token can be distributed in current period. /// @return undistributed The amount of reward token still locked in current period. function pendingRewards(address token) external view returns (uint256 distributable, uint256 undistributed); /**************************** * Public Mutated Functions * ****************************/ /// @notice Deposit new rewards to this contract. /// /// @param token The address of reward token. /// @param amount The amount of new rewards. function depositReward(address token, uint256 amount) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IRewardDistributor { /********** * Events * **********/ /// @notice Emitted when a reward token is deposited. /// /// @param amount The amount of reward token deposited. event DepositReward(uint256 amount); /************************* * Public View Functions * *************************/ /// @notice Return the address of reward token. function rewardToken() external view returns (address); /// @notice Return the amount of pending distributed rewards in current period. /// @return distributable The amount of reward token can be distributed in current period. /// @return undistributed The amount of reward token still locked in current period. function pendingRewards() external view returns (uint256 distributable, uint256 undistributed); /**************************** * Public Mutated Functions * ****************************/ /// @notice Deposit new rewards to this contract. /// /// @param amount The amount of new rewards. function depositReward(uint256 amount) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { IMultipleRewardDistributor } from "./IMultipleRewardDistributor.sol"; import { LinearReward } from "./LinearReward.sol"; // solhint-disable no-empty-blocks // solhint-disable not-rely-on-time abstract contract LinearMultipleRewardDistributor is AccessControlUpgradeable, IMultipleRewardDistributor { using EnumerableSet for EnumerableSet.AddressSet; using SafeERC20 for IERC20; using LinearReward for LinearReward.RewardData; /************* * Constants * *************/ /// @notice The role used to manage rewards. bytes32 public constant REWARD_MANAGER_ROLE = keccak256("REWARD_MANAGER_ROLE"); /// @notice The length of reward period in seconds. /// @dev If the value is zero, the reward will be distributed immediately. /// @dev It is either zero or at least 1 day (which is 86400). uint40 public immutable periodLength; /************* * Variables * *************/ /// @inheritdoc IMultipleRewardDistributor mapping(address => address) public override distributors; /// @notice Mapping from reward token address to linear distribution reward data. mapping(address => LinearReward.RewardData) public rewardData; /// @dev The list of active reward tokens. EnumerableSet.AddressSet internal activeRewardTokens; /// @dev The list of historical reward tokens. EnumerableSet.AddressSet private historicalRewardTokens; /// @dev reserved slots. uint256[46] private __gap; /*************** * Constructor * ***************/ constructor(uint40 _periodLength) { require(_periodLength == 0 || (_periodLength >= 1 days && _periodLength <= 28 days), "invalid period length"); periodLength = _periodLength; } // solhint-disable-next-line func-name-mixedcase function __LinearMultipleRewardDistributor_init() internal onlyInitializing {} /************************* * Public View Functions * *************************/ /// @inheritdoc IMultipleRewardDistributor function getActiveRewardTokens() public view override returns (address[] memory _rewardTokens) { uint256 _length = activeRewardTokens.length(); _rewardTokens = new address[](_length); for (uint256 i = 0; i < _length; i++) { _rewardTokens[i] = activeRewardTokens.at(i); } } /// @inheritdoc IMultipleRewardDistributor function getHistoricalRewardTokens() public view override returns (address[] memory _rewardTokens) { uint256 _length = historicalRewardTokens.length(); _rewardTokens = new address[](_length); for (uint256 i = 0; i < _length; i++) { _rewardTokens[i] = historicalRewardTokens.at(i); } } /// @inheritdoc IMultipleRewardDistributor function pendingRewards(address _token) external view override returns (uint256, uint256) { return rewardData[_token].pending(); } /**************************** * Public Mutated Functions * ****************************/ /// @inheritdoc IMultipleRewardDistributor function depositReward(address _token, uint256 _amount) external override { address _distributor = _msgSender(); if (!activeRewardTokens.contains(_token)) revert NotActiveRewardToken(); if (distributors[_token] != _distributor) revert NotRewardDistributor(); if (_amount > 0) { IERC20(_token).safeTransferFrom(_distributor, address(this), _amount); } _distributePendingReward(); _notifyReward(_token, _amount); emit DepositReward(_token, _amount); } /************************ * Restricted Functions * ************************/ /// @notice Register a new reward token. /// @dev Make sure no fee on transfer token is added as reward token. /// /// @param _token The address of reward token. /// @param _distributor The address of reward distributor. function registerRewardToken(address _token, address _distributor) external onlyRole(REWARD_MANAGER_ROLE) { if (_distributor == address(0)) revert RewardDistributorIsZero(); if (activeRewardTokens.contains(_token)) revert DuplicatedRewardToken(); activeRewardTokens.add(_token); distributors[_token] = _distributor; historicalRewardTokens.remove(_token); emit RegisterRewardToken(_token, _distributor); } /// @notice Update the distributor for reward token. /// /// @param _token The address of reward token. /// @param _newDistributor The address of new reward distributor. function updateRewardDistributor(address _token, address _newDistributor) external onlyRole(REWARD_MANAGER_ROLE) { if (_newDistributor == address(0)) revert RewardDistributorIsZero(); if (!activeRewardTokens.contains(_token)) revert NotActiveRewardToken(); address _oldDistributor = distributors[_token]; distributors[_token] = _newDistributor; emit UpdateRewardDistributor(_token, _oldDistributor, _newDistributor); } /// @notice Unregister an existing reward token. /// /// @param _token The address of reward token. function unregisterRewardToken(address _token) external onlyRole(REWARD_MANAGER_ROLE) { if (!activeRewardTokens.contains(_token)) revert NotActiveRewardToken(); LinearReward.RewardData memory _data = rewardData[_token]; unchecked { (uint256 _distributable, uint256 _undistributed) = _data.pending(); if (_data.queued < periodLength) _data.queued = 0; // ignore round error if (_data.queued + _distributable + _undistributed > 0) revert RewardDistributionNotFinished(); } activeRewardTokens.remove(_token); distributors[_token] = address(0); historicalRewardTokens.add(_token); emit UnregisterRewardToken(_token); } /********************** * Internal Functions * **********************/ /// @dev Internal function to notify new rewards. /// /// @param _token The address of token. /// @param _amount The amount of new rewards. function _notifyReward(address _token, uint256 _amount) internal { if (periodLength == 0) { _accumulateReward(_token, _amount); } else { LinearReward.RewardData memory _data = rewardData[_token]; _data.increase(periodLength, _amount); rewardData[_token] = _data; } } /// @dev Internal function to distribute all pending reward tokens. function _distributePendingReward() internal { if (periodLength == 0 || activeRewardTokens.length() == 0) return; address[] memory _activeRewardTokens = getActiveRewardTokens(); for (uint256 i = 0; i < _activeRewardTokens.length; i++) { address _token = _activeRewardTokens[i]; (uint256 _pending, ) = rewardData[_token].pending(); rewardData[_token].lastUpdate = uint40(block.timestamp); if (_pending > 0) { _accumulateReward(_token, _pending); } } } /// @dev Internal function to accumulate distributed rewards. /// /// @param _token The address of token. /// @param _amount The amount of rewards to accumulate. function _accumulateReward(address _token, uint256 _amount) internal virtual; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { SafeCast } from "@openzeppelin/contracts/utils/math/SafeCast.sol"; // solhint-disable not-rely-on-time library LinearReward { using SafeCast for uint256; /// @dev Compiler will pack this into single `uint256`. /// Usually, we assume the amount of rewards won't exceed `uint96.max`. /// In such case, the rate won't exceed `uint80.max`, since `periodLength` is at least `86400`. /// Also `uint40.max` is enough for timestamp, which is about 30000 years. struct RewardData { // The amount of rewards pending to distribute. uint96 queued; // The current reward rate per second. uint80 rate; // The last timestamp when the reward is distributed. uint40 lastUpdate; // The timestamp when this period will finish. uint40 finishAt; } /// @dev Add new rewards to current one. It is possible that the rewards will not distribute immediately. /// The rewards will be only distributed when current period is end or the current increase or /// decrease no more than 10%. /// /// @param _data The struct of reward data, will be modified inplace. /// @param _periodLength The length of a period, caller should make sure it is at least `86400`. /// @param _amount The amount of new rewards to distribute. function increase( RewardData memory _data, uint256 _periodLength, uint256 _amount ) internal view { _amount = _amount + _data.queued; _data.queued = 0; if (block.timestamp >= _data.finishAt) { // period finished, distribute to next period _data.rate = (_amount / _periodLength).toUint80(); _data.queued = uint96(_amount - (_data.rate * _periodLength)); // keep rounding error _data.lastUpdate = uint40(block.timestamp); _data.finishAt = uint40(block.timestamp + _periodLength); } else { uint256 _elapsed = block.timestamp - (_data.finishAt - _periodLength); uint256 _distributed = uint256(_data.rate) * _elapsed; if (_distributed * 9 <= _amount * 10) { // APR increase or drop no more than 10%, distribute _amount = _amount + uint256(_data.rate) * (_data.finishAt - _data.lastUpdate); _data.rate = (_amount / _periodLength).toUint80(); _data.queued = uint96(_amount - (_data.rate * _periodLength)); // keep rounding error _data.lastUpdate = uint40(block.timestamp); _data.finishAt = uint40(block.timestamp + _periodLength); _data.lastUpdate = uint40(block.timestamp); } else { // APR drop more than 10%, wait for more rewards _data.queued = _amount.toUint96(); } } } /// @dev Return the amount of pending distributed rewards in current period. /// /// @param _data The struct of reward data. function pending(RewardData memory _data) internal view returns (uint256, uint256) { uint256 _elapsed; uint256 _left; if (block.timestamp > _data.finishAt) { // finishAt >= lastUpdate will happen, if `_notifyReward` is not called during current period. _elapsed = _data.finishAt >= _data.lastUpdate ? _data.finishAt - _data.lastUpdate : 0; } else { unchecked { _elapsed = block.timestamp - _data.lastUpdate; _left = uint256(_data.finishAt) - block.timestamp; } } return (uint256(_data.rate) * _elapsed, uint256(_data.rate) * _left); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import { IRewardDistributor } from "./IRewardDistributor.sol"; import { LinearReward } from "./LinearReward.sol"; // solhint-disable no-empty-blocks // solhint-disable not-rely-on-time abstract contract LinearRewardDistributor is AccessControlUpgradeable, IRewardDistributor { using SafeERC20 for IERC20; using LinearReward for LinearReward.RewardData; /************* * Constants * *************/ /// @notice The role used to deposit rewards. bytes32 public constant REWARD_DEPOSITOR_ROLE = keccak256("REWARD_DEPOSITOR_ROLE"); /// @notice The length of reward period in seconds. /// @dev If the value is zero, the reward will be distributed immediately. /// It is either zero or at least 1 day (which is 86400). uint40 public immutable periodLength; /************* * Variables * *************/ /// @notice The linear distribution reward data. LinearReward.RewardData public rewardData; /// @inheritdoc IRewardDistributor address public override rewardToken; /// @dev reserved slots. uint256[48] private __gap; /*************** * Constructor * ***************/ constructor(uint40 _periodLength) { require(_periodLength == 0 || (_periodLength >= 1 days && _periodLength <= 28 days), "invalid period length"); periodLength = _periodLength; } // solhint-disable-next-line func-name-mixedcase function __LinearRewardDistributor_init(address _rewardToken) internal onlyInitializing { rewardToken = _rewardToken; } /************************* * Public View Functions * *************************/ /// @inheritdoc IRewardDistributor function pendingRewards() public view override returns (uint256, uint256) { return rewardData.pending(); } /**************************** * Public Mutated Functions * ****************************/ /// @inheritdoc IRewardDistributor function depositReward(uint256 _amount) external override onlyRole(REWARD_DEPOSITOR_ROLE) { if (_amount > 0) { IERC20(rewardToken).safeTransferFrom(msg.sender, address(this), _amount); } _distributePendingReward(); _notifyReward(_amount); _afterRewardDeposit(_amount); emit DepositReward(_amount); } /********************** * Internal Functions * **********************/ /// @dev Internal function to notify new rewards. /// /// @param _amount The amount of new rewards. function _notifyReward(uint256 _amount) internal { if (periodLength == 0) { _accumulateReward(_amount); } else { LinearReward.RewardData memory _data = rewardData; _data.increase(periodLength, _amount); rewardData = _data; } } /// @dev Internal function to distribute all pending reward tokens. function _distributePendingReward() internal { if (periodLength == 0) return; (uint256 _pending, ) = rewardData.pending(); rewardData.lastUpdate = uint40(block.timestamp); if (_pending > 0) { _accumulateReward(_pending); } } /// @dev Internal function to accumulate distributed rewards. /// /// @param _amount The amount of rewards to accumulate. function _accumulateReward(uint256 _amount) internal virtual; /// @dev The hook for the deposited rewards. /// @param _amount The amount of rewards deposited. function _afterRewardDeposit(uint256 _amount) internal virtual {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; // solhint-disable avoid-low-level-calls // solhint-disable no-inline-assembly abstract contract PermissionedSwap is AccessControlUpgradeable { using SafeERC20 for IERC20; /********** * Errors * **********/ /// @dev Thrown when the amount of output token is not enough. error InsufficientOutputToken(); /************* * Constants * *************/ /// @notice The role for permissioned trader. bytes32 public constant PERMISSIONED_TRADER_ROLE = keccak256("PERMISSIONED_TRADER_ROLE"); /// @notice The role for permissioned trading router. bytes32 public constant PERMISSIONED_ROUTER_ROLE = keccak256("PERMISSIONED_ROUTER_ROLE"); /*********** * Structs * ***********/ /// @notice The struct for trading parameters. /// /// @param router The address of trading router. /// @param data The calldata passing to the router contract. /// @param minOut The minimum amount of output token should receive. struct TradingParameter { address router; bytes data; uint256 minOut; } /************* * Variables * *************/ /// @dev reserved slots. uint256[50] private __gap; /************************ * Restricted Functions * ************************/ /// @notice Withdraw base token to someone else. /// @dev This should be only used when we are retiring this contract. /// @param baseToken The address of base token. function withdraw(address baseToken, address recipient) external onlyRole(DEFAULT_ADMIN_ROLE) { uint256 amountIn = IERC20(baseToken).balanceOf(address(this)); IERC20(baseToken).safeTransfer(recipient, amountIn); } /********************** * Internal Functions * **********************/ /// @dev Internal function to convert token with routes. /// @param srcToken The address of source token. /// @param dstToken The address of destination token. /// @param amountIn The amount of input token. /// @param params The token converting parameters. /// @return amountOut The amount of output token received. function _doTrade( address srcToken, address dstToken, uint256 amountIn, TradingParameter memory params ) internal virtual onlyRole(PERMISSIONED_TRADER_ROLE) returns (uint256 amountOut) { if (srcToken == dstToken) return amountIn; // router should be permissioned _checkRole(PERMISSIONED_ROUTER_ROLE, params.router); // approve to router IERC20(srcToken).forceApprove(params.router, amountIn); // do trading amountOut = IERC20(dstToken).balanceOf(address(this)); (bool success, ) = params.router.call(params.data); if (!success) { // below lines will propagate inner error up assembly { let ptr := mload(0x40) let size := returndatasize() returndatacopy(ptr, 0, size) revert(ptr, size) } } amountOut = IERC20(dstToken).balanceOf(address(this)) - amountOut; if (amountOut < params.minOut) { revert InsufficientOutputToken(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.25; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import { IERC3156FlashBorrower } from "../common/ERC3156/IERC3156FlashBorrower.sol"; import { IERC3156FlashLender } from "../common/ERC3156/IERC3156FlashLender.sol"; import { ProtocolFees } from "./ProtocolFees.sol"; contract FlashLoans is ProtocolFees, ReentrancyGuardUpgradeable, IERC3156FlashLender { using SafeERC20 for IERC20; /********** * Errors * **********/ /// @dev Thrown when the returned balance after flash loan is not enough. error ErrorInsufficientFlashLoanReturn(); /// @dev Thrown when the returned value of `ERC3156Callback` is wrong. error ErrorERC3156CallbackFailed(); /************* * Constants * *************/ /// @dev The correct value of the return value of `ERC3156FlashBorrower.onFlashLoan`. bytes32 private constant CALLBACK_SUCCESS = keccak256("ERC3156FlashBorrower.onFlashLoan"); /************* * Variables * *************/ /// @dev Slots for future use. uint256[50] private _gap; /*************** * Constructor * ***************/ function __FlashLoans_init() internal onlyInitializing {} /************************* * Public View Functions * *************************/ /// @inheritdoc IERC3156FlashLender function maxFlashLoan(address token) external view override returns (uint256) { return IERC20(token).balanceOf(address(this)); } /// @inheritdoc IERC3156FlashLender function flashFee(address /*token*/, uint256 amount) public view returns (uint256) { return (amount * getFlashLoanFeeRatio()) / FEE_PRECISION; } /**************************** * Public Mutated Functions * ****************************/ /// @inheritdoc IERC3156FlashLender function flashLoan( IERC3156FlashBorrower receiver, address token, uint256 amount, bytes calldata data ) external nonReentrant returns (bool) { // save the current balance uint256 prevBalance = IERC20(token).balanceOf(address(this)); uint256 fee = flashFee(token, amount); // transfer token to receiver IERC20(token).safeTransfer(address(receiver), amount); // invoke the recipient's callback if (receiver.onFlashLoan(_msgSender(), token, amount, fee, data) != CALLBACK_SUCCESS) { revert ErrorERC3156CallbackFailed(); } // ensure that the tokens + fee have been deposited back to the network uint256 returnedAmount = IERC20(token).balanceOf(address(this)) - prevBalance; if (returnedAmount < amount + fee) { revert ErrorInsufficientFlashLoanReturn(); } if (fee > 0) { IERC20(token).safeTransfer(treasury, fee); } return true; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.25; import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import { ERC20PermitUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20PermitUpgradeable.sol"; import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/ReentrancyGuardUpgradeable.sol"; import { AggregatorV3Interface } from "../interfaces/Chainlink/AggregatorV3Interface.sol"; import { IPegKeeper } from "../interfaces/IPegKeeper.sol"; import { IPool } from "../interfaces/IPool.sol"; import { IPoolManager } from "../interfaces/IPoolManager.sol"; import { IFxUSDBasePool } from "../interfaces/IFxUSDBasePool.sol"; import { AssetManagement } from "../fund/AssetManagement.sol"; import { Math } from "../libraries/Math.sol"; contract FxUSDBasePool is ERC20PermitUpgradeable, AccessControlUpgradeable, ReentrancyGuardUpgradeable, AssetManagement, IFxUSDBasePool { using SafeERC20 for IERC20; /********** * Errors * **********/ /// @dev Thrown when the deposited amount is zero. error ErrDepositZeroAmount(); /// @dev Thrown when the minted shares are not enough. error ErrInsufficientSharesOut(); /// @dev Thrown the input token in invalid. error ErrInvalidTokenIn(); /// @dev Thrown when the redeemed shares is zero. error ErrRedeemZeroShares(); error ErrorCallerNotPegKeeper(); error ErrorStableTokenDepeg(); error ErrorSwapExceedBalance(); error ErrorInsufficientOutput(); error ErrorInsufficientArbitrage(); error ErrorRedeemCoolDownPeriodTooLarge(); error ErrorRedeemMoreThanBalance(); error ErrorRedeemLockedShares(); error ErrorInsufficientFreeBalance(); /************* * Constants * *************/ /// @dev The exchange rate precision. uint256 internal constant PRECISION = 1e18; /*********************** * Immutable Variables * ***********************/ /// @notice The address of `PoolManager` contract. address public immutable poolManager; /// @notice The address of `PegKeeper` contract. address public immutable pegKeeper; /// @inheritdoc IFxUSDBasePool /// @dev This is also the address of FxUSD token. address public immutable yieldToken; /// @inheritdoc IFxUSDBasePool /// @dev The address of USDC token. address public immutable stableToken; uint256 private immutable stableTokenScale; /// @notice The Chainlink USDC/USD price feed. /// @dev The encoding is below. /// ```text /// | 32 bits | 64 bits | 160 bits | /// | heartbeat | scale | price_feed | /// |low high | /// ``` bytes32 public immutable Chainlink_USDC_USD_Spot; /*********** * Structs * ***********/ struct RebalanceMemoryVar { uint256 stablePrice; uint256 totalYieldToken; uint256 totalStableToken; uint256 yieldTokenToUse; uint256 stableTokenToUse; uint256 colls; uint256 yieldTokenUsed; uint256 stableTokenUsed; } struct RedeemRequest { uint128 amount; uint128 unlockAt; } /************* * Variables * *************/ /// @inheritdoc IFxUSDBasePool uint256 public totalYieldToken; /// @inheritdoc IFxUSDBasePool uint256 public totalStableToken; /// @notice The depeg price for stable token. uint256 public stableDepegPrice; /// @notice Mapping from user address to redeem request. mapping(address => RedeemRequest) public redeemRequests; /// @notice The number of seconds of cool down before redeem from this pool. uint256 public redeemCoolDownPeriod; /************* * Modifiers * *************/ modifier onlyValidToken(address token) { if (token != stableToken && token != yieldToken) { revert ErrInvalidTokenIn(); } _; } modifier onlyPegKeeper() { if (_msgSender() != pegKeeper) revert ErrorCallerNotPegKeeper(); _; } /*************** * Constructor * ***************/ constructor( address _poolManager, address _pegKeeper, address _yieldToken, address _stableToken, bytes32 _Chainlink_USDC_USD_Spot ) { poolManager = _poolManager; pegKeeper = _pegKeeper; yieldToken = _yieldToken; stableToken = _stableToken; Chainlink_USDC_USD_Spot = _Chainlink_USDC_USD_Spot; stableTokenScale = 10 ** (18 - IERC20Metadata(_stableToken).decimals()); } function initialize( address admin, string memory _name, string memory _symbol, uint256 _stableDepegPrice, uint256 _redeemCoolDownPeriod ) external initializer { __Context_init(); __ERC165_init(); __AccessControl_init(); __ReentrancyGuard_init(); __ERC20_init(_name, _symbol); __ERC20Permit_init(_name); _grantRole(DEFAULT_ADMIN_ROLE, admin); _updateStableDepegPrice(_stableDepegPrice); _updateRedeemCoolDownPeriod(_redeemCoolDownPeriod); // approve IERC20(yieldToken).forceApprove(poolManager, type(uint256).max); IERC20(stableToken).forceApprove(poolManager, type(uint256).max); } /************************* * Public View Functions * *************************/ /// @inheritdoc IFxUSDBasePool function previewDeposit( address tokenIn, uint256 amountTokenToDeposit ) public view override onlyValidToken(tokenIn) returns (uint256 amountSharesOut) { uint256 price = getStableTokenPriceWithScale(); uint256 amountUSD = amountTokenToDeposit; if (tokenIn == stableToken) { amountUSD = (amountUSD * price) / PRECISION; } uint256 _totalSupply = totalSupply(); if (_totalSupply == 0) { amountSharesOut = amountUSD; } else { uint256 totalUSD = totalYieldToken + (totalStableToken * price) / PRECISION; amountSharesOut = (amountUSD * _totalSupply) / totalUSD; } } /// @inheritdoc IFxUSDBasePool function previewRedeem( uint256 amountSharesToRedeem ) external view returns (uint256 amountYieldOut, uint256 amountStableOut) { uint256 cachedTotalYieldToken = totalYieldToken; uint256 cachedTotalStableToken = totalStableToken; uint256 cachedTotalSupply = totalSupply(); amountYieldOut = (amountSharesToRedeem * cachedTotalYieldToken) / cachedTotalSupply; amountStableOut = (amountSharesToRedeem * cachedTotalStableToken) / cachedTotalSupply; } /// @inheritdoc IFxUSDBasePool function nav() external view returns (uint256) { uint256 _totalSupply = totalSupply(); if (_totalSupply == 0) { return PRECISION; } else { uint256 stablePrice = getStableTokenPriceWithScale(); uint256 yieldPrice = IPegKeeper(pegKeeper).getFxUSDPrice(); return (totalYieldToken * yieldPrice + totalStableToken * stablePrice) / _totalSupply; } } /// @inheritdoc IFxUSDBasePool function getStableTokenPrice() public view returns (uint256) { bytes32 encoding = Chainlink_USDC_USD_Spot; address aggregator; uint256 scale; uint256 heartbeat; assembly { aggregator := shr(96, encoding) scale := and(shr(32, encoding), 0xffffffffffffffff) heartbeat := and(encoding, 0xffffffff) } (, int256 answer, , uint256 updatedAt, ) = AggregatorV3Interface(aggregator).latestRoundData(); if (answer < 0) revert("invalid"); if (block.timestamp - updatedAt > heartbeat) revert("expired"); return uint256(answer) * scale; } /// @inheritdoc IFxUSDBasePool function getStableTokenPriceWithScale() public view returns (uint256) { return getStableTokenPrice() * stableTokenScale; } /**************************** * Public Mutated Functions * ****************************/ /// @inheritdoc IFxUSDBasePool function deposit( address receiver, address tokenIn, uint256 amountTokenToDeposit, uint256 minSharesOut ) external override nonReentrant onlyValidToken(tokenIn) returns (uint256 amountSharesOut) { if (amountTokenToDeposit == 0) revert ErrDepositZeroAmount(); // we are very sure every token is normal token, so no fot check here. IERC20(tokenIn).safeTransferFrom(_msgSender(), address(this), amountTokenToDeposit); amountSharesOut = _deposit(tokenIn, amountTokenToDeposit); if (amountSharesOut < minSharesOut) revert ErrInsufficientSharesOut(); _mint(receiver, amountSharesOut); emit Deposit(_msgSender(), receiver, tokenIn, amountTokenToDeposit, amountSharesOut); } /// @inheritdoc IFxUSDBasePool function requestRedeem(uint256 shares) external { address caller = _msgSender(); uint256 balance = balanceOf(caller); RedeemRequest memory request = redeemRequests[caller]; if (request.amount + shares > balance) revert ErrorRedeemMoreThanBalance(); request.amount += uint128(shares); request.unlockAt = uint128(block.timestamp + redeemCoolDownPeriod); redeemRequests[caller] = request; emit RequestRedeem(caller, shares, request.unlockAt); } /// @inheritdoc IFxUSDBasePool function redeem( address receiver, uint256 amountSharesToRedeem ) external nonReentrant returns (uint256 amountYieldOut, uint256 amountStableOut) { address caller = _msgSender(); RedeemRequest memory request = redeemRequests[caller]; if (request.unlockAt > block.timestamp) revert ErrorRedeemLockedShares(); if (request.amount < amountSharesToRedeem) { amountSharesToRedeem = request.amount; } if (amountSharesToRedeem == 0) revert ErrRedeemZeroShares(); request.amount -= uint128(amountSharesToRedeem); redeemRequests[caller] = request; uint256 cachedTotalYieldToken = totalYieldToken; uint256 cachedTotalStableToken = totalStableToken; uint256 cachedTotalSupply = totalSupply(); amountYieldOut = (amountSharesToRedeem * cachedTotalYieldToken) / cachedTotalSupply; amountStableOut = (amountSharesToRedeem * cachedTotalStableToken) / cachedTotalSupply; _burn(caller, amountSharesToRedeem); if (amountYieldOut > 0) { IERC20(yieldToken).safeTransfer(receiver, amountYieldOut); unchecked { totalYieldToken = cachedTotalYieldToken - amountYieldOut; } } if (amountStableOut > 0) { IERC20(stableToken).safeTransfer(receiver, amountStableOut); unchecked { totalStableToken = cachedTotalStableToken - amountStableOut; } } emit Redeem(caller, receiver, amountSharesToRedeem, amountYieldOut, amountStableOut); } /// @inheritdoc IFxUSDBasePool function rebalance( address pool, int16 tickId, address tokenIn, uint256 maxAmount, uint256 minCollOut ) external onlyValidToken(tokenIn) nonReentrant returns (uint256 tokenUsed, uint256 colls) { RebalanceMemoryVar memory op = _beforeRebalanceOrLiquidate(tokenIn, maxAmount); (op.colls, op.yieldTokenUsed, op.stableTokenUsed) = IPoolManager(poolManager).rebalance( pool, _msgSender(), tickId, op.yieldTokenToUse, op.stableTokenToUse ); tokenUsed = _afterRebalanceOrLiquidate(tokenIn, minCollOut, op); colls = op.colls; } /// @inheritdoc IFxUSDBasePool function rebalance( address pool, uint32 positionId, address tokenIn, uint256 maxAmount, uint256 minCollOut ) external onlyValidToken(tokenIn) nonReentrant returns (uint256 tokenUsed, uint256 colls) { RebalanceMemoryVar memory op = _beforeRebalanceOrLiquidate(tokenIn, maxAmount); (op.colls, op.yieldTokenUsed, op.stableTokenUsed) = IPoolManager(poolManager).rebalance( pool, _msgSender(), positionId, op.yieldTokenToUse, op.stableTokenToUse ); tokenUsed = _afterRebalanceOrLiquidate(tokenIn, minCollOut, op); colls = op.colls; } /// @inheritdoc IFxUSDBasePool function liquidate( address pool, uint32 positionId, address tokenIn, uint256 maxAmount, uint256 minCollOut ) external onlyValidToken(tokenIn) nonReentrant returns (uint256 tokenUsed, uint256 colls) { RebalanceMemoryVar memory op = _beforeRebalanceOrLiquidate(tokenIn, maxAmount); (op.colls, op.yieldTokenUsed, op.stableTokenUsed) = IPoolManager(poolManager).liquidate( pool, _msgSender(), positionId, op.yieldTokenToUse, op.stableTokenToUse ); tokenUsed = _afterRebalanceOrLiquidate(tokenIn, minCollOut, op); colls = op.colls; } /// @inheritdoc IFxUSDBasePool function arbitrage( address srcToken, uint256 amountIn, address receiver, bytes calldata data ) external onlyValidToken(srcToken) onlyPegKeeper nonReentrant returns (uint256 amountOut, uint256 bonusOut) { address dstToken; uint256 expectedOut; uint256 cachedTotalYieldToken = totalYieldToken; uint256 cachedTotalStableToken = totalStableToken; { uint256 price = getStableTokenPrice(); uint256 scaledPrice = price * stableTokenScale; if (srcToken == yieldToken) { // check if usdc depeg if (price < stableDepegPrice) revert ErrorStableTokenDepeg(); if (amountIn > cachedTotalYieldToken) revert ErrorSwapExceedBalance(); dstToken = stableToken; unchecked { // rounding up expectedOut = Math.mulDivUp(amountIn, PRECISION, scaledPrice); cachedTotalYieldToken -= amountIn; cachedTotalStableToken += expectedOut; } } else { if (amountIn > cachedTotalStableToken) revert ErrorSwapExceedBalance(); dstToken = yieldToken; unchecked { // rounding up expectedOut = Math.mulDivUp(amountIn, scaledPrice, PRECISION); cachedTotalStableToken -= amountIn; cachedTotalYieldToken += expectedOut; } } } IERC20(srcToken).safeTransfer(pegKeeper, amountIn); uint256 actualOut = IERC20(dstToken).balanceOf(address(this)); amountOut = IPegKeeper(pegKeeper).onSwap(srcToken, dstToken, amountIn, data); actualOut = IERC20(dstToken).balanceOf(address(this)) - actualOut; // check actual fxUSD swapped in case peg keeper is hacked. if (amountOut > actualOut) revert ErrorInsufficientOutput(); // check swapped token has no loss if (amountOut < expectedOut) revert ErrorInsufficientArbitrage(); totalYieldToken = cachedTotalYieldToken; totalStableToken = cachedTotalStableToken; bonusOut = amountOut - expectedOut; if (bonusOut > 0) { IERC20(dstToken).safeTransfer(receiver, bonusOut); } emit Arbitrage(_msgSender(), srcToken, amountIn, amountOut, bonusOut); } /************************ * Restricted Functions * ************************/ /// @notice Update depeg price for stable token. /// @param newPrice The new depeg price of stable token, multiplied by 1e18 function updateStableDepegPrice(uint256 newPrice) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateStableDepegPrice(newPrice); } /// @notice Update redeem cool down period. /// @param newPeriod The new redeem cool down period, in seconds. function updateRedeemCoolDownPeriod(uint256 newPeriod) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateRedeemCoolDownPeriod(newPeriod); } /********************** * Internal Functions * **********************/ /// @inheritdoc ERC20Upgradeable function _update(address from, address to, uint256 value) internal virtual override { // make sure from don't transfer more than free balance if (from != address(0) && to != address(0)) { uint256 leftover = balanceOf(from) - redeemRequests[from].amount; if (value > leftover) revert ErrorInsufficientFreeBalance(); } super._update(from, to, value); } /// @dev Internal function to update depeg price for stable token. /// @param newPrice The new depeg price of stable token, multiplied by 1e18 function _updateStableDepegPrice(uint256 newPrice) internal { uint256 oldPrice = stableDepegPrice; stableDepegPrice = newPrice; emit UpdateStableDepegPrice(oldPrice, newPrice); } /// @dev Internal function to update redeem cool down period. /// @param newPeriod The new redeem cool down period, in seconds. function _updateRedeemCoolDownPeriod(uint256 newPeriod) internal { if (newPeriod > 7 days) revert ErrorRedeemCoolDownPeriodTooLarge(); uint256 oldPeriod = redeemCoolDownPeriod; redeemCoolDownPeriod = newPeriod; emit UpdateRedeemCoolDownPeriod(oldPeriod, newPeriod); } /// @dev mint shares based on the deposited base tokens /// @param tokenIn base token address used to mint shares /// @param amountDeposited amount of base tokens deposited /// @return amountSharesOut amount of shares minted function _deposit(address tokenIn, uint256 amountDeposited) internal virtual returns (uint256 amountSharesOut) { uint256 price = getStableTokenPriceWithScale(); if (price < stableDepegPrice * stableTokenScale) revert ErrorStableTokenDepeg(); uint256 amountUSD = amountDeposited; if (tokenIn == stableToken) { amountUSD = (amountUSD * price) / PRECISION; } uint256 cachedTotalYieldToken = totalYieldToken; uint256 cachedTotalStableToken = totalStableToken; uint256 totalUSD = cachedTotalYieldToken + (cachedTotalStableToken * price) / PRECISION; uint256 cachedTotalSupply = totalSupply(); if (cachedTotalSupply == 0) { amountSharesOut = amountUSD; } else { amountSharesOut = (amountUSD * cachedTotalSupply) / totalUSD; } if (tokenIn == stableToken) { totalStableToken = cachedTotalStableToken + amountDeposited; } else { totalYieldToken = cachedTotalYieldToken + amountDeposited; } } /// @dev Internal hook function to prepare before rebalance or liquidate. /// @param tokenIn The address of input token. /// @param maxAmount The maximum amount of input tokens. function _beforeRebalanceOrLiquidate( address tokenIn, uint256 maxAmount ) internal view returns (RebalanceMemoryVar memory op) { op.stablePrice = getStableTokenPriceWithScale(); op.totalYieldToken = totalYieldToken; op.totalStableToken = totalStableToken; uint256 amountYieldToken = op.totalYieldToken; uint256 amountStableToken; // we always, try use fxUSD first then USDC if (tokenIn == yieldToken) { // user pays fxUSD if (maxAmount < amountYieldToken) amountYieldToken = maxAmount; else { amountStableToken = ((maxAmount - amountYieldToken) * PRECISION) / op.stablePrice; } } else { // user pays USDC uint256 maxAmountInUSD = (maxAmount * op.stablePrice) / PRECISION; if (maxAmountInUSD < amountYieldToken) amountYieldToken = maxAmountInUSD; else { amountStableToken = ((maxAmountInUSD - amountYieldToken) * PRECISION) / op.stablePrice; } } if (amountStableToken > op.totalStableToken) { amountStableToken = op.totalStableToken; } op.yieldTokenToUse = amountYieldToken; op.stableTokenToUse = amountStableToken; } /// @dev Internal hook function after rebalance or liquidate. /// @param tokenIn The address of input token. /// @param minCollOut The minimum expected collateral tokens. /// @param op The memory variable for rebalance or liquidate. /// @return tokenUsed The amount of input token used. function _afterRebalanceOrLiquidate( address tokenIn, uint256 minCollOut, RebalanceMemoryVar memory op ) internal returns (uint256 tokenUsed) { if (op.colls < minCollOut) revert ErrorInsufficientOutput(); op.totalYieldToken -= op.yieldTokenUsed; op.totalStableToken -= op.stableTokenUsed; uint256 amountUSD = op.yieldTokenUsed + (op.stableTokenUsed * op.stablePrice) / PRECISION; if (tokenIn == yieldToken) { tokenUsed = amountUSD; op.totalYieldToken += tokenUsed; } else { // rounding up tokenUsed = Math.mulDivUp(amountUSD, PRECISION, op.stablePrice); op.totalStableToken += tokenUsed; } totalYieldToken = op.totalYieldToken; totalStableToken = op.totalStableToken; // transfer token from caller, the collateral is already transferred to caller. IERC20(tokenIn).safeTransferFrom(_msgSender(), address(this), tokenUsed); emit Rebalance(_msgSender(), tokenIn, tokenUsed, op.colls, op.yieldTokenUsed, op.stableTokenUsed); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import { IMultiPathConverter } from "../helpers/interfaces/IMultiPathConverter.sol"; import { ICurveStableSwapNG } from "../interfaces/Curve/ICurveStableSwapNG.sol"; import { IFxUSDRegeneracy } from "../interfaces/IFxUSDRegeneracy.sol"; import { IPegKeeper } from "../interfaces/IPegKeeper.sol"; import { IFxUSDBasePool } from "../interfaces/IFxUSDBasePool.sol"; contract PegKeeper is AccessControlUpgradeable, IPegKeeper { using SafeERC20 for IERC20; /********** * Errors * **********/ error ErrorNotInCallbackContext(); error ErrorZeroAddress(); error ErrorInsufficientOutput(); /************* * Constants * *************/ /// @dev The precision used to compute nav. uint256 private constant PRECISION = 1e18; /// @notice The role for buyback. bytes32 public constant BUYBACK_ROLE = keccak256("BUYBACK_ROLE"); /// @notice The role for stabilize. bytes32 public constant STABILIZE_ROLE = keccak256("STABILIZE_ROLE"); /// @dev contexts for buyback and stabilize callback uint8 private constant CONTEXT_NO_CONTEXT = 1; uint8 private constant CONTEXT_BUYBACK = 2; uint8 private constant CONTEXT_STABILIZE = 3; /*********************** * Immutable Variables * ***********************/ /// @notice The address of fxUSD. address public immutable fxUSD; /// @notice The address of stable token. address public immutable stable; /// @notice The address of FxUSDBasePool. address public immutable fxBASE; /********************* * Storage Variables * *********************/ /// @dev The context for buyback and stabilize callback. uint8 private context; /// @notice The address of MultiPathConverter. address public converter; /// @notice The curve pool for stable and fxUSD address public curvePool; /// @notice The fxUSD depeg price threshold. uint256 public priceThreshold; /************* * Modifiers * *************/ modifier setContext(uint8 c) { context = c; _; context = CONTEXT_NO_CONTEXT; } /*************** * Constructor * ***************/ constructor(address _fxBASE) { fxBASE = _fxBASE; fxUSD = IFxUSDBasePool(_fxBASE).yieldToken(); stable = IFxUSDBasePool(_fxBASE).stableToken(); } function initialize(address admin, address _converter, address _curvePool) external initializer { __Context_init(); __ERC165_init(); __AccessControl_init(); _grantRole(DEFAULT_ADMIN_ROLE, admin); _updateConverter(_converter); _updateCurvePool(_curvePool); _updatePriceThreshold(995000000000000000); // 0.995 context = CONTEXT_NO_CONTEXT; } /************************* * Public View Functions * *************************/ /// @inheritdoc IPegKeeper function isBorrowAllowed() external view returns (bool) { return _getFxUSDEmaPrice() >= priceThreshold; } /// @inheritdoc IPegKeeper function isFundingEnabled() external view returns (bool) { return _getFxUSDEmaPrice() < priceThreshold; } /// @inheritdoc IPegKeeper function getFxUSDPrice() external view returns (uint256) { return _getFxUSDEmaPrice(); } /**************************** * Public Mutated Functions * ****************************/ /// @inheritdoc IPegKeeper function buyback( uint256 amountIn, bytes calldata data ) external onlyRole(BUYBACK_ROLE) setContext(CONTEXT_BUYBACK) returns (uint256 amountOut, uint256 bonus) { (amountOut, bonus) = IFxUSDRegeneracy(fxUSD).buyback(amountIn, _msgSender(), data); } /// @inheritdoc IPegKeeper function stabilize( address srcToken, uint256 amountIn, bytes calldata data ) external onlyRole(STABILIZE_ROLE) setContext(CONTEXT_STABILIZE) returns (uint256 amountOut, uint256 bonus) { (amountOut, bonus) = IFxUSDBasePool(fxBASE).arbitrage(srcToken, amountIn, _msgSender(), data); } /// @inheritdoc IPegKeeper /// @dev This function will be called in `buyback`, `stabilize`. function onSwap( address srcToken, address targetToken, uint256 amountIn, bytes calldata data ) external returns (uint256 amountOut) { // check callback validity if (context == CONTEXT_NO_CONTEXT) revert ErrorNotInCallbackContext(); amountOut = _doSwap(srcToken, amountIn, data); IERC20(targetToken).safeTransfer(_msgSender(), amountOut); } /************************ * Restricted Functions * ************************/ /// @notice Update the address of converter. /// @param newConverter The address of converter. function updateConverter(address newConverter) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateConverter(newConverter); } /// @notice Update the address of curve pool. /// @param newPool The address of curve pool. function updateCurvePool(address newPool) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateCurvePool(newPool); } /// @notice Update the value of depeg price threshold. /// @param newThreshold The value of new price threshold. function updatePriceThreshold(uint256 newThreshold) external onlyRole(DEFAULT_ADMIN_ROLE) { _updatePriceThreshold(newThreshold); } /********************** * Internal Functions * **********************/ /// @dev Internal function to update the address of converter. /// @param newConverter The address of converter. function _updateConverter(address newConverter) internal { if (newConverter == address(0)) revert ErrorZeroAddress(); address oldConverter = converter; converter = newConverter; emit UpdateConverter(oldConverter, newConverter); } /// @dev Internal function to update the address of curve pool. /// @param newPool The address of curve pool. function _updateCurvePool(address newPool) internal { if (newPool == address(0)) revert ErrorZeroAddress(); address oldPool = curvePool; curvePool = newPool; emit UpdateCurvePool(oldPool, newPool); } /// @dev Internal function to update the value of depeg price threshold. /// @param newThreshold The value of new price threshold. function _updatePriceThreshold(uint256 newThreshold) internal { uint256 oldThreshold = priceThreshold; priceThreshold = newThreshold; emit UpdatePriceThreshold(oldThreshold, newThreshold); } /// @dev Internal function to do swap. /// @param srcToken The address of source token. /// @param amountIn The amount of token to use. /// @param data The callback data. /// @return amountOut The amount of token swapped. function _doSwap(address srcToken, uint256 amountIn, bytes calldata data) internal returns (uint256 amountOut) { IERC20(srcToken).forceApprove(converter, amountIn); (uint256 minOut, uint256 encoding, uint256[] memory routes) = abi.decode(data, (uint256, uint256, uint256[])); amountOut = IMultiPathConverter(converter).convert(srcToken, amountIn, encoding, routes); if (amountOut < minOut) revert ErrorInsufficientOutput(); } /// @dev Internal function to get curve ema price for fxUSD. /// @return price The value of ema price, multiplied by 1e18. function _getFxUSDEmaPrice() internal view returns (uint256 price) { address cachedCurvePool = curvePool; // gas saving address firstCoin = ICurveStableSwapNG(cachedCurvePool).coins(0); price = ICurveStableSwapNG(cachedCurvePool).price_oracle(0); if (firstCoin == fxUSD) { price = (PRECISION * PRECISION) / price; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { IAaveV3Pool } from "../../interfaces/Aave/IAaveV3Pool.sol"; import { IAaveFundingPool } from "../../interfaces/IAaveFundingPool.sol"; import { IPegKeeper } from "../../interfaces/IPegKeeper.sol"; import { WordCodec } from "../../common/codec/WordCodec.sol"; import { Math } from "../../libraries/Math.sol"; import { BasePool } from "./BasePool.sol"; contract AaveFundingPool is BasePool, IAaveFundingPool { using WordCodec for bytes32; /************* * Constants * *************/ /// @dev The offset of *open ratio* in `fundingMiscData`. uint256 private constant OPEN_RATIO_OFFSET = 0; /// @dev The offset of *open ratio step* in `fundingMiscData`. uint256 private constant OPEN_RATIO_STEP_OFFSET = 30; /// @dev The offset of *close fee ratio* in `fundingMiscData`. uint256 private constant CLOSE_FEE_RATIO_OFFSET = 90; /// @dev The offset of *funding ratio* in `fundingMiscData`. uint256 private constant FUNDING_RATIO_OFFSET = 120; /// @dev The offset of *interest rate* in `fundingMiscData`. uint256 private constant INTEREST_RATE_OFFSET = 152; /// @dev The offset of *timestamp* in `fundingMiscData`. uint256 private constant TIMESTAMP_OFFSET = 220; /// @dev The maximum value of *funding ratio*. uint256 private constant MAX_FUNDING_RATIO = 4294967295; /// @dev The minimum Aave borrow index snapshot delay. uint256 private constant MIN_SNAPSHOT_DELAY = 30 minutes; /*********************** * Immutable Variables * ***********************/ /// @dev The address of Aave V3 `LendingPool` contract. address private immutable lendingPool; /// @dev The address of asset used for interest calculation. address private immutable baseAsset; /*********** * Structs * ***********/ /// @dev The struct for AAVE borrow rate snapshot. /// @param borrowIndex The current borrow index of AAVE, multiplied by 1e27. /// @param lastInterestRate The last recorded interest rate, multiplied by 1e18. /// @param timestamp The timestamp when the snapshot is taken. struct BorrowRateSnapshot { // The initial value of `borrowIndex` is `10^27`, it is very unlikely this value will exceed `2^128`. uint128 borrowIndex; uint80 lastInterestRate; uint48 timestamp; } /********************* * Storage Variables * *********************/ /// @dev `fundingMiscData` is a storage slot that can be used to store unrelated pieces of information. /// /// - The *open ratio* is the fee ratio for opening position, multiplied by 1e9. /// - The *open ratio step* is the fee ratio step for opening position, multiplied by 1e18. /// - The *close fee ratio* is the fee ratio for closing position, multiplied by 1e9. /// - The *funding ratio* is the scalar for funding rate, multiplied by 1e9. /// The maximum value is `4.294967296`. /// /// [ open ratio | open ratio step | close fee ratio | funding ratio | reserved ] /// [ 30 bits | 60 bits | 30 bits | 32 bits | 104 bits ] /// [ MSB LSB ] bytes32 private fundingMiscData; /// @notice The snapshot for AAVE borrow rate. BorrowRateSnapshot public borrowRateSnapshot; /*************** * Constructor * ***************/ constructor(address _poolManager, address _lendingPool, address _baseAsset) BasePool(_poolManager) { _checkAddressNotZero(_lendingPool); _checkAddressNotZero(_baseAsset); lendingPool = _lendingPool; baseAsset = _baseAsset; } function initialize( address admin, string memory name_, string memory symbol_, address _collateralToken, address _priceOracle ) external initializer { __Context_init(); __ERC165_init(); __ERC721_init(name_, symbol_); __AccessControl_init(); __PoolStorage_init(_collateralToken, _priceOracle); __TickLogic_init(); __PositionLogic_init(); __BasePool_init(); _grantRole(DEFAULT_ADMIN_ROLE, admin); _updateOpenRatio(1000000, 50000000000000000); // 0.1% and 5% _updateCloseFeeRatio(1000000); // 0.1% uint256 borrowIndex = IAaveV3Pool(lendingPool).getReserveNormalizedVariableDebt(baseAsset); IAaveV3Pool.ReserveDataLegacy memory reserveData = IAaveV3Pool(lendingPool).getReserveData(baseAsset); _updateInterestRate(borrowIndex, reserveData.currentVariableBorrowRate / 1e9); } /************************* * Public View Functions * *************************/ /// @notice Get open fee ratio related parameters. /// @return ratio The value of open ratio, multiplied by 1e9. /// @return step The value of open ratio step, multiplied by 1e18. function getOpenRatio() external view returns (uint256 ratio, uint256 step) { return _getOpenRatio(); } /// @notice Return the value of funding ratio, multiplied by 1e9. function getFundingRatio() external view returns (uint256) { return _getFundingRatio(); } /// @notice Return the fee ratio for opening position, multiplied by 1e9. function getOpenFeeRatio() public view returns (uint256) { (uint256 openRatio, uint256 openRatioStep) = _getOpenRatio(); (, uint256 rate) = _getAverageInterestRate(borrowRateSnapshot); unchecked { uint256 aaveRatio = rate <= openRatioStep ? 1 : (rate - 1) / openRatioStep; return aaveRatio * openRatio; } } /// @notice Return the fee ratio for closing position, multiplied by 1e9. function getCloseFeeRatio() external view returns (uint256) { return _getCloseFeeRatio(); } /************************ * Restricted Functions * ************************/ /// @notice Update the fee ratio for opening position. /// @param ratio The open ratio value, multiplied by 1e9. /// @param step The open ratio step value, multiplied by 1e18. function updateOpenRatio(uint256 ratio, uint256 step) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateOpenRatio(ratio, step); } /// @notice Update the fee ratio for closing position. /// @param ratio The close ratio value, multiplied by 1e9. function updateCloseFeeRatio(uint256 ratio) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateCloseFeeRatio(ratio); } /// @notice Update the funding ratio. /// @param ratio The funding ratio value, multiplied by 1e9. function updateFundingRatio(uint256 ratio) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateFundingRatio(ratio); } /********************** * Internal Functions * **********************/ /// @dev Internal function to get open ratio and open ratio step. /// @return ratio The value of open ratio, multiplied by 1e9. /// @return step The value of open ratio step, multiplied by 1e18. function _getOpenRatio() internal view returns (uint256 ratio, uint256 step) { bytes32 data = fundingMiscData; ratio = data.decodeUint(OPEN_RATIO_OFFSET, 30); step = data.decodeUint(OPEN_RATIO_STEP_OFFSET, 60); } /// @dev Internal function to update the fee ratio for opening position. /// @param ratio The open ratio value, multiplied by 1e9. /// @param step The open ratio step value, multiplied by 1e18. function _updateOpenRatio(uint256 ratio, uint256 step) internal { _checkValueTooLarge(ratio, FEE_PRECISION); _checkValueTooLarge(step, PRECISION); bytes32 data = fundingMiscData; data = data.insertUint(ratio, OPEN_RATIO_OFFSET, 30); fundingMiscData = data.insertUint(step, OPEN_RATIO_STEP_OFFSET, 60); emit UpdateOpenRatio(ratio, step); } /// @dev Internal function to get the value of close ratio, multiplied by 1e9. function _getCloseFeeRatio() internal view returns (uint256) { return fundingMiscData.decodeUint(CLOSE_FEE_RATIO_OFFSET, 30); } /// @dev Internal function to update the fee ratio for closing position. /// @param newRatio The close fee ratio value, multiplied by 1e9. function _updateCloseFeeRatio(uint256 newRatio) internal { _checkValueTooLarge(newRatio, FEE_PRECISION); bytes32 data = fundingMiscData; uint256 oldRatio = data.decodeUint(CLOSE_FEE_RATIO_OFFSET, 30); fundingMiscData = data.insertUint(newRatio, CLOSE_FEE_RATIO_OFFSET, 30); emit UpdateCloseFeeRatio(oldRatio, newRatio); } /// @dev Internal function to get the value of funding ratio, multiplied by 1e9. function _getFundingRatio() internal view returns (uint256) { return fundingMiscData.decodeUint(FUNDING_RATIO_OFFSET, 32); } /// @dev Internal function to update the funding ratio. /// @param newRatio The funding ratio value, multiplied by 1e9. function _updateFundingRatio(uint256 newRatio) internal { _checkValueTooLarge(newRatio, MAX_FUNDING_RATIO); bytes32 data = fundingMiscData; uint256 oldRatio = data.decodeUint(FUNDING_RATIO_OFFSET, 32); fundingMiscData = data.insertUint(newRatio, FUNDING_RATIO_OFFSET, 32); emit UpdateFundingRatio(oldRatio, newRatio); } /// @dev Internal function to return interest rate snapshot. /// @param snapshot The previous borrow index snapshot. /// @return newBorrowIndex The current borrow index, multiplied by 1e27. /// @return rate The annual interest rate, multiplied by 1e18. function _getAverageInterestRate( BorrowRateSnapshot memory snapshot ) internal view returns (uint256 newBorrowIndex, uint256 rate) { uint256 prevBorrowIndex = snapshot.borrowIndex; newBorrowIndex = IAaveV3Pool(lendingPool).getReserveNormalizedVariableDebt(baseAsset); // absolute rate change is (new - prev) / prev // annual interest rate is (new - prev) / prev / duration * 365 days uint256 duration = block.timestamp - snapshot.timestamp; if (duration < MIN_SNAPSHOT_DELAY) { rate = snapshot.lastInterestRate; } else { rate = ((newBorrowIndex - prevBorrowIndex) * 365 days * PRECISION) / (prevBorrowIndex * duration); if (rate == 0) rate = snapshot.lastInterestRate; } } /// @dev Internal function to update interest rate snapshot. function _updateInterestRate(uint256 newBorrowIndex, uint256 lastInterestRate) internal { BorrowRateSnapshot memory snapshot = borrowRateSnapshot; // don't update snapshot when the duration is too small. if (snapshot.timestamp > 0 && block.timestamp - snapshot.timestamp < MIN_SNAPSHOT_DELAY) return; snapshot.borrowIndex = uint128(newBorrowIndex); snapshot.lastInterestRate = uint80(lastInterestRate); snapshot.timestamp = uint48(block.timestamp); borrowRateSnapshot = snapshot; emit SnapshotAaveBorrowIndex(newBorrowIndex, block.timestamp); } /// @inheritdoc BasePool function _updateCollAndDebtIndex() internal virtual override returns (uint256 newCollIndex, uint256 newDebtIndex) { (newDebtIndex, newCollIndex) = _getDebtAndCollateralIndex(); BorrowRateSnapshot memory snapshot = borrowRateSnapshot; uint256 duration = block.timestamp - snapshot.timestamp; if (duration > 0) { (uint256 borrowIndex, uint256 interestRate) = _getAverageInterestRate(snapshot); if (IPegKeeper(pegKeeper).isFundingEnabled()) { (, uint256 totalColls) = _getDebtAndCollateralShares(); uint256 totalRawColls = _convertToRawColl(totalColls, newCollIndex, Math.Rounding.Down); uint256 funding = (totalRawColls * interestRate * duration) / (365 days * PRECISION); funding = ((funding * _getFundingRatio()) / FEE_PRECISION); // update collateral index with funding costs newCollIndex = (newCollIndex * totalRawColls) / (totalRawColls - funding); _updateCollateralIndex(newCollIndex); } // update interest snapshot _updateInterestRate(borrowIndex, interestRate); } } /// @inheritdoc BasePool function _deductProtocolFees(int256 rawColl) internal view virtual override returns (uint256) { if (rawColl > 0) { // open position or add collateral uint256 feeRatio = getOpenFeeRatio(); if (feeRatio > FEE_PRECISION) feeRatio = FEE_PRECISION; return (uint256(rawColl) * feeRatio) / FEE_PRECISION; } else { // close position or remove collateral return (uint256(-rawColl) * _getCloseFeeRatio()) / FEE_PRECISION; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { IPegKeeper } from "../../interfaces/IPegKeeper.sol"; import { IPool } from "../../interfaces/IPool.sol"; import { IPoolManager } from "../../interfaces/IPoolManager.sol"; import { IPriceOracle } from "../../price-oracle/interfaces/IPriceOracle.sol"; import { WordCodec } from "../../common/codec/WordCodec.sol"; import { Math } from "../../libraries/Math.sol"; import { TickBitmap } from "../../libraries/TickBitmap.sol"; import { PositionLogic } from "./PositionLogic.sol"; import { TickLogic } from "./TickLogic.sol"; abstract contract BasePool is TickLogic, PositionLogic { using TickBitmap for mapping(int8 => uint256); using WordCodec for bytes32; /*********** * Structs * ***********/ struct OperationMemoryVar { int256 tick; uint48 node; uint256 positionColl; uint256 positionDebt; int256 newColl; int256 newDebt; uint256 collIndex; uint256 debtIndex; uint256 globalColl; uint256 globalDebt; uint256 price; } /************* * Modifiers * *************/ modifier onlyPoolManager() { if (_msgSender() != poolManager) { revert ErrorCallerNotPoolManager(); } _; } /*************** * Constructor * ***************/ constructor(address _poolManager) { _checkAddressNotZero(_poolManager); poolManager = _poolManager; fxUSD = IPoolManager(_poolManager).fxUSD(); pegKeeper = IPoolManager(_poolManager).pegKeeper(); } function __BasePool_init() internal onlyInitializing { _updateDebtIndex(E96); _updateCollateralIndex(E96); _updateDebtRatioRange(500000000000000000, 857142857142857142); // 1/2 ~ 6/7 _updateMaxRedeemRatioPerTick(200000000); // 20% } /**************************** * Public Mutated Functions * ****************************/ /// @inheritdoc IPool function operate( uint256 positionId, int256 newRawColl, int256 newRawDebt, address owner ) external onlyPoolManager returns (uint256, int256, int256, uint256) { if (newRawColl == 0 && newRawDebt == 0) revert ErrorNoSupplyAndNoBorrow(); if (newRawColl != 0 && (newRawColl > -MIN_COLLATERAL && newRawColl < MIN_COLLATERAL)) { revert ErrorCollateralTooSmall(); } if (newRawDebt != 0 && (newRawDebt > -MIN_DEBT && newRawDebt < MIN_DEBT)) { revert ErrorDebtTooSmall(); } if (newRawDebt > 0 && (_isBorrowPaused() || !IPegKeeper(pegKeeper).isBorrowAllowed())) { revert ErrorBorrowPaused(); } OperationMemoryVar memory op; // price precision and ratio precision are both 1e18, use min price here (, op.price, ) = IPriceOracle(priceOracle).getPrice(); (op.globalDebt, op.globalColl) = _getDebtAndCollateralShares(); (op.collIndex, op.debtIndex) = _updateCollAndDebtIndex(); if (positionId == 0) { positionId = _mintPosition(owner); } else { // make sure position is owned and check owner only in case of withdraw or borrow if (ownerOf(positionId) != owner && (newRawColl < 0 || newRawDebt > 0)) { revert ErrorNotPositionOwner(); } PositionInfo memory position = _getAndUpdatePosition(positionId); // temporarily remove position from tick tree for simplicity _removePositionFromTick(position); op.tick = position.tick; op.node = position.nodeId; op.positionDebt = position.debts; op.positionColl = position.colls; // cannot withdraw or borrow when the position is above liquidation ratio if (newRawColl < 0 || newRawDebt > 0) { uint256 rawColls = _convertToRawColl(op.positionColl, op.collIndex, Math.Rounding.Down); uint256 rawDebts = _convertToRawDebt(op.positionDebt, op.debtIndex, Math.Rounding.Down); (uint256 debtRatio, ) = _getLiquidateRatios(); if (rawDebts * PRECISION * PRECISION > debtRatio * rawColls * op.price) revert ErrorPositionInLiquidationMode(); } } uint256 protocolFees; // supply or withdraw if (newRawColl > 0) { protocolFees = _deductProtocolFees(newRawColl); newRawColl -= int256(protocolFees); op.newColl = int256(_convertToCollShares(uint256(newRawColl), op.collIndex, Math.Rounding.Down)); op.positionColl += uint256(op.newColl); op.globalColl += uint256(op.newColl); } else if (newRawColl < 0) { if (newRawColl == type(int256).min) { // this is max withdraw newRawColl = -int256(_convertToRawColl(op.positionColl, op.collIndex, Math.Rounding.Down)); op.newColl = -int256(op.positionColl); } else { // this is partial withdraw, rounding up removing extra wei from collateral op.newColl = -int256(_convertToCollShares(uint256(-newRawColl), op.collIndex, Math.Rounding.Up)); if (uint256(-op.newColl) > op.positionColl) revert ErrorWithdrawExceedSupply(); } unchecked { op.positionColl -= uint256(-op.newColl); op.globalColl -= uint256(-op.newColl); } protocolFees = _deductProtocolFees(newRawColl); newRawColl += int256(protocolFees); } // borrow or repay if (newRawDebt > 0) { // rounding up adding extra wei in debt op.newDebt = int256(_convertToDebtShares(uint256(newRawDebt), op.debtIndex, Math.Rounding.Up)); op.positionDebt += uint256(op.newDebt); op.globalDebt += uint256(op.newDebt); } else if (newRawDebt < 0) { if (newRawDebt == type(int256).min) { // this is max repay, rounding up amount that will be transferred in to pay back full debt: // subtracting -1 of negative debtAmount newDebt_ for safe rounding (increasing payback) newRawDebt = -int256(_convertToRawDebt(op.positionDebt, op.debtIndex, Math.Rounding.Up)); op.newDebt = -int256(op.positionDebt); } else { // this is partial repay, safe rounding up negative amount to rounding reduce payback op.newDebt = -int256(_convertToDebtShares(uint256(-newRawDebt), op.debtIndex, Math.Rounding.Up)); } op.positionDebt -= uint256(-op.newDebt); op.globalDebt -= uint256(-op.newDebt); } // final debt ratio check { // check position debt ratio is between `minDebtRatio` and `maxDebtRatio`. uint256 rawColls = _convertToRawColl(op.positionColl, op.collIndex, Math.Rounding.Down); uint256 rawDebts = _convertToRawDebt(op.positionDebt, op.debtIndex, Math.Rounding.Down); (uint256 minDebtRatio, uint256 maxDebtRatio) = _getDebtRatioRange(); if (rawDebts * PRECISION * PRECISION > maxDebtRatio * rawColls * op.price) revert ErrorDebtRatioTooLarge(); if (rawDebts * PRECISION * PRECISION < minDebtRatio * rawColls * op.price) revert ErrorDebtRatioTooSmall(); } // update position state to storage (op.tick, op.node) = _addPositionToTick(op.positionColl, op.positionDebt, true); if (op.positionColl > type(uint96).max) revert ErrorOverflow(); if (op.positionDebt > type(uint96).max) revert ErrorOverflow(); positionData[positionId] = PositionInfo(int16(op.tick), op.node, uint96(op.positionColl), uint96(op.positionDebt)); // update global state to storage _updateDebtAndCollateralShares(op.globalDebt, op.globalColl); emit PositionSnapshot(positionId, int16(op.tick), op.positionColl, op.positionDebt, op.price); return (positionId, newRawColl, newRawDebt, protocolFees); } /// @inheritdoc IPool function redeem(uint256 rawDebts) external onlyPoolManager returns (uint256 rawColls) { if (_isRedeemPaused()) revert ErrorRedeemPaused(); (uint256 cachedCollIndex, uint256 cachedDebtIndex) = _updateCollAndDebtIndex(); (uint256 cachedTotalDebts, uint256 cachedTotalColls) = _getDebtAndCollateralShares(); (, , uint256 price) = IPriceOracle(priceOracle).getPrice(); // use max price // check global debt ratio, if global debt ratio >= 1, disable redeem { uint256 totalRawColls = _convertToRawColl(cachedTotalColls, cachedCollIndex, Math.Rounding.Down); uint256 totalRawDebts = _convertToRawDebt(cachedTotalDebts, cachedDebtIndex, Math.Rounding.Down); if (totalRawDebts * PRECISION >= totalRawColls * price) revert ErrorPoolUnderCollateral(); } int16 tick = _getTopTick(); bool hasDebt = true; uint256 debtShare = _convertToDebtShares(rawDebts, cachedDebtIndex, Math.Rounding.Down); while (debtShare > 0) { if (!hasDebt) { (tick, hasDebt) = tickBitmap.nextDebtPositionWithinOneWord(tick - 1); } else { uint256 node = tickData[tick]; bytes32 value = tickTreeData[node].value; uint256 tickDebtShare = value.decodeUint(DEBT_SHARE_OFFSET, 128); // skip bad debt { uint256 tickCollShare = value.decodeUint(COLL_SHARE_OFFSET, 128); if ( _convertToRawDebt(tickDebtShare, cachedDebtIndex, Math.Rounding.Down) * PRECISION > _convertToRawColl(tickCollShare, cachedCollIndex, Math.Rounding.Down) * price ) { hasDebt = false; tick = tick; continue; } } // redeem at most `maxRedeemRatioPerTick` uint256 debtShareToRedeem = (tickDebtShare * _getMaxRedeemRatioPerTick()) / FEE_PRECISION; if (debtShareToRedeem > debtShare) debtShareToRedeem = debtShare; uint256 rawCollRedeemed = (_convertToRawDebt(debtShareToRedeem, cachedDebtIndex, Math.Rounding.Down) * PRECISION) / price; uint256 collShareRedeemed = _convertToCollShares(rawCollRedeemed, cachedCollIndex, Math.Rounding.Down); _liquidateTick(tick, collShareRedeemed, debtShareToRedeem, price); debtShare -= debtShareToRedeem; rawColls += rawCollRedeemed; cachedTotalColls -= collShareRedeemed; cachedTotalDebts -= debtShareToRedeem; (tick, hasDebt) = tickBitmap.nextDebtPositionWithinOneWord(tick - 1); } if (tick == type(int16).min) break; } _updateDebtAndCollateralShares(cachedTotalDebts, cachedTotalColls); } /// @inheritdoc IPool function rebalance(int16 tick, uint256 maxRawDebts) external onlyPoolManager returns (RebalanceResult memory result) { (uint256 cachedCollIndex, uint256 cachedDebtIndex) = _updateCollAndDebtIndex(); (, uint256 price, ) = IPriceOracle(priceOracle).getPrice(); // use min price uint256 node = tickData[tick]; bytes32 value = tickTreeData[node].value; uint256 tickRawColl = _convertToRawColl( value.decodeUint(COLL_SHARE_OFFSET, 128), cachedCollIndex, Math.Rounding.Down ); uint256 tickRawDebt = _convertToRawDebt( value.decodeUint(DEBT_SHARE_OFFSET, 128), cachedDebtIndex, Math.Rounding.Down ); (uint256 rebalanceDebtRatio, uint256 rebalanceBonusRatio) = _getRebalanceRatios(); (uint256 liquidateDebtRatio, ) = _getLiquidateRatios(); // rebalance only debt ratio >= `rebalanceDebtRatio` and ratio < `liquidateDebtRatio` if (tickRawDebt * PRECISION * PRECISION < rebalanceDebtRatio * tickRawColl * price) { revert ErrorRebalanceDebtRatioNotReached(); } if (tickRawDebt * PRECISION * PRECISION >= liquidateDebtRatio * tickRawColl * price) { revert ErrorRebalanceOnLiquidatableTick(); } // compute debts to rebalance to make debt ratio to `rebalanceDebtRatio` result.rawDebts = _getRawDebtToRebalance(tickRawColl, tickRawDebt, price, rebalanceDebtRatio, rebalanceBonusRatio); if (maxRawDebts < result.rawDebts) result.rawDebts = maxRawDebts; uint256 debtShareToRebalance = _convertToDebtShares(result.rawDebts, cachedDebtIndex, Math.Rounding.Down); result.rawColls = (result.rawDebts * PRECISION) / price; result.bonusRawColls = (result.rawColls * rebalanceBonusRatio) / FEE_PRECISION; if (result.bonusRawColls > tickRawColl - result.rawColls) { result.bonusRawColls = tickRawColl - result.rawColls; } uint256 collShareToRebalance = _convertToCollShares( result.rawColls + result.bonusRawColls, cachedCollIndex, Math.Rounding.Down ); _liquidateTick(tick, collShareToRebalance, debtShareToRebalance, price); unchecked { (uint256 totalDebts, uint256 totalColls) = _getDebtAndCollateralShares(); _updateDebtAndCollateralShares(totalDebts - debtShareToRebalance, totalColls - collShareToRebalance); } } /// @inheritdoc IPool function rebalance( uint32 positionId, uint256 maxRawDebts ) external onlyPoolManager returns (RebalanceResult memory result) { _requireOwned(positionId); (uint256 cachedCollIndex, uint256 cachedDebtIndex) = _updateCollAndDebtIndex(); (, uint256 price, ) = IPriceOracle(priceOracle).getPrice(); // use min price PositionInfo memory position = _getAndUpdatePosition(positionId); uint256 positionRawColl = _convertToRawColl(position.colls, cachedCollIndex, Math.Rounding.Down); uint256 positionRawDebt = _convertToRawDebt(position.debts, cachedDebtIndex, Math.Rounding.Down); (uint256 rebalanceDebtRatio, uint256 rebalanceBonusRatio) = _getRebalanceRatios(); // rebalance only debt ratio >= `rebalanceDebtRatio` and ratio < `liquidateDebtRatio` if (positionRawDebt * PRECISION * PRECISION < rebalanceDebtRatio * positionRawColl * price) { revert ErrorRebalanceDebtRatioNotReached(); } { (uint256 liquidateDebtRatio, ) = _getLiquidateRatios(); if (positionRawDebt * PRECISION * PRECISION >= liquidateDebtRatio * positionRawColl * price) { revert ErrorRebalanceOnLiquidatableTick(); } } _removePositionFromTick(position); // compute debts to rebalance to make debt ratio to `rebalanceDebtRatio` result.rawDebts = _getRawDebtToRebalance( positionRawColl, positionRawDebt, price, rebalanceDebtRatio, rebalanceBonusRatio ); if (maxRawDebts < result.rawDebts) result.rawDebts = maxRawDebts; uint256 debtShareToRebalance = _convertToDebtShares(result.rawDebts, cachedDebtIndex, Math.Rounding.Down); result.rawColls = (result.rawDebts * PRECISION) / price; result.bonusRawColls = (result.rawColls * rebalanceBonusRatio) / FEE_PRECISION; if (result.bonusRawColls > positionRawColl - result.rawColls) { result.bonusRawColls = positionRawColl - result.rawColls; } uint256 collShareToRebalance = _convertToCollShares( result.rawColls + result.bonusRawColls, cachedCollIndex, Math.Rounding.Down ); position.debts -= uint96(debtShareToRebalance); position.colls -= uint96(collShareToRebalance); { int256 tick; (tick, position.nodeId) = _addPositionToTick(position.colls, position.debts, false); position.tick = int16(tick); } positionData[positionId] = position; unchecked { (uint256 totalDebts, uint256 totalColls) = _getDebtAndCollateralShares(); _updateDebtAndCollateralShares(totalDebts - debtShareToRebalance, totalColls - collShareToRebalance); } emit PositionSnapshot(positionId, position.tick, position.colls, position.debts, price); } /// @inheritdoc IPool function liquidate( uint256 positionId, uint256 maxRawDebts, uint256 reservedRawColls ) external onlyPoolManager returns (LiquidateResult memory result) { _requireOwned(positionId); (uint256 cachedCollIndex, uint256 cachedDebtIndex) = _updateCollAndDebtIndex(); (, uint256 price, ) = IPriceOracle(priceOracle).getPrice(); // use min price PositionInfo memory position = _getAndUpdatePosition(positionId); uint256 positionRawColl = _convertToRawColl(position.colls, cachedCollIndex, Math.Rounding.Down); uint256 positionRawDebt = _convertToRawDebt(position.debts, cachedDebtIndex, Math.Rounding.Down); uint256 liquidateBonusRatio; // liquidate only debt ratio >= `liquidateDebtRatio` { uint256 liquidateDebtRatio; (liquidateDebtRatio, liquidateBonusRatio) = _getLiquidateRatios(); if (positionRawDebt * PRECISION * PRECISION < liquidateDebtRatio * positionRawColl * price) { revert ErrorLiquidateDebtRatioNotReached(); } } _removePositionFromTick(position); result.rawDebts = positionRawDebt; if (result.rawDebts > maxRawDebts) result.rawDebts = maxRawDebts; uint256 debtShareToLiquidate = result.rawDebts == positionRawDebt ? position.debts : _convertToDebtShares(result.rawDebts, cachedDebtIndex, Math.Rounding.Down); uint256 collShareToLiquidate; result.rawColls = (result.rawDebts * PRECISION) / price; if (positionRawColl < result.rawColls) { // adjust result.rawColls, result.rawDebts and debtShareToLiquidate result.rawColls = positionRawColl; result.rawDebts = (positionRawColl * price) / PRECISION; if (result.rawDebts > positionRawDebt) result.rawDebts = positionRawDebt; debtShareToLiquidate = result.rawDebts == positionRawDebt ? position.debts : _convertToDebtShares(result.rawDebts, cachedDebtIndex, Math.Rounding.Down); } result.bonusRawColls = (result.rawColls * liquidateBonusRatio) / FEE_PRECISION; if (result.bonusRawColls > positionRawColl - result.rawColls) { uint256 diff = result.bonusRawColls - (positionRawColl - result.rawColls); if (diff < reservedRawColls) result.bonusFromReserve = diff; else result.bonusFromReserve = reservedRawColls; result.bonusRawColls = positionRawColl - result.rawColls + result.bonusFromReserve; collShareToLiquidate = position.colls; } else { collShareToLiquidate = _convertToCollShares( result.rawColls + result.bonusRawColls, cachedCollIndex, Math.Rounding.Down ); } position.debts -= uint96(debtShareToLiquidate); position.colls -= uint96(collShareToLiquidate); unchecked { (uint256 totalDebts, uint256 totalColls) = _getDebtAndCollateralShares(); _updateDebtAndCollateralShares(totalDebts - debtShareToLiquidate, totalColls - collShareToLiquidate); } // try distribute bad debts if (position.colls == 0 && position.debts > 0) { (uint256 totalDebts, ) = _getDebtAndCollateralShares(); totalDebts -= position.debts; _updateDebtShares(totalDebts); uint256 rawBadDebt = _convertToRawDebt(position.debts, cachedDebtIndex, Math.Rounding.Down); _updateDebtIndex(cachedDebtIndex + (rawBadDebt * E96) / totalDebts); position.debts = 0; } { int256 tick; (tick, position.nodeId) = _addPositionToTick(position.colls, position.debts, false); position.tick = int16(tick); } positionData[positionId] = position; emit PositionSnapshot(positionId, position.tick, position.colls, position.debts, price); } /************************ * Restricted Functions * ************************/ /// @notice Update the borrow and redeem status. /// @param borrowStatus The new borrow status. /// @param redeemStatus The new redeem status. function updateBorrowAndRedeemStatus(bool borrowStatus, bool redeemStatus) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateBorrowStatus(borrowStatus); _updateRedeemStatus(redeemStatus); } /// @notice Update debt ratio range. /// @param minRatio The minimum allowed debt ratio to update, multiplied by 1e18. /// @param maxRatio The maximum allowed debt ratio to update, multiplied by 1e18. function updateDebtRatioRange(uint256 minRatio, uint256 maxRatio) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateDebtRatioRange(minRatio, maxRatio); } /// @notice Update maximum redeem ratio per tick. /// @param ratio The ratio to update, multiplied by 1e9. function updateMaxRedeemRatioPerTick(uint256 ratio) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateMaxRedeemRatioPerTick(ratio); } /// @notice Update ratio for rebalance. /// @param debtRatio The minimum debt ratio to start rebalance, multiplied by 1e18. /// @param bonusRatio The bonus ratio during rebalance, multiplied by 1e9. function updateRebalanceRatios(uint256 debtRatio, uint256 bonusRatio) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateRebalanceRatios(debtRatio, bonusRatio); } /// @notice Update ratio for liquidate. /// @param debtRatio The minimum debt ratio to start liquidate, multiplied by 1e18. /// @param bonusRatio The bonus ratio during liquidate, multiplied by 1e9. function updateLiquidateRatios(uint256 debtRatio, uint256 bonusRatio) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateLiquidateRatios(debtRatio, bonusRatio); } /// @notice Update the address of price oracle. /// @param newOracle The address of new price oracle. function updatePriceOracle(address newOracle) external onlyRole(DEFAULT_ADMIN_ROLE) { _updatePriceOracle(newOracle); } /********************** * Internal Functions * **********************/ /// @dev Internal function to compute the amount of debt to rebalance to reach certain debt ratio. /// @param coll The amount of collateral tokens. /// @param debt The amount of debt tokens. /// @param price The price of the collateral token. /// @param targetDebtRatio The target debt ratio, multiplied by 1e18. /// @param incentiveRatio The bonus ratio, multiplied by 1e9. /// @return rawDebts The amount of debt tokens to rebalance. function _getRawDebtToRebalance( uint256 coll, uint256 debt, uint256 price, uint256 targetDebtRatio, uint256 incentiveRatio ) internal pure returns (uint256 rawDebts) { // we have // 1. (debt - x) / (price * (coll - y * (1 + incentive))) <= target_ratio // 2. debt / (price * coll) >= target_ratio // then // => debt - x <= target * price * (coll - y * (1 + incentive)) and y = x / price // => debt - target_ratio * price * coll <= (1 - (1 + incentive) * target) * x // => x >= (debt - target_ratio * price * coll) / (1 - (1 + incentive) * target) rawDebts = (debt * PRECISION * PRECISION - targetDebtRatio * price * coll) / (PRECISION * PRECISION - (PRECISION * targetDebtRatio * (FEE_PRECISION + incentiveRatio)) / FEE_PRECISION); } /// @dev Internal function to update collateral and debt index. /// @return newCollIndex The updated collateral index. /// @return newDebtIndex The updated debt index. function _updateCollAndDebtIndex() internal virtual returns (uint256 newCollIndex, uint256 newDebtIndex); /// @dev Internal function to compute the protocol fees. /// @param rawColl The amount of collateral tokens involved. /// @return fees The expected protocol fees. function _deductProtocolFees(int256 rawColl) internal view virtual returns (uint256 fees); /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { IPool } from "../../interfaces/IPool.sol"; abstract contract PoolConstant is IPool { /************* * Constants * *************/ /// @dev The value of minimum collateral. int256 internal constant MIN_COLLATERAL = 1e9; /// @dev The value of minimum debts. int256 internal constant MIN_DEBT = 1e9; /// @dev The precision used for various calculation. uint256 internal constant PRECISION = 1e18; /// @dev The precision used for fee ratio calculation. uint256 internal constant FEE_PRECISION = 1e9; /// @dev bit operation related constants uint256 internal constant E60 = 2 ** 60; // 2^60 uint256 internal constant E96 = 2 ** 96; // 2^96 uint256 internal constant X60 = 0xfffffffffffffff; // 2^60 - 1 uint256 internal constant X96 = 0xffffffffffffffffffffffff; // 2^96 - 1 /*********************** * Immutable Variables * ***********************/ /// @inheritdoc IPool address public immutable fxUSD; /// @inheritdoc IPool address public immutable poolManager; /// @inheritdoc IPool address public immutable pegKeeper; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; abstract contract PoolErrors { /********** * Errors * **********/ /// @dev Thrown when the given address is zero. error ErrorZeroAddress(); /// @dev Thrown when the given value exceeds maximum value. error ErrorValueTooLarge(); /// @dev Thrown when the caller is not pool manager. error ErrorCallerNotPoolManager(); /// @dev Thrown when the debt amount is too small. error ErrorDebtTooSmall(); /// @dev Thrown when the collateral amount is too small. error ErrorCollateralTooSmall(); /// @dev Thrown when both collateral amount and debt amount are zero. error ErrorNoSupplyAndNoBorrow(); /// @dev Thrown when borrow is paused. error ErrorBorrowPaused(); /// @dev Thrown when redeem is paused. error ErrorRedeemPaused(); /// @dev Thrown when the caller is not position owner during withdraw or borrow. error ErrorNotPositionOwner(); /// @dev Thrown when withdraw more than supplied. error ErrorWithdrawExceedSupply(); /// @dev Thrown when the debt ratio is too small. error ErrorDebtRatioTooSmall(); /// @dev Thrown when the debt ratio is too large. error ErrorDebtRatioTooLarge(); /// @dev Thrown when pool is under collateral. error ErrorPoolUnderCollateral(); /// @dev Thrown when the current debt ratio <= rebalance debt ratio. error ErrorRebalanceDebtRatioNotReached(); /// @dev Thrown when the current debt ratio <= liquidate debt ratio. error ErrorLiquidateDebtRatioNotReached(); /// @dev Thrown when the current debt ratio > liquidate debt ratio. error ErrorPositionInLiquidationMode(); error ErrorRebalanceOnLiquidatableTick(); error ErrorRebalanceOnLiquidatablePosition(); error ErrorInsufficientCollateralToLiquidate(); error ErrorOverflow(); /********************** * Internal Functions * **********************/ /// @dev Internal function to check value not too large. /// @param value The value to check. /// @param upperBound The upper bound for the given value. function _checkValueTooLarge(uint256 value, uint256 upperBound) internal pure { if (value > upperBound) revert ErrorValueTooLarge(); } function _checkAddressNotZero(address value) internal pure { if (value == address(0)) revert ErrorZeroAddress(); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import { ERC721Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol"; import { IPool } from "../../interfaces/IPool.sol"; import { WordCodec } from "../../common/codec/WordCodec.sol"; import { PoolConstant } from "./PoolConstant.sol"; import { PoolErrors } from "./PoolErrors.sol"; abstract contract PoolStorage is ERC721Upgradeable, AccessControlUpgradeable, PoolConstant, PoolErrors { using WordCodec for bytes32; /************* * Constants * *************/ /// @dev Below are offsets of each variables in `miscData`. uint256 private constant BORROW_FLAG_OFFSET = 0; uint256 private constant REDEEM_FLAG_OFFSET = 1; uint256 private constant TOP_TICK_OFFSET = 2; uint256 private constant NEXT_POSITION_OFFSET = 18; uint256 private constant NEXT_NODE_OFFSET = 50; uint256 private constant MIN_DEBT_RATIO_OFFSET = 98; uint256 private constant MAX_DEBT_RATIO_OFFSET = 158; uint256 private constant MAX_REDEEM_RATIO_OFFSET = 218; /// @dev Below are offsets of each variables in `rebalanceRatioData`. uint256 private constant REBALANCE_DEBT_RATIO_OFFSET = 0; uint256 private constant REBALANCE_BONUS_RATIO_OFFSET = 60; uint256 private constant LIQUIDATE_DEBT_RATIO_OFFSET = 90; uint256 private constant LIQUIDATE_BONUS_RATIO_OFFSET = 150; /// @dev Below are offsets of each variables in `indexData`. uint256 private constant DEBT_INDEX_OFFSET = 0; uint256 private constant COLLATERAL_INDEX_OFFSET = 128; /// @dev Below are offsets of each variables in `sharesData`. uint256 private constant DEBT_SHARES_OFFSET = 0; uint256 private constant COLLATERAL_SHARES_OFFSET = 128; /*********** * Structs * ***********/ /// @dev if nodeId = 0, tick is not used and this position only has collateral /// /// @param tick The tick this position belongs to at the beginning. /// @param nodeId The tree node id this position belongs to at the beginning. /// @param colls The collateral shares this position has. /// @param debts The debt shares this position has. struct PositionInfo { int16 tick; uint48 nodeId; // `uint96` is enough, since we use `86` bits in `PoolManager`. uint96 colls; // `uint96` is enough, since we use `96` bits in `PoolManager`. uint96 debts; } /// @dev The compiler will pack it into two `uint256`. /// @param metadata The metadata for tree node. /// ```text /// * Field Bits Index Comments /// * parent 48 0 The index for parent tree node. /// * tick 16 48 The original tick for this tree node. /// * coll ratio 64 64 The remained coll share ratio base on parent node, the value is real ratio * 2^60. /// * debt ratio 64 128 The remained debt share ratio base on parent node, the value is real ratio * 2^60. /// ``` /// @param value The value for tree node /// ```text /// * Field Bits Index Comments /// * coll share 128 0 The original total coll share before rebalance or redeem. /// * debt share 128 128 The original total debt share before rebalance or redeem. /// ``` struct TickTreeNode { bytes32 metadata; bytes32 value; } /********************* * Storage Variables * *********************/ /// @inheritdoc IPool address public collateralToken; /// @inheritdoc IPool address public priceOracle; /// @dev `miscData` is a storage slot that can be used to store unrelated pieces of information. /// /// - The *borrow flag* indicates whether borrow fxUSD is allowed, 1 means paused. /// - The *redeem flag* indicates whether redeem fxUSD is allowed, 1 means paused. /// - The *top tick* is the largest tick with debts. /// - The *next position* is the next unassigned position id. /// - The *next node* is the next unassigned tree node id. /// - The *min debt ratio* is the minimum allowed debt ratio, multiplied by 1e18. /// - The *max debt ratio* is the maximum allowed debt ratio, multiplied by 1e18. /// - The *max redeem ratio* is the maximum allowed redeem ratio per tick, multiplied by 1e9. /// /// [ borrow flag | redeem flag | top tick | next position | next node | min debt ratio | max debt ratio | max redeem ratio | reserved ] /// [ 1 bit | 1 bit | 16 bits | 32 bits | 48 bits | 60 bits | 60 bits | 30 bits | 8 bits ] /// [ MSB LSB ] bytes32 private miscData; /// @dev `rebalanceRatioData` is a storage slot used to store rebalance and liquidate information. /// /// - The *rebalance debt ratio* is the min debt ratio to start rebalance, multiplied by 1e18. /// - The *rebalance bonus ratio* is the bonus ratio during rebalance, multiplied by 1e9. /// - The *liquidate debt ratio* is the min debt ratio to start liquidate, multiplied by 1e18. /// - The *liquidate bonus ratio* is the bonus ratio during liquidate, multiplied by 1e9. /// /// [ rebalance debt ratio | rebalance bonus ratio | liquidate debt ratio | liquidate bonus ratio | reserved ] /// [ 60 bits | 30 bits | 60 bits | 30 bits | 76 bits ] /// [ MSB LSB ] bytes32 private rebalanceRatioData; /// @dev `indexData` is a storage slot used to store debt/collateral index. /// /// - The *debt index* is the index for each debt shares, only increasing, starting from 2^96, max 2^128-1. /// - The *collateral index* is the index for each collateral shares, only increasing, starting from 2^96, max 2^128-1 /// /// [ debt index | collateral index ] /// [ 128 bits | 128 bits ] /// [ MSB LSB ] bytes32 private indexData; /// @dev `sharesData` is a storage slot used to store debt/collateral shares. /// /// - The *debt shares* is the total debt shares. The actual number of total debts /// is `<debt shares> * <debt index>`. /// - The *collateral shares* is the total collateral shares. The actual number of /// total collateral is `<collateral shares> / <collateral index>`. /// /// [ debt shares | collateral shares ] /// [ 128 bits | 128 bits ] /// [ MSB LSB ] bytes32 private sharesData; /// @dev Mapping from position id to position information. mapping(uint256 => PositionInfo) public positionData; /// @dev Mapping from position id to position metadata. /// [ open timestamp | reserved ] /// [ 40 bits | 216 bits ] /// [ MSB LSB ] mapping(uint256 => bytes32) public positionMetadata; /// @dev The bitmap for ticks with debts. mapping(int8 => uint256) public tickBitmap; /// @dev Mapping from tick to tree node id. mapping(int256 => uint48) public tickData; /// @dev Mapping from tree node id to tree node data. mapping(uint256 => TickTreeNode) public tickTreeData; /*************** * Constructor * ***************/ function __PoolStorage_init(address _collateralToken, address _priceOracle) internal onlyInitializing { _checkAddressNotZero(_collateralToken); collateralToken = _collateralToken; _updatePriceOracle(_priceOracle); } /************************* * Public View Functions * *************************/ /// @inheritdoc AccessControlUpgradeable function supportsInterface( bytes4 interfaceId ) public view virtual override(AccessControlUpgradeable, ERC721Upgradeable) returns (bool) { return super.supportsInterface(interfaceId); } /// @inheritdoc IPool function isBorrowPaused() external view returns (bool) { return _isBorrowPaused(); } /// @inheritdoc IPool function isRedeemPaused() external view returns (bool) { return _isRedeemPaused(); } /// @inheritdoc IPool function getTopTick() external view returns (int16) { return _getTopTick(); } /// @inheritdoc IPool function getNextPositionId() external view returns (uint32) { return _getNextPositionId(); } /// @inheritdoc IPool function getNextTreeNodeId() external view returns (uint48) { return _getNextTreeNodeId(); } /// @inheritdoc IPool function getDebtRatioRange() external view returns (uint256, uint256) { return _getDebtRatioRange(); } /// @inheritdoc IPool function getMaxRedeemRatioPerTick() external view returns (uint256) { return _getMaxRedeemRatioPerTick(); } /// @inheritdoc IPool function getRebalanceRatios() external view returns (uint256, uint256) { return _getRebalanceRatios(); } /// @inheritdoc IPool function getLiquidateRatios() external view returns (uint256, uint256) { return _getLiquidateRatios(); } /// @inheritdoc IPool function getDebtAndCollateralIndex() external view returns (uint256, uint256) { return _getDebtAndCollateralIndex(); } /// @inheritdoc IPool function getDebtAndCollateralShares() external view returns (uint256, uint256) { return _getDebtAndCollateralShares(); } /********************** * Internal Functions * **********************/ /// @dev Internal function to update price oracle. /// @param newOracle The address of new price oracle; function _updatePriceOracle(address newOracle) internal { _checkAddressNotZero(newOracle); address oldOracle = priceOracle; priceOracle = newOracle; emit UpdatePriceOracle(oldOracle, newOracle); } /************************************* * Internal Functions For `miscData` * *************************************/ /// @dev Internal function to get the borrow pause status. function _isBorrowPaused() internal view returns (bool) { return miscData.decodeBool(BORROW_FLAG_OFFSET); } /// @dev Internal function to update borrow pause status. /// @param status The status to update. function _updateBorrowStatus(bool status) internal { miscData = miscData.insertBool(status, BORROW_FLAG_OFFSET); emit UpdateBorrowStatus(status); } /// @dev Internal function to get the redeem pause status. function _isRedeemPaused() internal view returns (bool) { return miscData.decodeBool(REDEEM_FLAG_OFFSET); } /// @dev Internal function to update redeem pause status. /// @param status The status to update. function _updateRedeemStatus(bool status) internal { miscData = miscData.insertBool(status, REDEEM_FLAG_OFFSET); emit UpdateRedeemStatus(status); } /// @dev Internal function to get the value of top tick. function _getTopTick() internal view returns (int16) { return int16(miscData.decodeInt(TOP_TICK_OFFSET, 16)); } /// @dev Internal function to update the top tick. /// @param tick The new top tick. function _updateTopTick(int16 tick) internal { miscData = miscData.insertInt(tick, TOP_TICK_OFFSET, 16); } /// @dev Internal function to get next available position id. function _getNextPositionId() internal view returns (uint32) { return uint32(miscData.decodeUint(NEXT_POSITION_OFFSET, 32)); } /// @dev Internal function to update next available position id. /// @param id The position id to update. function _updateNextPositionId(uint32 id) internal { miscData = miscData.insertUint(id, NEXT_POSITION_OFFSET, 32); } /// @dev Internal function to get next available tree node id. function _getNextTreeNodeId() internal view returns (uint48) { return uint48(miscData.decodeUint(NEXT_NODE_OFFSET, 48)); } /// @dev Internal function to update next available tree node id. /// @param id The tree node id to update. function _updateNextTreeNodeId(uint48 id) internal { miscData = miscData.insertUint(id, NEXT_NODE_OFFSET, 48); } /// @dev Internal function to get `minDebtRatio` and `maxDebtRatio`, both multiplied by 1e18. function _getDebtRatioRange() internal view returns (uint256 minDebtRatio, uint256 maxDebtRatio) { bytes32 data = miscData; minDebtRatio = data.decodeUint(MIN_DEBT_RATIO_OFFSET, 60); maxDebtRatio = data.decodeUint(MAX_DEBT_RATIO_OFFSET, 60); } /// @dev Internal function to update debt ratio range. /// @param minDebtRatio The minimum allowed debt ratio to update, multiplied by 1e18. /// @param maxDebtRatio The maximum allowed debt ratio to update, multiplied by 1e18. function _updateDebtRatioRange(uint256 minDebtRatio, uint256 maxDebtRatio) internal { _checkValueTooLarge(minDebtRatio, maxDebtRatio); _checkValueTooLarge(maxDebtRatio, PRECISION); bytes32 data = miscData; data = data.insertUint(minDebtRatio, MIN_DEBT_RATIO_OFFSET, 60); miscData = data.insertUint(maxDebtRatio, MAX_DEBT_RATIO_OFFSET, 60); emit UpdateDebtRatioRange(minDebtRatio, maxDebtRatio); } /// @dev Internal function to get the `maxRedeemRatioPerTick`. function _getMaxRedeemRatioPerTick() internal view returns (uint256) { return miscData.decodeUint(MAX_REDEEM_RATIO_OFFSET, 30); } /// @dev Internal function to update maximum redeem ratio per tick. /// @param ratio The ratio to update, multiplied by 1e9. function _updateMaxRedeemRatioPerTick(uint256 ratio) internal { _checkValueTooLarge(ratio, FEE_PRECISION); miscData = miscData.insertUint(ratio, MAX_REDEEM_RATIO_OFFSET, 30); emit UpdateMaxRedeemRatioPerTick(ratio); } /*********************************************** * Internal Functions For `rebalanceRatioData` * ***********************************************/ /// @dev Internal function to get `debtRatio` and `bonusRatio` for rebalance. /// @return debtRatio The minimum debt ratio to start rebalance, multiplied by 1e18. /// @return bonusRatio The bonus ratio during rebalance, multiplied by 1e9. function _getRebalanceRatios() internal view returns (uint256 debtRatio, uint256 bonusRatio) { bytes32 data = rebalanceRatioData; debtRatio = data.decodeUint(REBALANCE_DEBT_RATIO_OFFSET, 60); bonusRatio = data.decodeUint(REBALANCE_BONUS_RATIO_OFFSET, 30); } /// @dev Internal function to update ratio for rebalance. /// @param debtRatio The minimum debt ratio to start rebalance, multiplied by 1e18. /// @param bonusRatio The bonus ratio during rebalance, multiplied by 1e9. function _updateRebalanceRatios(uint256 debtRatio, uint256 bonusRatio) internal { _checkValueTooLarge(debtRatio, PRECISION); _checkValueTooLarge(bonusRatio, FEE_PRECISION); bytes32 data = rebalanceRatioData; data = data.insertUint(debtRatio, REBALANCE_DEBT_RATIO_OFFSET, 60); rebalanceRatioData = data.insertUint(bonusRatio, REBALANCE_BONUS_RATIO_OFFSET, 30); emit UpdateRebalanceRatios(debtRatio, bonusRatio); } /// @dev Internal function to get `debtRatio` and `bonusRatio` for liquidate. /// @return debtRatio The minimum debt ratio to start liquidate, multiplied by 1e18. /// @return bonusRatio The bonus ratio during liquidate, multiplied by 1e9. function _getLiquidateRatios() internal view returns (uint256 debtRatio, uint256 bonusRatio) { bytes32 data = rebalanceRatioData; debtRatio = data.decodeUint(LIQUIDATE_DEBT_RATIO_OFFSET, 60); bonusRatio = data.decodeUint(LIQUIDATE_BONUS_RATIO_OFFSET, 30); } /// @dev Internal function to update ratio for liquidate. /// @param debtRatio The minimum debt ratio to start liquidate, multiplied by 1e18. /// @param bonusRatio The bonus ratio during liquidate, multiplied by 1e9. function _updateLiquidateRatios(uint256 debtRatio, uint256 bonusRatio) internal { _checkValueTooLarge(debtRatio, PRECISION); _checkValueTooLarge(bonusRatio, FEE_PRECISION); bytes32 data = rebalanceRatioData; data = data.insertUint(debtRatio, LIQUIDATE_DEBT_RATIO_OFFSET, 60); rebalanceRatioData = data.insertUint(bonusRatio, LIQUIDATE_BONUS_RATIO_OFFSET, 30); emit UpdateLiquidateRatios(debtRatio, bonusRatio); } /************************************** * Internal Functions For `indexData` * **************************************/ /// @dev Internal function to get debt and collateral index. /// @return debtIndex The index for debt shares. /// @return collIndex The index for collateral shares. function _getDebtAndCollateralIndex() internal view returns (uint256 debtIndex, uint256 collIndex) { bytes32 data = indexData; debtIndex = data.decodeUint(DEBT_INDEX_OFFSET, 128); collIndex = data.decodeUint(COLLATERAL_INDEX_OFFSET, 128); } /// @dev Internal function to update debt index. /// @param index The debt index to update. function _updateDebtIndex(uint256 index) internal { indexData = indexData.insertUint(index, DEBT_INDEX_OFFSET, 128); emit DebtIndexSnapshot(index); } /// @dev Internal function to update collateral index. /// @param index The collateral index to update. function _updateCollateralIndex(uint256 index) internal { indexData = indexData.insertUint(index, COLLATERAL_INDEX_OFFSET, 128); emit CollateralIndexSnapshot(index); } /************************************** * Internal Functions For `sharesData` * **************************************/ /// @dev Internal function to get debt and collateral shares. /// @return debtShares The total number of debt shares. /// @return collShares The total number of collateral shares. function _getDebtAndCollateralShares() internal view returns (uint256 debtShares, uint256 collShares) { bytes32 data = sharesData; debtShares = data.decodeUint(DEBT_SHARES_OFFSET, 128); collShares = data.decodeUint(COLLATERAL_SHARES_OFFSET, 128); } /// @dev Internal function to update debt and collateral shares. /// @param debtShares The debt shares to update. /// @param collShares The collateral shares to update. function _updateDebtAndCollateralShares(uint256 debtShares, uint256 collShares) internal { bytes32 data = sharesData; data = data.insertUint(debtShares, DEBT_SHARES_OFFSET, 128); sharesData = data.insertUint(collShares, COLLATERAL_SHARES_OFFSET, 128); } /// @dev Internal function to update debt shares. /// @param shares The debt shares to update. function _updateDebtShares(uint256 shares) internal { sharesData = sharesData.insertUint(shares, DEBT_SHARES_OFFSET, 128); } /// @dev Internal function to update collateral shares. /// @param shares The collateral shares to update. function _updateCollateralShares(uint256 shares) internal { sharesData = sharesData.insertUint(shares, COLLATERAL_SHARES_OFFSET, 128); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. */ uint256[40] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { IPool } from "../../interfaces/IPool.sol"; import { IPriceOracle } from "../../price-oracle/interfaces/IPriceOracle.sol"; import { WordCodec } from "../../common/codec/WordCodec.sol"; import { Math } from "../../libraries/Math.sol"; import { TickLogic } from "./TickLogic.sol"; abstract contract PositionLogic is TickLogic { using WordCodec for bytes32; /*************** * Constructor * ***************/ function __PositionLogic_init() internal onlyInitializing { _updateNextPositionId(1); } /************************* * Public View Functions * *************************/ /// @inheritdoc IPool function getPosition(uint256 tokenId) public view returns (uint256 rawColls, uint256 rawDebts) { // compute actual shares PositionInfo memory position = positionData[tokenId]; rawColls = position.colls; rawDebts = position.debts; if (position.nodeId > 0) { (, uint256 collRatio, uint256 debtRatio) = _getRootNode(position.nodeId); rawColls = (rawColls * collRatio) >> 60; rawDebts = (rawDebts * debtRatio) >> 60; } // convert shares to actual amount (uint256 debtIndex, uint256 collIndex) = _getDebtAndCollateralIndex(); rawColls = _convertToRawColl(rawColls, collIndex, Math.Rounding.Down); rawDebts = _convertToRawDebt(rawDebts, debtIndex, Math.Rounding.Down); } /// @inheritdoc IPool function getPositionDebtRatio(uint256 tokenId) external view returns (uint256 debtRatio) { (uint256 rawColls, uint256 rawDebts) = getPosition(tokenId); // price precision and ratio precision are both 1e18, use anchor price here (uint256 price, , ) = IPriceOracle(priceOracle).getPrice(); if (rawColls == 0) return 0; return (rawDebts * PRECISION * PRECISION) / (price * rawColls); } /// @inheritdoc IPool function getTotalRawCollaterals() external view returns (uint256) { (, uint256 totalColls) = _getDebtAndCollateralShares(); (, uint256 collIndex) = _getDebtAndCollateralIndex(); return _convertToRawColl(totalColls, collIndex, Math.Rounding.Down); } /// @inheritdoc IPool function getTotalRawDebts() external view returns (uint256) { (uint256 totalDebts, ) = _getDebtAndCollateralShares(); (uint256 debtIndex, ) = _getDebtAndCollateralIndex(); return _convertToRawDebt(totalDebts, debtIndex, Math.Rounding.Down); } /********************** * Internal Functions * **********************/ /// @dev Internal function to mint a new position. /// @param owner The address of position owner. /// @return positionId The id of the position. function _mintPosition(address owner) internal returns (uint32 positionId) { unchecked { positionId = _getNextPositionId(); _updateNextPositionId(positionId + 1); } positionMetadata[positionId] = bytes32(0).insertUint(block.timestamp, 0, 40); _mint(owner, positionId); } /// @dev Internal function to get and update position. /// @param tokenId The id of the position. /// @return position The position struct. function _getAndUpdatePosition(uint256 tokenId) internal returns (PositionInfo memory position) { position = positionData[tokenId]; if (position.nodeId > 0) { (uint256 root, uint256 collRatio, uint256 debtRatio) = _getRootNodeAndCompress(position.nodeId); position.colls = uint96((position.colls * collRatio) >> 60); position.debts = uint96((position.debts * debtRatio) >> 60); position.nodeId = uint32(root); positionData[tokenId] = position; } } /// @dev Internal function to convert raw collateral amounts to collateral shares. function _convertToCollShares( uint256 raw, uint256 index, Math.Rounding rounding ) internal pure returns (uint256 shares) { shares = Math.mulDiv(raw, index, E96, rounding); } /// @dev Internal function to convert raw debt amounts to debt shares. function _convertToDebtShares( uint256 raw, uint256 index, Math.Rounding rounding ) internal pure returns (uint256 shares) { shares = Math.mulDiv(raw, E96, index, rounding); } /// @dev Internal function to convert raw collateral shares to collateral amounts. function _convertToRawColl( uint256 shares, uint256 index, Math.Rounding rounding ) internal pure returns (uint256 raw) { raw = Math.mulDiv(shares, E96, index, rounding); } /// @dev Internal function to convert raw debt shares to debt amounts. function _convertToRawDebt( uint256 shares, uint256 index, Math.Rounding rounding ) internal pure returns (uint256 raw) { raw = Math.mulDiv(shares, index, E96, rounding); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { WordCodec } from "../../common/codec/WordCodec.sol"; import { TickBitmap } from "../../libraries/TickBitmap.sol"; import { TickMath } from "../../libraries/TickMath.sol"; import { PoolStorage } from "./PoolStorage.sol"; abstract contract TickLogic is PoolStorage { using TickBitmap for mapping(int8 => uint256); using WordCodec for bytes32; /************* * Constants * *************/ /// @dev Below are offsets of each variables in `TickTreeNode.metadata`. uint256 private constant PARENT_OFFSET = 0; uint256 private constant TICK_OFFSET = 48; uint256 private constant COLL_RATIO_OFFSET = 64; uint256 private constant DEBT_RATIO_OFFSET = 128; /// @dev Below are offsets of each variables in `TickTreeNode.value`. uint256 internal constant COLL_SHARE_OFFSET = 0; uint256 internal constant DEBT_SHARE_OFFSET = 128; /*************** * Constructor * ***************/ function __TickLogic_init() internal onlyInitializing { _updateNextTreeNodeId(1); _updateTopTick(type(int16).min); } /********************** * Internal Functions * **********************/ /// @dev Internal function to get the root of the given tree node. /// @param node The id of the given tree node. /// @return root The root node id. /// @return collRatio The actual collateral ratio of the given node, multiplied by 2^60. /// @return debtRatio The actual debt ratio of the given node, multiplied by 2^60. function _getRootNode(uint256 node) internal view returns (uint256 root, uint256 collRatio, uint256 debtRatio) { collRatio = E60; debtRatio = E60; while (true) { bytes32 metadata = tickTreeData[node].metadata; uint256 parent = metadata.decodeUint(PARENT_OFFSET, 48); collRatio = (collRatio * metadata.decodeUint(COLL_RATIO_OFFSET, 64)) >> 60; debtRatio = (debtRatio * metadata.decodeUint(DEBT_RATIO_OFFSET, 64)) >> 60; if (parent == 0) break; node = parent; } root = node; } /// @dev Internal function to get the root of the given tree node and compress path. /// @param node The id of the given tree node. /// @return root The root node id. /// @return collRatio The actual collateral ratio of the given node, multiplied by 2^60. /// @return debtRatio The actual debt ratio of the given node, multiplied by 2^60. function _getRootNodeAndCompress(uint256 node) internal returns (uint256 root, uint256 collRatio, uint256 debtRatio) { // @note We can change it to non-recursive version to avoid stack overflow. Normally, the depth should be `log(n)`, // where `n` is the total number of tree nodes. So we don't need to worry much about this. bytes32 metadata = tickTreeData[node].metadata; uint256 parent = metadata.decodeUint(PARENT_OFFSET, 48); collRatio = metadata.decodeUint(COLL_RATIO_OFFSET, 64); debtRatio = metadata.decodeUint(DEBT_RATIO_OFFSET, 64); if (parent == 0) { root = node; } else { uint256 collRatioCompressed; uint256 debtRatioCompressed; (root, collRatioCompressed, debtRatioCompressed) = _getRootNodeAndCompress(parent); collRatio = (collRatio * collRatioCompressed) >> 60; debtRatio = (debtRatio * debtRatioCompressed) >> 60; metadata = metadata.insertUint(root, PARENT_OFFSET, 48); metadata = metadata.insertUint(collRatio, COLL_RATIO_OFFSET, 64); metadata = metadata.insertUint(debtRatio, DEBT_RATIO_OFFSET, 64); tickTreeData[node].metadata = metadata; } } /// @dev Internal function to create a new tree node. /// @param tick The tick where this tree node belongs to. /// @return node The created tree node id. function _newTickTreeNode(int16 tick) internal returns (uint48 node) { unchecked { node = _getNextTreeNodeId(); _updateNextTreeNodeId(node + 1); } tickData[tick] = node; bytes32 metadata = bytes32(0); metadata = metadata.insertInt(tick, TICK_OFFSET, 16); // set tick metadata = metadata.insertUint(E60, COLL_RATIO_OFFSET, 64); // set coll ratio metadata = metadata.insertUint(E60, DEBT_RATIO_OFFSET, 64); // set debt ratio tickTreeData[node].metadata = metadata; } /// @dev Internal function to find first tick such that `TickMath.getRatioAtTick(tick) >= debts/colls`. /// @param colls The collateral shares. /// @param debts The debt shares. /// @return tick The value of found first tick. function _getTick(uint256 colls, uint256 debts) internal pure returns (int256 tick) { uint256 ratio = (debts * TickMath.ZERO_TICK_SCALED_RATIO) / colls; uint256 ratioAtTick; (tick, ratioAtTick) = TickMath.getTickAtRatio(ratio); if (ratio != ratioAtTick) { tick++; ratio = (ratioAtTick * 10015) / 10000; } } /// @dev Internal function to retrieve or create a tree node. /// @param tick The tick where this tree node belongs to. /// @return node The tree node id. function _getOrCreateTickNode(int256 tick) internal returns (uint48 node) { node = tickData[tick]; if (node == 0) { node = _newTickTreeNode(int16(tick)); } } /// @dev Internal function to add position collaterals and debts to some tick. /// @param colls The collateral shares. /// @param debts The debt shares. /// @param checkDebts Whether we should check the value of `debts`. /// @return tick The tick where this position belongs to. /// @return node The corresponding tree node id for this tick. function _addPositionToTick( uint256 colls, uint256 debts, bool checkDebts ) internal returns (int256 tick, uint48 node) { if (debts > 0) { if (checkDebts && int256(debts) < MIN_DEBT) { revert ErrorDebtTooSmall(); } tick = _getTick(colls, debts); node = _getOrCreateTickNode(tick); bytes32 value = tickTreeData[node].value; uint256 newColls = value.decodeUint(COLL_SHARE_OFFSET, 128) + colls; uint256 newDebts = value.decodeUint(DEBT_SHARE_OFFSET, 128) + debts; value = value.insertUint(newColls, COLL_SHARE_OFFSET, 128); value = value.insertUint(newDebts, DEBT_SHARE_OFFSET, 128); tickTreeData[node].value = value; if (newDebts == debts) { tickBitmap.flipTick(int16(tick)); } // update top tick if (tick > _getTopTick()) { _updateTopTick(int16(tick)); } } } /// @dev Internal function to remove position from tick. /// @param position The position struct to remove. function _removePositionFromTick(PositionInfo memory position) internal { if (position.nodeId == 0) return; bytes32 value = tickTreeData[position.nodeId].value; uint256 newColls = value.decodeUint(COLL_SHARE_OFFSET, 128) - position.colls; uint256 newDebts = value.decodeUint(DEBT_SHARE_OFFSET, 128) - position.debts; value = value.insertUint(newColls, COLL_SHARE_OFFSET, 128); value = value.insertUint(newDebts, DEBT_SHARE_OFFSET, 128); tickTreeData[position.nodeId].value = value; if (newDebts == 0) { int16 tick = int16(tickTreeData[position.nodeId].metadata.decodeInt(TICK_OFFSET, 16)); tickBitmap.flipTick(tick); // top tick gone, update it to new one int16 topTick = _getTopTick(); if (topTick == tick) { _resetTopTick(topTick); } } } /// @dev Internal function to liquidate a tick. /// The caller make sure `max(liquidatedColl, liquidatedDebt) > 0`. /// /// @param tick The id of tick to liquidate. /// @param liquidatedColl The amount of collateral shares liquidated. /// @param liquidatedDebt The amount of debt shares liquidated. function _liquidateTick(int16 tick, uint256 liquidatedColl, uint256 liquidatedDebt, uint256 price) internal { uint48 node = tickData[tick]; // create new tree node for this tick _newTickTreeNode(tick); // clear bitmap first, and it will be updated later if needed. tickBitmap.flipTick(tick); bytes32 value = tickTreeData[node].value; bytes32 metadata = tickTreeData[node].metadata; uint256 tickColl = value.decodeUint(COLL_SHARE_OFFSET, 128); uint256 tickDebt = value.decodeUint(DEBT_SHARE_OFFSET, 128); uint256 tickCollAfter = tickColl - liquidatedColl; uint256 tickDebtAfter = tickDebt - liquidatedDebt; uint256 collRatio = (tickCollAfter * E60) / tickColl; uint256 debtRatio = (tickDebtAfter * E60) / tickDebt; // update metadata metadata = metadata.insertUint(collRatio, COLL_RATIO_OFFSET, 64); metadata = metadata.insertUint(debtRatio, DEBT_RATIO_OFFSET, 64); int256 newTick = type(int256).min; if (tickDebtAfter > 0) { // partial liquidated, move funds to another tick uint48 parentNode; (newTick, parentNode) = _addPositionToTick(tickCollAfter, tickDebtAfter, false); metadata = metadata.insertUint(parentNode, PARENT_OFFSET, 48); } emit TickMovement(tick, int16(newTick), tickCollAfter, tickDebtAfter, price); // top tick liquidated, update it to new one int16 topTick = _getTopTick(); if (topTick == tick && newTick != int256(tick)) { _resetTopTick(topTick); } tickTreeData[node].metadata = metadata; } /// @dev Internal function to reset top tick. /// @param oldTopTick The previous value of top tick. function _resetTopTick(int16 oldTopTick) internal { while (oldTopTick > type(int16).min) { bool hasDebt; (oldTopTick, hasDebt) = tickBitmap.nextDebtPositionWithinOneWord(oldTopTick - 1); if (hasDebt) break; } _updateTopTick(oldTopTick); } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { IFxUSDRegeneracy } from "../interfaces/IFxUSDRegeneracy.sol"; import { IPool } from "../interfaces/IPool.sol"; import { IPoolManager } from "../interfaces/IPoolManager.sol"; import { IReservePool } from "../interfaces/IReservePool.sol"; import { IRewardSplitter } from "../interfaces/IRewardSplitter.sol"; import { IFxUSDBasePool } from "../interfaces/IFxUSDBasePool.sol"; import { IRateProvider } from "../rate-provider/interfaces/IRateProvider.sol"; import { WordCodec } from "../common/codec/WordCodec.sol"; import { AssetManagement } from "../fund/AssetManagement.sol"; import { FlashLoans } from "./FlashLoans.sol"; import { ProtocolFees } from "./ProtocolFees.sol"; contract PoolManager is ProtocolFees, FlashLoans, AssetManagement, IPoolManager { using EnumerableSet for EnumerableSet.AddressSet; using SafeERC20 for IERC20; using WordCodec for bytes32; /********** * Errors * **********/ error ErrorCollateralExceedCapacity(); error ErrorDebtExceedCapacity(); error ErrorPoolNotRegistered(); error ErrorInvalidPool(); error ErrorCallerNotFxUSDSave(); error ErrorRedeemExceedBalance(); error ErrorInsufficientRedeemedCollateral(); /************* * Constants * *************/ /// @dev The precision for token rate. uint256 internal constant PRECISION = 1e18; /// @dev The precision for token rate. int256 internal constant PRECISION_I256 = 1e18; bytes32 private constant OPERATOR_ROLE = keccak256("OPERATOR_ROLE"); /*********************** * Immutable Variables * ***********************/ /// @inheritdoc IPoolManager address public immutable fxUSD; /// @inheritdoc IPoolManager address public immutable fxBASE; /// @inheritdoc IPoolManager address public immutable pegKeeper; /*********** * Structs * ***********/ /// @dev The struct for pool information. /// @param collateralData The data for collateral. /// ```text /// * Field Bits Index Comments /// * collateral capacity 85 0 The maximum allowed amount of collateral tokens. /// * collateral balance 85 85 The amount of collateral tokens deposited. /// * raw collateral balance 86 170 The amount of raw collateral tokens (without token rate) managed in pool. /// ``` /// @param debtData The data for debt. /// ```text /// * Field Bits Index Comments /// * debt capacity 96 0 The maximum allowed amount of debt tokens. /// * debt balance 96 96 The amount of debt tokens borrowed. /// * reserved 64 192 Reserved data. /// ``` struct PoolStruct { bytes32 collateralData; bytes32 debtData; } /// @dev The struct for token rate information. /// @param scalar The token scalar to reach 18 decimals. /// @param rateProvider The address of token rate provider. struct TokenRate { uint96 scalar; address rateProvider; } /// @dev Memory variables for liquidate or rebalance. /// @param stablePrice The USD price of stable token (with scalar). /// @param scalingFactor The scaling factor for collateral token. /// @param collateralToken The address of collateral token. /// @param rawColls The amount of raw collateral tokens liquidated or rebalanced, including bonus. /// @param bonusRawColls The amount of raw collateral tokens used as bonus. /// @param rawDebts The amount of raw debt tokens liquidated or rebalanced. struct LiquidateOrRebalanceMemoryVar { uint256 stablePrice; uint256 scalingFactor; address collateralToken; uint256 rawColls; uint256 bonusRawColls; uint256 rawDebts; } /********************* * Storage Variables * *********************/ /// @dev The list of registered pools. EnumerableSet.AddressSet private pools; /// @notice Mapping to pool address to pool struct. mapping(address => PoolStruct) private poolInfo; /// @notice Mapping from pool address to rewards splitter. mapping(address => address) public rewardSplitter; /// @notice Mapping from token address to token rate struct. mapping(address => TokenRate) public tokenRates; /// @notice The threshold for permissioned liquidate or rebalance. uint256 public permissionedLiquidationThreshold; /************* * Modifiers * *************/ modifier onlyRegisteredPool(address pool) { if (!pools.contains(pool)) revert ErrorPoolNotRegistered(); _; } modifier onlyFxUSDSave() { if (_msgSender() != fxBASE) { // allow permissonless rebalance or liquidate when insufficient fxUSD/USDC in fxBASE. uint256 totalYieldToken = IFxUSDBasePool(fxBASE).totalYieldToken(); uint256 totalStableToken = IFxUSDBasePool(fxBASE).totalStableToken(); uint256 price = IFxUSDBasePool(fxBASE).getStableTokenPriceWithScale(); if (totalYieldToken + (totalStableToken * price) / PRECISION >= permissionedLiquidationThreshold) { revert ErrorCallerNotFxUSDSave(); } } _; } /*************** * Constructor * ***************/ constructor(address _fxUSD, address _fxBASE, address _pegKeeper) { fxUSD = _fxUSD; fxBASE = _fxBASE; pegKeeper = _pegKeeper; } function initialize( address admin, uint256 _expenseRatio, uint256 _harvesterRatio, uint256 _flashLoanFeeRatio, address _treasury, address _revenuePool, address _reservePool ) external initializer { __Context_init(); __AccessControl_init(); __ERC165_init(); _grantRole(DEFAULT_ADMIN_ROLE, admin); __ProtocolFees_init(_expenseRatio, _harvesterRatio, _flashLoanFeeRatio, _treasury, _revenuePool, _reservePool); __FlashLoans_init(); // default 10000 fxUSD _updateThreshold(10000 ether); } /************************* * Public View Functions * *************************/ /// @notice Return the pool information. /// @param pool The address of pool to query. /// @return collateralCapacity The maximum allowed amount of collateral tokens. /// @return collateralBalance The amount of collateral tokens deposited. /// @return debtCapacity The maximum allowed amount of debt tokens. /// @return debtBalance The amount of debt tokens borrowed. function getPoolInfo( address pool ) external view returns (uint256 collateralCapacity, uint256 collateralBalance, uint256 debtCapacity, uint256 debtBalance) { bytes32 data = poolInfo[pool].collateralData; collateralCapacity = data.decodeUint(0, 85); collateralBalance = data.decodeUint(85, 85); data = poolInfo[pool].debtData; debtCapacity = data.decodeUint(0, 96); debtBalance = data.decodeUint(96, 96); } /**************************** * Public Mutated Functions * ****************************/ /// @inheritdoc IPoolManager function operate( address pool, uint256 positionId, int256 newColl, int256 newDebt ) external onlyRegisteredPool(pool) onlyRole(OPERATOR_ROLE) nonReentrant returns (uint256) { address collateralToken = IPool(pool).collateralToken(); uint256 scalingFactor = _getTokenScalingFactor(collateralToken); int256 newRawColl = newColl; if (newRawColl != type(int256).min) { newRawColl = _scaleUp(newRawColl, scalingFactor); } uint256 rawProtocolFees; // the `newRawColl` is the result without `protocolFees` (positionId, newRawColl, newDebt, rawProtocolFees) = IPool(pool).operate( positionId, newRawColl, newDebt, _msgSender() ); newColl = _scaleDown(newRawColl, scalingFactor); uint256 protocolFees = _scaleDown(rawProtocolFees, scalingFactor); _accumulatePoolFee(pool, protocolFees); _changePoolDebts(pool, newDebt); if (newRawColl > 0) { _changePoolCollateral(pool, newColl, newRawColl); IERC20(collateralToken).safeTransferFrom(_msgSender(), address(this), uint256(newColl) + protocolFees); } else if (newRawColl < 0) { _changePoolCollateral(pool, newColl - int256(protocolFees), newRawColl - int256(rawProtocolFees)); IERC20(collateralToken).safeTransfer(_msgSender(), uint256(-newColl)); } if (newDebt > 0) { IFxUSDRegeneracy(fxUSD).mint(_msgSender(), uint256(newDebt)); } else if (newDebt < 0) { IFxUSDRegeneracy(fxUSD).burn(_msgSender(), uint256(-newDebt)); } emit Operate(pool, positionId, newColl, newDebt, protocolFees); return positionId; } /// @inheritdoc IPoolManager function redeem( address pool, uint256 debts, uint256 minColls ) external onlyRegisteredPool(pool) nonReentrant returns (uint256 colls) { if (debts > IERC20(fxUSD).balanceOf(_msgSender())) { revert ErrorRedeemExceedBalance(); } uint256 rawColls = IPool(pool).redeem(debts); address collateralToken = IPool(pool).collateralToken(); uint256 scalingFactor = _getTokenScalingFactor(collateralToken); colls = _scaleDown(rawColls, scalingFactor); _changePoolCollateral(pool, -int256(colls), -int256(rawColls)); _changePoolDebts(pool, -int256(debts)); uint256 protocolFees = (colls * getRedeemFeeRatio()) / FEE_PRECISION; _accumulatePoolFee(pool, protocolFees); colls -= protocolFees; if (colls < minColls) revert ErrorInsufficientRedeemedCollateral(); IERC20(collateralToken).safeTransfer(_msgSender(), colls); IFxUSDRegeneracy(fxUSD).burn(_msgSender(), debts); emit Redeem(pool, colls, debts, protocolFees); } /// @inheritdoc IPoolManager function rebalance( address pool, address receiver, int16 tick, uint256 maxFxUSD, uint256 maxStable ) external onlyRegisteredPool(pool) nonReentrant onlyFxUSDSave returns (uint256 colls, uint256 fxUSDUsed, uint256 stableUsed) { LiquidateOrRebalanceMemoryVar memory op = _beforeRebalanceOrLiquidate(pool); IPool.RebalanceResult memory result = IPool(pool).rebalance(tick, maxFxUSD + _scaleUp(maxStable, op.stablePrice)); op.rawColls = result.rawColls + result.bonusRawColls; op.bonusRawColls = result.bonusRawColls; op.rawDebts = result.rawDebts; (colls, fxUSDUsed, stableUsed) = _afterRebalanceOrLiquidate(pool, maxFxUSD, op, receiver); emit RebalanceTick(pool, tick, colls, fxUSDUsed, stableUsed); } /// @inheritdoc IPoolManager function rebalance( address pool, address receiver, uint32 position, uint256 maxFxUSD, uint256 maxStable ) external onlyRegisteredPool(pool) nonReentrant onlyFxUSDSave returns (uint256 colls, uint256 fxUSDUsed, uint256 stableUsed) { LiquidateOrRebalanceMemoryVar memory op = _beforeRebalanceOrLiquidate(pool); IPool.RebalanceResult memory result = IPool(pool).rebalance( position, maxFxUSD + _scaleUp(maxStable, op.stablePrice) ); op.rawColls = result.rawColls + result.bonusRawColls; op.bonusRawColls = result.bonusRawColls; op.rawDebts = result.rawDebts; (colls, fxUSDUsed, stableUsed) = _afterRebalanceOrLiquidate(pool, maxFxUSD, op, receiver); emit RebalancePosition(pool, position, colls, fxUSDUsed, stableUsed); } /// @inheritdoc IPoolManager function liquidate( address pool, address receiver, uint32 position, uint256 maxFxUSD, uint256 maxStable ) external onlyRegisteredPool(pool) nonReentrant onlyFxUSDSave returns (uint256 colls, uint256 fxUSDUsed, uint256 stableUsed) { LiquidateOrRebalanceMemoryVar memory op = _beforeRebalanceOrLiquidate(pool); { IPool.LiquidateResult memory result; uint256 reservedRawColls = IReservePool(reservePool).getBalance(op.collateralToken); reservedRawColls = _scaleUp(reservedRawColls, op.scalingFactor); result = IPool(pool).liquidate(position, maxFxUSD + _scaleUp(maxStable, op.stablePrice), reservedRawColls); op.rawColls = result.rawColls + result.bonusRawColls; op.bonusRawColls = result.bonusRawColls; op.rawDebts = result.rawDebts; // take bonus from reserve pool uint256 bonusFromReserve = result.bonusFromReserve; if (bonusFromReserve > 0) { bonusFromReserve = _scaleDown(result.bonusFromReserve, op.scalingFactor); IReservePool(reservePool).requestBonus(IPool(pool).collateralToken(), address(this), bonusFromReserve); // increase pool reserve first _changePoolCollateral(pool, int256(bonusFromReserve), int256(result.bonusFromReserve)); } } (colls, fxUSDUsed, stableUsed) = _afterRebalanceOrLiquidate(pool, maxFxUSD, op, receiver); emit LiquidatePosition(pool, position, colls, fxUSDUsed, stableUsed); } /// @inheritdoc IPoolManager function harvest( address pool ) external onlyRegisteredPool(pool) nonReentrant returns (uint256 amountRewards, uint256 amountFunding) { address collateralToken = IPool(pool).collateralToken(); uint256 scalingFactor = _getTokenScalingFactor(collateralToken); uint256 collateralRecorded; uint256 rawCollateralRecorded; { bytes32 data = poolInfo[pool].collateralData; collateralRecorded = data.decodeUint(85, 85); rawCollateralRecorded = data.decodeUint(170, 86); } uint256 performanceFee; uint256 harvestBounty; uint256 pendingRewards; // compute funding uint256 rawCollateral = IPool(pool).getTotalRawCollaterals(); if (rawCollateralRecorded > rawCollateral) { unchecked { amountFunding = _scaleDown(rawCollateralRecorded - rawCollateral, scalingFactor); _changePoolCollateral(pool, -int256(amountFunding), -int256(rawCollateralRecorded - rawCollateral)); performanceFee = (getFundingExpenseRatio() * amountFunding) / FEE_PRECISION; harvestBounty = (getHarvesterRatio() * amountFunding) / FEE_PRECISION; pendingRewards = amountFunding - harvestBounty - performanceFee; } } // compute rewards rawCollateral = _scaleUp(collateralRecorded, scalingFactor); if (rawCollateral > rawCollateralRecorded) { unchecked { amountRewards = _scaleDown(rawCollateral - rawCollateralRecorded, scalingFactor); _changePoolCollateral(pool, -int256(amountRewards), -int256(rawCollateral - rawCollateralRecorded)); uint256 performanceFeeRewards = (getRewardsExpenseRatio() * amountRewards) / FEE_PRECISION; uint256 harvestBountyRewards = (getHarvesterRatio() * amountRewards) / FEE_PRECISION; pendingRewards += amountRewards - harvestBountyRewards - performanceFeeRewards; performanceFee += performanceFeeRewards; harvestBounty += harvestBountyRewards; } } // transfer performance fee to treasury if (performanceFee > 0) { IERC20(collateralToken).safeTransfer(treasury, performanceFee); } // transfer various fees to revenue pool _takeAccumulatedPoolFee(pool); // transfer harvest bounty if (harvestBounty > 0) { IERC20(collateralToken).safeTransfer(_msgSender(), harvestBounty); } // transfer rewards for fxBASE if (pendingRewards > 0) { address splitter = rewardSplitter[pool]; IERC20(collateralToken).safeTransfer(splitter, pendingRewards); IRewardSplitter(splitter).split(collateralToken); } emit Harvest(_msgSender(), pool, amountRewards, amountFunding, performanceFee, harvestBounty); } /************************ * Restricted Functions * ************************/ /// @notice Register a new pool with reward splitter. /// @param pool The address of pool. /// @param splitter The address of reward splitter. function registerPool( address pool, address splitter, uint96 collateralCapacity, uint96 debtCapacity ) external onlyRole(DEFAULT_ADMIN_ROLE) { if (fxUSD != IPool(pool).fxUSD()) revert ErrorInvalidPool(); if (pools.add(pool)) { emit RegisterPool(pool); _updateRewardSplitter(pool, splitter); _updatePoolCapacity(pool, collateralCapacity, debtCapacity); } } /// @notice Update rate provider for the given token. /// @param token The address of the token. /// @param provider The address of corresponding rate provider. function updateRateProvider(address token, address provider) external onlyRole(DEFAULT_ADMIN_ROLE) { uint256 scale = 10 ** (18 - IERC20Metadata(token).decimals()); tokenRates[token] = TokenRate(uint96(scale), provider); emit UpdateTokenRate(token, scale, provider); } /// @notice Update the address of reward splitter for the given pool. /// @param pool The address of the pool. /// @param newSplitter The address of reward splitter. function updateRewardSplitter( address pool, address newSplitter ) external onlyRole(DEFAULT_ADMIN_ROLE) onlyRegisteredPool(pool) { _updateRewardSplitter(pool, newSplitter); } /// @notice Update the pool capacity. /// @param pool The address of fx pool. /// @param collateralCapacity The capacity for collateral token. /// @param debtCapacity The capacity for debt token. function updatePoolCapacity( address pool, uint96 collateralCapacity, uint96 debtCapacity ) external onlyRole(DEFAULT_ADMIN_ROLE) onlyRegisteredPool(pool) { _updatePoolCapacity(pool, collateralCapacity, debtCapacity); } /// @notice Update threshold for permissionless liquidation. /// @param newThreshold The value of new threshold. function updateThreshold(uint256 newThreshold) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateThreshold(newThreshold); } /********************** * Internal Functions * **********************/ /// @dev Internal function to update the address of reward splitter for the given pool. /// @param pool The address of the pool. /// @param newSplitter The address of reward splitter. function _updateRewardSplitter(address pool, address newSplitter) internal { address oldSplitter = rewardSplitter[pool]; rewardSplitter[pool] = newSplitter; emit UpdateRewardSplitter(pool, oldSplitter, newSplitter); } /// @dev Internal function to update the pool capacity. /// @param pool The address of fx pool. /// @param collateralCapacity The capacity for collateral token. /// @param debtCapacity The capacity for debt token. function _updatePoolCapacity(address pool, uint96 collateralCapacity, uint96 debtCapacity) internal { poolInfo[pool].collateralData = poolInfo[pool].collateralData.insertUint(collateralCapacity, 0, 96); poolInfo[pool].debtData = poolInfo[pool].debtData.insertUint(debtCapacity, 0, 96); emit UpdatePoolCapacity(pool, collateralCapacity, debtCapacity); } /// @dev Internal function to update threshold for permissionless liquidation. /// @param newThreshold The value of new threshold. function _updateThreshold(uint256 newThreshold) internal { uint256 oldThreshold = permissionedLiquidationThreshold; permissionedLiquidationThreshold = newThreshold; emit UpdatePermissionedLiquidationThreshold(oldThreshold, newThreshold); } /// @dev Internal function to scaler up for `uint256`. function _scaleUp(uint256 value, uint256 scale) internal pure returns (uint256) { return (value * scale) / PRECISION; } /// @dev Internal function to scaler up for `int256`. function _scaleUp(int256 value, uint256 scale) internal pure returns (int256) { return (value * int256(scale)) / PRECISION_I256; } /// @dev Internal function to scaler down for `uint256`, rounding down. function _scaleDown(uint256 value, uint256 scale) internal pure returns (uint256) { return (value * PRECISION) / scale; } /// @dev Internal function to scaler down for `uint256`, rounding up. function _scaleDownRoundingUp(uint256 value, uint256 scale) internal pure returns (uint256) { return (value * PRECISION + scale - 1) / scale; } /// @dev Internal function to scaler down for `int256`. function _scaleDown(int256 value, uint256 scale) internal pure returns (int256) { return (value * PRECISION_I256) / int256(scale); } /// @dev Internal function to prepare variables before rebalance or liquidate. /// @param pool The address of pool to liquidate or rebalance. function _beforeRebalanceOrLiquidate(address pool) internal view returns (LiquidateOrRebalanceMemoryVar memory op) { op.stablePrice = IFxUSDBasePool(fxBASE).getStableTokenPriceWithScale(); op.collateralToken = IPool(pool).collateralToken(); op.scalingFactor = _getTokenScalingFactor(op.collateralToken); } /// @dev Internal function to do actions after rebalance or liquidate. /// @param pool The address of pool to liquidate or rebalance. /// @param maxFxUSD The maximum amount of fxUSD can be used. /// @param op The memory helper variable. /// @param receiver The address collateral token receiver. /// @return colls The actual amount of collateral token rebalanced or liquidated. /// @return fxUSDUsed The amount of fxUSD used. /// @return stableUsed The amount of stable token (a.k.a USDC) used. function _afterRebalanceOrLiquidate( address pool, uint256 maxFxUSD, LiquidateOrRebalanceMemoryVar memory op, address receiver ) internal returns (uint256 colls, uint256 fxUSDUsed, uint256 stableUsed) { colls = _scaleDown(op.rawColls, op.scalingFactor); _changePoolCollateral(pool, -int256(colls), -int256(op.rawColls)); _changePoolDebts(pool, -int256(op.rawDebts)); // burn fxUSD or transfer USDC fxUSDUsed = op.rawDebts; if (fxUSDUsed > maxFxUSD) { // rounding up here stableUsed = _scaleDownRoundingUp(fxUSDUsed - maxFxUSD, op.stablePrice); fxUSDUsed = maxFxUSD; } if (fxUSDUsed > 0) { IFxUSDRegeneracy(fxUSD).burn(_msgSender(), fxUSDUsed); } if (stableUsed > 0) { IERC20(IFxUSDBasePool(fxBASE).stableToken()).safeTransferFrom(_msgSender(), fxUSD, stableUsed); IFxUSDRegeneracy(fxUSD).onRebalanceWithStable(stableUsed, op.rawDebts - maxFxUSD); } // transfer collateral uint256 protocolRevenue = (_scaleDown(op.bonusRawColls, op.scalingFactor) * getLiquidationExpenseRatio()) / FEE_PRECISION; _accumulatePoolFee(pool, protocolRevenue); unchecked { colls -= protocolRevenue; } IERC20(op.collateralToken).safeTransfer(receiver, colls); } /// @dev Internal function to update collateral balance. function _changePoolCollateral(address pool, int256 delta, int256 rawDelta) internal { bytes32 data = poolInfo[pool].collateralData; uint256 capacity = data.decodeUint(0, 85); uint256 balance = uint256(int256(data.decodeUint(85, 85)) + delta); if (balance > capacity) revert ErrorCollateralExceedCapacity(); data = data.insertUint(balance, 85, 85); balance = uint256(int256(data.decodeUint(170, 86)) + rawDelta); poolInfo[pool].collateralData = data.insertUint(balance, 170, 86); } /// @dev Internal function to update debt balance. function _changePoolDebts(address pool, int256 delta) internal { bytes32 data = poolInfo[pool].debtData; uint256 capacity = data.decodeUint(0, 96); uint256 balance = uint256(int256(data.decodeUint(96, 96)) + delta); if (balance > capacity) revert ErrorDebtExceedCapacity(); poolInfo[pool].debtData = data.insertUint(balance, 96, 96); } /// @dev Internal function to get token scaling factor. function _getTokenScalingFactor(address token) internal view returns (uint256 value) { TokenRate memory rate = tokenRates[token]; value = rate.scalar; unchecked { if (rate.rateProvider != address(0)) { value *= IRateProvider(rate.rateProvider).getRate(); } else { value *= PRECISION; } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; import { IPool } from "../interfaces/IPool.sol"; import { IProtocolFees } from "../interfaces/IProtocolFees.sol"; import { WordCodec } from "../common/codec/WordCodec.sol"; abstract contract ProtocolFees is AccessControlUpgradeable, IProtocolFees { using SafeERC20 for IERC20; using WordCodec for bytes32; /********** * Errors * **********/ /// @dev Thrown when the given address is zero. error ErrorZeroAddress(); /// @dev Thrown when the expense ratio exceeds `MAX_EXPENSE_RATIO`. error ErrorExpenseRatioTooLarge(); /// @dev Thrown when the harvester ratio exceeds `MAX_HARVESTER_RATIO`. error ErrorHarvesterRatioTooLarge(); /// @dev Thrown when the flash loan fee ratio exceeds `MAX_FLASH_LOAN_FEE_RATIO`. error ErrorFlashLoanFeeRatioTooLarge(); /// @dev Thrown when the redeem fee ratio exceeds `MAX_REDEEM_FEE_RATIO`. error ErrorRedeemFeeRatioTooLarge(); /************* * Constants * *************/ /// @dev The maximum expense ratio. uint256 private constant MAX_EXPENSE_RATIO = 5e8; // 50% /// @dev The maximum harvester ratio. uint256 private constant MAX_HARVESTER_RATIO = 2e8; // 20% /// @dev The maximum flash loan fee ratio. uint256 private constant MAX_FLASH_LOAN_FEE_RATIO = 1e8; // 10% /// @dev The maximum redeem fee ratio. uint256 private constant MAX_REDEEM_FEE_RATIO = 1e8; // 10% /// @dev The offset of general expense ratio in `_miscData`. uint256 private constant REWARDS_EXPENSE_RATIO_OFFSET = 0; /// @dev The offset of harvester ratio in `_miscData`. uint256 private constant HARVESTER_RATIO_OFFSET = 30; /// @dev The offset of flash loan ratio in `_miscData`. uint256 private constant FLASH_LOAN_RATIO_OFFSET = 60; /// @dev The offset of redeem fee ratio in `_miscData`. uint256 private constant REDEEM_FEE_RATIO_OFFSET = 90; /// @dev The offset of funding expense ratio in `_miscData`. uint256 private constant FUNDING_EXPENSE_RATIO_OFFSET = 120; /// @dev The offset of liquidation expense ratio in `_miscData`. uint256 private constant LIQUIDATION_EXPENSE_RATIO_OFFSET = 150; /// @dev The precision used to compute fees. uint256 internal constant FEE_PRECISION = 1e9; /************* * Variables * *************/ /// @dev `_miscData` is a storage slot that can be used to store unrelated pieces of information. /// All pools store the *expense ratio*, *harvester ratio* and *withdraw fee percentage*, but /// the `miscData`can be extended to store more pieces of information. /// /// The *expense ratio* is stored in the first most significant 32 bits, and the *harvester ratio* is /// stored in the next most significant 32 bits, and the *withdraw fee percentage* is stored in the /// next most significant 32 bits, leaving the remaining 160 bits free to store any other information /// derived pools might need. /// /// - The *expense ratio* and *harvester ratio* are charged each time when harvester harvest the pool revenue. /// - The *withdraw fee percentage* is charged each time when user try to withdraw assets from the pool. /// /// [ rewards expense ratio | harvester ratio | flash loan ratio | redeem ratio | funding expense ratio | liquidation expense ratio | available ] /// [ 30 bits | 30 bits | 30 bits | 30 bits | 30 bits | 30 bits | 76 bits ] /// [ MSB LSB ] bytes32 internal _miscData; /// @inheritdoc IProtocolFees address public treasury; /// @inheritdoc IProtocolFees /// @dev Hold fees including open, close, redeem, liquidation and rebalance. address public revenuePool; /// @inheritdoc IProtocolFees address public reservePool; /// @inheritdoc IProtocolFees mapping(address => uint256) public accumulatedPoolFees; /*************** * Constructor * ***************/ function __ProtocolFees_init( uint256 _expenseRatio, uint256 _harvesterRatio, uint256 _flashLoanFeeRatio, address _treasury, address _revenuePool, address _reservePool ) internal onlyInitializing { _updateFundingExpenseRatio(_expenseRatio); _updateRewardsExpenseRatio(_expenseRatio); _updateLiquidationExpenseRatio(_expenseRatio); _updateHarvesterRatio(_harvesterRatio); _updateFlashLoanFeeRatio(_flashLoanFeeRatio); _updateTreasury(_treasury); _updateRevenuePool(_revenuePool); _updateReservePool(_reservePool); } /************************* * Public View Functions * *************************/ /// @inheritdoc IProtocolFees function getFundingExpenseRatio() public view returns (uint256) { return _miscData.decodeUint(FUNDING_EXPENSE_RATIO_OFFSET, 30); } /// @inheritdoc IProtocolFees function getRewardsExpenseRatio() public view returns (uint256) { return _miscData.decodeUint(REWARDS_EXPENSE_RATIO_OFFSET, 30); } /// @inheritdoc IProtocolFees function getLiquidationExpenseRatio() public view returns (uint256) { return _miscData.decodeUint(LIQUIDATION_EXPENSE_RATIO_OFFSET, 30); } /// @inheritdoc IProtocolFees function getHarvesterRatio() public view returns (uint256) { return _miscData.decodeUint(HARVESTER_RATIO_OFFSET, 30); } /// @inheritdoc IProtocolFees function getFundingFxSaveRatio() external view returns (uint256) { return FEE_PRECISION - getFundingExpenseRatio() - getHarvesterRatio(); } /// @inheritdoc IProtocolFees function getRewardsFxSaveRatio() external view returns (uint256) { return FEE_PRECISION - getRewardsExpenseRatio() - getHarvesterRatio(); } /// @inheritdoc IProtocolFees function getFlashLoanFeeRatio() public view returns (uint256) { return _miscData.decodeUint(FLASH_LOAN_RATIO_OFFSET, 30); } /// @inheritdoc IProtocolFees function getRedeemFeeRatio() public view returns (uint256) { return _miscData.decodeUint(REDEEM_FEE_RATIO_OFFSET, 30); } /**************************** * Public Mutated Functions * ****************************/ /// @inheritdoc IProtocolFees function withdrawAccumulatedPoolFee(address[] memory pools) external { for (uint256 i = 0; i < pools.length; ++i) { _takeAccumulatedPoolFee(pools[i]); } } /************************ * Restricted Functions * ************************/ /// @notice Change address of reserve pool contract. /// @param _newReservePool The new address of reserve pool contract. function updateReservePool(address _newReservePool) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateReservePool(_newReservePool); } /// @notice Change address of treasury contract. /// @param _newTreasury The new address of treasury contract. function updateTreasury(address _newTreasury) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateTreasury(_newTreasury); } /// @notice Change address of revenue pool contract. /// @param _newPool The new address of revenue pool contract. function updateRevenuePool(address _newPool) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateRevenuePool(_newPool); } /// @notice Update the fee ratio distributed to treasury. /// @param newRewardsRatio The new ratio for rewards to update, multiplied by 1e9. /// @param newFundingRatio The new ratio for funding to update, multiplied by 1e9. /// @param newLiquidationRatio The new ratio for liquidation/rebalance to update, multiplied by 1e9. function updateExpenseRatio( uint32 newRewardsRatio, uint32 newFundingRatio, uint32 newLiquidationRatio ) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateRewardsExpenseRatio(newRewardsRatio); _updateFundingExpenseRatio(newFundingRatio); _updateLiquidationExpenseRatio(newLiquidationRatio); } /// @notice Update the fee ratio distributed to harvester. /// @param newRatio The new ratio to update, multiplied by 1e9. function updateHarvesterRatio(uint32 newRatio) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateHarvesterRatio(newRatio); } /// @notice Update the flash loan fee ratio. /// @param newRatio The new ratio to update, multiplied by 1e9. function updateFlashLoanFeeRatio(uint32 newRatio) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateFlashLoanFeeRatio(newRatio); } /// @notice Update the redeem fee ratio. /// @param newRatio The new ratio to update, multiplied by 1e9. function updateRedeemFeeRatio(uint32 newRatio) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateRedeemFeeRatio(newRatio); } /********************** * Internal Functions * **********************/ /// @dev Internal function to change address of treasury contract. /// @param _newTreasury The new address of treasury contract. function _updateTreasury(address _newTreasury) private { if (_newTreasury == address(0)) revert ErrorZeroAddress(); address _oldTreasury = treasury; treasury = _newTreasury; emit UpdateTreasury(_oldTreasury, _newTreasury); } /// @dev Internal function to change address of revenue pool contract. /// @param _newPool The new address of revenue pool contract. function _updateRevenuePool(address _newPool) private { if (_newPool == address(0)) revert ErrorZeroAddress(); address _oldPool = revenuePool; revenuePool = _newPool; emit UpdateRevenuePool(_oldPool, _newPool); } /// @dev Internal function to change the address of reserve pool contract. /// @param newReservePool The new address of reserve pool contract. function _updateReservePool(address newReservePool) private { if (newReservePool == address(0)) revert ErrorZeroAddress(); address oldReservePool = reservePool; reservePool = newReservePool; emit UpdateReservePool(oldReservePool, newReservePool); } /// @dev Internal function to update the fee ratio distributed to treasury. /// @param newRatio The new ratio to update, multiplied by 1e9. function _updateRewardsExpenseRatio(uint256 newRatio) private { if (uint256(newRatio) > MAX_EXPENSE_RATIO) { revert ErrorExpenseRatioTooLarge(); } bytes32 _data = _miscData; uint256 _oldRatio = _miscData.decodeUint(REWARDS_EXPENSE_RATIO_OFFSET, 30); _miscData = _data.insertUint(newRatio, REWARDS_EXPENSE_RATIO_OFFSET, 30); emit UpdateRewardsExpenseRatio(_oldRatio, newRatio); } /// @dev Internal function to update the fee ratio distributed to treasury. /// @param newRatio The new ratio to update, multiplied by 1e9. function _updateLiquidationExpenseRatio(uint256 newRatio) private { if (uint256(newRatio) > MAX_EXPENSE_RATIO) { revert ErrorExpenseRatioTooLarge(); } bytes32 _data = _miscData; uint256 _oldRatio = _miscData.decodeUint(LIQUIDATION_EXPENSE_RATIO_OFFSET, 30); _miscData = _data.insertUint(newRatio, LIQUIDATION_EXPENSE_RATIO_OFFSET, 30); emit UpdateLiquidationExpenseRatio(_oldRatio, newRatio); } /// @dev Internal function to update the fee ratio distributed to treasury. /// @param newRatio The new ratio to update, multiplied by 1e9. function _updateFundingExpenseRatio(uint256 newRatio) private { if (uint256(newRatio) > MAX_EXPENSE_RATIO) { revert ErrorExpenseRatioTooLarge(); } bytes32 _data = _miscData; uint256 _oldRatio = _miscData.decodeUint(FUNDING_EXPENSE_RATIO_OFFSET, 30); _miscData = _data.insertUint(newRatio, FUNDING_EXPENSE_RATIO_OFFSET, 30); emit UpdateFundingExpenseRatio(_oldRatio, newRatio); } /// @dev Internal function to update the fee ratio distributed to harvester. /// @param newRatio The new ratio to update, multiplied by 1e9. function _updateHarvesterRatio(uint256 newRatio) private { if (uint256(newRatio) > MAX_HARVESTER_RATIO) { revert ErrorHarvesterRatioTooLarge(); } bytes32 _data = _miscData; uint256 _oldRatio = _miscData.decodeUint(HARVESTER_RATIO_OFFSET, 30); _miscData = _data.insertUint(newRatio, HARVESTER_RATIO_OFFSET, 30); emit UpdateHarvesterRatio(_oldRatio, newRatio); } /// @dev Internal function to update the flash loan fee ratio. /// @param newRatio The new ratio to update, multiplied by 1e9. function _updateFlashLoanFeeRatio(uint256 newRatio) private { if (uint256(newRatio) > MAX_FLASH_LOAN_FEE_RATIO) { revert ErrorFlashLoanFeeRatioTooLarge(); } bytes32 _data = _miscData; uint256 _oldRatio = _miscData.decodeUint(FLASH_LOAN_RATIO_OFFSET, 30); _miscData = _data.insertUint(newRatio, FLASH_LOAN_RATIO_OFFSET, 30); emit UpdateFlashLoanFeeRatio(_oldRatio, newRatio); } /// @dev Internal function to update the redeem fee ratio. /// @param newRatio The new ratio to update, multiplied by 1e9. function _updateRedeemFeeRatio(uint256 newRatio) private { if (uint256(newRatio) > MAX_REDEEM_FEE_RATIO) { revert ErrorRedeemFeeRatioTooLarge(); } bytes32 _data = _miscData; uint256 _oldRatio = _miscData.decodeUint(REDEEM_FEE_RATIO_OFFSET, 30); _miscData = _data.insertUint(newRatio, REDEEM_FEE_RATIO_OFFSET, 30); emit UpdateRedeemFeeRatio(_oldRatio, newRatio); } /// @dev Internal function to accumulate protocol fee for the given pool. /// @param pool The address of pool. /// @param amount The amount of protocol fee. function _accumulatePoolFee(address pool, uint256 amount) internal { if (amount > 0) { accumulatedPoolFees[pool] += amount; } } /// @dev Internal function to withdraw accumulated protocol fee for the given pool. /// @param pool The address of pool. function _takeAccumulatedPoolFee(address pool) internal returns (uint256 fees) { fees = accumulatedPoolFees[pool]; if (fees > 0) { address collateralToken = IPool(pool).collateralToken(); IERC20(collateralToken).safeTransfer(revenuePool, fees); accumulatedPoolFees[pool] = 0; } } /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. */ uint256[45] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.25; pragma abicoder v2; import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { IReservePool } from "../interfaces/IReservePool.sol"; contract ReservePool is AccessControl, IReservePool { using EnumerableSet for EnumerableSet.AddressSet; using SafeERC20 for IERC20; /********** * Errors * **********/ /// @dev Thrown the bonus ratio is too large. error ErrorRatioTooLarge(); /// @dev Thrown when add an already added rebalance pool. error ErrorRebalancePoolAlreadyAdded(); /// @dev Thrown when remove an unknown rebalance pool. error ErrorRebalancePoolNotAdded(); /// @dev Thrown when the caller is not `FxOmniVault`. error ErrorCallerNotPoolManager(); /************* * Constants * *************/ /// @dev The address of `PoolManager` contract. address public immutable poolManager; /************* * Variables * *************/ /*************** * Constructor * ***************/ constructor(address admin, address _poolManager) { poolManager = _poolManager; _grantRole(DEFAULT_ADMIN_ROLE, admin); } /************************* * Public View Functions * *************************/ /// @inheritdoc IReservePool function getBalance(address token) external view returns (uint256) { return _getBalance(token); } /**************************** * Public Mutated Functions * ****************************/ // solhint-disable-next-line no-empty-blocks receive() external payable {} /// @inheritdoc IReservePool function requestBonus(address _token, address _recipient, uint256 _bonus) external { if (_msgSender() != poolManager) revert ErrorCallerNotPoolManager(); uint256 _balance = _getBalance(_token); if (_bonus > _balance) { _bonus = _balance; } if (_bonus > 0) { _transferToken(_token, _recipient, _bonus); emit RequestBonus(_token, _recipient, _bonus); } } /************************ * Restricted Functions * ************************/ /// @notice Withdraw dust assets in this contract. /// @param _token The address of token to withdraw. /// @param _recipient The address of token receiver. function withdrawFund(address _token, uint256 amount, address _recipient) external onlyRole(DEFAULT_ADMIN_ROLE) { _transferToken(_token, _recipient, amount); } /********************** * Internal Functions * **********************/ /// @dev Internal function to return the balance of the token in this contract. /// @param _token The address of token to query. function _getBalance(address _token) internal view returns (uint256) { if (_token == address(0)) { return address(this).balance; } else { return IERC20(_token).balanceOf(address(this)); } } /// @dev Internal function to transfer ETH or ERC20 tokens to some `_receiver`. /// /// @param _token The address of token to transfer, user `_token=address(0)` if transfer ETH. /// @param _receiver The address of token receiver. /// @param _amount The amount of token to transfer. function _transferToken(address _token, address _receiver, uint256 _amount) internal { if (_token == address(0)) { Address.sendValue(payable(_receiver), _amount); } else { IERC20(_token).safeTransfer(_receiver, _amount); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol"; abstract contract AssetManagement is AccessControlUpgradeable { /** * @dev This empty reserved space is put in place to allow future versions to add new * variables without shifting down storage in the inheritance chain. */ uint256[50] private __gap; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IWrappedEther } from "../../interfaces/IWrappedEther.sol"; import { ITokenConverter } from "../interfaces/ITokenConverter.sol"; import { IMultiPathConverter } from "../interfaces/IMultiPathConverter.sol"; contract MultiPathConverter is IMultiPathConverter { using SafeERC20 for IERC20; /************* * Constants * *************/ /// @dev The address of WETH token. address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; /************* * Variables * *************/ /// @notice The address of GeneralTokenConverter contract. address public immutable converter; /*************** * Constructor * ***************/ constructor(address _converter) { converter = _converter; } /************************* * Public View Functions * *************************/ /// @inheritdoc IMultiPathConverter function queryConvert( uint256 _amount, uint256 _encoding, uint256[] calldata _routes ) external returns (uint256 amountOut) { uint256 _offset; for (uint256 i = 0; i < 8; i++) { uint256 _ratio = _encoding & 0xfffff; uint256 _length = (_encoding >> 20) & 0xfff; if (_ratio == 0) break; uint256 _amountIn = (_amount * _ratio) / 0xfffff; for (uint256 j = 0; j < _length; j++) { _amountIn = ITokenConverter(converter).queryConvert(_routes[_offset], _amountIn); _offset += 1; } _encoding >>= 32; amountOut += _amountIn; } } /**************************** * Public Mutated Functions * ****************************/ /// @inheritdoc IMultiPathConverter function convert( address _tokenIn, uint256 _amount, uint256 _encoding, uint256[] memory _routes ) external payable returns (uint256 amountOut) { if (_tokenIn == address(0)) { IWrappedEther(WETH).deposit{ value: _amount }(); IERC20(WETH).safeTransfer(converter, _amount); } else { // convert all approved. if (_amount == type(uint256).max) { _amount = IERC20(_tokenIn).allowance(msg.sender, address(this)); } IERC20(_tokenIn).safeTransferFrom(msg.sender, converter, _amount); } uint256 _offset; for (uint256 i = 0; i < 8; i++) { uint256 _ratio = _encoding & 0xfffff; uint256 _length = (_encoding >> 20) & 0xfff; if (_ratio == 0) break; uint256 _amountIn = (_amount * _ratio) / 0xfffff; for (uint256 j = 0; j < _length; j++) { address _recipient = j < _length - 1 ? converter : msg.sender; _amountIn = ITokenConverter(converter).convert(_routes[_offset], _amountIn, _recipient); _offset += 1; } _encoding >>= 32; amountOut += _amountIn; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; contract EmptyContract {}
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import "@openzeppelin/contracts-v4/proxy/transparent/ProxyAdmin.sol"; import "@openzeppelin/contracts-v4/proxy/transparent/TransparentUpgradeableProxy.sol";
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { IMultipleRewardDistributor } from "../common/rewards/distributor/IMultipleRewardDistributor.sol"; import { IRewardSplitter } from "../interfaces/IRewardSplitter.sol"; import { PermissionedSwap } from "../common/utils/PermissionedSwap.sol"; contract GaugeRewarder is PermissionedSwap, IRewardSplitter { using SafeERC20 for IERC20; /*********************** * Immutable Variables * ***********************/ /// @notice The address of `LiquidityGauge` contract. address public immutable gauge; /*************** * Constructor * ***************/ constructor(address _gauge) initializer { __Context_init(); __ERC165_init(); __AccessControl_init(); gauge = _gauge; _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); } /**************************** * Public Mutated Functions * ****************************/ /// @inheritdoc IRewardSplitter function split(address token) external override { // do nothing } /// @inheritdoc IRewardSplitter function depositReward(address token, uint256 amount) external override { IERC20(token).safeTransferFrom(_msgSender(), address(this), amount); } /// @notice Harvest base token to target token by amm trading and distribute to fxBASE gauge. /// @param baseToken The address of base token to use. /// @param targetToken The address target token. /// @param params The parameters used for trading. /// @return amountOut The amount of target token received. function swapAndDistribute( address baseToken, address targetToken, TradingParameter memory params ) external returns (uint256 amountOut) { uint256 amountIn = IERC20(baseToken).balanceOf(address(this)); // swap base token to target amountOut = _doTrade(baseToken, targetToken, amountIn, params); // deposit target token to gauge IERC20(targetToken).forceApprove(gauge, amountOut); IMultipleRewardDistributor(gauge).depositReward(targetToken, amountOut); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IMultiPathConverter { function queryConvert( uint256 _amount, uint256 _encoding, uint256[] calldata _routes ) external returns (uint256 amountOut); function convert( address _tokenIn, uint256 _amount, uint256 _encoding, uint256[] calldata _routes ) external payable returns (uint256 amountOut); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ITokenConverter { /************************* * Public View Functions * *************************/ /// @notice The address of Converter Registry. function registry() external view returns (address); /// @notice Return the input token and output token for the route. /// @param route The encoding of the route. /// @return tokenIn The address of input token. /// @return tokenOut The address of output token. function getTokenPair(uint256 route) external view returns (address tokenIn, address tokenOut); /// @notice Query the output token amount according to the encoding. /// /// @dev See the comments in `convert` for the meaning of encoding. /// /// @param encoding The encoding used to convert. /// @param amountIn The amount of input token. /// @param amountOut The amount of output token received. function queryConvert(uint256 encoding, uint256 amountIn) external returns (uint256 amountOut); /**************************** * Public Mutated Functions * ****************************/ /// @notice Convert input token to output token according to the encoding. /// Assuming that the input token is already in the contract. /// /// @dev encoding for single route /// | 8 bits | 2 bits | 246 bits | /// | pool_type | action | customized | /// /// + pool_type = 0: UniswapV2, only action = 0 /// customized = | 160 bits | 24 bits | 1 bit | 1 bit | ... | /// | pool address | fee_num | zero_for_one | twamm | ... | /// + pool_type = 1: UniswapV3, only action = 0 /// customized = | 160 bits | 24 bits | 1 bit | ... | /// | pool address | fee_num | zero_for_one | ... | /// + pool_type = 2: BalancerV1, only action = 0 /// customized = | 160 bits | 3 bits | 3 bits | 3 bits | ... | /// | pool address | tokens | index in | index out | ... | /// + pool_type = 3: BalancerV2, only action = 0 /// customized = | 160 bits | 3 bits | 3 bits | 3 bits | ... | /// | pool address | tokens | index in | index out | ... | /// + pool_type = 4: CurvePlainPool or CurveFactoryPlainPool /// customized = | 160 bits | 3 bits | 3 bits | 3 bits | 1 bit | ... | /// | pool address | tokens | index in | index out | use_eth | ... | /// + pool_type = 5: CurveAPool /// customized = | 160 bits | 3 bits | 3 bits | 3 bits | 1 bits | ... | /// | pool address | tokens | index in | index out | use_underlying | ... | /// + pool_type = 6: CurveYPool /// customized = | 160 bits | 3 bits | 3 bits | 3 bits | 1 bits | ... | /// | pool address | tokens | index in | index out | use_underlying | ... | /// + pool_type = 7: CurveMetaPool or CurveFactoryMetaPool /// customized = | 160 bits | 3 bits | 3 bits | 3 bits | ... | /// | pool address | tokens | index in | index out | ... | /// + pool_type = 8: CurveCryptoPool or CurveFactoryCryptoPool /// customized = | 160 bits | 3 bits | 3 bits | 3 bits | 1 bit | ... | /// | pool address | tokens | index in | index out | use_eth | ... | /// + pool_type = 9: ERC4626, no action 0 /// customized = | 160 bits | ... | /// | pool address | ... | /// + pool_type = 10: Lido, no action 0 /// customized = | 160 bits | ... | /// | pool address | ... | /// + pool_type = 11: ETHLSDConverter v1, no action 0 /// supported in other pool type /// puffer: pufETH is ERC4626, base is stETH /// frax: sfrxETH is ERC4626, base is frxETH /// pirex: apxETH is ERC4626, base is pxETH /// supported in this pool type /// 0=wBETH: mint wBETH from ETH /// 1=RocketPool: mint rETH from ETH /// 2=frax: mint frxETH from ETH /// 3=pirex: mint pxETH from ETH /// 4=renzo: mint ezETH from ETH, stETH, wBETH /// 5=ether.fi: mint eETH from ETH, mint weETH from eETH, unwrap weETH to eETH /// 6=kelpdao.xyz: mint rsETH from ETH, ETHx, stETH, sfrxETH, and etc. /// customized = | 160 bits | 8 bits | ... | /// | pool address | protocol | ... | /// + pool_type = 12: CurveStableSwapNG /// customized = | 160 bits | 3 bits | 3 bits | 3 bits | ... | /// | pool address | tokens | index in | index out | ... | /// + pool_type = 13: CurveStableSwapMetaNG /// customized = | 160 bits | 3 bits | 3 bits | 3 bits | ... | /// | pool address | tokens | index in | index out | ... | /// + pool_type = 14: WETH /// customized = | 160 bits | ... | /// | pool address | ... | /// /// Note: tokens + 1 is the number of tokens of the pool /// /// + action = 0: swap /// + action = 1: add liquidity / wrap / stake /// + action = 2: remove liquidity / unwrap / unstake /// /// @param encoding The encoding used to convert. /// @param amountIn The amount of input token. /// @param recipient The address of token receiver. /// @return amountOut The amount of output token received. function convert( uint256 encoding, uint256 amountIn, address recipient ) external payable returns (uint256 amountOut); /// @notice Withdraw dust assets in this contract. /// @param token The address of token to withdraw. /// @param recipient The address of token receiver. function withdrawFund(address token, address recipient) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; // copy from: https://github.com/AladdinDAO/aladdin-v3-contracts/blob/main/contracts/helpers/PlatformFeeSpliter.sol contract RevenuePool is Ownable { using SafeERC20 for IERC20; /********** * Events * **********/ /// @notice Emitted when the address of staker contract is updated. /// @param staker The address of new staker contract. event UpdateStaker(address staker); /// @notice Emitted when the address of treasury contract is updated. /// @param treasury The address of new treasury contract. event UpdateTreasury(address treasury); /// @notice Emitted when the address of ecosystem contract is updated. /// @param ecosystem The address of new ecosystem contract. event UpdateEcosystem(address ecosystem); /// @notice Emitted when a new reward token is added. /// @param token The address of reward token. /// @param burner The address of token burner contract. /// @param stakerRatio The ratio of token distributed to liquidity stakers, multipled by 1e9. /// @param treasuryRatio The ratio of token distributed to treasury, multipled by 1e9. /// @param lockerRatio The ratio of token distributed to ve token lockers, multipled by 1e9. event AddRewardToken(address token, address burner, uint256 stakerRatio, uint256 treasuryRatio, uint256 lockerRatio); /// @notice Emitted when the percentage is updated for existing reward token. /// @param token The address of reward token. /// @param stakerRatio The ratio of token distributed to liquidity stakers, multipled by 1e9. /// @param treasuryRatio The ratio of token distributed to treasury, multipled by 1e9. /// @param lockerRatio The ratio of token distributed to ve token lockers, multipled by 1e9. event UpdateRewardTokenRatio(address token, uint256 stakerRatio, uint256 treasuryRatio, uint256 lockerRatio); /// @notice Emitted when the address of token burner is updated. /// @param token The address of reward token. /// @param burner The address of token burner contract. event UpdateRewardTokenBurner(address token, address burner); /// @notice Emitted when a reward token is removed. /// @param token The address of reward token. event RemoveRewardToken(address token); /************* * Constants * *************/ /// @dev The fee denominator used for ratio calculation. uint256 private constant FEE_PRECISION = 1e9; /*********** * Structs * ***********/ struct RewardInfo { // The address of reward token. address token; // The ratio of token distributed to liquidity stakers, multipled by 1e9. uint32 stakerRatio; // The ratio of token distributed to treasury, multipled by 1e9. uint32 treasuryRatio; // The ratio of token distributed to ve token lockers, multipled by 1e9. uint32 lockerRatio; // @note The rest token will transfer to ecosystem fund for future usage. } /************* * Variables * *************/ /// @notice The address of contract used to hold treasury fund. address public treasury; /// @notice The address of contract used to hold ecosystem fund. address public ecosystem; /// @notice The address of contract used to distribute incentive for liquidity stakers. address public staker; /// @notice The list of rewards token. RewardInfo[] public rewards; /// @notice Mapping from reward token address to corresponding token burner. mapping(address => address) public burners; /*************** * Constructor * ***************/ constructor( address _treasury, address _ecosystem, address _staker ) Ownable(_msgSender()) { _ensureNonZeroAddress(_treasury, "treasury"); _ensureNonZeroAddress(_ecosystem, "ecosystem"); _ensureNonZeroAddress(_staker, "staker"); treasury = _treasury; ecosystem = _ecosystem; staker = _staker; } /// @notice Return the number of reward tokens. function getRewardCount() external view returns (uint256) { return rewards.length; } /**************************** * Public Mutated Functions * ****************************/ /// @notice Claim and distribute pending rewards to staker/treasury/locker/ecosystem contract. /// @dev The function can only be called by staker contract. function claim() external { address _staker = staker; require(msg.sender == _staker, "not staker"); address _treasury = treasury; address _ecosystem = ecosystem; uint256 _length = rewards.length; for (uint256 i = 0; i < _length; i++) { RewardInfo memory _reward = rewards[i]; uint256 _balance = IERC20(_reward.token).balanceOf(address(this)); if (_balance > 0) { uint256 _stakerAmount = (_reward.stakerRatio * _balance) / FEE_PRECISION; uint256 _treasuryAmount = (_reward.treasuryRatio * _balance) / FEE_PRECISION; uint256 _lockerAmount = (_reward.lockerRatio * _balance) / FEE_PRECISION; uint256 _ecosystemAmount = _balance - _stakerAmount - _treasuryAmount - _lockerAmount; if (_stakerAmount > 0) { IERC20(_reward.token).safeTransfer(_staker, _stakerAmount); } if (_treasuryAmount > 0) { IERC20(_reward.token).safeTransfer(_treasury, _treasuryAmount); } if (_lockerAmount > 0) { IERC20(_reward.token).safeTransfer(burners[_reward.token], _lockerAmount); } if (_ecosystemAmount > 0) { IERC20(_reward.token).safeTransfer(_ecosystem, _ecosystemAmount); } } } } /************************ * Restricted Functions * ************************/ /// @notice Update the address of treasury contract. /// @param _treasury The address of new treasury contract. function updateTreasury(address _treasury) external onlyOwner { _ensureNonZeroAddress(_treasury, "treasury"); treasury = _treasury; emit UpdateTreasury(_treasury); } /// @notice Update the address of ecosystem contract. /// @param _ecosystem The address of new ecosystem contract. function updateEcosystem(address _ecosystem) external onlyOwner { _ensureNonZeroAddress(_ecosystem, "ecosystem"); ecosystem = _ecosystem; emit UpdateEcosystem(_ecosystem); } /// @notice Update the address of staker contract. /// @param _staker The address of new staker contract. function updateStaker(address _staker) external onlyOwner { _ensureNonZeroAddress(_staker, "staker"); staker = _staker; emit UpdateStaker(_staker); } /// @notice Add a new reward token. /// @param _token The address of reward token. /// @param _burner The address of corresponding token burner. /// @param _stakerRatio The ratio of token distributed to liquidity stakers, multipled by 1e9. /// @param _treasuryRatio The ratio of token distributed to treasury, multipled by 1e9. /// @param _lockerRatio The ratio of token distributed to ve token lockers, multipled by 1e9. function addRewardToken( address _token, address _burner, uint32 _stakerRatio, uint32 _treasuryRatio, uint32 _lockerRatio ) external onlyOwner { _checkRatioRange(_stakerRatio, _treasuryRatio, _lockerRatio); _ensureNonZeroAddress(_burner, "burner"); require(burners[_token] == address(0), "duplicated reward token"); burners[_token] = _burner; rewards.push(RewardInfo(_token, _stakerRatio, _treasuryRatio, _lockerRatio)); emit AddRewardToken(_token, _burner, _stakerRatio, _treasuryRatio, _lockerRatio); } /// @notice Update reward ratio of existing reward token. /// @param _index The index of reward token. /// @param _stakerRatio The ratio of token distributed to liquidity stakers, multipled by 1e9. /// @param _treasuryRatio The ratio of token distributed to treasury, multipled by 1e9. /// @param _lockerRatio The ratio of token distributed to ve token lockers, multipled by 1e9. function updateRewardTokenRatio( uint256 _index, uint32 _stakerRatio, uint32 _treasuryRatio, uint32 _lockerRatio ) external onlyOwner { _checkRatioRange(_stakerRatio, _treasuryRatio, _lockerRatio); require(_index < rewards.length, "index out of range"); RewardInfo memory _info = rewards[_index]; _info.stakerRatio = _stakerRatio; _info.treasuryRatio = _treasuryRatio; _info.lockerRatio = _lockerRatio; rewards[_index] = _info; emit UpdateRewardTokenRatio(_info.token, _stakerRatio, _treasuryRatio, _lockerRatio); } /// @notice Update the token burner of existing reward token. /// @param _token The address of the reward token. /// @param _burner The address of corresponding token burner. function updateRewardTokenBurner(address _token, address _burner) external onlyOwner { _ensureNonZeroAddress(_burner, "new burner"); _ensureNonZeroAddress(burners[_token], "old burner"); burners[_token] = _burner; emit UpdateRewardTokenBurner(_token, _burner); } /// @notice Remove an existing reward token. /// @param _index The index of reward token. function removeRewardToken(uint256 _index) external onlyOwner { uint256 _length = rewards.length; require(_index < _length, "index out of range"); address _token = rewards[_index].token; if (_index != _length - 1) { rewards[_index] = rewards[_length - 1]; } rewards.pop(); burners[_token] = address(0); emit RemoveRewardToken(_token); } /********************** * Internal Functions * **********************/ function _checkRatioRange( uint32 _stakerRatio, uint32 _treasuryRatio, uint32 _lockerRatio ) internal pure { require(_stakerRatio <= FEE_PRECISION, "staker ratio too large"); require(_treasuryRatio <= FEE_PRECISION, "treasury ratio too large"); require(_lockerRatio <= FEE_PRECISION, "locker ratio too large"); require(_stakerRatio + _treasuryRatio + _lockerRatio <= FEE_PRECISION, "ecosystem ratio too small"); } function _ensureNonZeroAddress(address _addr, string memory _name) internal pure { require(_addr != address(0), string(abi.encodePacked(_name, " address should not be zero"))); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IAaveV3Pool { 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: DEPRECATED: stable rate borrowing enabled //bit 60: asset is paused //bit 61: borrowing in isolation mode is enabled //bit 62: siloed borrowing enabled //bit 63: flashloaning enabled //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: DEPRECATED: 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: virtual accounting is enabled for the reserve //bit 253-255 unused uint256 data; } /** * This exists specifically to maintain the `getReserveData()` interface, since the new, internal * `ReserveData` struct includes the reserve's `virtualUnderlyingBalance`. */ struct ReserveDataLegacy { //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; // DEPRECATED on v3.2.0 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; // DEPRECATED on v3.2.0 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; } /** * @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 (ReserveDataLegacy memory); /** * @notice Returns the normalized variable debt per unit of asset * @dev WARNING: This function is intended to be used primarily by the protocol itself to get a * "dynamic" variable index based on time, current stored index and virtual rate at the current * moment (approx. a borrower would get if opening a position). This means that is always used in * combination with variable debt supply/balances. * If using this function externally, consider that is possible to have an increasing normalized * variable debt that is not equivalent to how the variable debt index would be updated in storage * (e.g. only updates with non-zero variable debt supply) * @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); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; pragma abicoder v2; interface IBalancerVault { enum JoinKind { INIT, EXACT_TOKENS_IN_FOR_BPT_OUT, TOKEN_IN_FOR_EXACT_BPT_OUT, ALL_TOKENS_IN_FOR_EXACT_BPT_OUT } enum ExitKind { EXACT_BPT_IN_FOR_ONE_TOKEN_OUT, EXACT_BPT_IN_FOR_TOKENS_OUT, BPT_IN_FOR_EXACT_TOKENS_OUT } enum SwapKind { GIVEN_IN, GIVEN_OUT } struct SingleSwap { bytes32 poolId; SwapKind kind; address assetIn; address assetOut; uint256 amount; bytes userData; } struct FundManagement { address sender; bool fromInternalBalance; address payable recipient; bool toInternalBalance; } function getPoolTokens(bytes32 poolId) external view returns ( address[] memory tokens, uint256[] memory balances, uint256 lastChangeBlock ); function swap( SingleSwap memory singleSwap, FundManagement memory funds, uint256 limit, uint256 deadline ) external payable returns (uint256 amountCalculated); struct JoinPoolRequest { address[] assets; uint256[] maxAmountsIn; bytes userData; bool fromInternalBalance; } function joinPool( bytes32 poolId, address sender, address recipient, JoinPoolRequest memory request ) external payable; struct ExitPoolRequest { address[] assets; uint256[] minAmountsOut; bytes userData; bool toInternalBalance; } function exitPool( bytes32 poolId, address sender, address payable recipient, ExitPoolRequest memory request ) external; /** * @dev Data for each individual swap executed by `batchSwap`. The asset in and out fields are indexes into the * `assets` array passed to that function, and ETH assets are converted to WETH. * * If `amount` is zero, the multihop mechanism is used to determine the actual amount based on the amount in/out * from the previous swap, depending on the swap kind. * * 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 BatchSwapStep { bytes32 poolId; uint256 assetInIndex; uint256 assetOutIndex; uint256 amount; bytes userData; } // This function is not marked as `nonReentrant` because the underlying mechanism relies on reentrancy function queryBatchSwap( SwapKind kind, BatchSwapStep[] memory swaps, address[] memory assets, FundManagement memory funds ) external returns (int256[] memory); function flashLoan( address recipient, address[] memory tokens, uint256[] memory amounts, bytes memory userData ) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; 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( address[] memory tokens, uint256[] memory amounts, uint256[] memory feeAmounts, bytes memory userData ) external; }
// 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 latestAnswer() 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: MIT pragma solidity ^0.8.0; // solhint-disable func-name-mixedcase interface ICurvePoolOracle { /******************** * Common Functions * ********************/ function ma_exp_time() external view returns (uint256); function ma_last_time() external view returns (uint256); /*************************** * Functions of Plain Pool * ***************************/ function get_p() external view returns (uint256); function last_price() external view returns (uint256); function last_prices() external view returns (uint256); function ema_price() external view returns (uint256); function price_oracle() external view returns (uint256); /************************ * Functions of NG Pool * ************************/ function get_p(uint256 index) external view returns (uint256); /// @notice Returns last price of the coin at index `k` w.r.t the coin /// at index 0. /// @dev last_prices returns the quote by the AMM for an infinitesimally small swap /// after the last trade. It is not equivalent to the last traded price, and /// is computed by taking the partial differential of `x` w.r.t `y`. The /// derivative is calculated in `get_p` and then multiplied with price_scale /// to give last_prices. /// @param index The index of the coin. /// @return uint256 Last logged price of coin. function last_price(uint256 index) external view returns (uint256); function last_prices(uint256 index) external view returns (uint256); function ema_price(uint256 index) external view returns (uint256); function price_oracle(uint256 index) external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ICurveStableSwapNG { /************************* * Public View Functions * *************************/ function coins(uint256 index) external view returns (address); function last_price(uint256 index) external view returns (uint256); function ema_price(uint256 index) external view returns (uint256); /// @notice Returns the AMM State price of token /// @dev if i = 0, it will return the state price of coin[1]. /// @param i index of state price (0 for coin[1], 1 for coin[2], ...) /// @return uint256 The state price quoted by the AMM for coin[i+1] function get_p(uint256 i) external view returns (uint256); function price_oracle(uint256 index) external view returns (uint256); function D_oracle() external view returns (uint256); function A() external view returns (uint256); function A_precise() external view returns (uint256); /// @notice Calculate the current input dx given output dy /// @dev Index values can be found via the `coins` public getter method /// @param i Index value for the coin to send /// @param j Index value of the coin to receive /// @param dy Amount of `j` being received after exchange /// @return Amount of `i` predicted function get_dx( int128 i, int128 j, uint256 dy ) external view returns (uint256); /// @notice Calculate the current output dy given input dx /// @dev Index values can be found via the `coins` public getter method /// @param i Index value for the coin to send /// @param j Index value of the coin to receive /// @param dx Amount of `i` being exchanged /// @return Amount of `j` predicted function get_dy( int128 i, int128 j, uint256 dx ) external view returns (uint256); /// @notice Calculate the amount received when withdrawing a single coin /// @param burn_amount Amount of LP tokens to burn in the withdrawal /// @param i Index value of the coin to withdraw /// @return Amount of coin received function calc_withdraw_one_coin(uint256 burn_amount, int128 i) external view returns (uint256); /// @notice The current virtual price of the pool LP token /// @dev Useful for calculating profits. /// The method may be vulnerable to donation-style attacks if implementation /// contains rebasing tokens. For integrators, caution is advised. /// @return LP token virtual price normalized to 1e18 function get_virtual_price() external view returns (uint256); /// @notice Calculate addition or reduction in token supply from a deposit or withdrawal /// @param amounts Amount of each coin being deposited /// @param is_deposit set True for deposits, False for withdrawals /// @return Expected amount of LP tokens received function calc_token_amount(uint256[] calldata amounts, bool is_deposit) external view returns (uint256); /// @notice Get the current balance of a coin within the /// pool, less the accrued admin fees /// @param i Index value for the coin to query balance of /// @return Token balance function balances(uint256 i) external view returns (uint256); function get_balances() external view returns (uint256[] memory); function stored_rates() external view returns (uint256[] memory); /// @notice Return the fee for swapping between `i` and `j` /// @param i Index value for the coin to send /// @param j Index value of the coin to receive /// @return Swap fee expressed as an integer with 1e10 precision function dynamic_fee(int128 i, int128 j) external view returns (uint256); /**************************** * Public Mutated Functions * ****************************/ /// @notice Perform an exchange between two coins /// @dev Index values can be found via the `coins` public getter method /// @param i Index value for the coin to send /// @param j Index value of the coin to receive /// @param dx Amount of `i` being exchanged /// @param min_dy Minimum amount of `j` to receive /// @return Actual amount of `j` received function exchange( int128 i, int128 j, uint256 dx, uint256 min_dy ) external returns (uint256); /// @notice Perform an exchange between two coins /// @dev Index values can be found via the `coins` public getter method /// @param i Index value for the coin to send /// @param j Index value of the coin to receive /// @param dx Amount of `i` being exchanged /// @param min_dy Minimum amount of `j` to receive /// @param receiver Address that receives `j` /// @return Actual amount of `j` received function exchange( int128 i, int128 j, uint256 dx, uint256 min_dy, address receiver ) external returns (uint256); /// @notice Perform an exchange between two coins without transferring token in /// @dev The contract swaps tokens based on a change in balance of coin[i]. The /// dx = ERC20(coin[i]).balanceOf(self) - self.stored_balances[i]. Users of /// this method are dex aggregators, arbitrageurs, or other users who do not /// wish to grant approvals to the contract: they would instead send tokens /// directly to the contract and call `exchange_received`. /// Note: This is disabled if pool contains rebasing tokens. /// @param i Index value for the coin to send /// @param j Index value of the coin to receive /// @param dx Amount of `i` being exchanged /// @param min_dy Minimum amount of `j` to receive /// @return Actual amount of `j` received function exchange_received( int128 i, int128 j, uint256 dx, uint256 min_dy ) external returns (uint256); /// @notice Perform an exchange between two coins without transferring token in /// @dev The contract swaps tokens based on a change in balance of coin[i]. The /// dx = ERC20(coin[i]).balanceOf(self) - self.stored_balances[i]. Users of /// this method are dex aggregators, arbitrageurs, or other users who do not /// wish to grant approvals to the contract: they would instead send tokens /// directly to the contract and call `exchange_received`. /// Note: This is disabled if pool contains rebasing tokens. /// @param i Index value for the coin to send /// @param j Index value of the coin to receive /// @param dx Amount of `i` being exchanged /// @param min_dy Minimum amount of `j` to receive /// @param receiver Address that receives `j` /// @return Actual amount of `j` received function exchange_received( int128 i, int128 j, uint256 dx, uint256 min_dy, address receiver ) external returns (uint256); /// @notice Deposit coins into the pool /// @param amounts List of amounts of coins to deposit /// @param min_mint_amount Minimum amount of LP tokens to mint from the deposit /// @return Amount of LP tokens received by depositing function add_liquidity(uint256[] calldata amounts, uint256 min_mint_amount) external returns (uint256); /// @notice Deposit coins into the pool /// @param amounts List of amounts of coins to deposit /// @param min_mint_amount Minimum amount of LP tokens to mint from the deposit /// @param receiver Address that owns the minted LP tokens /// @return Amount of LP tokens received by depositing function add_liquidity( uint256[] calldata amounts, uint256 min_mint_amount, address receiver ) external returns (uint256); /// @notice Withdraw a single coin from the pool /// @param burn_amount Amount of LP tokens to burn in the withdrawal /// @param i Index value of the coin to withdraw /// @param min_received Minimum amount of coin to receive /// @return Amount of coin received function remove_liquidity_one_coin( uint256 burn_amount, int128 i, uint256 min_received ) external returns (uint256); /// @notice Withdraw a single coin from the pool /// @param burn_amount Amount of LP tokens to burn in the withdrawal /// @param i Index value of the coin to withdraw /// @param min_received Minimum amount of coin to receive /// @param receiver Address that receives the withdrawn coins /// @return Amount of coin received function remove_liquidity_one_coin( uint256 burn_amount, int128 i, uint256 min_received, address receiver ) external returns (uint256); /// @notice Withdraw coins from the pool in an imbalanced amount /// @param amounts List of amounts of underlying coins to withdraw /// @param max_burn_amount Maximum amount of LP token to burn in the withdrawal /// @return Actual amount of the LP token burned in the withdrawal function remove_liquidity_imbalance(uint256[] calldata amounts, uint256 max_burn_amount) external returns (uint256); /// @notice Withdraw coins from the pool in an imbalanced amount /// @param amounts List of amounts of underlying coins to withdraw /// @param max_burn_amount Maximum amount of LP token to burn in the withdrawal /// @param receiver Address that receives the withdrawn coins /// @return Actual amount of the LP token burned in the withdrawal function remove_liquidity_imbalance( uint256[] calldata amounts, uint256 max_burn_amount, address receiver ) external returns (uint256); /// @notice Withdraw coins from the pool /// @dev Withdrawal amounts are based on current deposit ratios /// @param burn_amount Quantity of LP tokens to burn in the withdrawal /// @param min_amounts Minimum amounts of underlying coins to receive /// @return List of amounts of coins that were withdrawn function remove_liquidity(uint256 burn_amount, uint256[] calldata min_amounts) external returns (uint256[] memory); /// @notice Withdraw coins from the pool /// @dev Withdrawal amounts are based on current deposit ratios /// @param burn_amount Quantity of LP tokens to burn in the withdrawal /// @param min_amounts Minimum amounts of underlying coins to receive /// @param receiver Address that receives the withdrawn coins /// @return List of amounts of coins that were withdrawn function remove_liquidity( uint256 burn_amount, uint256[] calldata min_amounts, address receiver ) external returns (uint256[] memory); /// @notice Withdraw coins from the pool /// @dev Withdrawal amounts are based on current deposit ratios /// @param burn_amount Quantity of LP tokens to burn in the withdrawal /// @param min_amounts Minimum amounts of underlying coins to receive /// @param receiver Address that receives the withdrawn coins /// @return List of amounts of coins that were withdrawn function remove_liquidity( uint256 burn_amount, uint256[] calldata min_amounts, address receiver, bool claim_admin_fees ) external returns (uint256[] memory); /// @notice Claim admin fees. Callable by anyone. function withdraw_admin_fees() external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IPool } from "./IPool.sol"; interface IAaveFundingPool is IPool { /********** * Events * **********/ /// @notice Emitted when interest snapshot is taken. /// @param borrowIndex The borrow index, multiplied by 1e27. /// @param timestamp The timestamp when this snapshot is taken. event SnapshotAaveBorrowIndex(uint256 borrowIndex, uint256 timestamp); /// @notice Emitted when the open fee ratio related parameters are updated. /// @param ratio The open ratio value, multiplied by 1e9. /// @param step The open ratio step value, multiplied by 1e18. event UpdateOpenRatio(uint256 ratio, uint256 step); /// @notice Emitted when the open fee ratio is updated. /// @param oldRatio The value of previous close fee ratio, multiplied by 1e9. /// @param newRatio The value of current close fee ratio, multiplied by 1e9. event UpdateCloseFeeRatio(uint256 oldRatio, uint256 newRatio); /// @notice Emitted when the funding fee ratio is updated. /// @param oldRatio The value of previous funding fee ratio, multiplied by 1e9. /// @param newRatio The value of current funding fee ratio, multiplied by 1e9. event UpdateFundingRatio(uint256 oldRatio, uint256 newRatio); /************************* * Public View Functions * *************************/ /// @notice Return the value of funding ratio, multiplied by 1e9. function getFundingRatio() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IFxUSDBasePool { /********** * Events * **********/ /// @notice Emitted when the stable depeg price is updated. /// @param oldPrice The value of previous depeg price, multiplied by 1e18. /// @param newPrice The value of current depeg price, multiplied by 1e18. event UpdateStableDepegPrice(uint256 oldPrice, uint256 newPrice); /// @notice Emitted when the redeem cool down period is updated. /// @param oldPeriod The value of previous redeem cool down period. /// @param newPeriod The value of current redeem cool down period. event UpdateRedeemCoolDownPeriod(uint256 oldPeriod, uint256 newPeriod); /// @notice Emitted when deposit tokens. /// @param caller The address of caller. /// @param receiver The address of pool share recipient. /// @param tokenIn The address of input token. /// @param amountDeposited The amount of input tokens. /// @param amountSharesOut The amount of pool shares minted. event Deposit( address indexed caller, address indexed receiver, address indexed tokenIn, uint256 amountDeposited, uint256 amountSharesOut ); /// @notice Emitted when users request redeem. /// @param caller The address of caller. /// @param shares The amount of shares to redeem. /// @param unlockAt The timestamp when this share can be redeemed. event RequestRedeem(address indexed caller, uint256 shares, uint256 unlockAt); /// @notice Emitted when redeem pool shares. /// @param caller The address of caller. /// @param receiver The address of pool share recipient. /// @param amountSharesToRedeem The amount of pool shares burned. /// @param amountYieldTokenOut The amount of yield tokens redeemed. /// @param amountStableTokenOut The amount of stable tokens redeemed. event Redeem( address indexed caller, address indexed receiver, uint256 amountSharesToRedeem, uint256 amountYieldTokenOut, uint256 amountStableTokenOut ); /// @notice Emitted when rebalance or liquidate. /// @param caller The address of caller. /// @param tokenIn The address of input token. /// @param amountTokenIn The amount of input token used. /// @param amountCollateral The amount of collateral token rebalanced. /// @param amountYieldToken The amount of yield token used. /// @param amountStableToken The amount of stable token used. event Rebalance( address indexed caller, address indexed tokenIn, uint256 amountTokenIn, uint256 amountCollateral, uint256 amountYieldToken, uint256 amountStableToken ); /// @notice Emitted when arbitrage in curve pool. /// @param caller The address of caller. /// @param tokenIn The address of input token. /// @param amountIn The amount of input token used. /// @param amountOut The amount of output token swapped. /// @param bonusOut The amount of bonus token. event Arbitrage( address indexed caller, address indexed tokenIn, uint256 amountIn, uint256 amountOut, uint256 bonusOut ); /************************* * Public View Functions * *************************/ /// @notice The address of yield token. function yieldToken() external view returns (address); /// @notice The address of stable token. function stableToken() external view returns (address); /// @notice The total amount of yield token managed in this contract function totalYieldToken() external view returns (uint256); /// @notice The total amount of stable token managed in this contract function totalStableToken() external view returns (uint256); /// @notice The net asset value, multiplied by 1e18. function nav() external view returns (uint256); /// @notice Return the stable token price, multiplied by 1e18. function getStableTokenPrice() external view returns (uint256); /// @notice Return the stable token price with scaling to 18 decimals, multiplied by 1e18. function getStableTokenPriceWithScale() external view returns (uint256); /// @notice Preview the result of deposit. /// @param tokenIn The address of input token. /// @param amount The amount of input tokens to deposit. /// @return amountSharesOut The amount of pool shares should receive. function previewDeposit(address tokenIn, uint256 amount) external view returns (uint256 amountSharesOut); /// @notice Preview the result of redeem. /// @param amountSharesToRedeem The amount of pool shares to redeem. /// @return amountYieldOut The amount of yield token should receive. /// @return amountStableOut The amount of stable token should receive. function previewRedeem( uint256 amountSharesToRedeem ) external view returns (uint256 amountYieldOut, uint256 amountStableOut); /**************************** * Public Mutated Functions * ****************************/ /// @notice Deposit token. /// @param receiver The address of pool shares recipient. /// @param tokenIn The address of input token. /// @param amountTokenToDeposit The amount of input tokens to deposit. /// @param minSharesOut The minimum amount of pool shares should receive. /// @return amountSharesOut The amount of pool shares received. function deposit( address receiver, address tokenIn, uint256 amountTokenToDeposit, uint256 minSharesOut ) external returns (uint256 amountSharesOut); /// @notice Request redeem. /// @param shares The amount of shares to request. function requestRedeem(uint256 shares) external; /// @notice Redeem pool shares. /// @param receiver The address of token recipient. /// @param shares The amount of pool shares to redeem. /// @return amountYieldOut The amount of yield token should received. /// @return amountStableOut The amount of stable token should received. function redeem(address receiver, uint256 shares) external returns (uint256 amountYieldOut, uint256 amountStableOut); /// @notice Rebalance all positions in the given tick. /// @param pool The address of pool to rebalance. /// @param tick The index of tick to rebalance. /// @param tokenIn The address of token to rebalance. /// @param maxAmount The maximum amount of input token to rebalance. /// @param minBaseOut The minimum amount of collateral tokens should receive. /// @return tokenUsed The amount of input token used to rebalance. /// @return baseOut The amount of collateral tokens rebalanced. function rebalance( address pool, int16 tick, address tokenIn, uint256 maxAmount, uint256 minBaseOut ) external returns (uint256 tokenUsed, uint256 baseOut); /// @notice Rebalance the give position. /// @param pool The address of pool to rebalance. /// @param position The index of position to rebalance. /// @param tokenIn The address of token to rebalance. /// @param maxAmount The maximum amount of input token to rebalance. /// @param minBaseOut The minimum amount of collateral tokens should receive. /// @return tokenUsed The amount of input token used to rebalance. /// @return baseOut The amount of collateral tokens rebalanced. function rebalance( address pool, uint32 position, address tokenIn, uint256 maxAmount, uint256 minBaseOut ) external returns (uint256 tokenUsed, uint256 baseOut); /// @notice Liquidate the give position. /// @param pool The address of pool to rebalance. /// @param position The index of position to rebalance. /// @param tokenIn The address of token to rebalance. /// @param maxAmount The maximum amount of input token to rebalance. /// @param minBaseOut The minimum amount of collateral tokens should receive. /// @return tokenUsed The amount of input token used to rebalance. /// @return baseOut The amount of collateral tokens rebalanced. function liquidate( address pool, uint32 position, address tokenIn, uint256 maxAmount, uint256 minBaseOut ) external returns (uint256 tokenUsed, uint256 baseOut); /// @notice Arbitrage between yield token and stable token. /// @param srcToken The address of source token. /// @param amountIn The amount of source token to use. /// @param receiver The address of bonus receiver. /// @param data The hook data to `onSwap`. /// @return amountOut The amount of target token swapped. /// @return bonusOut The amount of bonus token. function arbitrage( address srcToken, uint256 amountIn, address receiver, bytes calldata data ) external returns (uint256 amountOut, uint256 bonusOut); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IFxUSDRegeneracy { /********** * Events * **********/ /// @notice Emitted when rebalance/liquidate with stable token. /// @param amountStable The amount of stable token used. /// @param amountFxUSD The corresponding amount of fxUSD. event RebalanceWithStable(uint256 amountStable, uint256 amountFxUSD); /// @notice Emitted when buyback fxUSD with stable reserve. /// @param amountStable the amount of stable token used. /// @param amountFxUSD The amount of fxUSD bought. /// @param bonusFxUSD The amount of fxUSD as bonus for caller. event Buyback(uint256 amountStable, uint256 amountFxUSD, uint256 bonusFxUSD); /************************* * Public View Functions * *************************/ /// @notice The address of `PoolManager` contract. function poolManager() external view returns (address); /// @notice The address of stable token. function stableToken() external view returns (address); /// @notice The address of `PegKeeper` contract. function pegKeeper() external view returns (address); /**************************** * Public Mutated Functions * ****************************/ /// @notice Mint fxUSD token someone. function mint(address to, uint256 amount) external; /// @notice Burn fxUSD from someone. function burn(address from, uint256 amount) external; /// @notice Hook for rebalance/liquidate with stable token. /// @param amountStableToken The amount of stable token. /// @param amountFxUSD The amount of fxUSD. function onRebalanceWithStable(uint256 amountStableToken, uint256 amountFxUSD) external; /// @notice Buyback fxUSD with stable token. /// @param amountIn the amount of stable token to use. /// @param receiver The address of bonus receiver. /// @param data The hook data to PegKeeper. /// @return amountOut The amount of fxUSD swapped. /// @return bonusOut The amount of bonus fxUSD. function buyback( uint256 amountIn, address receiver, bytes calldata data ) external returns (uint256 amountOut, uint256 bonusOut); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IPegKeeper { /********** * Events * **********/ /// @notice Emitted when the converter contract is updated. /// @param oldConverter The address of previous converter contract. /// @param newConverter The address of current converter contract. event UpdateConverter(address indexed oldConverter, address indexed newConverter); /// @notice Emitted when the curve pool contract is updated. /// @param oldPool The address of previous curve pool contract. /// @param newPool The address of current curve pool contract. event UpdateCurvePool(address indexed oldPool, address indexed newPool); /// @notice Emitted when the price threshold is updated. /// @param oldThreshold The value of previous price threshold /// @param newThreshold The value of current price threshold event UpdatePriceThreshold(uint256 oldThreshold, uint256 newThreshold); /************************* * Public View Functions * *************************/ /// @notice Return whether borrow for fxUSD is allowed. function isBorrowAllowed() external view returns (bool); /// @notice Return whether funding costs is enabled. function isFundingEnabled() external view returns (bool); /// @notice Return the price of fxUSD, multiplied by 1e18 function getFxUSDPrice() external view returns (uint256); /**************************** * Public Mutated Functions * ****************************/ /// @notice Buyback fxUSD with stable reserve in FxUSDSave. /// @param amountIn the amount of stable token to use. /// @param data The hook data to `onSwap`. /// @return amountOut The amount of fxUSD swapped. /// @return bonusOut The amount of bonus fxUSD. function buyback(uint256 amountIn, bytes calldata data) external returns (uint256 amountOut, uint256 bonusOut); /// @notice Stabilize the fxUSD price in curve pool. /// @param srcToken The address of source token (fxUSD or stable token). /// @param amountIn the amount of source token to use. /// @param data The hook data to `onSwap`. /// @return amountOut The amount of target token swapped. /// @return bonusOut The amount of bonus token. function stabilize( address srcToken, uint256 amountIn, bytes calldata data ) external returns (uint256 amountOut, uint256 bonusOut); /// @notice Swap callback from `buyback` and `stabilize`. /// @param srcToken The address of source token. /// @param srcToken The address of target token. /// @param amountIn the amount of source token to use. /// @param data The callback data. /// @return amountOut The amount of target token swapped. function onSwap( address srcToken, address targetToken, uint256 amountIn, bytes calldata data ) external returns (uint256 amountOut); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IPool { /********** * Events * **********/ /// @notice Emitted when price oracle is updated. /// @param oldOracle The previous address of price oracle. /// @param newOracle The current address of price oracle. event UpdatePriceOracle(address oldOracle, address newOracle); /// @notice Emitted when borrow status is updated. /// @param status The updated borrow status. event UpdateBorrowStatus(bool status); /// @notice Emitted when redeem status is updated. /// @param status The updated redeem status. event UpdateRedeemStatus(bool status); /// @notice Emitted when debt ratio range is updated. /// @param minDebtRatio The current value of minimum debt ratio, multiplied by 1e18. /// @param maxDebtRatio The current value of maximum debt ratio, multiplied by 1e18. event UpdateDebtRatioRange(uint256 minDebtRatio, uint256 maxDebtRatio); /// @notice Emitted when max redeem ratio per tick is updated. /// @param ratio The current value of max redeem ratio per tick, multiplied by 1e9. event UpdateMaxRedeemRatioPerTick(uint256 ratio); /// @notice Emitted when the rebalance ratio is updated. /// @param debtRatio The current value of rebalance debt ratio, multiplied by 1e18. /// @param bonusRatio The current value of rebalance bonus ratio, multiplied by 1e9. event UpdateRebalanceRatios(uint256 debtRatio, uint256 bonusRatio); /// @notice Emitted when the liquidate ratio is updated. /// @param debtRatio The current value of liquidate debt ratio, multiplied by 1e18. /// @param bonusRatio The current value of liquidate bonus ratio, multiplied by 1e9. event UpdateLiquidateRatios(uint256 debtRatio, uint256 bonusRatio); /// @notice Emitted when position is updated. /// @param position The index of this position. /// @param tick The index of tick, this position belongs to. /// @param collShares The amount of collateral shares in this position. /// @param debtShares The amount of debt shares in this position. /// @param price The price used for this operation. event PositionSnapshot(uint256 position, int16 tick, uint256 collShares, uint256 debtShares, uint256 price); /// @notice Emitted when tick moved due to rebalance, liquidate or redeem. /// @param oldTick The index of the previous tick. /// @param newTick The index of the current tick. /// @param collShares The amount of collateral shares added to new tick. /// @param debtShares The amount of debt shares added to new tick. /// @param price The price used for this operation. event TickMovement(int16 oldTick, int16 newTick, uint256 collShares, uint256 debtShares, uint256 price); /// @notice Emitted when debt index increase. event DebtIndexSnapshot(uint256 index); /// @notice Emitted when collateral index increase. event CollateralIndexSnapshot(uint256 index); /*********** * Structs * ***********/ /// @dev The result for liquidation. /// @param rawColls The amount of collateral tokens liquidated. /// @param rawDebts The amount of debt tokens liquidated. /// @param bonusRawColls The amount of bonus collateral tokens given. /// @param bonusFromReserve The amount of bonus collateral tokens coming from reserve pool. struct LiquidateResult { uint256 rawColls; uint256 rawDebts; uint256 bonusRawColls; uint256 bonusFromReserve; } /// @dev The result for rebalance. /// @param rawColls The amount of collateral tokens rebalanced. /// @param rawDebts The amount of debt tokens rebalanced. /// @param bonusRawColls The amount of bonus collateral tokens given. struct RebalanceResult { uint256 rawColls; uint256 rawDebts; uint256 bonusRawColls; } /************************* * Public View Functions * *************************/ /// @notice The address of fxUSD. function fxUSD() external view returns (address); /// @notice The address of `PoolManager` contract. function poolManager() external view returns (address); /// @notice The address of `PegKeeper` contract. function pegKeeper() external view returns (address); /// @notice The address of collateral token. function collateralToken() external view returns (address); /// @notice The address of price oracle. function priceOracle() external view returns (address); /// @notice Return whether borrow is paused. function isBorrowPaused() external view returns (bool); /// @notice Return whether redeem is paused. function isRedeemPaused() external view returns (bool); /// @notice Return the current top tick with debts. function getTopTick() external view returns (int16); /// @notice Return the next position id. function getNextPositionId() external view returns (uint32); /// @notice Return the next tick tree node id. function getNextTreeNodeId() external view returns (uint48); /// @notice Return the debt ratio range. /// @param minDebtRatio The minimum required debt ratio, multiplied by 1e18. /// @param maxDebtRatio The minimum allowed debt ratio, multiplied by 1e18. function getDebtRatioRange() external view returns (uint256 minDebtRatio, uint256 maxDebtRatio); /// @notice Return the maximum redeem percentage per tick, multiplied by 1e9. function getMaxRedeemRatioPerTick() external view returns (uint256); /// @notice Get `debtRatio` and `bonusRatio` for rebalance. /// @return debtRatio The minimum debt ratio to start rebalance, multiplied by 1e18. /// @return bonusRatio The bonus ratio during rebalance, multiplied by 1e9. function getRebalanceRatios() external view returns (uint256 debtRatio, uint256 bonusRatio); /// @notice Get `debtRatio` and `bonusRatio` for liquidate. /// @return debtRatio The minimum debt ratio to start liquidate, multiplied by 1e18. /// @return bonusRatio The bonus ratio during liquidate, multiplied by 1e9. function getLiquidateRatios() external view returns (uint256 debtRatio, uint256 bonusRatio); /// @notice Get debt and collateral index. /// @return debtIndex The index for debt shares. /// @return collIndex The index for collateral shares. function getDebtAndCollateralIndex() external view returns (uint256 debtIndex, uint256 collIndex); /// @notice Get debt and collateral shares. /// @return debtShares The total number of debt shares. /// @return collShares The total number of collateral shares. function getDebtAndCollateralShares() external view returns (uint256 debtShares, uint256 collShares); /// @notice Return the details of the given position. /// @param tokenId The id of position to query. /// @return rawColls The amount of collateral tokens supplied in this position. /// @return rawDebts The amount of debt tokens borrowed in this position. function getPosition(uint256 tokenId) external view returns (uint256 rawColls, uint256 rawDebts); /// @notice Return the debt ratio of the given position. /// @param tokenId The id of position to query. /// @return debtRatio The debt ratio of this position. function getPositionDebtRatio(uint256 tokenId) external view returns (uint256 debtRatio); /// @notice The total amount of raw collateral tokens. function getTotalRawCollaterals() external view returns (uint256); /// @notice The total amount of raw debt tokens. function getTotalRawDebts() external view returns (uint256); /**************************** * Public Mutated Functions * ****************************/ /// @notice Open a new position or operate on an old position. /// @param positionId The id of the position. If `positionId=0`, it means we need to open a new position. /// @param newRawColl The amount of collateral token to supply (positive value) or withdraw (negative value). /// @param newRawColl The amount of debt token to borrow (positive value) or repay (negative value). /// @param owner The address of position owner. /// @return actualPositionId The id of this position. /// @return actualRawColl The actual amount of collateral tokens supplied (positive value) or withdrawn (negative value). /// @return actualRawDebt The actual amount of debt tokens borrowed (positive value) or repay (negative value). function operate( uint256 positionId, int256 newRawColl, int256 newRawDebt, address owner ) external returns (uint256 actualPositionId, int256 actualRawColl, int256 actualRawDebt, uint256 protocolFees); /// @notice Redeem debt tokens to get collateral tokens. /// @param rawDebts The amount of debt tokens to redeem. /// @return rawColls The amount of collateral tokens to redeemed. function redeem(uint256 rawDebts) external returns (uint256 rawColls); /// @notice Rebalance all positions in the given tick. /// @param tick The id of tick to rebalance. /// @param maxRawDebts The maximum amount of debt tokens to rebalance. /// @return result The result of rebalance. function rebalance(int16 tick, uint256 maxRawDebts) external returns (RebalanceResult memory result); /// @notice Rebalance the given position. /// @param positionId The id of position to rebalance. /// @param maxRawDebts The maximum amount of debt tokens to rebalance. /// @return result The result of rebalance. function rebalance(uint32 positionId, uint256 maxRawDebts) external returns (RebalanceResult memory result); /// @notice Liquidate the given position. /// @param positionId The id of position to liquidate. /// @param maxRawDebts The maximum amount of debt tokens to liquidate. /// @param reservedRawColls The amount of collateral tokens in reserve pool. /// @return result The result of liquidate. function liquidate( uint256 positionId, uint256 maxRawDebts, uint256 reservedRawColls ) external returns (LiquidateResult memory result); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IPoolManager { /********** * Events * **********/ /// @notice Register a new pool. /// @param pool The address of fx pool. event RegisterPool(address indexed pool); /// @notice Emitted when the reward splitter contract is updated. /// @param pool The address of fx pool. /// @param oldSplitter The address of previous reward splitter contract. /// @param newSplitter The address of current reward splitter contract. event UpdateRewardSplitter(address indexed pool, address indexed oldSplitter, address indexed newSplitter); /// @notice Emitted when the threshold for permissionless liquidate/rebalance is updated. /// @param oldThreshold The value of previous threshold. /// @param newThreshold The value of current threshold. event UpdatePermissionedLiquidationThreshold(uint256 oldThreshold, uint256 newThreshold); /// @notice Emitted when token rate is updated. /// @param scalar The token scalar to reach 18 decimals. /// @param provider The address of token rate provider. event UpdateTokenRate(address indexed token, uint256 scalar, address provider); /// @notice Emitted when pool capacity is updated. /// @param pool The address of fx pool. /// @param collateralCapacity The capacity for collateral token. /// @param debtCapacity The capacity for debt token. event UpdatePoolCapacity(address indexed pool, uint256 collateralCapacity, uint256 debtCapacity); /// @notice Emitted when position is updated. /// @param pool The address of pool where the position belongs to. /// @param position The id of the position. /// @param deltaColls The amount of collateral token changes. /// @param deltaDebts The amount of debt token changes. /// @param protocolFees The amount of protocol fees charges. event Operate( address indexed pool, uint256 indexed position, int256 deltaColls, int256 deltaDebts, uint256 protocolFees ); /// @notice Emitted when redeem happened. /// @param pool The address of pool redeemed. /// @param colls The amount of collateral tokens redeemed. /// @param debts The amount of debt tokens redeemed. /// @param protocolFees The amount of protocol fees charges. event Redeem(address indexed pool, uint256 colls, uint256 debts, uint256 protocolFees); /// @notice Emitted when rebalance for a tick happened. /// @param pool The address of pool rebalanced. /// @param tick The index of tick rebalanced. /// @param colls The amount of collateral tokens rebalanced. /// @param fxUSDDebts The amount of fxUSD rebalanced. /// @param stableDebts The amount of stable token (a.k.a USDC) rebalanced. event RebalanceTick(address indexed pool, int16 indexed tick, uint256 colls, uint256 fxUSDDebts, uint256 stableDebts); /// @notice Emitted when rebalance for a position happened. /// @param pool The address of pool rebalanced. /// @param position The index of position rebalanced. /// @param colls The amount of collateral tokens rebalanced. /// @param fxUSDDebts The amount of fxUSD rebalanced. /// @param stableDebts The amount of stable token (a.k.a USDC) rebalanced. event RebalancePosition(address indexed pool, uint256 indexed position, uint256 colls, uint256 fxUSDDebts, uint256 stableDebts); /// @notice Emitted when liquidate for a position happened. /// @param pool The address of pool liquidated. /// @param position The index of position liquidated. /// @param colls The amount of collateral tokens liquidated. /// @param fxUSDDebts The amount of fxUSD liquidated. /// @param stableDebts The amount of stable token (a.k.a USDC) liquidated. event LiquidatePosition(address indexed pool, uint256 indexed position, uint256 colls, uint256 fxUSDDebts, uint256 stableDebts); /// @notice Emitted when someone harvest pending rewards. /// @param caller The address of caller. /// @param amountRewards The amount of total harvested rewards. /// @param amountFunding The amount of total harvested funding. /// @param performanceFee The amount of harvested rewards distributed to protocol revenue. /// @param harvestBounty The amount of harvested rewards distributed to caller as harvest bounty. event Harvest( address indexed caller, address indexed pool, uint256 amountRewards, uint256 amountFunding, uint256 performanceFee, uint256 harvestBounty ); /************************* * Public View Functions * *************************/ /// @notice The address of fxUSD. function fxUSD() external view returns (address); /// @notice The address of FxUSDSave. function fxBASE() external view returns (address); /// @notice The address of `PegKeeper`. function pegKeeper() external view returns (address); /// @notice The address of reward splitter. function rewardSplitter(address pool) external view returns (address); /**************************** * Public Mutated Functions * ****************************/ /// @notice Open a new position or operate on an old position. /// @param pool The address of pool to operate. /// @param positionId The id of the position. If `positionId=0`, it means we need to open a new position. /// @param newColl The amount of collateral token to supply (positive value) or withdraw (negative value). /// @param newDebt The amount of debt token to borrow (positive value) or repay (negative value). /// @return actualPositionId The id of this position. function operate( address pool, uint256 positionId, int256 newColl, int256 newDebt ) external returns (uint256 actualPositionId); /// @notice Redeem debt tokens to get collateral tokens. /// @param pool The address of pool to redeem. /// @param debts The amount of debt tokens to redeem. /// @param minColls The minimum amount of collateral tokens should redeem. /// @return colls The amount of collateral tokens redeemed. function redeem(address pool, uint256 debts, uint256 minColls) external returns (uint256 colls); /// @notice Rebalance all positions in the given tick. /// @param pool The address of pool to rebalance. /// @param receiver The address of recipient for rebalanced tokens. /// @param tick The index of tick to rebalance. /// @param maxFxUSD The maximum amount of fxUSD to rebalance. /// @param maxStable The maximum amount of stable token (a.k.a USDC) to rebalance. /// @return colls The amount of collateral tokens rebalanced. /// @return fxUSDUsed The amount of fxUSD used to rebalance. /// @return stableUsed The amount of stable token used to rebalance. function rebalance( address pool, address receiver, int16 tick, uint256 maxFxUSD, uint256 maxStable ) external returns (uint256 colls, uint256 fxUSDUsed, uint256 stableUsed); /// @notice Rebalance a given position. /// @param pool The address of pool to rebalance. /// @param receiver The address of recipient for rebalanced tokens. /// @param positionId The id of position to rebalance. /// @param maxFxUSD The maximum amount of fxUSD to rebalance. /// @param maxStable The maximum amount of stable token (a.k.a USDC) to rebalance. /// @return colls The amount of collateral tokens rebalanced. /// @return fxUSDUsed The amount of fxUSD used to rebalance. /// @return stableUsed The amount of stable token used to rebalance. function rebalance( address pool, address receiver, uint32 positionId, uint256 maxFxUSD, uint256 maxStable ) external returns (uint256 colls, uint256 fxUSDUsed, uint256 stableUsed); /// @notice Liquidate a given position. /// @param pool The address of pool to liquidate. /// @param receiver The address of recipient for liquidated tokens. /// @param positionId The id of position to liquidate. /// @param maxFxUSD The maximum amount of fxUSD to liquidate. /// @param maxStable The maximum amount of stable token (a.k.a USDC) to liquidate. /// @return colls The amount of collateral tokens liquidated. /// @return fxUSDUsed The amount of fxUSD used to liquidate. /// @return stableUsed The amount of stable token used to liquidate. function liquidate( address pool, address receiver, uint32 positionId, uint256 maxFxUSD, uint256 maxStable ) external returns (uint256 colls, uint256 fxUSDUsed, uint256 stableUsed); /// @notice Harvest pending rewards of the given pool. /// @param pool The address of pool to harvest. /// @return amountRewards The amount of rewards harvested. /// @return amountFunding The amount of funding harvested. function harvest(address pool) external returns (uint256 amountRewards, uint256 amountFunding); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IProtocolFees { /********** * Events * **********/ /// @notice Emitted when the reserve pool contract is updated. /// @param oldReservePool The address of previous reserve pool. /// @param newReservePool The address of current reserve pool. event UpdateReservePool(address indexed oldReservePool, address indexed newReservePool); /// @notice Emitted when the treasury contract is updated. /// @param oldTreasury The address of previous treasury contract. /// @param newTreasury The address of current treasury contract. event UpdateTreasury(address indexed oldTreasury, address indexed newTreasury); /// @notice Emitted when the revenue pool contract is updated. /// @param oldPool The address of previous revenue pool contract. /// @param newPool The address of current revenue pool contract. event UpdateRevenuePool(address indexed oldPool, address indexed newPool); /// @notice Emitted when the ratio for treasury is updated. /// @param oldRatio The value of the previous ratio, multiplied by 1e9. /// @param newRatio The value of the current ratio, multiplied by 1e9. event UpdateRewardsExpenseRatio(uint256 oldRatio, uint256 newRatio); /// @notice Emitted when the ratio for treasury is updated. /// @param oldRatio The value of the previous ratio, multiplied by 1e9. /// @param newRatio The value of the current ratio, multiplied by 1e9. event UpdateFundingExpenseRatio(uint256 oldRatio, uint256 newRatio); /// @notice Emitted when the ratio for treasury is updated. /// @param oldRatio The value of the previous ratio, multiplied by 1e9. /// @param newRatio The value of the current ratio, multiplied by 1e9. event UpdateLiquidationExpenseRatio(uint256 oldRatio, uint256 newRatio); /// @notice Emitted when the ratio for harvester is updated. /// @param oldRatio The value of the previous ratio, multiplied by 1e9. /// @param newRatio The value of the current ratio, multiplied by 1e9. event UpdateHarvesterRatio(uint256 oldRatio, uint256 newRatio); /// @notice Emitted when the flash loan fee ratio is updated. /// @param oldRatio The value of the previous ratio, multiplied by 1e9. /// @param newRatio The value of the current ratio, multiplied by 1e9. event UpdateFlashLoanFeeRatio(uint256 oldRatio, uint256 newRatio); /// @notice Emitted when the redeem fee ratio is updated. /// @param oldRatio The value of the previous ratio, multiplied by 1e9. /// @param newRatio The value of the current ratio, multiplied by 1e9. event UpdateRedeemFeeRatio(uint256 oldRatio, uint256 newRatio); /************************* * Public View Functions * *************************/ /// @notice Return the fee ratio distributed as protocol revenue in funding costs, multiplied by 1e9. function getFundingExpenseRatio() external view returns (uint256); /// @notice Return the fee ratio distributed as protocol revenue in general rewards, multiplied by 1e9. function getRewardsExpenseRatio() external view returns (uint256); /// @notice Return the fee ratio distributed as protocol revenue in liquidation/rebalance, multiplied by 1e9. function getLiquidationExpenseRatio() external view returns (uint256); /// @notice Return the fee ratio distributed to fxBASE in funding costs, multiplied by 1e9. function getFundingFxSaveRatio() external view returns (uint256); /// @notice Return the fee ratio distributed to fxBASE in general rewards, multiplied by 1e9. function getRewardsFxSaveRatio() external view returns (uint256); /// @notice Return the fee ratio distributed ad harvester bounty, multiplied by 1e9. function getHarvesterRatio() external view returns (uint256); /// @notice Return the flash loan fee ratio, multiplied by 1e9. function getFlashLoanFeeRatio() external view returns (uint256); /// @notice Return the redeem fee ratio, multiplied by 1e9. function getRedeemFeeRatio() external view returns (uint256); /// @notice Return the address of reserve pool. function reservePool() external view returns (address); /// @notice Return the address of protocol treasury. function treasury() external view returns (address); /// @notice Return the address of protocol revenue pool. function revenuePool() external view returns (address); /// @notice Return the amount of protocol fees accumulated by the given pool. function accumulatedPoolFees(address pool) external view returns (uint256); /**************************** * Public Mutated Functions * ****************************/ /// @notice Withdraw accumulated pool fee for the given pool lists. /// @param pools The list of pool addresses to withdraw. function withdrawAccumulatedPoolFee(address[] memory pools) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IReservePool { /// @notice Emitted when the market request bonus. /// @param token The address of the token requested. /// @param receiver The address of token receiver. /// @param bonus The amount of bonus token. event RequestBonus(address indexed token, address indexed receiver, uint256 bonus); /************************* * Public View Functions * *************************/ /// @notice Return the balance of token in this contract. function getBalance(address token) external view returns (uint256); /**************************** * Public Mutated Functions * ****************************/ /// @notice Request bonus token from Reserve Pool. /// @param token The address of token to request. /// @param receiver The address recipient for the bonus token. /// @param bonus The amount of bonus token to send. function requestBonus(address token, address receiver, uint256 bonus) external; /// @notice Withdraw dust assets in this contract. /// @param token The address of token to withdraw. /// @param amount The amount of token to withdraw. /// @param recipient The address of token receiver. function withdrawFund(address token, uint256 amount, address recipient) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IRewardSplitter { /**************************** * Public Mutated Functions * ****************************/ /// @notice Split token to different RebalancePool. /// @param token The address of token to split. function split(address token) external; /// @notice Deposit new rewards to this contract. /// /// @param token The address of reward token. /// @param amount The amount of new rewards. function depositReward(address token, uint256 amount) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IWrappedEther { function deposit() external payable; function withdraw(uint256 wad) external; }
// SPDX-License-Identifier: GPL-2.0-or-later pragma solidity ^0.8.0; /// @title BitMath /// @dev This library provides functionality for computing bit properties of an unsigned integer /// /// copy from: https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/BitMath.sol library BitMath { /// @notice Returns the index of the most significant bit of the number, /// where the least significant bit is at index 0 and the most significant bit is at index 255 /// @dev The function satisfies the property: /// x >= 2**mostSignificantBit(x) and x < 2**(mostSignificantBit(x)+1) /// @param x the value for which to compute the most significant bit, must be greater than 0 /// @return r the index of the most significant bit function mostSignificantBit(uint256 x) internal pure returns (uint8 r) { require(x > 0); if (x >= 0x100000000000000000000000000000000) { x >>= 128; r += 128; } if (x >= 0x10000000000000000) { x >>= 64; r += 64; } if (x >= 0x100000000) { x >>= 32; r += 32; } if (x >= 0x10000) { x >>= 16; r += 16; } if (x >= 0x100) { x >>= 8; r += 8; } if (x >= 0x10) { x >>= 4; r += 4; } if (x >= 0x4) { x >>= 2; r += 2; } if (x >= 0x2) r += 1; } /// @notice Returns the index of the least significant bit of the number, /// where the least significant bit is at index 0 and the most significant bit is at index 255 /// @dev The function satisfies the property: /// (x & 2**leastSignificantBit(x)) != 0 and (x & (2**(leastSignificantBit(x)) - 1)) == 0) /// @param x the value for which to compute the least significant bit, must be greater than 0 /// @return r the index of the least significant bit function leastSignificantBit(uint256 x) internal pure returns (uint8 r) { require(x > 0); r = 255; if (x & type(uint128).max > 0) { r -= 128; } else { x >>= 128; } if (x & type(uint64).max > 0) { r -= 64; } else { x >>= 64; } if (x & type(uint32).max > 0) { r -= 32; } else { x >>= 32; } if (x & type(uint16).max > 0) { r -= 16; } else { x >>= 16; } if (x & type(uint8).max > 0) { r -= 8; } else { x >>= 8; } if (x & 0xf > 0) { r -= 4; } else { x >>= 4; } if (x & 0x3 > 0) { r -= 2; } else { x >>= 2; } if (x & 0x1 > 0) r -= 1; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; library Math { enum Rounding { Up, Down } /// @dev Internal return the value of min(a, b). function min(uint256 a, uint256 b) internal pure returns (uint256) { return a < b ? a : b; } /// @dev Internal return the value of max(a, b). function max(uint256 a, uint256 b) internal pure returns (uint256) { return a > b ? a : b; } /// @dev Internal return the value of a * b / c, with rounding. function mulDiv(uint256 a, uint256 b, uint256 c, Rounding rounding) internal pure returns (uint256) { return rounding == Rounding.Down ? mulDivDown(a, b, c) : mulDivUp(a, b, c); } /// @dev Internal return the value of ceil(a * b / c). function mulDivUp(uint256 a, uint256 b, uint256 c) internal pure returns (uint256) { return (a * b + c - 1) / c; } /// @dev Internal return the value of floor(a * b / c). function mulDivDown(uint256 a, uint256 b, uint256 c) internal pure returns (uint256) { return (a * b) / c; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { BitMath } from "./BitMath.sol"; library TickBitmap { function position(int16 tick) private pure returns (int8 wordPos, uint8 bitPos) { assembly { wordPos := shr(8, tick) bitPos := and(tick, 255) } } function flipTick(mapping(int8 => uint256) storage self, int16 tick) internal { (int8 wordPos, uint8 bitPos) = position(tick); uint256 mask = 1 << bitPos; self[wordPos] ^= mask; } function isBitSet(mapping(int8 => uint256) storage self, int16 tick) internal view returns (bool) { (int8 wordPos, uint8 bitPos) = position(tick); uint256 mask = 1 << bitPos; return (self[wordPos] & mask) > 0; } /// @notice Returns the next initialized tick contained in the same word (or adjacent word) as the tick that is /// to the left (less than or equal to). function nextDebtPositionWithinOneWord( mapping(int8 => uint256) storage self, int16 tick ) internal view returns (int16 next, bool hasDebt) { unchecked { // start from the word of the next tick, since the current tick state doesn't matter (int8 wordPos, uint8 bitPos) = position(tick); // all the 1s at or to the right of the current bitPos uint256 mask = (1 << bitPos) - 1 + (1 << bitPos); uint256 masked = self[wordPos] & mask; // if there are no initialized ticks to the left of the current tick, return leftmost in the word hasDebt = masked != 0; // overflow/underflow is possible, but prevented externally by limiting tick next = hasDebt ? (tick - int16(uint16(bitPos - BitMath.mostSignificantBit(masked)))) : (tick - int16(uint16(bitPos))); } } }
// SPDX-License-Identifier: BUSL-1.1 pragma solidity ^0.8.26; /// @title library that calculates number "tick" and "ratioX96" from this: ratioX96 = (1.0015^tick) * 2^96 /// @notice this library is used in Fluid Vault protocol for optimiziation. /// @dev "tick" supports between -32767 and 32767. "ratioX96" supports between 37075072 and 169307877264527972847801929085841449095838922544595 /// /// @dev Copy from https://github.com/Instadapp/fluid-contracts-public/blob/main/contracts/libraries/tickMath.sol library TickMath { /// The minimum tick that can be passed in getRatioAtTick. 1.0015**-32767 int24 internal constant MIN_TICK = -32767; /// The maximum tick that can be passed in getRatioAtTick. 1.0015**32767 int24 internal constant MAX_TICK = 32767; uint256 internal constant FACTOR00 = 0x100000000000000000000000000000000; uint256 internal constant FACTOR01 = 0xff9dd7de423466c20352b1246ce4856f; // 2^128/1.0015**1 = 339772707859149738855091969477551883631 uint256 internal constant FACTOR02 = 0xff3bd55f4488ad277531fa1c725a66d0; // 2^128/1.0015**2 = 339263812140938331358054887146831636176 uint256 internal constant FACTOR03 = 0xfe78410fd6498b73cb96a6917f853259; // 2^128/1.0015**4 = 338248306163758188337119769319392490073 uint256 internal constant FACTOR04 = 0xfcf2d9987c9be178ad5bfeffaa123273; // 2^128/1.0015**8 = 336226404141693512316971918999264834163 uint256 internal constant FACTOR05 = 0xf9ef02c4529258b057769680fc6601b3; // 2^128/1.0015**16 = 332218786018727629051611634067491389875 uint256 internal constant FACTOR06 = 0xf402d288133a85a17784a411f7aba082; // 2^128/1.0015**32 = 324346285652234375371948336458280706178 uint256 internal constant FACTOR07 = 0xe895615b5beb6386553757b0352bda90; // 2^128/1.0015**64 = 309156521885964218294057947947195947664 uint256 internal constant FACTOR08 = 0xd34f17a00ffa00a8309940a15930391a; // 2^128/1.0015**128 = 280877777739312896540849703637713172762 uint256 internal constant FACTOR09 = 0xae6b7961714e20548d88ea5123f9a0ff; // 2^128/1.0015**256 = 231843708922198649176471782639349113087 uint256 internal constant FACTOR10 = 0x76d6461f27082d74e0feed3b388c0ca1; // 2^128/1.0015**512 = 157961477267171621126394973980180876449 uint256 internal constant FACTOR11 = 0x372a3bfe0745d8b6b19d985d9a8b85bb; // 2^128/1.0015**1024 = 73326833024599564193373530205717235131 uint256 internal constant FACTOR12 = 0x0be32cbee48979763cf7247dd7bb539d; // 2^128/1.0015**2048 = 15801066890623697521348224657638773661 uint256 internal constant FACTOR13 = 0x8d4f70c9ff4924dac37612d1e2921e; // 2^128/1.0015**4096 = 733725103481409245883800626999235102 uint256 internal constant FACTOR14 = 0x4e009ae5519380809a02ca7aec77; // 2^128/1.0015**8192 = 1582075887005588088019997442108535 uint256 internal constant FACTOR15 = 0x17c45e641b6e95dee056ff10; // 2^128/1.0015**16384 = 7355550435635883087458926352 /// The minimum value that can be returned from getRatioAtTick. Equivalent to getRatioAtTick(MIN_TICK). ~ Equivalent to `(1 << 96) * (1.0015**-32767)` uint256 internal constant MIN_RATIOX96 = 37075072; /// The maximum value that can be returned from getRatioAtTick. Equivalent to getRatioAtTick(MAX_TICK). /// ~ Equivalent to `(1 << 96) * (1.0015**32767)`, rounding etc. leading to minor difference uint256 internal constant MAX_RATIOX96 = 169307877264527972847801929085841449095838922544595; uint256 internal constant ZERO_TICK_SCALED_RATIO = 0x1000000000000000000000000; // 1 << 96 // 79228162514264337593543950336 uint256 internal constant _1E26 = 1e26; /// @notice ratioX96 = (1.0015^tick) * 2^96 /// @dev Throws if |tick| > max tick /// @param tick The input tick for the above formula /// @return ratioX96 ratio = (debt amount/collateral amount) function getRatioAtTick(int tick) internal pure returns (uint256 ratioX96) { assembly { let absTick_ := sub(xor(tick, sar(255, tick)), sar(255, tick)) if gt(absTick_, MAX_TICK) { revert(0, 0) } let factor_ := FACTOR00 if and(absTick_, 0x1) { factor_ := FACTOR01 } if and(absTick_, 0x2) { factor_ := shr(128, mul(factor_, FACTOR02)) } if and(absTick_, 0x4) { factor_ := shr(128, mul(factor_, FACTOR03)) } if and(absTick_, 0x8) { factor_ := shr(128, mul(factor_, FACTOR04)) } if and(absTick_, 0x10) { factor_ := shr(128, mul(factor_, FACTOR05)) } if and(absTick_, 0x20) { factor_ := shr(128, mul(factor_, FACTOR06)) } if and(absTick_, 0x40) { factor_ := shr(128, mul(factor_, FACTOR07)) } if and(absTick_, 0x80) { factor_ := shr(128, mul(factor_, FACTOR08)) } if and(absTick_, 0x100) { factor_ := shr(128, mul(factor_, FACTOR09)) } if and(absTick_, 0x200) { factor_ := shr(128, mul(factor_, FACTOR10)) } if and(absTick_, 0x400) { factor_ := shr(128, mul(factor_, FACTOR11)) } if and(absTick_, 0x800) { factor_ := shr(128, mul(factor_, FACTOR12)) } if and(absTick_, 0x1000) { factor_ := shr(128, mul(factor_, FACTOR13)) } if and(absTick_, 0x2000) { factor_ := shr(128, mul(factor_, FACTOR14)) } if and(absTick_, 0x4000) { factor_ := shr(128, mul(factor_, FACTOR15)) } let precision_ := 0 if iszero(and(tick, 0x8000000000000000000000000000000000000000000000000000000000000000)) { factor_ := div(0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff, factor_) // we round up in the division so getTickAtRatio of the output price is always consistent if mod(factor_, 0x100000000) { precision_ := 1 } } ratioX96 := add(shr(32, factor_), precision_) } } /// @notice ratioX96 = (1.0015^tick) * 2^96 /// @dev Throws if ratioX96 > max ratio || ratioX96 < min ratio /// @param ratioX96 The input ratio; ratio = (debt amount/collateral amount) /// @return tick The output tick for the above formula. Returns in round down form. if tick is 123.23 then 123, if tick is -123.23 then returns -124 /// @return perfectRatioX96 perfect ratio for the above tick function getTickAtRatio(uint256 ratioX96) internal pure returns (int tick, uint perfectRatioX96) { assembly { if or(gt(ratioX96, MAX_RATIOX96), lt(ratioX96, MIN_RATIOX96)) { revert(0, 0) } let cond := lt(ratioX96, ZERO_TICK_SCALED_RATIO) let factor_ if iszero(cond) { // if ratioX96 >= ZERO_TICK_SCALED_RATIO factor_ := div(mul(ratioX96, _1E26), ZERO_TICK_SCALED_RATIO) } if cond { // ratioX96 < ZERO_TICK_SCALED_RATIO factor_ := div(mul(ZERO_TICK_SCALED_RATIO, _1E26), ratioX96) } // put in https://www.wolframalpha.com/ whole equation: (1.0015^tick) * 2^96 * 10^26 / 79228162514264337593543950336 // for tick = 16384 // ratioX96 = (1.0015^16384) * 2^96 = 3665252098134783297721995888537077351735 // 3665252098134783297721995888537077351735 * 10^26 / 79228162514264337593543950336 = // 4626198540796508716348404308345255985.06131964639489434655721 if iszero(lt(factor_, 4626198540796508716348404308345255985)) { tick := or(tick, 0x4000) factor_ := div(mul(factor_, _1E26), 4626198540796508716348404308345255985) } // for tick = 8192 // ratioX96 = (1.0015^8192) * 2^96 = 17040868196391020479062776466509865 // 17040868196391020479062776466509865 * 10^26 / 79228162514264337593543950336 = // 21508599537851153911767490449162.3037648642153898377655505172 if iszero(lt(factor_, 21508599537851153911767490449162)) { tick := or(tick, 0x2000) factor_ := div(mul(factor_, _1E26), 21508599537851153911767490449162) } // for tick = 4096 // ratioX96 = (1.0015^4096) * 2^96 = 36743933851015821532611831851150 // 36743933851015821532611831851150 * 10^26 / 79228162514264337593543950336 = // 46377364670549310883002866648.9777607649742626173648716941385 if iszero(lt(factor_, 46377364670549310883002866649)) { tick := or(tick, 0x1000) factor_ := div(mul(factor_, _1E26), 46377364670549310883002866649) } // for tick = 2048 // ratioX96 = (1.0015^2048) * 2^96 = 1706210527034005899209104452335 // 1706210527034005899209104452335 * 10^26 / 79228162514264337593543950336 = // 2153540449365864845468344760.06357108484096046743300420319322 if iszero(lt(factor_, 2153540449365864845468344760)) { tick := or(tick, 0x800) factor_ := div(mul(factor_, _1E26), 2153540449365864845468344760) } // for tick = 1024 // ratioX96 = (1.0015^1024) * 2^96 = 367668226692760093024536487236 // 367668226692760093024536487236 * 10^26 / 79228162514264337593543950336 = // 464062544207767844008185024.950588990554136265212906454481127 if iszero(lt(factor_, 464062544207767844008185025)) { tick := or(tick, 0x400) factor_ := div(mul(factor_, _1E26), 464062544207767844008185025) } // for tick = 512 // ratioX96 = (1.0015^512) * 2^96 = 170674186729409605620119663668 // 170674186729409605620119663668 * 10^26 / 79228162514264337593543950336 = // 215421109505955298802281577.031879604792139232258508172947569 if iszero(lt(factor_, 215421109505955298802281577)) { tick := or(tick, 0x200) factor_ := div(mul(factor_, _1E26), 215421109505955298802281577) } // for tick = 256 // ratioX96 = (1.0015^256) * 2^96 = 116285004205991934861656513301 // 116285004205991934861656513301 * 10^26 / 79228162514264337593543950336 = // 146772309890508740607270614.667650899656438875541505058062410 if iszero(lt(factor_, 146772309890508740607270615)) { tick := or(tick, 0x100) factor_ := div(mul(factor_, _1E26), 146772309890508740607270615) } // for tick = 128 // ratioX96 = (1.0015^128) * 2^96 = 95984619659632141743747099590 // 95984619659632141743747099590 * 10^26 / 79228162514264337593543950336 = // 121149622323187099817270416.157248837742741760456796835775887 if iszero(lt(factor_, 121149622323187099817270416)) { tick := or(tick, 0x80) factor_ := div(mul(factor_, _1E26), 121149622323187099817270416) } // for tick = 64 // ratioX96 = (1.0015^64) * 2^96 = 87204845308406958006717891124 // 87204845308406958006717891124 * 10^26 / 79228162514264337593543950336 = // 110067989135437147685980801.568068573422377364214113968609839 if iszero(lt(factor_, 110067989135437147685980801)) { tick := or(tick, 0x40) factor_ := div(mul(factor_, _1E26), 110067989135437147685980801) } // for tick = 32 // ratioX96 = (1.0015^32) * 2^96 = 83120873769022354029916374475 // 83120873769022354029916374475 * 10^26 / 79228162514264337593543950336 = // 104913292358707887270979599.831816586773651266562785765558183 if iszero(lt(factor_, 104913292358707887270979600)) { tick := or(tick, 0x20) factor_ := div(mul(factor_, _1E26), 104913292358707887270979600) } // for tick = 16 // ratioX96 = (1.0015^16) * 2^96 = 81151180492336368327184716176 // 81151180492336368327184716176 * 10^26 / 79228162514264337593543950336 = // 102427189924701091191840927.762844039579442328381455567932128 if iszero(lt(factor_, 102427189924701091191840928)) { tick := or(tick, 0x10) factor_ := div(mul(factor_, _1E26), 102427189924701091191840928) } // for tick = 8 // ratioX96 = (1.0015^8) * 2^96 = 80183906840906820640659903620 // 80183906840906820640659903620 * 10^26 / 79228162514264337593543950336 = // 101206318935480056907421312.890625 if iszero(lt(factor_, 101206318935480056907421313)) { tick := or(tick, 0x8) factor_ := div(mul(factor_, _1E26), 101206318935480056907421313) } // for tick = 4 // ratioX96 = (1.0015^4) * 2^96 = 79704602139525152702959747603 // 79704602139525152702959747603 * 10^26 / 79228162514264337593543950336 = // 100601351350506250000000000 if iszero(lt(factor_, 100601351350506250000000000)) { tick := or(tick, 0x4) factor_ := div(mul(factor_, _1E26), 100601351350506250000000000) } // for tick = 2 // ratioX96 = (1.0015^2) * 2^96 = 79466025265172787701084167660 // 79466025265172787701084167660 * 10^26 / 79228162514264337593543950336 = // 100300225000000000000000000 if iszero(lt(factor_, 100300225000000000000000000)) { tick := or(tick, 0x2) factor_ := div(mul(factor_, _1E26), 100300225000000000000000000) } // for tick = 1 // ratioX96 = (1.0015^1) * 2^96 = 79347004758035734099934266261 // 79347004758035734099934266261 * 10^26 / 79228162514264337593543950336 = // 100150000000000000000000000 if iszero(lt(factor_, 100150000000000000000000000)) { tick := or(tick, 0x1) factor_ := div(mul(factor_, _1E26), 100150000000000000000000000) } if iszero(cond) { // if ratioX96 >= ZERO_TICK_SCALED_RATIO perfectRatioX96 := div(mul(ratioX96, _1E26), factor_) } if cond { // ratioX96 < ZERO_TICK_SCALED_RATIO tick := not(tick) perfectRatioX96 := div(mul(ratioX96, factor_), 100150000000000000000000000) } // perfect ratio should always be <= ratioX96 // not sure if it can ever be bigger but better to have extra checks if gt(perfectRatioX96, ratioX96) { revert(0, 0) } } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { IAaveV3Pool } from "../interfaces/Aave/IAaveV3Pool.sol"; contract MockAaveV3Pool is IAaveV3Pool { uint128 public variableBorrowRate; uint256 public reserveNormalizedVariableDebt; constructor(uint128 _variableBorrowRate) { variableBorrowRate = _variableBorrowRate; } function setVariableBorrowRate(uint128 _variableBorrowRate) external { variableBorrowRate = _variableBorrowRate; } function setReserveNormalizedVariableDebt(uint256 _reserveNormalizedVariableDebt) external { reserveNormalizedVariableDebt = _reserveNormalizedVariableDebt; } function getReserveData(address) external view returns (ReserveDataLegacy memory result) { result.currentVariableBorrowRate = variableBorrowRate; } function getReserveNormalizedVariableDebt(address) external view returns (uint256) { return reserveNormalizedVariableDebt; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { AggregatorV3Interface } from "../interfaces/Chainlink/AggregatorV3Interface.sol"; contract MockAggregatorV3Interface is AggregatorV3Interface { uint8 public immutable decimals; int256 public price; constructor(uint8 _decimals, int256 _price) { decimals = _decimals; price = _price; } function setPrice(int256 _price) external { price = _price; } function description() external view override returns (string memory) {} function version() external view override returns (uint256) {} function latestAnswer() external view override returns (uint256) { return uint256(price); } function getRoundData( uint80 ) external view override returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) { roundId = 0; answer = price; startedAt = block.timestamp; updatedAt = block.timestamp; answeredInRound = 0; } function latestRoundData() external view override returns (uint80 roundId, int256 answer, uint256 startedAt, uint256 updatedAt, uint80 answeredInRound) { roundId = 0; answer = price; startedAt = block.timestamp; updatedAt = block.timestamp; answeredInRound = 0; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; contract MockCurveStableSwapNG { mapping(uint256 => address) public coins; mapping(uint256 => uint256) public price_oracle; function setCoin(uint256 index, address token) external { coins[index] = token; } function setPriceOracle(uint256 index, uint256 value) external { price_oracle[index] = value; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract MockERC20 is ERC20 { uint8 private immutable _decimals; constructor(string memory _name, string memory _symbol, uint8 __decimals) ERC20(_name, _symbol) { _decimals = __decimals; } function decimals() public view virtual override returns (uint8) { return _decimals; } function mint(address _recipient, uint256 _amount) external { _mint(_recipient, _amount); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IWrappedEther } from "../interfaces/IWrappedEther.sol"; contract MockMultiPathConverter { using SafeERC20 for IERC20; /************* * Constants * *************/ /// @dev The address of WETH token. address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; address tokenOut; uint256 amountOut; function setTokenOut(address _tokenOut, uint256 _amountOut) external { tokenOut = _tokenOut; amountOut = _amountOut; } function convert(address _tokenIn, uint256 _amount, uint256, uint256[] calldata) external payable returns (uint256) { if (_tokenIn == address(0)) { IWrappedEther(WETH).deposit{ value: _amount }(); IERC20(WETH).safeTransfer(address(this), _amount); } else { // convert all approved. if (_amount == type(uint256).max) { _amount = IERC20(_tokenIn).allowance(msg.sender, address(this)); } IERC20(_tokenIn).safeTransferFrom(msg.sender, address(this), _amount); } IERC20(tokenOut).safeTransfer(msg.sender, amountOut); return amountOut; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { LinearMultipleRewardDistributor } from "../common/rewards/distributor/LinearMultipleRewardDistributor.sol"; contract MockMultipleRewardDistributor is LinearMultipleRewardDistributor { constructor() LinearMultipleRewardDistributor(1 weeks) {} function initialize() external { _grantRole(DEFAULT_ADMIN_ROLE, _msgSender()); } function _accumulateReward(address _token, uint256 _amount) internal virtual override {} }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { IPriceOracle } from "../price-oracle/interfaces/IPriceOracle.sol"; contract MockPriceOracle is IPriceOracle { uint256 public anchorPrice; uint256 public minPrice; uint256 public maxPrice; constructor(uint256 _anchorPrice, uint256 _minPrice, uint256 _maxPrice) { anchorPrice = _anchorPrice; minPrice = _minPrice; maxPrice = _maxPrice; } function setPrices(uint256 _anchorPrice, uint256 _minPrice, uint256 _maxPrice) external { anchorPrice = _anchorPrice; minPrice = _minPrice; maxPrice = _maxPrice; } function getPrice() external view returns (uint256, uint256, uint256) { return (anchorPrice, minPrice, maxPrice); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { IRateProvider } from "../rate-provider/interfaces/IRateProvider.sol"; contract MockRateProvider is IRateProvider { uint256 public rate; constructor(uint256 _rate) { rate = _rate; } function setRate(uint256 _rate) external { rate = _rate; } function getRate() external view returns (uint256) { return rate; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol"; import { AggregatorV3Interface } from "../interfaces/Chainlink/AggregatorV3Interface.sol"; import { IPoolManager } from "../interfaces/IPoolManager.sol"; contract MockFxUSDSave { /// @notice The address of `PoolManager` contract. address public immutable poolManager; /// @notice The address of `PegKeeper` contract. address public immutable pegKeeper; /// @dev This is also the address of FxUSD token. address public immutable yieldToken; /// @dev The address of USDC token. address public immutable stableToken; uint256 private immutable stableTokenScale; /// @notice The Chainlink USDC/USD price feed. /// @dev The encoding is below. /// ```text /// | 32 bits | 64 bits | 160 bits | /// | heartbeat | scale | price_feed | /// |low high | /// ``` bytes32 public immutable Chainlink_USDC_USD_Spot; constructor( address _poolManager, address _pegKeeper, address _yieldToken, address _stableToken, bytes32 _Chainlink_USDC_USD_Spot ) { poolManager = _poolManager; pegKeeper = _pegKeeper; yieldToken = _yieldToken; stableToken = _stableToken; Chainlink_USDC_USD_Spot = _Chainlink_USDC_USD_Spot; stableTokenScale = 10 ** (18 - IERC20Metadata(_stableToken).decimals()); } function totalYieldToken() external view returns (uint256) { return IERC20Metadata(yieldToken).balanceOf(address(this)); } /// @notice The total amount of stable token managed in this contract function totalStableToken() external view returns (uint256) { return IERC20Metadata(stableToken).balanceOf(address(this)); } function getStableTokenPrice() public view returns (uint256) { bytes32 encoding = Chainlink_USDC_USD_Spot; address aggregator; uint256 scale; uint256 heartbeat; assembly { aggregator := shr(96, encoding) scale := and(shr(32, encoding), 0xffffffffffffffff) heartbeat := and(encoding, 0xffffffff) } (, int256 answer, , uint256 updatedAt, ) = AggregatorV3Interface(aggregator).latestRoundData(); if (answer < 0) revert("invalid"); if (block.timestamp - updatedAt > heartbeat) revert("expired"); return uint256(answer) * scale; } function getStableTokenPriceWithScale() public view returns (uint256) { return getStableTokenPrice() * stableTokenScale; } function rebalance( address pool, int16 tickId, uint256 maxFxUSD, uint256 maxStable ) external returns (uint256 colls, uint256 yieldTokenUsed, uint256 stableTokenUsed) { IERC20Metadata(yieldToken).approve(poolManager, type(uint256).max); IERC20Metadata(stableToken).approve(poolManager, type(uint256).max); (colls, yieldTokenUsed, stableTokenUsed) = IPoolManager(poolManager).rebalance( pool, msg.sender, tickId, maxFxUSD, maxStable ); } function rebalance( address pool, uint32 positionId, uint256 maxFxUSD, uint256 maxStable ) external returns (uint256 colls, uint256 yieldTokenUsed, uint256 stableTokenUsed) { IERC20Metadata(yieldToken).approve(poolManager, type(uint256).max); IERC20Metadata(stableToken).approve(poolManager, type(uint256).max); (colls, yieldTokenUsed, stableTokenUsed) = IPoolManager(poolManager).rebalance( pool, msg.sender, positionId, maxFxUSD, maxStable ); } function liquidate( address pool, uint32 positionId, uint256 maxFxUSD, uint256 maxStable ) external returns (uint256 colls, uint256 yieldTokenUsed, uint256 stableTokenUsed) { IERC20Metadata(yieldToken).approve(poolManager, type(uint256).max); IERC20Metadata(stableToken).approve(poolManager, type(uint256).max); (colls, yieldTokenUsed, stableTokenUsed) = IPoolManager(poolManager).liquidate( pool, msg.sender, positionId, maxFxUSD, maxStable ); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IFlashLoanRecipient } from "../../interfaces/Balancer/IFlashLoanRecipient.sol"; import { LibRouter } from "../libraries/LibRouter.sol"; contract FlashLoanCallbackFacet is IFlashLoanRecipient { using SafeERC20 for IERC20; /********** * Errors * **********/ /// @dev Thrown when the caller is not balancer vault. error ErrorNotFromBalancer(); error ErrorNotFromRouterFlashLoan(); /*********************** * Immutable Variables * ***********************/ /// @dev The address of Balancer V2 Vault. address private immutable balancer; /*************** * Constructor * ***************/ constructor(address _balancer) { balancer = _balancer; } /**************************** * Public Mutated Functions * ****************************/ /// @inheritdoc IFlashLoanRecipient /// @dev Balancer V2 callback function receiveFlashLoan( address[] memory tokens, uint256[] memory amounts, uint256[] memory feeAmounts, bytes memory userData ) external { if (msg.sender != balancer) revert ErrorNotFromBalancer(); // make sure call invoked by router LibRouter.RouterStorage storage $ = LibRouter.routerStorage(); if ($.flashLoanContext != LibRouter.HAS_FLASH_LOAN) revert ErrorNotFromRouterFlashLoan(); (bool success, ) = address(this).call(userData); // below lines will propagate inner error up if (!success) { // solhint-disable-next-line no-inline-assembly assembly { let ptr := mload(0x40) let size := returndatasize() returndatacopy(ptr, 0, size) revert(ptr, size) } } for (uint256 i = 0; i < tokens.length; i++) { IERC20(tokens[i]).safeTransfer(msg.sender, amounts[i] + feeAmounts[i]); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import { IBalancerVault } from "../../interfaces/Balancer/IBalancerVault.sol"; import { LibRouter } from "../libraries/LibRouter.sol"; abstract contract FlashLoanFacetBase { /********** * Errors * **********/ /// @dev Thrown when the caller is not self. error ErrorNotFromSelf(); /// @dev Unauthorized reentrant call. error ReentrancyGuardReentrantCall(); /*********************** * Immutable Variables * ***********************/ /// @dev The address of Balancer V2 Vault. address private immutable balancer; /************* * Modifiers * *************/ modifier onlySelf() { if (msg.sender != address(this)) revert ErrorNotFromSelf(); _; } modifier onFlashLoan() { LibRouter.RouterStorage storage $ = LibRouter.routerStorage(); $.flashLoanContext = LibRouter.HAS_FLASH_LOAN; _; $.flashLoanContext = LibRouter.NOT_FLASH_LOAN; } modifier nonReentrant() { LibRouter.RouterStorage storage $ = LibRouter.routerStorage(); if ($.reentrantContext == LibRouter.HAS_ENTRANT) { revert ReentrancyGuardReentrantCall(); } $.reentrantContext = LibRouter.HAS_ENTRANT; _; $.reentrantContext = LibRouter.NOT_ENTRANT; } /*************** * Constructor * ***************/ constructor(address _balancer) { balancer = _balancer; } /********************** * Internal Functions * **********************/ function _invokeFlashLoan(address token, uint256 amount, bytes memory data) internal onFlashLoan { address[] memory tokens = new address[](1); uint256[] memory amounts = new uint256[](1); tokens[0] = token; amounts[0] = amount; IBalancerVault(balancer).flashLoan(address(this), tokens, amounts, data); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IFxUSDBasePool } from "../../interfaces/IFxUSDBasePool.sol"; import { IFxShareableRebalancePool } from "../../v2/interfaces/IFxShareableRebalancePool.sol"; import { IFxUSD } from "../../v2/interfaces/IFxUSD.sol"; import { ILiquidityGauge } from "../../voting-escrow/interfaces/ILiquidityGauge.sol"; import { WordCodec } from "../../common/codec/WordCodec.sol"; import { LibRouter } from "../libraries/LibRouter.sol"; contract FxUSDBasePoolFacet { using SafeERC20 for IERC20; /************* * Constants * *************/ /// @notice The address of USDC token. address private constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; /// @notice The address of fxUSD token. address private constant fxUSD = 0x085780639CC2cACd35E474e71f4d000e2405d8f6; /*********************** * Immutable Variables * ***********************/ /// @dev The address of `PoolManager` contract. address private immutable poolManager; /// @dev The address of `FxUSDBasePool` contract. address private immutable fxBASE; /// @dev The address of fxBASE gauge contract. address private immutable gauge; /*************** * Constructor * ***************/ constructor(address _poolManager, address _fxBASE, address _gauge) { poolManager = _poolManager; fxBASE = _fxBASE; gauge = _gauge; } /**************************** * Public Mutated Functions * ****************************/ /// @notice Migrate fxUSD from rebalance pool to fxBASE. /// @param pool The address of rebalance pool. /// @param amountIn The amount of rebalance pool shares to migrate. /// @param minShares The minimum shares should receive. /// @param receiver The address of fxBASE share recipient. function migrateToFxBase(address pool, uint256 amountIn, uint256 minShares, address receiver) external { IFxShareableRebalancePool(pool).withdrawFrom(msg.sender, amountIn, address(this)); address baseToken = IFxShareableRebalancePool(pool).baseToken(); address asset = IFxShareableRebalancePool(pool).asset(); LibRouter.approve(asset, fxUSD, amountIn); IFxUSD(fxUSD).wrap(baseToken, amountIn, address(this)); LibRouter.approve(fxUSD, fxBASE, amountIn); IFxUSDBasePool(fxBASE).deposit(receiver, fxUSD, amountIn, minShares); } /// @notice Migrate fxUSD from rebalance pool to fxBASE gauge. /// @param pool The address of rebalance pool. /// @param amountIn The amount of rebalance pool shares to migrate. /// @param minShares The minimum shares should receive. /// @param receiver The address of fxBASE share recipient. function migrateToFxBaseGauge(address pool, uint256 amountIn, uint256 minShares, address receiver) external { IFxShareableRebalancePool(pool).withdrawFrom(msg.sender, amountIn, address(this)); address baseToken = IFxShareableRebalancePool(pool).baseToken(); address asset = IFxShareableRebalancePool(pool).asset(); LibRouter.approve(asset, fxUSD, amountIn); IFxUSD(fxUSD).wrap(baseToken, amountIn, address(this)); LibRouter.approve(fxUSD, fxBASE, amountIn); uint256 shares = IFxUSDBasePool(fxBASE).deposit(address(this), fxUSD, amountIn, minShares); LibRouter.approve(fxBASE, gauge, shares); ILiquidityGauge(gauge).deposit(shares, receiver); } /// @notice Deposit token to fxBASE. /// @param params The parameters to convert source token to `tokenOut`. /// @param tokenOut The target token, USDC or fxUSD. /// @param minShares The minimum shares should receive. /// @param receiver The address of fxBASE share recipient. function depositToFxBase( LibRouter.ConvertInParams memory params, address tokenOut, uint256 minShares, address receiver ) external payable { uint256 amountIn = LibRouter.transferInAndConvert(params, tokenOut); LibRouter.approve(tokenOut, fxBASE, amountIn); IFxUSDBasePool(fxBASE).deposit(receiver, tokenOut, amountIn, minShares); } /// @notice Deposit token to fxBase and then deposit to gauge. /// @param params The parameters to convert source token to `tokenOut`. /// @param tokenOut The target token, USDC or fxUSD. /// @param minShares The minimum shares should receive. /// @param receiver The address of gauge share recipient. function depositToFxBaseGauge( LibRouter.ConvertInParams memory params, address tokenOut, uint256 minShares, address receiver ) external payable { uint256 amountIn = LibRouter.transferInAndConvert(params, tokenOut); LibRouter.approve(tokenOut, fxBASE, amountIn); uint256 shares = IFxUSDBasePool(fxBASE).deposit(address(this), tokenOut, amountIn, minShares); LibRouter.approve(fxBASE, gauge, shares); ILiquidityGauge(gauge).deposit(shares, receiver); } /* /// @notice Burn fxBASE shares and then convert USDC and fxUSD to another token. /// @param fxusdParams The parameters to convert fxUSD to target token. /// @param usdcParams The parameters to convert USDC to target token. /// @param amountIn The amount of fxBASE to redeem. /// @param receiver The address of token recipient. function redeemFromFxBase( LibRouter.ConvertOutParams memory fxusdParams, LibRouter.ConvertOutParams memory usdcParams, uint256 amountIn, address receiver ) external { IERC20(fxBASE).safeTransferFrom(msg.sender, address(this), amountIn); (uint256 amountFxUSD, uint256 amountUSDC) = IFxUSDBasePool(fxBASE).redeem(address(this), amountIn); LibRouter.convertAndTransferOut(fxusdParams, fxUSD, amountFxUSD, receiver); LibRouter.convertAndTransferOut(usdcParams, USDC, amountUSDC, receiver); } /// @notice Burn fxBASE shares from gauge and then convert USDC and fxUSD to another token. /// @param fxusdParams The parameters to convert fxUSD to target token. /// @param usdcParams The parameters to convert USDC to target token. /// @param amountIn The amount of fxBASE to redeem. /// @param receiver The address of token recipient. function redeemFromFxBaseGauge( LibRouter.ConvertOutParams memory fxusdParams, LibRouter.ConvertOutParams memory usdcParams, uint256 amountIn, address receiver ) external { IERC20(gauge).safeTransferFrom(msg.sender, address(this), amountIn); ILiquidityGauge(gauge).withdraw(amountIn); (uint256 amountFxUSD, uint256 amountUSDC) = IFxUSDBasePool(fxBASE).redeem(address(this), amountIn); LibRouter.convertAndTransferOut(fxusdParams, fxUSD, amountFxUSD, receiver); LibRouter.convertAndTransferOut(usdcParams, USDC, amountUSDC, receiver); } */ }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import { IMultiPathConverter } from "../../helpers/interfaces/IMultiPathConverter.sol"; import { IBalancerVault } from "../../interfaces/Balancer/IBalancerVault.sol"; import { IPool } from "../../interfaces/IPool.sol"; import { IPoolManager } from "../../interfaces/IPoolManager.sol"; import { IFxMarketV2 } from "../../v2/interfaces/IFxMarketV2.sol"; import { IFxUSD } from "../../v2/interfaces/IFxUSD.sol"; import { WordCodec } from "../../common/codec/WordCodec.sol"; import { LibRouter } from "../libraries/LibRouter.sol"; import { FlashLoanFacetBase } from "./FlashLoanFacetBase.sol"; contract MigrateFacet is FlashLoanFacetBase { using SafeERC20 for IERC20; using WordCodec for bytes32; /********** * Errors * **********/ /// @dev Thrown when the amount of tokens swapped are not enough. error ErrorInsufficientAmountSwapped(); /// @dev Thrown when debt ratio out of range. error ErrorDebtRatioOutOfRange(); /************* * Constants * *************/ /// @dev The address of USDC token. address private constant USDC = 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48; /// @dev The address of fxUSD token. address private constant fxUSD = 0x085780639CC2cACd35E474e71f4d000e2405d8f6; /// @dev The address of wstETH market contract. address private constant wstETHMarket = 0xAD9A0E7C08bc9F747dF97a3E7E7f620632CB6155; /// @dev The address of wstETH token. address private constant wstETH = 0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0; /// @dev The address of fstETH token. address private constant fstETH = 0xD6B8162e2fb9F3EFf09bb8598ca0C8958E33A23D; /// @dev The address of xstETH token. address private constant xstETH = 0x5a097b014C547718e79030a077A91Ae37679EfF5; /// @dev The address of sfrxETH market contract. address private constant sfrxETHMarket = 0x714B853b3bA73E439c652CfE79660F329E6ebB42; /// @dev The address of sfrxETH token. address private constant sfrxETH = 0xac3E018457B222d93114458476f3E3416Abbe38F; /// @dev The address of ffrxETH token. address private constant ffrxETH = 0xa87F04c9743Fd1933F82bdDec9692e9D97673769; /// @dev The address of xfrxETH token. address private constant xfrxETH = 0x2bb0C32101456F5960d4e994Bac183Fe0dc6C82c; /*********************** * Immutable Variables * ***********************/ /// @dev The address of `PoolManager` contract. address private immutable poolManager; /// @dev The address of `MultiPathConverter` contract. address private immutable converter; /*************** * Constructor * ***************/ constructor(address _balancer, address _poolManager, address _converter) FlashLoanFacetBase(_balancer) { poolManager = _poolManager; converter = _converter; } /**************************** * Public Mutated Functions * ****************************/ /// @notice Migrate xstETH to fx position. /// @param pool The address of fx position pool. /// @param positionId The index of position. /// @param xTokenAmount The amount of xstETH to migrate. /// @param borrowAmount The amount of USDC to borrow. /// @param data The calldata passing to `onMigrateXstETHPosition` hook function. function migrateXstETHPosition( address pool, uint256 positionId, uint256 xTokenAmount, uint256 borrowAmount, bytes calldata data ) external nonReentrant { IERC20(xstETH).safeTransferFrom(msg.sender, address(this), xTokenAmount); if (positionId > 0) { IERC721(pool).transferFrom(msg.sender, address(this), positionId); } _invokeFlashLoan( USDC, borrowAmount, abi.encodeCall( MigrateFacet.onMigrateXstETHPosition, (pool, positionId, xTokenAmount, borrowAmount, msg.sender, data) ) ); // refund USDC to caller LibRouter.refundERC20(USDC, LibRouter.routerStorage().revenuePool); } /// @notice Migrate xfrxETH to fx position. /// @param pool The address of fx position pool. /// @param positionId The index of position. /// @param xTokenAmount The amount of xfrxETH to migrate. /// @param borrowAmount The amount of USDC to borrow. /// @param data The calldata passing to `onMigrateXfrxETHPosition` hook function. function migrateXfrxETHPosition( address pool, uint256 positionId, uint256 xTokenAmount, uint256 borrowAmount, bytes calldata data ) external nonReentrant { IERC20(xfrxETH).safeTransferFrom(msg.sender, address(this), xTokenAmount); if (positionId > 0) { IERC721(pool).transferFrom(msg.sender, address(this), positionId); } _invokeFlashLoan( USDC, borrowAmount, abi.encodeCall( MigrateFacet.onMigrateXfrxETHPosition, (pool, positionId, xTokenAmount, borrowAmount, msg.sender, data) ) ); // refund USDC to caller LibRouter.refundERC20(USDC, LibRouter.routerStorage().revenuePool); } /// @notice Hook for `migrateXstETHPosition`. /// @param pool The address of fx position pool. /// @param positionId The index of position. /// @param xTokenAmount The amount of xstETH to migrate. /// @param borrowAmount The amount of USDC to borrow. /// @param recipient The address of position holder. /// @param data Hook data. function onMigrateXstETHPosition( address pool, uint256 positionId, uint256 xTokenAmount, uint256 borrowAmount, address recipient, bytes memory data ) external onlySelf { uint256 fTokenAmount = (xTokenAmount * IERC20(fstETH).totalSupply()) / IERC20(xstETH).totalSupply(); // swap USDC to fxUSD fTokenAmount = _swapUSDCToFxUSD(borrowAmount, fTokenAmount, data); // unwrap fxUSD as fToken IFxUSD(fxUSD).unwrap(wstETH, fTokenAmount, address(this)); uint256 wstETHAmount; { wstETHAmount = IFxMarketV2(wstETHMarket).redeemXToken(xTokenAmount, address(this), 0); (uint256 baseOut, uint256 bonus) = IFxMarketV2(wstETHMarket).redeemFToken(fTokenAmount, address(this), 0); wstETHAmount += baseOut + bonus; } // since we need to swap back to USDC, mint 0.1% more fxUSD to cover slippage. fTokenAmount = (fTokenAmount * 1001) / 1000; LibRouter.approve(wstETH, poolManager, wstETHAmount); positionId = IPoolManager(poolManager).operate(pool, positionId, int256(wstETHAmount), int256(fTokenAmount)); _checkPositionDebtRatio(pool, positionId, abi.decode(data, (bytes32))); IERC721(pool).transferFrom(address(this), recipient, positionId); // swap fxUSD to USDC and pay debts _swapFxUSDToUSDC(IERC20(fxUSD).balanceOf(address(this)), borrowAmount, data); } /// @notice Hook for `migrateXfrxETHPosition`. /// @param pool The address of fx position pool. /// @param positionId The index of position. /// @param xTokenAmount The amount of xstETH to migrate. /// @param borrowAmount The amount of USDC to borrow. /// @param recipient The address of position holder. /// @param data Hook data. function onMigrateXfrxETHPosition( address pool, uint256 positionId, uint256 xTokenAmount, uint256 borrowAmount, address recipient, bytes memory data ) external onlySelf { uint256 fTokenAmount = (xTokenAmount * IERC20(ffrxETH).totalSupply()) / IERC20(xfrxETH).totalSupply(); // swap USDC to fxUSD fTokenAmount = _swapUSDCToFxUSD(borrowAmount, fTokenAmount, data); // unwrap fxUSD as fToken IFxUSD(fxUSD).unwrap(sfrxETH, fTokenAmount, address(this)); uint256 wstETHAmount; { // redeem wstETHAmount = IFxMarketV2(sfrxETHMarket).redeemXToken(xTokenAmount, address(this), 0); (uint256 baseOut, uint256 bonus) = IFxMarketV2(sfrxETHMarket).redeemFToken(fTokenAmount, address(this), 0); wstETHAmount += baseOut + bonus; // swap sfrxETH to wstETH wstETHAmount = _swapSfrxETHToWstETH(wstETHAmount, 0, data); } // since we need to swap back to USDC, mint 0.1% more fxUSD to cover slippage. fTokenAmount = (fTokenAmount * 1001) / 1000; LibRouter.approve(wstETH, poolManager, wstETHAmount); positionId = IPoolManager(poolManager).operate(pool, positionId, int256(wstETHAmount), int256(fTokenAmount)); _checkPositionDebtRatio(pool, positionId, abi.decode(data, (bytes32))); IERC721(pool).transferFrom(address(this), recipient, positionId); // swap fxUSD to USDC and pay debts _swapFxUSDToUSDC(IERC20(fxUSD).balanceOf(address(this)), borrowAmount, data); } /********************** * Internal Functions * **********************/ /// @dev Internal function to swap USDC to fxUSD. /// @param amountUSDC The amount of USDC to use. /// @param minFxUSD The minimum amount of fxUSD should receive. /// @param data The swap route encoding. /// @return amountFxUSD The amount of fxUSD received. function _swapUSDCToFxUSD( uint256 amountUSDC, uint256 minFxUSD, bytes memory data ) internal returns (uint256 amountFxUSD) { (, uint256 swapEncoding, uint256[] memory swapRoutes) = abi.decode(data, (bytes32, uint256, uint256[])); return _swap(USDC, amountUSDC, minFxUSD, swapEncoding, swapRoutes); } /// @dev Internal function to swap fxUSD to USDC. /// @param amountFxUSD The amount of fxUSD to use. /// @param minUSDC The minimum amount of USDC should receive. /// @param data The swap route encoding. /// @return amountUSDC The amount of USDC received. function _swapFxUSDToUSDC( uint256 amountFxUSD, uint256 minUSDC, bytes memory data ) internal returns (uint256 amountUSDC) { (, , , uint256 swapEncoding, uint256[] memory swapRoutes) = abi.decode( data, (bytes32, uint256, uint256[], uint256, uint256[]) ); return _swap(fxUSD, amountFxUSD, minUSDC, swapEncoding, swapRoutes); } /// @dev Internal function to swap sfrxETH to wstETH. /// @param amountSfrxETH The amount of sfrxETH to use. /// @param minWstETH The minimum amount of wstETH should receive. /// @param data The swap route encoding. /// @return amountWstETH The amount of wstETH received. function _swapSfrxETHToWstETH( uint256 amountSfrxETH, uint256 minWstETH, bytes memory data ) internal returns (uint256 amountWstETH) { (, , , , , uint256 swapEncoding, uint256[] memory swapRoutes) = abi.decode( data, (bytes32, uint256, uint256[], uint256, uint256[], uint256, uint256[]) ); return _swap(sfrxETH, amountSfrxETH, minWstETH, swapEncoding, swapRoutes); } /// @dev Internal function to do swap. /// @param token The address of input token. /// @param amountIn The amount of input token. /// @param minOut The minimum amount of output tokens should receive. /// @param encoding The encoding for swap routes. /// @param routes The swap routes to `MultiPathConverter`. /// @return amountOut The amount of output tokens received. function _swap( address token, uint256 amountIn, uint256 minOut, uint256 encoding, uint256[] memory routes ) internal returns (uint256 amountOut) { LibRouter.approve(token, converter, amountIn); amountOut = IMultiPathConverter(converter).convert(token, amountIn, encoding, routes); if (amountOut < minOut) revert ErrorInsufficientAmountSwapped(); } /// @dev Internal function to check debt ratio for the position. /// @param pool The address of fx position pool. /// @param positionId The index of the position. /// @param miscData The encoded data for debt ratio range. function _checkPositionDebtRatio(address pool, uint256 positionId, bytes32 miscData) internal view { uint256 debtRatio = IPool(pool).getPositionDebtRatio(positionId); uint256 minDebtRatio = miscData.decodeUint(0, 60); uint256 maxDebtRatio = miscData.decodeUint(60, 60); if (debtRatio < minDebtRatio || debtRatio > maxDebtRatio) { revert ErrorDebtRatioOutOfRange(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC721 } from "@openzeppelin/contracts/token/ERC721/IERC721.sol"; import { IMultiPathConverter } from "../../helpers/interfaces/IMultiPathConverter.sol"; import { IPoolManager } from "../../interfaces/IPoolManager.sol"; import { IPool } from "../../interfaces/IPool.sol"; import { WordCodec } from "../../common/codec/WordCodec.sol"; import { LibRouter } from "../libraries/LibRouter.sol"; import { FlashLoanFacetBase } from "./FlashLoanFacetBase.sol"; contract PositionOperateFlashLoanFacet is FlashLoanFacetBase { using SafeERC20 for IERC20; using WordCodec for bytes32; /********** * Events * **********/ event OpenOrAdd(address pool, uint256 position, address recipient, uint256 colls, uint256 debts, uint256 borrows); event CloseOrRemove(address pool, uint256 position, address recipient, uint256 colls, uint256 debts, uint256 borrows); /********** * Errors * **********/ /// @dev Thrown when the amount of tokens swapped are not enough. error ErrorInsufficientAmountSwapped(); /// @dev Thrown when debt ratio out of range. error ErrorDebtRatioOutOfRange(); /************* * Constants * *************/ address private constant fxUSD = 0x085780639CC2cACd35E474e71f4d000e2405d8f6; /*********************** * Immutable Variables * ***********************/ /// @dev The address of `PoolManager` contract. address private immutable poolManager; /// @dev The address of `MultiPathConverter` contract. address private immutable converter; /*************** * Constructor * ***************/ constructor(address _balancer, address _poolManager, address _converter) FlashLoanFacetBase(_balancer) { poolManager = _poolManager; converter = _converter; } /**************************** * Public Mutated Functions * ****************************/ /// @notice Open a new position or add collateral to position with any tokens. /// @param params The parameters to convert source token to collateral token. /// @param pool The address of fx position pool. /// @param positionId The index of position. /// @param borrowAmount The amount of collateral token to borrow. /// @param data Hook data passing to `onOpenOrAddPositionFlashLoan`. function openOrAddPositionFlashLoan( LibRouter.ConvertInParams memory params, address pool, uint256 positionId, uint256 borrowAmount, bytes calldata data ) external payable nonReentrant { uint256 amountIn = LibRouter.transferInAndConvert(params, IPool(pool).collateralToken()) + borrowAmount; _invokeFlashLoan( IPool(pool).collateralToken(), borrowAmount, abi.encodeCall( PositionOperateFlashLoanFacet.onOpenOrAddPositionFlashLoan, (pool, positionId, amountIn, borrowAmount, msg.sender, data) ) ); // refund collateral token to caller LibRouter.refundERC20(IPool(pool).collateralToken(), LibRouter.routerStorage().revenuePool); } /// @notice Close a position or remove collateral from position. /// @param params The parameters to convert collateral token to target token. /// @param positionId The index of position. /// @param pool The address of fx position pool. /// @param borrowAmount The amount of collateral token to borrow. /// @param data Hook data passing to `onCloseOrRemovePositionFlashLoan`. function closeOrRemovePositionFlashLoan( LibRouter.ConvertOutParams memory params, address pool, uint256 positionId, uint256 amountOut, uint256 borrowAmount, bytes calldata data ) external nonReentrant { address collateralToken = IPool(pool).collateralToken(); _invokeFlashLoan( collateralToken, borrowAmount, abi.encodeCall( PositionOperateFlashLoanFacet.onCloseOrRemovePositionFlashLoan, (pool, positionId, amountOut, borrowAmount, msg.sender, data) ) ); // convert collateral token to other token amountOut = IERC20(collateralToken).balanceOf(address(this)); LibRouter.convertAndTransferOut(params, collateralToken, amountOut, msg.sender); // refund rest fxUSD and leveraged token LibRouter.refundERC20(fxUSD, LibRouter.routerStorage().revenuePool); } /// @notice Hook for `openOrAddPositionFlashLoan`. /// @param pool The address of fx position pool. /// @param position The index of position. /// @param amount The amount of collateral token to supply. /// @param repayAmount The amount of collateral token to repay. /// @param recipient The address of position holder. /// @param data Hook data passing to `onOpenOrAddPositionFlashLoan`. function onOpenOrAddPositionFlashLoan( address pool, uint256 position, uint256 amount, uint256 repayAmount, address recipient, bytes memory data ) external onlySelf { (bytes32 miscData, uint256 fxUSDAmount, uint256 swapEncoding, uint256[] memory swapRoutes) = abi.decode( data, (bytes32, uint256, uint256, uint256[]) ); // open or add collateral to position if (position != 0) { IERC721(pool).transferFrom(recipient, address(this), position); } LibRouter.approve(IPool(pool).collateralToken(), poolManager, amount); position = IPoolManager(poolManager).operate(pool, position, int256(amount), int256(fxUSDAmount)); _checkPositionDebtRatio(pool, position, miscData); IERC721(pool).transferFrom(address(this), recipient, position); emit OpenOrAdd(pool, position, recipient, amount, fxUSDAmount, repayAmount); // swap fxUSD to collateral token _swap(fxUSD, fxUSDAmount, repayAmount, swapEncoding, swapRoutes); } /// @notice Hook for `closeOrRemovePositionFlashLoan`. /// @param pool The address of fx position pool. /// @param position The index of position. /// @param amount The amount of collateral token to withdraw. /// @param repayAmount The amount of collateral token to repay. /// @param recipient The address of position holder. /// @param data Hook data passing to `onCloseOrRemovePositionFlashLoan`. function onCloseOrRemovePositionFlashLoan( address pool, uint256 position, uint256 amount, uint256 repayAmount, address recipient, bytes memory data ) external onlySelf { (bytes32 miscData, uint256 fxUSDAmount, uint256 swapEncoding, uint256[] memory swapRoutes) = abi.decode( data, (bytes32, uint256, uint256, uint256[]) ); // swap collateral token to fxUSD _swap(IPool(pool).collateralToken(), repayAmount, fxUSDAmount, swapEncoding, swapRoutes); // close or remove collateral from position IERC721(pool).transferFrom(recipient, address(this), position); (, uint256 maxFxUSD) = IPool(pool).getPosition(position); if (fxUSDAmount >= maxFxUSD) { // close entire position IPoolManager(poolManager).operate(pool, position, type(int256).min, type(int256).min); } else { IPoolManager(poolManager).operate(pool, position, -int256(amount), -int256(fxUSDAmount)); _checkPositionDebtRatio(pool, position, miscData); } IERC721(pool).transferFrom(address(this), recipient, position); emit CloseOrRemove(pool, position, recipient, amount, fxUSDAmount, repayAmount); } /********************** * Internal Functions * **********************/ /// @dev Internal function to do swap. /// @param token The address of input token. /// @param amountIn The amount of input token. /// @param minOut The minimum amount of output tokens should receive. /// @param encoding The encoding for swap routes. /// @param routes The swap routes to `MultiPathConverter`. /// @return amountOut The amount of output tokens received. function _swap( address token, uint256 amountIn, uint256 minOut, uint256 encoding, uint256[] memory routes ) internal returns (uint256 amountOut) { if (amountIn == 0) return 0; LibRouter.approve(token, converter, amountIn); amountOut = IMultiPathConverter(converter).convert(token, amountIn, encoding, routes); if (amountOut < minOut) revert ErrorInsufficientAmountSwapped(); } /// @dev Internal function to check debt ratio for the position. /// @param pool The address of fx position pool. /// @param positionId The index of the position. /// @param miscData The encoded data for debt ratio range. function _checkPositionDebtRatio(address pool, uint256 positionId, bytes32 miscData) internal view { uint256 debtRatio = IPool(pool).getPositionDebtRatio(positionId); uint256 minDebtRatio = miscData.decodeUint(0, 60); uint256 maxDebtRatio = miscData.decodeUint(60, 60); if (debtRatio < minDebtRatio || debtRatio > maxDebtRatio) { revert ErrorDebtRatioOutOfRange(); } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { LibDiamond } from "../../common/EIP2535/libraries/LibDiamond.sol"; import { LibRouter } from "../libraries/LibRouter.sol"; contract RouterManagementFacet { using EnumerableSet for EnumerableSet.AddressSet; /************************* * Public View Functions * *************************/ /// @notice Return the token approve spender for the given target. function getSpender(address target) external view returns (address _spender) { LibRouter.RouterStorage storage $ = LibRouter.routerStorage(); _spender = $.spenders[target]; if (_spender == address(0)) _spender = target; } /// @notice Return the list of approved targets. function getApprovedTargets() external view returns (address[] memory _accounts) { LibRouter.RouterStorage storage $ = LibRouter.routerStorage(); uint256 _numAccount = $.approvedTargets.length(); _accounts = new address[](_numAccount); for (uint256 i = 0; i < _numAccount; i++) { _accounts[i] = $.approvedTargets.at(i); } } /// @notice Return the whitelist kind for the given target. function getWhitelisted() external view returns (address[] memory _accounts) { LibRouter.RouterStorage storage $ = LibRouter.routerStorage(); uint256 _numAccount = $.whitelisted.length(); _accounts = new address[](_numAccount); for (uint256 i = 0; i < _numAccount; i++) { _accounts[i] = $.whitelisted.at(i); } } function getRevenuePool() external view returns (address) { LibRouter.RouterStorage storage $ = LibRouter.routerStorage(); return $.revenuePool; } /************************ * Restricted Functions * ************************/ /// @notice Approve contract to be used in token converting. function approveTarget(address target, address spender) external { LibDiamond.enforceIsContractOwner(); LibRouter.approveTarget(target, spender); } /// @notice Remove approve contract in token converting. function removeTarget(address target) external { LibDiamond.enforceIsContractOwner(); LibRouter.removeTarget(target); } /// @notice Update whitelist status of the given contract. function updateWhitelist(address target, bool status) external { LibDiamond.enforceIsContractOwner(); LibRouter.updateWhitelist(target, status); } /// @notice Update revenue pool. function updateRevenuePool(address revenuePool) external { LibDiamond.enforceIsContractOwner(); LibRouter.updateRevenuePool(revenuePool); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { EnumerableSet } from "@openzeppelin/contracts/utils/structs/EnumerableSet.sol"; import { Address } from "@openzeppelin/contracts/utils/Address.sol"; import { IMultiPathConverter } from "../../helpers/interfaces/IMultiPathConverter.sol"; import { IWrappedEther } from "../../interfaces/IWrappedEther.sol"; library LibRouter { using SafeERC20 for IERC20; using EnumerableSet for EnumerableSet.AddressSet; /********** * Errors * **********/ /// @dev Thrown when use unapproved target contract. error ErrorTargetNotApproved(); /// @dev Thrown when msg.value is different from amount. error ErrorMsgValueMismatch(); /// @dev Thrown when the output token is not enough. error ErrorInsufficientOutput(); /// @dev Thrown when the whitelisted account type is incorrect. error ErrorNotWhitelisted(); /************* * Constants * *************/ /// @dev The storage slot for router storage. bytes32 private constant ROUTER_STORAGE_SLOT = keccak256("diamond.router.storage"); /// @dev The address of WETH token. address internal constant WETH = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; uint8 internal constant NOT_FLASH_LOAN = 0; uint8 internal constant HAS_FLASH_LOAN = 1; uint8 internal constant NOT_ENTRANT = 0; uint8 internal constant HAS_ENTRANT = 1; /*********** * Structs * ***********/ /// @param spenders Mapping from target address to token spender address. /// @param approvedTargets The list of approved target contracts. /// @param whitelisted The list of whitelisted contracts. struct RouterStorage { mapping(address => address) spenders; EnumerableSet.AddressSet approvedTargets; EnumerableSet.AddressSet whitelisted; address revenuePool; uint8 flashLoanContext; uint8 reentrantContext; } /// @notice The struct for input token convert parameters. /// /// @param tokenIn The address of source token. /// @param amount The amount of source token. /// @param target The address of converter contract. /// @param data The calldata passing to the target contract. /// @param minOut The minimum amount of output token should receive. /// @param signature The optional data for future usage. struct ConvertInParams { address tokenIn; uint256 amount; address target; bytes data; uint256 minOut; bytes signature; } /// @notice The struct for output token convert parameters. /// @param tokenOut The address of output token. /// @param converter The address of converter contract. /// @param encodings The encodings for `MultiPathConverter`. /// @param minOut The minimum amount of output token should receive. /// @param routes The convert route encodings. /// @param signature The optional data for future usage. struct ConvertOutParams { address tokenOut; address converter; uint256 encodings; uint256[] routes; uint256 minOut; bytes signature; } /********************** * Internal Functions * **********************/ /// @dev Return the RouterStorage reference. function routerStorage() internal pure returns (RouterStorage storage gs) { bytes32 position = ROUTER_STORAGE_SLOT; assembly { gs.slot := position } } /// @dev Approve contract to be used in token converting. function approveTarget(address target, address spender) internal { RouterStorage storage $ = routerStorage(); if ($.approvedTargets.add(target) && target != spender) { $.spenders[target] = spender; } } /// @dev Remove approve contract in token converting. function removeTarget(address target) internal { RouterStorage storage $ = routerStorage(); if ($.approvedTargets.remove(target)) { delete $.spenders[target]; } } /// @dev Whitelist account with type. function updateWhitelist(address account, bool status) internal { RouterStorage storage $ = routerStorage(); if (status) { $.whitelisted.add(account); } else { $.whitelisted.remove(account); } } /// @dev Check whether the account is whitelisted with specific type. function ensureWhitelisted(address account) internal view { RouterStorage storage $ = routerStorage(); if (!$.whitelisted.contains(account)) { revert ErrorNotWhitelisted(); } } function updateRevenuePool(address revenuePool) internal { RouterStorage storage $ = routerStorage(); $.revenuePool = revenuePool; } /// @dev Transfer token into this contract and convert to `tokenOut`. /// @param params The parameters used in token converting. /// @param tokenOut The address of final converted token. /// @return amountOut The amount of token received. function transferInAndConvert(ConvertInParams memory params, address tokenOut) internal returns (uint256 amountOut) { RouterStorage storage $ = routerStorage(); if (!$.approvedTargets.contains(params.target)) { revert ErrorTargetNotApproved(); } transferTokenIn(params.tokenIn, address(this), params.amount); amountOut = IERC20(tokenOut).balanceOf(address(this)); if (params.tokenIn == tokenOut) return amountOut; bool _success; if (params.tokenIn == address(0)) { (_success, ) = params.target.call{ value: params.amount }(params.data); } else { address _spender = $.spenders[params.target]; if (_spender == address(0)) _spender = params.target; approve(params.tokenIn, _spender, params.amount); (_success, ) = params.target.call(params.data); } // below lines will propagate inner error up if (!_success) { // solhint-disable-next-line no-inline-assembly assembly { let ptr := mload(0x40) let size := returndatasize() returndatacopy(ptr, 0, size) revert(ptr, size) } } amountOut = IERC20(tokenOut).balanceOf(address(this)) - amountOut; } /// @dev Convert `tokenIn` to other token and transfer out. /// @param params The parameters used in token converting. /// @param tokenIn The address of token to convert. /// @param amountIn The amount of token to convert. /// @return amountOut The amount of token received. function convertAndTransferOut( ConvertOutParams memory params, address tokenIn, uint256 amountIn, address receiver ) internal returns (uint256 amountOut) { RouterStorage storage $ = routerStorage(); if (!$.approvedTargets.contains(params.converter)) { revert ErrorTargetNotApproved(); } if (amountIn == 0) return 0; amountOut = amountIn; if (params.routes.length > 0) { approve(tokenIn, params.converter, amountIn); amountOut = IMultiPathConverter(params.converter).convert(tokenIn, amountIn, params.encodings, params.routes); } if (amountOut < params.minOut) revert ErrorInsufficientOutput(); if (params.tokenOut == address(0)) { IWrappedEther(WETH).withdraw(amountOut); Address.sendValue(payable(receiver), amountOut); } else { IERC20(params.tokenOut).safeTransfer(receiver, amountOut); } } /// @dev Internal function to transfer token to this contract. /// @param token The address of token to transfer. /// @param amount The amount of token to transfer. /// @return uint256 The amount of token transferred. function transferTokenIn(address token, address receiver, uint256 amount) internal returns (uint256) { if (token == address(0)) { if (msg.value != amount) revert ErrorMsgValueMismatch(); } else { IERC20(token).safeTransferFrom(msg.sender, receiver, amount); } return amount; } /// @dev Internal function to refund extra token. /// @param token The address of token to refund. /// @param recipient The address of the token receiver. function refundERC20(address token, address recipient) internal { uint256 _balance = IERC20(token).balanceOf(address(this)); if (_balance > 0) { IERC20(token).safeTransfer(recipient, _balance); } } /// @dev Internal function to approve token. function approve(address token, address spender, uint256 amount) internal { IERC20(token).forceApprove(spender, amount); } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IPriceOracle { /********** * Events * **********/ /// @notice Emitted when the value of maximum price deviation is updated. /// @param oldValue The value of the previous maximum price deviation. /// @param newValue The value of the current maximum price deviation. event UpdateMaxPriceDeviation(uint256 oldValue, uint256 newValue); /************************* * Public View Functions * *************************/ /// @notice Return the oracle price with 18 decimal places. /// @return anchorPrice The anchor price for this asset, multiplied by 1e18. It should be hard to manipulate, /// like time-weighted average price or chainlink spot price. /// @return minPrice The minimum oracle price among all available price sources (including twap), multiplied by 1e18. /// @return maxPrice The maximum oracle price among all available price sources (including twap), multiplied by 1e18. function getPrice() external view returns (uint256 anchorPrice, uint256 minPrice, uint256 maxPrice); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ISpotPriceOracle { /// @notice Return spot price with 18 decimal places. /// /// @dev encoding for single route /// | 8 bits | 160 bits | 88 bits | /// | pool_type | pool | customized | /// assume all base and quote token has no more than 18 decimals. /// /// + pool_type = 0: UniswapV2 /// customized = | 1 bit | 8 bits | 8 bits | ... | /// | base_index | base_scale | quote_scale | ... | /// + pool_type = 1: UniswapV3 /// customized = | 1 bit | 8 bits | 8 bits | ... | /// | base_index | base_scale | quote_scale | ... | /// + pool_type = 2: Balancer V2 Weighted /// customized = | 3 bit | 3 bit | 8 bits | 8 bits | ... | /// | base_index | quote_index | base_scale | quote_scale | ... | /// + pool_type = 3: Balancer V2 Stable /// customized = | 3 bits | 3 bits | ... | /// | base_index | quote_index | ... | /// + pool_type = 4: Curve Plain /// customized = | 3 bits | 3 bits | 3 bits | 1 bits | 8 bits | ... | 8 bits | ... | /// | tokens | base_index | quote_index | has_amm_precise | scale[0] | ... | scale[n] | ... | /// + pool_type = 5: Curve Plain with oracle /// customized = | 1 bit | 1 bit |... | /// | base_index | use_cache | ... | /// + pool_type = 6: Curve Plain NG /// customized = | 3 bits | 3 bits | 1 bit | ... | /// | base_index | quote_index | use_cache | ... | /// + pool_type = 7: Curve Crypto /// customized = | 1 bit | ... | /// | base_index | ... | /// + pool_type = 8: Curve TriCrypto /// customized = | 2 bits | 2 bits | ... | /// | base_index | quote_index | ... | /// + pool_type = 9: ERC4626 /// customized = | 1 bit | ... | /// | base_is_underlying | ... | /// + pool_type = 10: ETHLSD, wstETH, weETH, ezETH /// customized = | 1 bit | ... | /// | base_is_ETH | ... | /// + pool_type = 11: BalancerV2CachedRate /// customized = | 3 bits | ... | /// | base_index | ... | /// /// @param encoding The encoding of the price source. /// @return spotPrice The spot price with 18 decimal places. function getSpotPrice(uint256 encoding) external view returns (uint256 spotPrice); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface ITwapOracle { /// @notice Return TWAP with 18 decimal places in the epoch ending at the specified timestamp. /// Zero is returned if TWAP in the epoch is not available. /// @param timestamp End Timestamp in seconds of the epoch /// @return TWAP (18 decimal places) in the epoch, or zero if not available function getTwap(uint256 timestamp) external view returns (uint256); /// @notice Return the latest price with 18 decimal places. function getLatest() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import { Math } from "@openzeppelin/contracts/utils/math/Math.sol"; import { SpotPriceOracleBase } from "./SpotPriceOracleBase.sol"; import { IPriceOracle } from "./interfaces/IPriceOracle.sol"; import { ITwapOracle } from "./interfaces/ITwapOracle.sol"; abstract contract LSDPriceOracleBase is SpotPriceOracleBase, IPriceOracle { /************* * Constants * *************/ /// @notice The Chainlink ETH/USD price feed. /// @dev See comments of `_readSpotPriceByChainlink` for more details. bytes32 public immutable Chainlink_ETH_USD_Spot; /************* * Variables * *************/ /// @dev The encodings for ETH/USD spot sources. bytes private onchainSpotEncodings_ETHUSD; /// @dev The encodings for LSD/ETH spot sources. bytes private onchainSpotEncodings_LSDETH; /// @dev The encodings for LSD/USD spot sources. bytes private onchainSpotEncodings_LSDUSD; /// @notice The value of maximum price deviation, multiplied by 1e18. uint256 public maxPriceDeviation; /*************** * Constructor * ***************/ constructor(bytes32 _Chainlink_ETH_USD_Spot) { Chainlink_ETH_USD_Spot = _Chainlink_ETH_USD_Spot; _updateMaxPriceDeviation(1e16); // 1% } /************************* * Public View Functions * *************************/ /// @notice Return the ETH/USD spot price. /// @return chainlinkPrice The spot price from Chainlink price feed. /// @return minPrice The minimum spot price among all available sources. /// @return maxPrice The maximum spot price among all available sources. function getETHUSDSpotPrice() external view returns (uint256 chainlinkPrice, uint256 minPrice, uint256 maxPrice) { (chainlinkPrice, minPrice, maxPrice) = _getETHUSDSpotPrice(); } /// @notice Return the ETH/USD spot prices. /// @return prices The list of spot price among all available sources, multiplied by 1e18. function getETHUSDSpotPrices() external view returns (uint256[] memory prices) { prices = _getSpotPriceByEncoding(onchainSpotEncodings_ETHUSD); } /// @notice Return the LSD/ETH spot prices. /// @return prices The list of spot price among all available sources, multiplied by 1e18. function getLSDETHSpotPrices() public view returns (uint256[] memory prices) { prices = _getSpotPriceByEncoding(onchainSpotEncodings_LSDETH); } /// @notice Return the LSD/ETH spot prices. /// @return prices The list of spot price among all available sources, multiplied by 1e18. function getLSDUSDSpotPrices() public view returns (uint256[] memory prices) { prices = _getSpotPriceByEncoding(onchainSpotEncodings_LSDUSD); } /// @notice Return the LSD/USD anchor price, the price that is hard to manipulate in single tx. /// @return price The anchor price, multiplied by 1e18. function getLSDUSDAnchorPrice() external view returns (uint256 price) { price = _getLSDUSDAnchorPrice(); } /// @inheritdoc IPriceOracle /// @dev The price is valid iff |maxPrice-minPrice|/minPrice < maxPriceDeviation function getPrice() external view override returns (uint256 anchorPrice, uint256 minPrice, uint256 maxPrice) { anchorPrice = _getLSDUSDAnchorPrice(); (minPrice, maxPrice) = _getLSDMinMaxPrice(anchorPrice); uint256 cachedMaxPriceDeviation = maxPriceDeviation; // gas saving // use anchor price when the price deviation between anchor price and min price exceed threshold if ((anchorPrice - minPrice) * PRECISION > cachedMaxPriceDeviation * minPrice) { minPrice = anchorPrice; } // use anchor price when the price deviation between anchor price and max price exceed threshold if ((maxPrice - anchorPrice) * PRECISION > cachedMaxPriceDeviation * anchorPrice) { maxPrice = anchorPrice; } } /************************ * Restricted Functions * ************************/ /// @notice Update the on-chain spot encodings. /// @param encodings The encodings to update. See `_getSpotPriceByEncoding` for more details. /// @param spotType The type of the encodings. function updateOnchainSpotEncodings(bytes memory encodings, uint256 spotType) external onlyOwner { // validate encoding uint256[] memory prices = _getSpotPriceByEncoding(encodings); if (spotType == 0) { onchainSpotEncodings_ETHUSD = encodings; if (prices.length == 0) revert ErrorInvalidEncodings(); } else if (spotType == 1) { onchainSpotEncodings_LSDETH = encodings; } else if (spotType == 2) { onchainSpotEncodings_LSDUSD = encodings; } } /// @notice Update the value of maximum price deviation. /// @param newMaxPriceDeviation The new value of maximum price deviation, multiplied by 1e18. function updateMaxPriceDeviation(uint256 newMaxPriceDeviation) external onlyOwner { _updateMaxPriceDeviation(newMaxPriceDeviation); } /********************** * Internal Functions * **********************/ /// @dev Internal function to update the value of maximum price deviation. /// @param newMaxPriceDeviation The new value of maximum price deviation, multiplied by 1e18. function _updateMaxPriceDeviation(uint256 newMaxPriceDeviation) private { uint256 oldMaxPriceDeviation = maxPriceDeviation; if (oldMaxPriceDeviation == newMaxPriceDeviation) { revert ErrorParameterUnchanged(); } maxPriceDeviation = newMaxPriceDeviation; emit UpdateMaxPriceDeviation(oldMaxPriceDeviation, newMaxPriceDeviation); } /// @dev Internal function to calculate the ETH/USD spot price. /// @return chainlinkPrice The spot price from Chainlink price feed, multiplied by 1e18. /// @return minPrice The minimum spot price among all available sources, multiplied by 1e18. /// @return maxPrice The maximum spot price among all available sources, multiplied by 1e18. function _getETHUSDSpotPrice() internal view returns (uint256 chainlinkPrice, uint256 minPrice, uint256 maxPrice) { chainlinkPrice = _readSpotPriceByChainlink(Chainlink_ETH_USD_Spot); uint256[] memory prices = _getSpotPriceByEncoding(onchainSpotEncodings_ETHUSD); minPrice = maxPrice = chainlinkPrice; for (uint256 i = 0; i < prices.length; i++) { if (prices[i] > maxPrice) maxPrice = prices[i]; if (prices[i] < minPrice) minPrice = prices[i]; } } /// @dev Internal function to return the min/max LSD/USD prices. /// @param anchorPrice The LSD/USD anchor price, multiplied by 1e18. /// @return minPrice The minimum price among all available sources (including twap), multiplied by 1e18. /// @return maxPrice The maximum price among all available sources (including twap), multiplied by 1e18. function _getLSDMinMaxPrice(uint256 anchorPrice) internal view returns (uint256 minPrice, uint256 maxPrice) { minPrice = maxPrice = anchorPrice; (, uint256 minETHUSDPrice, uint256 maxETHUSDPrice) = _getETHUSDSpotPrice(); uint256[] memory LSD_ETH_prices = getLSDETHSpotPrices(); uint256[] memory LSD_USD_prices = getLSDUSDSpotPrices(); uint256 length = LSD_ETH_prices.length; uint256 LSD_ETH_minPrice = type(uint256).max; uint256 LSD_ETH_maxPrice; unchecked { for (uint256 i = 0; i < length; i++) { uint256 price = LSD_ETH_prices[i]; if (price > LSD_ETH_maxPrice) LSD_ETH_maxPrice = price; if (price < LSD_ETH_minPrice) LSD_ETH_minPrice = price; } if (LSD_ETH_maxPrice != 0) { minPrice = Math.min(minPrice, (LSD_ETH_minPrice * minETHUSDPrice) / PRECISION); maxPrice = Math.max(maxPrice, (LSD_ETH_maxPrice * maxETHUSDPrice) / PRECISION); } length = LSD_USD_prices.length; for (uint256 i = 0; i < length; i++) { uint256 price = LSD_USD_prices[i]; if (price > maxPrice) maxPrice = price; if (price < minPrice) minPrice = price; } } } /// @dev Internal function to return the LSD/USD anchor price. /// @return price The anchor price of LSD/USD, multiplied by 1e18. function _getLSDUSDAnchorPrice() internal view virtual returns (uint256 price); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.26; import { Ownable } from "@openzeppelin/contracts/access/Ownable.sol"; import { Ownable2Step } from "@openzeppelin/contracts/access/Ownable2Step.sol"; import { AggregatorV3Interface } from "../interfaces/Chainlink/AggregatorV3Interface.sol"; import { ISpotPriceOracle } from "./interfaces/ISpotPriceOracle.sol"; abstract contract SpotPriceOracleBase is Ownable2Step { /********** * Errors * **********/ /// @dev Thrown when the given encodings are invalid. error ErrorInvalidEncodings(); /// @dev Thrown when update some parameters to the same value. error ErrorParameterUnchanged(); /************* * Constants * *************/ /// @dev The precision for oracle price. uint256 internal constant PRECISION = 1e18; /// @dev The address of `SpotPriceOracle` contract. address immutable spotPriceOracle; /*************** * Constructor * ***************/ constructor(address _spotPriceOracle) Ownable(_msgSender()) { spotPriceOracle = _spotPriceOracle; } /********************** * Internal Functions * **********************/ /// @dev The encoding is below. /// ```text /// | 32 bits | 64 bits | 160 bits | /// | heartbeat | scale | price_feed | /// |low high | /// ``` function _readSpotPriceByChainlink(bytes32 encoding) internal view returns (uint256) { address aggregator; uint256 scale; uint256 heartbeat; assembly { aggregator := shr(96, encoding) scale := and(shr(32, encoding), 0xffffffffffffffff) heartbeat := and(encoding, 0xffffffff) } (, int256 answer, , uint256 updatedAt, ) = AggregatorV3Interface(aggregator).latestRoundData(); if (answer <= 0) revert("invalid"); if (block.timestamp - updatedAt > heartbeat) revert("expired"); return uint256(answer) * scale; } /// @dev Internal function to calculate spot price by encodings. /// /// The details of the encoding is below /// ```text /// | 1 byte | ... | ... | ... | ... | /// | num_source | source[0] | source[1] | ... | source[n] | /// /// source encoding: /// | 1 byte | 32 bytes | 32 bytes | ... | 32 bytes | /// | num_pool | pool[0] | pool[1] | ... | pool[n] | /// 1 <= num_pool <= 3 /// /// The encoding of each pool can be found in `SpotPriceOracle` contract. /// ``` /// @return prices The list of prices of each source, multiplied by 1e18. function _getSpotPriceByEncoding(bytes memory encodings) internal view returns (uint256[] memory prices) { uint256 ptr; uint256 length; assembly { ptr := add(encodings, 0x21) length := byte(0, mload(sub(ptr, 1))) } prices = new uint256[](length); for (uint256 i = 0; i < length; i++) { uint256 encoding1; uint256 encoding2; uint256 encoding3; assembly { let cnt := byte(0, mload(ptr)) ptr := add(ptr, 0x01) if gt(cnt, 0) { encoding1 := mload(ptr) ptr := add(ptr, 0x20) } if gt(cnt, 1) { encoding2 := mload(ptr) ptr := add(ptr, 0x20) } if gt(cnt, 2) { encoding3 := mload(ptr) ptr := add(ptr, 0x20) } } if (encoding1 == 0) { revert ErrorInvalidEncodings(); } else if (encoding2 == 0) { prices[i] = _readSpotPrice(encoding1); } else if (encoding3 == 0) { prices[i] = _readSpotPrice(encoding1, encoding2); } else { prices[i] = _readSpotPrice(encoding1, encoding2, encoding3); } } } /// @dev Internal function to calculate spot price of single pool. /// @param encoding The encoding for the pool. /// @return price The spot price of the source, multiplied by 1e18. function _readSpotPrice(uint256 encoding) private view returns (uint256 price) { price = ISpotPriceOracle(spotPriceOracle).getSpotPrice(encoding); } /// @dev Internal function to calculate spot price of two pools. /// @param encoding1 The encoding for the first pool. /// @param encoding2 The encoding for the second pool. /// @return price The spot price of the source, multiplied by 1e18. function _readSpotPrice(uint256 encoding1, uint256 encoding2) private view returns (uint256 price) { unchecked { price = (_readSpotPrice(encoding1) * _readSpotPrice(encoding2)) / PRECISION; } } /// @dev Internal function to calculate spot price of three pools. /// @param encoding1 The encoding for the first pool. /// @param encoding2 The encoding for the second pool. /// @param encoding3 The encoding for the third pool. /// @return price The spot price of the source, multiplied by 1e18. function _readSpotPrice( uint256 encoding1, uint256 encoding2, uint256 encoding3 ) private view returns (uint256 price) { unchecked { price = (_readSpotPrice(encoding1, encoding2) * _readSpotPrice(encoding3)) / PRECISION; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import { ICurvePoolOracle } from "../interfaces/Curve/ICurvePoolOracle.sol"; import { SpotPriceOracleBase } from "./SpotPriceOracleBase.sol"; import { LSDPriceOracleBase } from "./LSDPriceOracleBase.sol"; contract StETHPriceOracle is LSDPriceOracleBase { /*********************** * Immutable Variables * ***********************/ /// @notice The address of curve ETH/stETH pool. address public immutable Curve_ETH_stETH_Pool; /*************** * Constructor * ***************/ constructor( address _spotPriceOracle, bytes32 _Chainlink_ETH_USD_Spot, address _Curve_ETH_stETH_Pool ) SpotPriceOracleBase(_spotPriceOracle) LSDPriceOracleBase(_Chainlink_ETH_USD_Spot) { Curve_ETH_stETH_Pool = _Curve_ETH_stETH_Pool; } /********************** * Internal Functions * **********************/ /// @inheritdoc LSDPriceOracleBase /// @dev [Curve stETH/ETH ema price] * [Chainlink ETH/USD spot] function _getLSDUSDAnchorPrice() internal view virtual override returns (uint256) { uint256 stETH_ETH_CurveEma = ICurvePoolOracle(Curve_ETH_stETH_Pool).price_oracle(); uint256 ETH_USD_ChainlinkSpot = _readSpotPriceByChainlink(Chainlink_ETH_USD_Spot); unchecked { return (stETH_ETH_CurveEma * ETH_USD_ChainlinkSpot) / PRECISION; } } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IRateProvider { /// @notice Return the exchange rate from wrapped token to underlying rate, /// multiplied by 1e18. function getRate() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IFxBoostableRebalancePool { /********** * Events * **********/ /// @notice Emitted when user deposit asset into this contract. /// @param owner The address of asset owner. /// @param reciever The address of receiver of the asset in this contract. /// @param amount The amount of asset deposited. event Deposit(address indexed owner, address indexed reciever, uint256 amount); /// @notice Emitted when the amount of deposited asset changed due to liquidation or deposit or unlock. /// @param owner The address of asset owner. /// @param newDeposit The new amount of deposited asset. /// @param loss The amount of asset used by liquidation. event UserDepositChange(address indexed owner, uint256 newDeposit, uint256 loss); /// @notice Emitted when user withdraw asset. /// @param owner The address of asset owner. /// @param reciever The address of receiver of the asset. /// @param amount The amount of token to withdraw. event Withdraw(address indexed owner, address indexed reciever, uint256 amount); /// @notice Emitted when liquidation happens. /// @param liquidated The amount of asset liquidated. /// @param baseGained The amount of base token gained. event Liquidate(uint256 liquidated, uint256 baseGained); /// @notice Emitted when the address of reward wrapper is updated. /// @param oldWrapper The address of previous reward wrapper. /// @param newWrapper The address of current reward wrapper. event UpdateWrapper(address indexed oldWrapper, address indexed newWrapper); /// @notice Emitted when the liquidatable collateral ratio is updated. /// @param oldRatio The previous liquidatable collateral ratio. /// @param newRatio The current liquidatable collateral ratio. event UpdateLiquidatableCollateralRatio(uint256 oldRatio, uint256 newRatio); /********** * Errors * **********/ /// @dev Thrown then the src token mismatched. error ErrorWrapperSrcMismatch(); /// @dev Thrown then the dst token mismatched. error ErrorWrapperDstMismatch(); /// @dev Thrown when the deposited amount is zero. error DepositZeroAmount(); /// @dev Thrown when the withdrawn amount is zero. error WithdrawZeroAmount(); /// @dev Thrown the cannot liquidate. error CannotLiquidate(); /************************* * Public View Functions * *************************/ /// @notice Return the address of treasury contract. function treasury() external view returns (address); /// @notice Return the address of market contract. function market() external view returns (address); /// @notice Return the address of base token. function baseToken() external view returns (address); /// @notice Return the address of underlying token of this contract. function asset() external view returns (address); /// @notice Return the total amount of asset deposited to this contract. function totalSupply() external view returns (uint256); /// @notice Return the amount of deposited asset for some specific user. /// @param account The address of user to query. function balanceOf(address account) external view returns (uint256); /// @notice Return the current boost ratio for some specific user. /// @param account The address of user to query, multiplied by 1e18. function getBoostRatio(address account) external view returns (uint256); /**************************** * Public Mutated Functions * ****************************/ /// @notice Deposit some asset to this contract. /// @dev Use `amount=uint256(-1)` if you want to deposit all asset held. /// @param amount The amount of asset to deposit. /// @param receiver The address of recipient for the deposited asset. function deposit(uint256 amount, address receiver) external; /// @notice Withdraw asset from this contract. function withdraw(uint256 amount, address receiver) external; /// @notice Liquidate asset for base token. /// @param maxAmount The maximum amount of asset to liquidate. /// @param minBaseOut The minimum amount of base token should receive. /// @return liquidated The amount of asset liquidated. /// @return baseOut The amount of base token received. function liquidate(uint256 maxAmount, uint256 minBaseOut) external returns (uint256 liquidated, uint256 baseOut); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IFxFractionalTokenV2 { /********** * Errors * **********/ /// @dev Thrown when caller is not treasury contract. error ErrorCallerIsNotTreasury(); /************************* * Public View Functions * *************************/ /// @notice Return the net asset value for the token, multiplied by 1e18. function nav() external view returns (uint256); /**************************** * Public Mutated Functions * ****************************/ /// @notice Mint some token to someone. /// @param to The address of recipient. /// @param amount The amount of token to mint. function mint(address to, uint256 amount) external; /// @notice Burn some token from someone. /// @param from The address of owner to burn. /// @param amount The amount of token to burn. function burn(address from, uint256 amount) external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IFxMarketV2 { /********** * Events * **********/ /// @notice Emitted when fToken is minted. /// @param owner The address of base token owner. /// @param recipient The address of receiver for fToken or xToken. /// @param baseTokenIn The amount of base token deposited. /// @param fTokenOut The amount of fToken minted. /// @param mintFee The amount of mint fee charged. event MintFToken( address indexed owner, address indexed recipient, uint256 baseTokenIn, uint256 fTokenOut, uint256 mintFee ); /// @notice Emitted when xToken is minted. /// @param owner The address of base token owner. /// @param recipient The address of receiver for fToken or xToken. /// @param baseTokenIn The amount of base token deposited. /// @param xTokenOut The amount of xToken minted. /// @param bonus The amount of base token as bonus. /// @param mintFee The amount of mint fee charged. event MintXToken( address indexed owner, address indexed recipient, uint256 baseTokenIn, uint256 xTokenOut, uint256 bonus, uint256 mintFee ); /// @notice Emitted when someone redeem base token with fToken or xToken. /// @param owner The address of fToken and xToken owner. /// @param recipient The address of receiver for base token. /// @param fTokenBurned The amount of fToken burned. /// @param baseTokenOut The amount of base token redeemed. /// @param bonus The amount of base token as bonus. /// @param redeemFee The amount of redeem fee charged. event RedeemFToken( address indexed owner, address indexed recipient, uint256 fTokenBurned, uint256 baseTokenOut, uint256 bonus, uint256 redeemFee ); /// @notice Emitted when someone redeem base token with fToken or xToken. /// @param owner The address of fToken and xToken owner. /// @param recipient The address of receiver for base token. /// @param xTokenBurned The amount of xToken burned. /// @param baseTokenOut The amount of base token redeemed. /// @param redeemFee The amount of redeem fee charged. event RedeemXToken( address indexed owner, address indexed recipient, uint256 xTokenBurned, uint256 baseTokenOut, uint256 redeemFee ); /// @notice Emitted when the fee ratio for minting fToken is updated. /// @param defaultFeeRatio The new default fee ratio, multipled by 1e18. /// @param extraFeeRatio The new extra fee ratio, multipled by 1e18. event UpdateMintFeeRatioFToken(uint256 defaultFeeRatio, int256 extraFeeRatio); /// @notice Emitted when the fee ratio for minting xToken is updated. /// @param defaultFeeRatio The new default fee ratio, multipled by 1e18. /// @param extraFeeRatio The new extra fee ratio, multipled by 1e18. event UpdateMintFeeRatioXToken(uint256 defaultFeeRatio, int256 extraFeeRatio); /// @notice Emitted when the fee ratio for redeeming fToken is updated. /// @param defaultFeeRatio The new default fee ratio, multipled by 1e18. /// @param extraFeeRatio The new extra fee ratio, multipled by 1e18. event UpdateRedeemFeeRatioFToken(uint256 defaultFeeRatio, int256 extraFeeRatio); /// @notice Emitted when the fee ratio for redeeming xToken is updated. /// @param defaultFeeRatio The new default fee ratio, multipled by 1e18. /// @param extraFeeRatio The new extra fee ratio, multipled by 1e18. event UpdateRedeemFeeRatioXToken(uint256 defaultFeeRatio, int256 extraFeeRatio); /// @notice Emitted when the stability ratio is updated. /// @param oldRatio The previous collateral ratio to enter stability mode, multiplied by 1e18. /// @param newRatio The current collateral ratio to enter stability mode, multiplied by 1e18. event UpdateStabilityRatio(uint256 oldRatio, uint256 newRatio); /// @notice Emitted when the platform contract is updated. /// @param oldPlatform The address of previous platform contract. /// @param newPlatform The address of current platform contract. event UpdatePlatform(address indexed oldPlatform, address indexed newPlatform); /// @notice Emitted when the reserve pool contract is updated. /// @param oldReservePool The address of previous reserve pool contract. /// @param newReservePool The address of current reserve pool contract. event UpdateReservePool(address indexed oldReservePool, address indexed newReservePool); /// @notice Emitted when the RebalancePoolRegistry contract is updated. /// @param oldRegistry The address of previous RebalancePoolRegistry contract. /// @param newRegistry The address of current RebalancePoolRegistry contract. event UpdateRebalancePoolRegistry(address indexed oldRegistry, address indexed newRegistry); /// @notice Pause or unpause mint. /// @param oldStatus The previous status for mint. /// @param newStatus The current status for mint. event UpdateMintStatus(bool oldStatus, bool newStatus); /// @notice Pause or unpause redeem. /// @param oldStatus The previous status for redeem. /// @param newStatus The current status for redeem. event UpdateRedeemStatus(bool oldStatus, bool newStatus); /// @notice Pause or unpause fToken mint in stability mode. /// @param oldStatus The previous status for mint. /// @param newStatus The current status for mint. event UpdateFTokenMintStatusInStabilityMode(bool oldStatus, bool newStatus); /// @notice Pause or unpause xToken redeem in stability mode. /// @param oldStatus The previous status for redeem. /// @param newStatus The current status for redeem. event UpdateXTokenRedeemStatusInStabilityMode(bool oldStatus, bool newStatus); /********** * Errors * **********/ /// @dev Thrown when the caller if not fUSD contract. error ErrorCallerNotFUSD(); /// @dev Thrown when token mint is paused. error ErrorMintPaused(); /// @dev Thrown when fToken mint is paused in stability mode. error ErrorFTokenMintPausedInStabilityMode(); /// @dev Thrown when mint with zero amount base token. error ErrorMintZeroAmount(); /// @dev Thrown when the amount of fToken is not enough. error ErrorInsufficientFTokenOutput(); /// @dev Thrown when the amount of xToken is not enough. error ErrorInsufficientXTokenOutput(); /// @dev Thrown when token redeem is paused. error ErrorRedeemPaused(); /// @dev Thrown when xToken redeem is paused in stability mode. error ErrorXTokenRedeemPausedInStabilityMode(); /// @dev Thrown when redeem with zero amount fToken or xToken. error ErrorRedeemZeroAmount(); /// @dev Thrown when the amount of base token is not enough. error ErrorInsufficientBaseOutput(); /// @dev Thrown when the stability ratio is too large. error ErrorStabilityRatioTooLarge(); /// @dev Thrown when the default fee is too large. error ErrorDefaultFeeTooLarge(); /// @dev Thrown when the delta fee is too small. error ErrorDeltaFeeTooSmall(); /// @dev Thrown when the sum of default fee and delta fee is too large. error ErrorTotalFeeTooLarge(); /// @dev Thrown when the given address is zero. error ErrorZeroAddress(); /************************* * Public View Functions * *************************/ /// @notice The address of Treasury contract. function treasury() external view returns (address); /// @notice Return the address of base token. function baseToken() external view returns (address); /// @notice Return the address fractional base token. function fToken() external view returns (address); /// @notice Return the address leveraged base token. function xToken() external view returns (address); /// @notice Return the address of fxUSD token. function fxUSD() external view returns (address); /// @notice Return the collateral ratio to enter stability mode, multiplied by 1e18. function stabilityRatio() external view returns (uint256); /**************************** * Public Mutated Functions * ****************************/ /// @notice Mint some fToken with some base token. /// @param baseIn The amount of wrapped value of base token supplied, use `uint256(-1)` to supply all base token. /// @param recipient The address of receiver for fToken. /// @param minFTokenMinted The minimum amount of fToken should be received. /// @return fTokenMinted The amount of fToken should be received. function mintFToken( uint256 baseIn, address recipient, uint256 minFTokenMinted ) external returns (uint256 fTokenMinted); /// @notice Mint some xToken with some base token. /// @param baseIn The amount of wrapped value of base token supplied, use `uint256(-1)` to supply all base token. /// @param recipient The address of receiver for xToken. /// @param minXTokenMinted The minimum amount of xToken should be received. /// @return xTokenMinted The amount of xToken should be received. /// @return bonus The amount of wrapped value of base token as bonus. function mintXToken( uint256 baseIn, address recipient, uint256 minXTokenMinted ) external returns (uint256 xTokenMinted, uint256 bonus); /// @notice Redeem base token with fToken. /// @param fTokenIn the amount of fToken to redeem, use `uint256(-1)` to redeem all fToken. /// @param recipient The address of receiver for base token. /// @param minBaseOut The minimum amount of wrapped value of base token should be received. /// @return baseOut The amount of wrapped value of base token should be received. /// @return bonus The amount of wrapped value of base token as bonus. function redeemFToken( uint256 fTokenIn, address recipient, uint256 minBaseOut ) external returns (uint256 baseOut, uint256 bonus); /// @notice Redeem base token with xToken. /// @param xTokenIn the amount of xToken to redeem, use `uint256(-1)` to redeem all xToken. /// @param recipient The address of receiver for base token. /// @param minBaseOut The minimum amount of wrapped value of base token should be received. /// @return baseOut The amount of wrapped value of base token should be received. function redeemXToken( uint256 xTokenIn, address recipient, uint256 minBaseOut ) external returns (uint256 baseOut); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IFxRebalancePoolRegistry { /********** * Events * **********/ /// @notice Emitted when a new rebalance pool is added. /// @param pool The address of the rebalance pool. event RegisterPool(address indexed pool); /// @notice Emitted when an exsited rebalance pool is removed. /// @param pool The address of the rebalance pool. event DeregisterPool(address indexed pool); /************************* * Public View Functions * *************************/ /// @notice Return the address list of all registered RebalancePool. function getPools() external view returns (address[] memory pools); /// @notice Return the total amount of asset managed by all registered RebalancePool. function totalSupply() external view returns (uint256); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IFxReservePool { /// @notice Request bonus token from Reserve Pool. /// @param token The address of token to request. /// @param receiver The address recipient for the bonus token. /// @param originalAmount The original amount of token used. /// @param bonus The amount of bonus token received. function requestBonus( address token, address receiver, uint256 originalAmount ) external returns (uint256 bonus); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import { IFxBoostableRebalancePool } from "./IFxBoostableRebalancePool.sol"; interface IFxShareableRebalancePool is IFxBoostableRebalancePool { /********** * Events * **********/ /// @notice Emitted when one user share votes to another user. /// @param owner The address of votes owner. /// @param staker The address of staker to share votes. event ShareVote(address indexed owner, address indexed staker); /// @notice Emitted when the owner cancel sharing to some staker. /// @param owner The address of votes owner. /// @param staker The address of staker to cancel votes share. event CancelShareVote(address indexed owner, address indexed staker); /// @notice Emitted when staker accept the vote sharing. /// @param staker The address of the staker. /// @param oldOwner The address of the previous vote sharing owner. /// @param newOwner The address of the current vote sharing owner. event AcceptSharedVote(address indexed staker, address indexed oldOwner, address indexed newOwner); /********** * Errors * **********/ /// @dev Thrown when caller shares votes to self. error ErrorSelfSharingIsNotAllowed(); /// @dev Thrown when a staker with shared votes try to share its votes to others. error ErrorCascadedSharingIsNotAllowed(); /// @dev Thrown when staker try to accept non-allowed vote sharing. error ErrorVoteShareNotAllowed(); /// @dev Thrown when staker try to reject a non-existed vote sharing. error ErrorNoAcceptedSharedVote(); /// @dev Thrown when the staker has ability to share ve balance. error ErrorVoteOwnerCannotStake(); /// @dev Thrown when staker try to accept twice. error ErrorRepeatAcceptSharedVote(); /************************* * Public View Functions * *************************/ /// @notice Return the owner of votes of some staker. /// @param account The address of user to query. function getStakerVoteOwner(address account) external view returns (address); /**************************** * Public Mutated Functions * ****************************/ /// @notice Withdraw asset from this contract on behalf of someone function withdrawFrom( address owner, uint256 amount, address receiver ) external; /// @notice Owner changes the vote sharing state for some user. /// @param staker The address of user to change. function toggleVoteSharing(address staker) external; /// @notice Staker accepts the vote sharing. /// @param newOwner The address of the owner of the votes. function acceptSharedVote(address newOwner) external; /// @notice Staker reject the current vote sharing. function rejectSharedVote() external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IFxTreasuryV2 { /********** * Events * **********/ /// @notice Emitted when the platform contract is updated. /// @param oldPlatform The address of previous platform contract. /// @param newPlatform The address of current platform contract. event UpdatePlatform(address indexed oldPlatform, address indexed newPlatform); /// @notice Emitted when the RebalancePoolSplitter contract is updated. /// @param oldRebalancePoolSplitter The address of previous RebalancePoolSplitter contract. /// @param newRebalancePoolSplitter The address of current RebalancePoolSplitter. event UpdateRebalancePoolSplitter(address indexed oldRebalancePoolSplitter, address indexed newRebalancePoolSplitter); /// @notice Emitted when the price oracle contract is updated. /// @param oldPriceOracle The address of previous price oracle. /// @param newPriceOracle The address of current price oracle. event UpdatePriceOracle(address indexed oldPriceOracle, address indexed newPriceOracle); /// @notice Emitted when the strategy contract is updated. /// @param oldStrategy The address of previous strategy. /// @param newStrategy The address of current strategy. event UpdateStrategy(address indexed oldStrategy, address indexed newStrategy); /// @notice Emitted when the base token cap is updated. /// @param oldBaseTokenCap The value of previous base token cap. /// @param newBaseTokenCap The value of current base token cap. event UpdateBaseTokenCap(uint256 oldBaseTokenCap, uint256 newBaseTokenCap); /// @notice Emitted when the EMA sample interval is updated. /// @param oldSampleInterval The value of previous EMA sample interval. /// @param newSampleInterval The value of current EMA sample interval. event UpdateEMASampleInterval(uint256 oldSampleInterval, uint256 newSampleInterval); /// @notice Emitted when the reference price is updated. /// @param oldPrice The value of previous reference price. /// @param newPrice The value of current reference price. event Settle(uint256 oldPrice, uint256 newPrice); /// @notice Emitted when the ratio for rebalance pool is updated. /// @param oldRatio The value of the previous ratio, multiplied by 1e9. /// @param newRatio The value of the current ratio, multiplied by 1e9. event UpdateRebalancePoolRatio(uint256 oldRatio, uint256 newRatio); /// @notice Emitted when the ratio for harvester is updated. /// @param oldRatio The value of the previous ratio, multiplied by 1e9. /// @param newRatio The value of the current ratio, multiplied by 1e9. event UpdateHarvesterRatio(uint256 oldRatio, uint256 newRatio); /// @notice Emitted when someone harvest pending stETH rewards. /// @param caller The address of caller. /// @param totalRewards The amount of total harvested rewards. /// @param rebalancePoolRewards The amount of harvested rewards distributed to stability pool. /// @param harvestBounty The amount of harvested rewards distributed to caller as harvest bounty. event Harvest(address indexed caller, uint256 totalRewards, uint256 rebalancePoolRewards, uint256 harvestBounty); /********** * Errors * **********/ /// @dev Thrown when the collateral ratio is smaller than 100%. error ErrorCollateralRatioTooSmall(); /// @dev Thrown when mint exceed total capacity. error ErrorExceedTotalCap(); /// @dev Thrown when the oracle price is invalid. error ErrorInvalidOraclePrice(); /// @dev Thrown when the twap price is invalid. error ErrorInvalidTwapPrice(); /// @dev Thrown when initialize protocol twice. error ErrorProtocolInitialized(); /// @dev Thrown when the initial amount of base token is not enough. error ErrorInsufficientInitialBaseToken(); /// @dev Thrown when current is under collateral. error ErrorUnderCollateral(); /// @dev Thrown when the sample internal for EMA is too small. error ErrorEMASampleIntervalTooSmall(); /// @dev Thrown when the expense ratio exceeds `MAX_REBALANCE_POOL_RATIO`. error ErrorRebalancePoolRatioTooLarge(); /// @dev Thrown when the harvester ratio exceeds `MAX_HARVESTER_RATIO`. error ErrorHarvesterRatioTooLarge(); /// @dev Thrown when the given address is zero. error ErrorZeroAddress(); /********* * Enums * *********/ enum Action { None, MintFToken, MintXToken, RedeemFToken, RedeemXToken } /************************* * Public View Functions * *************************/ /// @notice Return the address of price oracle contract. function priceOracle() external view returns (address); /// @notice Return the address of base token. function baseToken() external view returns (address); /// @notice Return the address fractional base token. function fToken() external view returns (address); /// @notice Return the address leveraged base token. function xToken() external view returns (address); /// @notice The reference base token price. function referenceBaseTokenPrice() external view returns (uint256); /// @notice The current base token price. function currentBaseTokenPrice() external view returns (uint256); /// @notice Return whether the price is valid. function isBaseTokenPriceValid() external view returns (bool); /// @notice Return the total amount of underlying value of base token deposited. function totalBaseToken() external view returns (uint256); /// @notice Return the address of strategy contract. function strategy() external view returns (address); /// @notice Return the total amount of base token managed by strategy. function strategyUnderlying() external view returns (uint256); /// @notice Return the current collateral ratio of fToken, multiplied by 1e18. function collateralRatio() external view returns (uint256); /// @notice Return whether the system is under collateral. function isUnderCollateral() external view returns (bool); /// @notice Compute the amount of base token needed to reach the new collateral ratio. /// @param newCollateralRatio The target collateral ratio, multiplied by 1e18. /// @return maxBaseIn The amount of underlying value of base token needed. /// @return maxFTokenMintable The amount of fToken can be minted. function maxMintableFToken(uint256 newCollateralRatio) external view returns (uint256 maxBaseIn, uint256 maxFTokenMintable); /// @notice Compute the amount of base token needed to reach the new collateral ratio. /// @param newCollateralRatio The target collateral ratio, multiplied by 1e18. /// @return maxBaseIn The amount of underlying value of base token needed. /// @return maxXTokenMintable The amount of xToken can be minted. function maxMintableXToken(uint256 newCollateralRatio) external view returns (uint256 maxBaseIn, uint256 maxXTokenMintable); /// @notice Compute the amount of fToken needed to reach the new collateral ratio. /// @param newCollateralRatio The target collateral ratio, multiplied by 1e18. /// @return maxBaseOut The amount of underlying value of base token redeemed. /// @return maxFTokenRedeemable The amount of fToken needed. function maxRedeemableFToken(uint256 newCollateralRatio) external view returns (uint256 maxBaseOut, uint256 maxFTokenRedeemable); /// @notice Compute the amount of xToken needed to reach the new collateral ratio. /// @param newCollateralRatio The target collateral ratio, multiplied by 1e18. /// @return maxBaseOut The amount of underlying value of base token redeemed. /// @return maxXTokenRedeemable The amount of xToken needed. function maxRedeemableXToken(uint256 newCollateralRatio) external view returns (uint256 maxBaseOut, uint256 maxXTokenRedeemable); /// @notice Return the exponential moving average of the leverage ratio. function leverageRatio() external view returns (uint256); /// @notice Convert underlying token amount to wrapped token amount. /// @param amount The underlying token amount. function getWrapppedValue(uint256 amount) external view returns (uint256); /// @notice Convert wrapped token amount to underlying token amount. /// @param amount The wrapped token amount. function getUnderlyingValue(uint256 amount) external view returns (uint256); /// @notice Return the fee ratio distributed to rebalance pool, multiplied by 1e9. function getRebalancePoolRatio() external view returns (uint256); /// @notice Return the fee ratio distributed to harvester, multiplied by 1e9. function getHarvesterRatio() external view returns (uint256); /**************************** * Public Mutated Functions * ****************************/ /// @notice Initialize the protocol. /// @param baseIn The amount of underlying value of the base token used to initialize. function initializeProtocol(uint256 baseIn) external returns (uint256 fTokenOut, uint256 xTokenOut); /// @notice Mint fToken with some base token. /// @param baseIn The amount of underlying value of base token deposited. /// @param recipient The address of receiver. /// @return fTokenOut The amount of fToken minted. function mintFToken(uint256 baseIn, address recipient) external returns (uint256 fTokenOut); /// @notice Mint xToken with some base token. /// @param baseIn The amount of underlying value of base token deposited. /// @param recipient The address of receiver. /// @return xTokenOut The amount of xToken minted. function mintXToken(uint256 baseIn, address recipient) external returns (uint256 xTokenOut); /// @notice Redeem fToken and xToken to base token. /// @param fTokenIn The amount of fToken to redeem. /// @param xTokenIn The amount of xToken to redeem. /// @param owner The owner of the fToken or xToken. /// @param baseOut The amount of underlying value of base token redeemed. function redeem( uint256 fTokenIn, uint256 xTokenIn, address owner ) external returns (uint256 baseOut); /// @notice Settle the nav of base token, fToken and xToken. function settle() external; /// @notice Transfer some base token to strategy contract. /// @param amount The amount of token to transfer. function transferToStrategy(uint256 amount) external; /// @notice Notify base token profit from strategy contract. /// @param amount The amount of base token. function notifyStrategyProfit(uint256 amount) external; /// @notice Harvest pending rewards to stability pool. function harvest() external; }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; interface IFxUSD { /********** * Events * **********/ /// @notice Emitted when a new market is added. /// @param baseToken The address of base token of the market. /// @param mintCap The mint capacity of the market. event AddMarket(address indexed baseToken, uint256 mintCap); /// @notice Emitted when the mint capacity is updated. /// @param baseToken The address of base token of the market. /// @param oldCap The value of previous mint capacity. /// @param newCap The value of current mint capacity. event UpdateMintCap(address indexed baseToken, uint256 oldCap, uint256 newCap); /// @notice Emitted when a new rebalance pool is added. /// @param baseToken The address of base token of the market. /// @param pool The address of the rebalance pool. event AddRebalancePool(address indexed baseToken, address indexed pool); /// @notice Emitted when a new rebalance pool is removed. /// @param baseToken The address of base token of the market. /// @param pool The address of the rebalance pool. event RemoveRebalancePool(address indexed baseToken, address indexed pool); /// @notice Emitted when someone wrap fToken as fxUSD. /// @param baseToken The address of base token of the market. /// @param owner The address of fToken owner. /// @param receiver The address of fxUSD recipient. /// @param amount The amount of fxUSD minted. event Wrap(address indexed baseToken, address indexed owner, address indexed receiver, uint256 amount); /// @notice Emitted when someone unwrap fxUSD as fToken. /// @param baseToken The address of base token of the market. /// @param owner The address of fxUSD owner. /// @param receiver The address of base token recipient. /// @param amount The amount of fxUSD burned. event Unwrap(address indexed baseToken, address indexed owner, address indexed receiver, uint256 amount); /********** * Errors * **********/ /// @dev Thrown when someone tries to interact with unsupported market. error ErrorUnsupportedMarket(); /// @dev Thrown when someone tries to interact with unsupported rebalance pool. error ErrorUnsupportedRebalancePool(); /// @dev Thrown when someone tries to interact with market in stability mode. error ErrorMarketInStabilityMode(); /// @dev Thrown when someone tries to interact with market has invalid price. error ErrorMarketWithInvalidPrice(); /// @dev Thrown when someone tries to add a supported market. error ErrorMarketAlreadySupported(); /// @dev Thrown when the total supply of fToken exceed mint capacity. error ErrorExceedMintCap(); /// @dev Thrown when the amount of fToken is not enough for redeem. error ErrorInsufficientLiquidity(); /// @dev Thrown when current is under collateral. error ErrorUnderCollateral(); /// @dev Thrown when the length of two arrays is mismatch. error ErrorLengthMismatch(); /************************* * Public View Functions * *************************/ /// @notice Return the list of supported markets. function getMarkets() external view returns (address[] memory); /// @notice Return the list of supported rebalance pools. function getRebalancePools() external view returns (address[] memory); /// @notice Return the nav of fxUSD. function nav() external view returns (uint256); /// @notice Return whether the system is under collateral. function isUnderCollateral() external view returns (bool); /**************************** * Public Mutated Functions * ****************************/ /// @notice Wrap fToken to fxUSD. /// @param baseToken The address of corresponding base token. /// @param amount The amount of fToken to wrap. /// @param receiver The address of fxUSD recipient. function wrap( address baseToken, uint256 amount, address receiver ) external; /// @notice Unwrap fxUSD to fToken. /// @param baseToken The address of corresponding base token. /// @param amount The amount of fxUSD to unwrap. /// @param receiver The address of fToken recipient. function unwrap( address baseToken, uint256 amount, address receiver ) external; /// @notice Wrap fToken from rebalance pool to fxUSD. /// @param pool The address of rebalance pool. /// @param amount The amount of fToken to wrap. /// @param receiver The address of fxUSD recipient. function wrapFrom( address pool, uint256 amount, address receiver ) external; /// @notice Mint fxUSD with base token. /// @param baseToken The address of the base token. /// @param amountIn The amount of base token to use. /// @param receiver The address of fxUSD recipient. /// @param minOut The minimum amount of fxUSD should receive. /// @return amountOut The amount of fxUSD received by the receiver. function mint( address baseToken, uint256 amountIn, address receiver, uint256 minOut ) external returns (uint256 amountOut); /// @notice Deposit fxUSD to rebalance pool. /// @param pool The address of rebalance pool. /// @param amount The amount of fxUSD to use. /// @param receiver The address of rebalance pool share recipient. function earn( address pool, uint256 amount, address receiver ) external; /// @notice Mint fxUSD with base token and deposit to rebalance pool. /// @param pool The address of rebalance pool. /// @param amountIn The amount of base token to use. /// @param receiver The address of rebalance pool recipient. /// @param minOut The minimum amount of rebalance pool shares should receive. /// @return amountOut The amount of rebalance pool shares received by the receiver. function mintAndEarn( address pool, uint256 amountIn, address receiver, uint256 minOut ) external returns (uint256 amountOut); /// @notice Redeem fxUSD to base token. /// @param baseToken The address of the base token. /// @param amountIn The amount of fxUSD to redeem. /// @param receiver The address of base token recipient. /// @param minOut The minimum amount of base token should receive. /// @return amountOut The amount of base token received by the receiver. /// @return bonusOut The amount of bonus base token received by the receiver. function redeem( address baseToken, uint256 amountIn, address receiver, uint256 minOut ) external returns (uint256 amountOut, uint256 bonusOut); /// @notice Redeem fToken from rebalance pool to base token. /// @param amountIn The amount of fxUSD to redeem. /// @param receiver The address of base token recipient. /// @param minOut The minimum amount of base token should receive. /// @return amountOut The amount of base token received by the receiver. /// @return bonusOut The amount of bonus base token received by the receiver. function redeemFrom( address pool, uint256 amountIn, address receiver, uint256 minOut ) external returns (uint256 amountOut, uint256 bonusOut); /// @notice Redeem fxUSD to base token optimally. /// @param amountIn The amount of fxUSD to redeem. /// @param receiver The address of base token recipient. /// @param minOuts The list of minimum amount of base token should receive. /// @return baseTokens The list of base token received by the receiver. /// @return amountOuts The list of amount of base token received by the receiver. /// @return bonusOuts The list of amount of bonus base token received by the receiver. function autoRedeem( uint256 amountIn, address receiver, uint256[] memory minOuts ) external returns ( address[] memory baseTokens, uint256[] memory amountOuts, uint256[] memory bonusOuts ); }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.20; import { AccessControlUpgradeable } from "@openzeppelin/contracts-upgradeable-v4/access/AccessControlUpgradeable.sol"; import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable-v4/security/ReentrancyGuardUpgradeable.sol"; import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable-v4/token/ERC20/IERC20Upgradeable.sol"; import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable-v4/token/ERC20/utils/SafeERC20Upgradeable.sol"; import { WordCodec } from "../common/codec/WordCodec.sol"; import { IFxMarketV2 } from "./interfaces/IFxMarketV2.sol"; import { IFxRebalancePoolRegistry } from "./interfaces/IFxRebalancePoolRegistry.sol"; import { IFxReservePool } from "./interfaces/IFxReservePool.sol"; import { IFxTreasuryV2 } from "./interfaces/IFxTreasuryV2.sol"; // solhint-disable max-states-count contract MarketV2 is AccessControlUpgradeable, ReentrancyGuardUpgradeable, IFxMarketV2 { using SafeERC20Upgradeable for IERC20Upgradeable; using WordCodec for bytes32; /************* * Constants * *************/ /// @notice The role for emergency dao. bytes32 public constant EMERGENCY_DAO_ROLE = keccak256("EMERGENCY_DAO_ROLE"); /// @notice The role for migrator. bytes32 public constant MIGRATOR_ROLE = keccak256("MIGRATOR_ROLE"); /// @dev The precision used to compute nav. uint256 private constant FEE_PRECISION = 1e18; /// @dev The offset of mint flag in `marketConfigData`. uint256 private constant MINT_FLAG_OFFSET = 0; /// @dev The offset of redeem flag in `marketConfigData`. uint256 private constant REDEEM_FLAG_OFFSET = 1; /// @dev The offset of stability mode mint flag in `marketConfigData`. uint256 private constant MINT_FLAG_STABILITY_OFFSET = 2; /// @dev The offset of stability mode redeem flag in `marketConfigData`. uint256 private constant REDEEM_FLAG_STABILITY_OFFSET = 3; /// @dev The offset of stability ratio in `marketConfigData`. uint256 private constant STABILITY_RATIO_OFFSET = 34; /// @dev The offset of default fToken fee ratio in `mintFeeData` and `redeemFeeData`. uint256 private constant FTOKEN_DEFAULT_FEE_OFFSET = 0; /// @dev The offset of delta fToken fee ratio in `mintFeeData` and `redeemFeeData`. uint256 private constant FTOKEN_DELTA_FEE_OFFSET = 64; /// @dev The offset of default xToken fee ratio in `mintFeeData` and `redeemFeeData`. uint256 private constant XTOKEN_DEFAULT_FEE_OFFSET = 128; /// @dev The offset of delta xToken fee ratio in `mintFeeData` and `redeemFeeData`. uint256 private constant XTOKEN_DELTA_FEE_OFFSET = 192; /// @inheritdoc IFxMarketV2 address public immutable override treasury; /// @inheritdoc IFxMarketV2 address public immutable override baseToken; /// @inheritdoc IFxMarketV2 address public immutable override fToken; /// @inheritdoc IFxMarketV2 address public immutable override xToken; /************* * Variables * *************/ /// @dev `marketConfigData` is a storage slot that can be used to store market configuration. /// /// - The *mint flag* indicate whether the token mint is paused (both fToken and xToken). /// - The *redeem flag* indicate whether the token redeem is paused (both fToken and xToken). /// - The *mint flag stability* indicate whether the fToken mint is paused in stability mode. /// - The *redeem flag stability* indicate whether the xToken redeem is paused in stability mode. /// - The *stability ratio* is the collateral ratio to enter stability mode, multiplied by 1e18. /// /// [ mint flag | redeem flag | mint flag stability | redeem flag stability | stability ratio | available ] /// [ 1 bit | 1 bit | 1 bit | 1 bit | 64 bits | 188 bits ] /// [ MSB LSB ] bytes32 private marketConfigData; /// @dev `mintFeeData` is a storage slot that can be used to store mint fee ratio. /// /// [ default fToken | delta fToken | default xToken | delta xToken | /// [ 64 bit | 64 bit | 64 bit | 64 bit ] /// [ MSB LSB ] bytes32 private mintFeeData; /// @dev `redeemFeeData` is a storage slot that can be used to store redeem fee ratio. /// /// [ default fToken | delta fToken | default xToken | delta xToken | /// [ 64 bit | 64 bit | 64 bit | 64 bit ] /// [ MSB LSB ] bytes32 private redeemFeeData; /// @notice The address of platform contract; address public platform; /// @notice The address of ReservePool contract. address public reservePool; /// @notice The address of RebalancePoolRegistry contract. address public registry; /// @inheritdoc IFxMarketV2 address public fxUSD; /// @dev Slots for future use. uint256[43] private _gap; /*************** * Constructor * ***************/ constructor(address _treasury) { treasury = _treasury; baseToken = IFxTreasuryV2(_treasury).baseToken(); fToken = IFxTreasuryV2(_treasury).fToken(); xToken = IFxTreasuryV2(_treasury).xToken(); } function initialize(address _platform, address _reservePool, address _registry) external initializer { __Context_init(); __ERC165_init(); __AccessControl_init(); __ReentrancyGuard_init(); _setupRole(DEFAULT_ADMIN_ROLE, _msgSender()); _updatePlatform(_platform); _updateReservePool(_reservePool); _updateRebalancePoolRegistry(_registry); } /************************* * Public View Functions * *************************/ /// @notice Return whether token mint is paused. function mintPaused() public view returns (bool) { return marketConfigData.decodeBool(MINT_FLAG_OFFSET); } /// @notice Return whether token redeem is paused. function redeemPaused() public view returns (bool) { return marketConfigData.decodeBool(REDEEM_FLAG_OFFSET); } /// @notice Return whether fToken mint is paused in stability mode. function fTokenMintPausedInStabilityMode() public view returns (bool) { return marketConfigData.decodeBool(MINT_FLAG_STABILITY_OFFSET); } /// @notice Return whether xToken redeem is paused in stability mode. function xTokenRedeemPausedInStabilityMode() public view returns (bool) { return marketConfigData.decodeBool(REDEEM_FLAG_STABILITY_OFFSET); } /// @inheritdoc IFxMarketV2 function stabilityRatio() public view returns (uint256) { return marketConfigData.decodeUint(STABILITY_RATIO_OFFSET, 64); } /// @notice The mint fee ratio for fToken. function fTokenMintFeeRatio() public view returns (uint256 defaultFee, int256 deltaFee) { bytes32 _mintFeeData = mintFeeData; defaultFee = _mintFeeData.decodeUint(FTOKEN_DEFAULT_FEE_OFFSET, 64); deltaFee = _mintFeeData.decodeInt(FTOKEN_DELTA_FEE_OFFSET, 64); } /// @notice The mint fee ratio for xToken. function xTokenMintFeeRatio() public view returns (uint256 defaultFee, int256 deltaFee) { bytes32 _mintFeeData = mintFeeData; defaultFee = _mintFeeData.decodeUint(XTOKEN_DEFAULT_FEE_OFFSET, 64); deltaFee = _mintFeeData.decodeInt(XTOKEN_DELTA_FEE_OFFSET, 64); } /// @notice The redeem fee ratio for fToken. function fTokenRedeemFeeRatio() public view returns (uint256 defaultFee, int256 deltaFee) { bytes32 _redeemFeeData = redeemFeeData; defaultFee = _redeemFeeData.decodeUint(FTOKEN_DEFAULT_FEE_OFFSET, 64); deltaFee = _redeemFeeData.decodeInt(FTOKEN_DELTA_FEE_OFFSET, 64); } /// @notice The redeem fee ratio for xToken. function xTokenRedeemFeeRatio() public view returns (uint256 defaultFee, int256 deltaFee) { bytes32 _redeemFeeData = redeemFeeData; defaultFee = _redeemFeeData.decodeUint(XTOKEN_DEFAULT_FEE_OFFSET, 64); deltaFee = _redeemFeeData.decodeInt(XTOKEN_DELTA_FEE_OFFSET, 64); } /**************************** * Public Mutated Functions * ****************************/ /// @inheritdoc IFxMarketV2 function mintFToken( uint256 _baseIn, address _recipient, uint256 _minFTokenMinted ) external override nonReentrant returns (uint256 _fTokenMinted) { if (mintPaused()) revert ErrorMintPaused(); // make sure caller is fxUSD, when fxUSD is enabled { address _fxUSD = fxUSD; if (_fxUSD != address(0) && _fxUSD != _msgSender()) revert ErrorCallerNotFUSD(); } _beforeMintFToken(); if (_baseIn == type(uint256).max) { _baseIn = IERC20Upgradeable(baseToken).balanceOf(_msgSender()); } if (_baseIn == 0) revert ErrorMintZeroAmount(); uint256 _stabilityRatio = stabilityRatio(); (uint256 _maxBaseInBeforeSystemStabilityMode, ) = IFxTreasuryV2(treasury).maxMintableFToken(_stabilityRatio); if (_maxBaseInBeforeSystemStabilityMode > 0) { _maxBaseInBeforeSystemStabilityMode = IFxTreasuryV2(treasury).getWrapppedValue( _maxBaseInBeforeSystemStabilityMode ); } if (fTokenMintPausedInStabilityMode()) { uint256 _collateralRatio = IFxTreasuryV2(treasury).collateralRatio(); if (_collateralRatio <= _stabilityRatio) revert ErrorFTokenMintPausedInStabilityMode(); // bound maximum amount of base token to mint fToken. if (_baseIn > _maxBaseInBeforeSystemStabilityMode) { _baseIn = _maxBaseInBeforeSystemStabilityMode; } } uint256 _amountWithoutFee = _deductFTokenMintFee(_baseIn, _maxBaseInBeforeSystemStabilityMode); IERC20Upgradeable(baseToken).safeTransferFrom(_msgSender(), treasury, _amountWithoutFee); _fTokenMinted = IFxTreasuryV2(treasury).mintFToken( IFxTreasuryV2(treasury).getUnderlyingValue(_amountWithoutFee), _recipient ); if (_fTokenMinted < _minFTokenMinted) revert ErrorInsufficientFTokenOutput(); emit MintFToken(_msgSender(), _recipient, _baseIn, _fTokenMinted, _baseIn - _amountWithoutFee); } /// @inheritdoc IFxMarketV2 function mintXToken( uint256 _baseIn, address _recipient, uint256 _minXTokenMinted ) external override nonReentrant returns (uint256 _xTokenMinted, uint256 _bonus) { if (mintPaused()) revert ErrorMintPaused(); _beforeMintXToken(); if (_baseIn == type(uint256).max) { _baseIn = IERC20Upgradeable(baseToken).balanceOf(_msgSender()); } if (_baseIn == 0) revert ErrorMintZeroAmount(); uint256 _stabilityRatio = stabilityRatio(); (uint256 _maxBaseInBeforeSystemStabilityMode, ) = IFxTreasuryV2(treasury).maxMintableXToken(_stabilityRatio); if (_maxBaseInBeforeSystemStabilityMode > 0) { _maxBaseInBeforeSystemStabilityMode = IFxTreasuryV2(treasury).getWrapppedValue( _maxBaseInBeforeSystemStabilityMode ); } uint256 _amountWithoutFee = _deductXTokenMintFee(_baseIn, _maxBaseInBeforeSystemStabilityMode); IERC20Upgradeable(baseToken).safeTransferFrom(_msgSender(), treasury, _amountWithoutFee); _xTokenMinted = IFxTreasuryV2(treasury).mintXToken( IFxTreasuryV2(treasury).getUnderlyingValue(_amountWithoutFee), _recipient ); if (_xTokenMinted < _minXTokenMinted) revert ErrorInsufficientXTokenOutput(); // give bnous if (_amountWithoutFee < _maxBaseInBeforeSystemStabilityMode) { _bonus = _amountWithoutFee; } else { _bonus = _maxBaseInBeforeSystemStabilityMode; } if (_bonus > 0 && IFxRebalancePoolRegistry(registry).totalSupply() == 0) { _bonus = IFxReservePool(reservePool).requestBonus(baseToken, _recipient, _bonus); } else { _bonus = 0; } emit MintXToken(_msgSender(), _recipient, _baseIn, _xTokenMinted, _bonus, _baseIn - _amountWithoutFee); } /// @inheritdoc IFxMarketV2 function redeemFToken( uint256 _fTokenIn, address _recipient, uint256 _minBaseOut ) external override nonReentrant returns (uint256 _baseOut, uint256 _bonus) { if (redeemPaused()) revert ErrorRedeemPaused(); _beforeRedeemFToken(); if (_fTokenIn == type(uint256).max) { _fTokenIn = IERC20Upgradeable(fToken).balanceOf(_msgSender()); } if (_fTokenIn == 0) revert ErrorRedeemZeroAmount(); uint256 _stabilityRatio = stabilityRatio(); (uint256 _maxBaseOut, uint256 _maxFTokenInBeforeSystemStabilityMode) = IFxTreasuryV2(treasury).maxRedeemableFToken( _stabilityRatio ); uint256 _feeRatio; if (!hasRole(MIGRATOR_ROLE, _msgSender())) { _feeRatio = _computeFTokenRedeemFeeRatio(_fTokenIn, _maxFTokenInBeforeSystemStabilityMode); } _baseOut = IFxTreasuryV2(treasury).redeem(_fTokenIn, 0, _msgSender()); // give bonus when redeem fToken if (_baseOut < _maxBaseOut) { _bonus = _baseOut; } else { _bonus = _maxBaseOut; } // request bonus if (_bonus > 0 && IFxRebalancePoolRegistry(registry).totalSupply() == 0) { (uint256 _defaultRatio, int256 _deltaRatio) = fTokenMintFeeRatio(); _bonus -= (_bonus * uint256(int256(_defaultRatio) + _deltaRatio)) / FEE_PRECISION; // deduct fee _bonus = IFxReservePool(reservePool).requestBonus( baseToken, _recipient, IFxTreasuryV2(treasury).getWrapppedValue(_bonus) ); } else { _bonus = 0; } _baseOut = IFxTreasuryV2(treasury).getWrapppedValue(_baseOut); uint256 _balance = IERC20Upgradeable(baseToken).balanceOf(address(this)); // consider possible slippage if (_balance < _baseOut) { _baseOut = _balance; } uint256 _fee = (_baseOut * _feeRatio) / FEE_PRECISION; if (_fee > 0) { IERC20Upgradeable(baseToken).safeTransfer(platform, _fee); _baseOut = _baseOut - _fee; } if (_baseOut < _minBaseOut) revert ErrorInsufficientBaseOutput(); IERC20Upgradeable(baseToken).safeTransfer(_recipient, _baseOut); emit RedeemFToken(_msgSender(), _recipient, _fTokenIn, _baseOut, _bonus, _fee); } /// @inheritdoc IFxMarketV2 function redeemXToken( uint256 _xTokenIn, address _recipient, uint256 _minBaseOut ) external override nonReentrant returns (uint256 _baseOut) { if (redeemPaused()) revert ErrorRedeemPaused(); _beforeRedeemXToken(); if (_xTokenIn == type(uint256).max) { _xTokenIn = IERC20Upgradeable(xToken).balanceOf(_msgSender()); } if (_xTokenIn == 0) revert ErrorRedeemZeroAmount(); uint256 _stabilityRatio = stabilityRatio(); uint256 _feeRatio; (, uint256 _maxXTokenInBeforeSystemStabilityMode) = IFxTreasuryV2(treasury).maxRedeemableXToken(_stabilityRatio); if (xTokenRedeemPausedInStabilityMode()) { uint256 _collateralRatio = IFxTreasuryV2(treasury).collateralRatio(); if (_collateralRatio <= _stabilityRatio) revert ErrorXTokenRedeemPausedInStabilityMode(); // bound maximum amount of xToken to redeem. if (_xTokenIn > _maxXTokenInBeforeSystemStabilityMode) { _xTokenIn = _maxXTokenInBeforeSystemStabilityMode; } } if (!hasRole(MIGRATOR_ROLE, _msgSender())) { _feeRatio = _computeXTokenRedeemFeeRatio(_xTokenIn, _maxXTokenInBeforeSystemStabilityMode); } _baseOut = IFxTreasuryV2(treasury).redeem(0, _xTokenIn, _msgSender()); _baseOut = IFxTreasuryV2(treasury).getWrapppedValue(_baseOut); uint256 _balance = IERC20Upgradeable(baseToken).balanceOf(address(this)); // consider possible slippage if (_balance < _baseOut) { _baseOut = _balance; } uint256 _fee = (_baseOut * _feeRatio) / FEE_PRECISION; if (_fee > 0) { IERC20Upgradeable(baseToken).safeTransfer(platform, _fee); _baseOut = _baseOut - _fee; } if (_baseOut < _minBaseOut) revert ErrorInsufficientBaseOutput(); IERC20Upgradeable(baseToken).safeTransfer(_recipient, _baseOut); emit RedeemXToken(_msgSender(), _recipient, _xTokenIn, _baseOut, _fee); } /******************************* * Public Restricted Functions * *******************************/ /// @notice Update the fee ratio for redeeming. /// @param _defaultFeeRatio The new default fee ratio, multipled by 1e18. /// @param _extraFeeRatio The new extra fee ratio, multipled by 1e18. /// @param _isFToken Whether we are updating for fToken. function updateRedeemFeeRatio( uint256 _defaultFeeRatio, int256 _extraFeeRatio, bool _isFToken ) external onlyRole(DEFAULT_ADMIN_ROLE) { _validateFeeRatio(_defaultFeeRatio, _extraFeeRatio); bytes32 _redeemFeeData = redeemFeeData; if (_isFToken) { _redeemFeeData = _redeemFeeData.insertUint(_defaultFeeRatio, FTOKEN_DEFAULT_FEE_OFFSET, 64); _redeemFeeData = _redeemFeeData.insertInt(_extraFeeRatio, FTOKEN_DELTA_FEE_OFFSET, 64); emit UpdateRedeemFeeRatioFToken(_defaultFeeRatio, _extraFeeRatio); } else { _redeemFeeData = _redeemFeeData.insertUint(_defaultFeeRatio, XTOKEN_DEFAULT_FEE_OFFSET, 64); _redeemFeeData = _redeemFeeData.insertInt(_extraFeeRatio, XTOKEN_DELTA_FEE_OFFSET, 64); emit UpdateRedeemFeeRatioXToken(_defaultFeeRatio, _extraFeeRatio); } redeemFeeData = _redeemFeeData; } /// @notice Update the fee ratio for minting. /// @param _defaultFeeRatio The new default fee ratio, multipled by 1e18. /// @param _extraFeeRatio The new extra fee ratio, multipled by 1e18. /// @param _isFToken Whether we are updating for fToken. function updateMintFeeRatio( uint128 _defaultFeeRatio, int128 _extraFeeRatio, bool _isFToken ) external onlyRole(DEFAULT_ADMIN_ROLE) { _validateFeeRatio(_defaultFeeRatio, _extraFeeRatio); bytes32 _mintFeeData = mintFeeData; if (_isFToken) { _mintFeeData = _mintFeeData.insertUint(_defaultFeeRatio, FTOKEN_DEFAULT_FEE_OFFSET, 64); _mintFeeData = _mintFeeData.insertInt(_extraFeeRatio, FTOKEN_DELTA_FEE_OFFSET, 64); emit UpdateMintFeeRatioFToken(_defaultFeeRatio, _extraFeeRatio); } else { _mintFeeData = _mintFeeData.insertUint(_defaultFeeRatio, XTOKEN_DEFAULT_FEE_OFFSET, 64); _mintFeeData = _mintFeeData.insertInt(_extraFeeRatio, XTOKEN_DELTA_FEE_OFFSET, 64); emit UpdateMintFeeRatioXToken(_defaultFeeRatio, _extraFeeRatio); } mintFeeData = _mintFeeData; } /// @notice Update the stability ratio. /// @param _newRatio The new collateral ratio to enter stability mode, multiplied by 1e18. function updateStabilityRatio(uint256 _newRatio) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateStabilityRatio(_newRatio); } /// @notice Update mint pause status. /// @param _newStatus The new mint pause status. function updateMintStatus(bool _newStatus) external onlyRole(EMERGENCY_DAO_ROLE) { bool _oldStatus = _updateBoolInMarketConfigData(MINT_FLAG_OFFSET, _newStatus); emit UpdateMintStatus(_oldStatus, _newStatus); } /// @notice Update redeem pause status. /// @param _newStatus The new redeem pause status. function updateRedeemStatus(bool _newStatus) external onlyRole(EMERGENCY_DAO_ROLE) { bool _oldStatus = _updateBoolInMarketConfigData(REDEEM_FLAG_OFFSET, _newStatus); emit UpdateRedeemStatus(_oldStatus, _newStatus); } /// @notice Update fToken mint pause status in stability mode. /// @param _newStatus The new mint pause status. function updateFTokenMintStatusInStabilityMode(bool _newStatus) external onlyRole(EMERGENCY_DAO_ROLE) { bool _oldStatus = _updateBoolInMarketConfigData(MINT_FLAG_STABILITY_OFFSET, _newStatus); emit UpdateFTokenMintStatusInStabilityMode(_oldStatus, _newStatus); } /// @notice Update xToken redeem status in stability mode /// @param _newStatus The new redeem pause status. function updateXTokenRedeemStatusInStabilityMode(bool _newStatus) external onlyRole(EMERGENCY_DAO_ROLE) { bool _oldStatus = _updateBoolInMarketConfigData(REDEEM_FLAG_STABILITY_OFFSET, _newStatus); emit UpdateXTokenRedeemStatusInStabilityMode(_oldStatus, _newStatus); } /// @notice Change address of platform contract. /// @param _newPlatform The new address of platform contract. function updatePlatform(address _newPlatform) external onlyRole(DEFAULT_ADMIN_ROLE) { _updatePlatform(_newPlatform); } /// @notice Change address of reserve pool contract. /// @param _newReservePool The new address of reserve pool contract. function updateReservePool(address _newReservePool) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateReservePool(_newReservePool); } /// @notice Change address of RebalancePoolRegistry contract. /// @param _newRegistry The new address of RebalancePoolRegistry contract. function updateRebalancePoolRegistry(address _newRegistry) external onlyRole(DEFAULT_ADMIN_ROLE) { _updateRebalancePoolRegistry(_newRegistry); } /// @notice Enable fxUSD mint. /// @param _fxUSD The address of fxUSD token. function enableFxUSD(address _fxUSD) external onlyRole(DEFAULT_ADMIN_ROLE) { if (_fxUSD == address(0)) revert ErrorZeroAddress(); if (fxUSD == address(0)) fxUSD = _fxUSD; } /********************** * Internal Functions * **********************/ /// @dev Hook function to call before mint fToken. function _beforeMintFToken() internal virtual {} /// @dev Hook function to call before mint xToken. function _beforeMintXToken() internal virtual {} /// @dev Hook function to call before redeem fToken. function _beforeRedeemFToken() internal virtual {} /// @dev Hook function to call before redeem xToken. function _beforeRedeemXToken() internal virtual {} /// @dev Internal function to validate fee ratio. function _validateFeeRatio(uint256 _defaultFeeRatio, int256 _extraFeeRatio) internal pure { if (_defaultFeeRatio > FEE_PRECISION) revert ErrorDefaultFeeTooLarge(); if (_extraFeeRatio < 0) { if (uint256(-_extraFeeRatio) > _defaultFeeRatio) revert ErrorDeltaFeeTooSmall(); } else { if (uint256(_extraFeeRatio) > FEE_PRECISION - _defaultFeeRatio) revert ErrorTotalFeeTooLarge(); } } /// @dev Internal function to update bool value in `marketConfigData`. /// @param offset The offset of the value in `marketConfigData`. /// @param newValue The value to update. /// @return oldValue The original value in the `offset`. function _updateBoolInMarketConfigData(uint256 offset, bool newValue) private returns (bool oldValue) { bytes32 _data = marketConfigData; oldValue = _data.decodeBool(offset); marketConfigData = _data.insertBool(newValue, offset); } /// @dev Internal function to update stability ratio. /// @param _newRatio The new collateral ratio to enter stability mode, multiplied by 1e18. function _updateStabilityRatio(uint256 _newRatio) private { if (_newRatio > type(uint64).max) revert ErrorStabilityRatioTooLarge(); bytes32 _data = marketConfigData; uint256 _oldRatio = _data.decodeUint(STABILITY_RATIO_OFFSET, 64); marketConfigData = _data.insertUint(_newRatio, STABILITY_RATIO_OFFSET, 64); emit UpdateStabilityRatio(_oldRatio, _newRatio); } /// @notice Change address of platform contract. /// @param _newPlatform The new address of platform contract. function _updatePlatform(address _newPlatform) private { if (_newPlatform == address(0)) revert ErrorZeroAddress(); address _oldPlatform = platform; platform = _newPlatform; emit UpdatePlatform(_oldPlatform, _newPlatform); } /// @notice Change address of reserve pool contract. /// @param _newReservePool The new address of reserve pool contract. function _updateReservePool(address _newReservePool) private { if (_newReservePool == address(0)) revert ErrorZeroAddress(); address _oldReservePool = reservePool; reservePool = _newReservePool; emit UpdateReservePool(_oldReservePool, _newReservePool); } /// @notice Change address of RebalancePoolRegistry contract. /// @param _newRegistry The new address of RebalancePoolRegistry contract. function _updateRebalancePoolRegistry(address _newRegistry) private { if (_newRegistry == address(0)) revert ErrorZeroAddress(); address _oldRegistry = registry; registry = _newRegistry; emit UpdateRebalancePoolRegistry(_oldRegistry, _newRegistry); } /// @dev Internal function to deduct fToken mint fee for base token. /// @param _baseIn The amount of base token. /// @param _maxBaseInBeforeSystemStabilityMode The maximum amount of base token can be deposit before entering system stability mode. /// @return _baseInWithoutFee The amount of base token without fee. function _deductFTokenMintFee( uint256 _baseIn, uint256 _maxBaseInBeforeSystemStabilityMode ) private returns (uint256 _baseInWithoutFee) { // [0, _maxBaseInBeforeSystemStabilityMode) => default = fee_ratio_0 // [_maxBaseInBeforeSystemStabilityMode, infinity) => default + extra = fee_ratio_1 (uint256 _defaultRatio, int256 _deltaRatio) = fTokenMintFeeRatio(); uint256 _feeRatio0 = _defaultRatio; uint256 _feeRatio1 = uint256(int256(_defaultRatio) + _deltaRatio); _baseInWithoutFee = _deductMintFee(_baseIn, _feeRatio0, _feeRatio1, _maxBaseInBeforeSystemStabilityMode); } /// @dev Internal function to deduct fToken mint fee for base token. /// @param _baseIn The amount of base token. /// @param _maxBaseInBeforeSystemStabilityMode The maximum amount of base token can be deposit before entering system stability mode. /// @return _baseInWithoutFee The amount of base token without fee. function _deductXTokenMintFee( uint256 _baseIn, uint256 _maxBaseInBeforeSystemStabilityMode ) private returns (uint256 _baseInWithoutFee) { // [0, _maxBaseInBeforeSystemStabilityMode) => default + extra = fee_ratio_0 // [_maxBaseInBeforeSystemStabilityMode, infinity) => default = fee_ratio_1 (uint256 _defaultRatio, int256 _deltaRatio) = xTokenMintFeeRatio(); uint256 _feeRatio0 = uint256(int256(_defaultRatio) + _deltaRatio); uint256 _feeRatio1 = _defaultRatio; _baseInWithoutFee = _deductMintFee(_baseIn, _feeRatio0, _feeRatio1, _maxBaseInBeforeSystemStabilityMode); } function _deductMintFee( uint256 _baseIn, uint256 _feeRatio0, uint256 _feeRatio1, uint256 _maxBaseInBeforeSystemStabilityMode ) private returns (uint256 _baseInWithoutFee) { uint256 _maxBaseIn = (_maxBaseInBeforeSystemStabilityMode * FEE_PRECISION) / (FEE_PRECISION - _feeRatio0); // compute fee uint256 _fee; if (_baseIn <= _maxBaseIn) { _fee = (_baseIn * _feeRatio0) / FEE_PRECISION; } else { _fee = (_maxBaseIn * _feeRatio0) / FEE_PRECISION; _fee += ((_baseIn - _maxBaseIn) * _feeRatio1) / FEE_PRECISION; } _baseInWithoutFee = _baseIn - _fee; // take fee to platform if (_fee > 0) { IERC20Upgradeable(baseToken).safeTransferFrom(_msgSender(), platform, _fee); } } /// @dev Internal function to deduct mint fee for base token. /// @param _amountIn The amount of fToken. /// @param _maxInBeforeSystemStabilityMode The maximum amount of fToken can be redeemed before leaving system stability mode. /// @return _feeRatio The computed fee ratio for base token redeemed. function _computeFTokenRedeemFeeRatio( uint256 _amountIn, uint256 _maxInBeforeSystemStabilityMode ) private view returns (uint256 _feeRatio) { // [0, _maxBaseInBeforeSystemStabilityMode) => default + extra = fee_ratio_0 // [_maxBaseInBeforeSystemStabilityMode, infinity) => default = fee_ratio_1 (uint256 _defaultRatio, int256 _deltaRatio) = fTokenRedeemFeeRatio(); uint256 _feeRatio0 = uint256(int256(_defaultRatio) + _deltaRatio); uint256 _feeRatio1 = _defaultRatio; _feeRatio = _computeRedeemFeeRatio(_amountIn, _feeRatio0, _feeRatio1, _maxInBeforeSystemStabilityMode); } /// @dev Internal function to deduct mint fee for base token. /// @param _amountIn The amount of xToken. /// @param _maxInBeforeSystemStabilityMode The maximum amount of xToken can be redeemed before entering system stability mode. /// @return _feeRatio The computed fee ratio for base token redeemed. function _computeXTokenRedeemFeeRatio( uint256 _amountIn, uint256 _maxInBeforeSystemStabilityMode ) private view returns (uint256 _feeRatio) { // [0, _maxBaseInBeforeSystemStabilityMode) => default = fee_ratio_0 // [_maxBaseInBeforeSystemStabilityMode, infinity) => default + extra = fee_ratio_1 (uint256 _defaultRatio, int256 _deltaRatio) = xTokenRedeemFeeRatio(); uint256 _feeRatio0 = _defaultRatio; uint256 _feeRatio1 = uint256(int256(_defaultRatio) + _deltaRatio); _feeRatio = _computeRedeemFeeRatio(_amountIn, _feeRatio0, _feeRatio1, _maxInBeforeSystemStabilityMode); } /// @dev Internal function to deduct mint fee for base token. /// @param _amountIn The amount of fToken or xToken. /// @param _feeRatio0 The default fee ratio. /// @param _feeRatio1 The second fee ratio. /// @param _maxInBeforeSystemStabilityMode The maximum amount of fToken/xToken can be redeemed before entering/leaving system stability mode. /// @return _feeRatio The computed fee ratio for base token redeemed. function _computeRedeemFeeRatio( uint256 _amountIn, uint256 _feeRatio0, uint256 _feeRatio1, uint256 _maxInBeforeSystemStabilityMode ) private pure returns (uint256 _feeRatio) { if (_amountIn <= _maxInBeforeSystemStabilityMode) { return _feeRatio0; } uint256 _fee = _maxInBeforeSystemStabilityMode * _feeRatio0; _fee += (_amountIn - _maxInBeforeSystemStabilityMode) * _feeRatio1; return _fee / _amountIn; } }
// SPDX-License-Identifier: MIT pragma solidity ^0.8.0; // solhint-disable func-name-mixedcase interface ILiquidityGauge { /********** * Events * **********/ /// @notice Emitted when user deposit staking token to this contract. /// @param owner The address of token owner. /// @param receiver The address of recipient for the pool share. /// @param amount The amount of staking token deposited. event Deposit(address indexed owner, address indexed receiver, uint256 amount); /// @notice Emitted when user withdraw staking token from this contract. /// @param owner The address of token owner. /// @param receiver The address of recipient for the staking token /// @param amount The amount of staking token withdrawn. event Withdraw(address indexed owner, address indexed receiver, uint256 amount); /// @notice Emitted then the working balance is updated. /// @param account The address of user updated. /// @param originalBalance The original pool share of the user. /// @param originalSupply The original total pool share of the contract. /// @param workingBalance The current working balance of the user. /// @param workingSupply The current working supply of the contract. event UpdateLiquidityLimit( address indexed account, uint256 originalBalance, uint256 originalSupply, uint256 workingBalance, uint256 workingSupply ); /// @notice Emitted when the address of liquidity manager is updated. /// @param oldLiquidityManager The address of previous liquidity manager contract. /// @param newLiquidityManager The address of current liquidity manager contract. event UpdateLiquidityManager(address indexed oldLiquidityManager, address indexed newLiquidityManager); /********** * Errors * **********/ /// @dev Thrown when someone deposit zero amount staking token. error DepositZeroAmount(); /// @dev Thrown when someone withdraw zero amount staking token. error WithdrawZeroAmount(); /// @dev Thrown when some unauthorized user call `user_checkpoint`. error UnauthorizedCaller(); /// @dev Throw when someone try to kick user who has no changes on their ve balance. error KickNotAllowed(); /// @dev Thrown when someone try to do unnecessary kick. error KickNotNeeded(); /// @dev Thrown when try to remove an active liquidity manager. error LiquidityManagerIsActive(); /// @dev Thrown when try to add an inactive liquidity manager. error LiquidityManagerIsNotActive(); /************************* * Public View Functions * *************************/ /// @notice Return whether the gauge is active. function isActive() external view returns (bool); /// @notice Return the address of staking token. function stakingToken() external view returns (address); /// @notice Return the amount of working supply. function workingSupply() external view returns (uint256); /// @notice Return the amount of working balance of some user. /// @param account The address of user to query. function workingBalanceOf(address account) external view returns (uint256); /// @notice Return the governance token reward integrate for some user. /// /// @dev This is used in TokenMinter. /// /// @param account The address of user to query. function integrate_fraction(address account) external view returns (uint256); /**************************** * Public Mutated Functions * ****************************/ /// @notice Initialize the state of LiquidityGauge. /// /// @param _stakingToken The address of staking token. function initialize(address _stakingToken) external; /// @notice Deposit some staking token to this contract. /// /// @dev Use `amount = type(uint256).max`, if caller wants to deposit all held staking tokens. /// /// @param amount The amount of staking token to deposit. function deposit(uint256 amount) external; /// @notice Deposit some staking token to this contract and transfer the share to others. /// /// @dev Use `amount = type(uint256).max`, if caller wants to deposit all held staking tokens. /// /// @param amount The amount of staking token to deposit. /// @param receiver The address of the pool share recipient. function deposit(uint256 amount, address receiver) external; /// @notice Deposit some staking token to this contract and transfer the share to others. /// /// @dev Use `amount = type(uint256).max`, if caller wants to deposit all held staking tokens. /// /// @param amount The amount of staking token to deposit. /// @param receiver The address of the pool share recipient. /// @param manage The parameter passed to possible `LiquidityManager`. function deposit( uint256 amount, address receiver, bool manage ) external; /// @notice Withdraw some staking token from this contract. /// /// @dev Use `amount = type(uint256).max`, if caller wants to deposit all held staking tokens. /// /// @param amount The amount of staking token to withdraw. function withdraw(uint256 amount) external; /// @notice Withdraw some staking token from this contract and transfer the token to others. /// /// @dev Use `amount = type(uint256).max`, if caller wants to deposit all held staking tokens. /// /// @param amount The amount of staking token to withdraw. /// @param receiver The address of the staking token recipient. function withdraw(uint256 amount, address receiver) external; /// @notice Update the snapshot for some user. /// /// @dev This is used in TokenMinter. /// /// @param account The address of user to update. function user_checkpoint(address account) external returns (bool); /// @notice Kick some user for abusing their boost. /// /// @dev Only if either they had another voting event, or their voting escrow lock expired. /// /// @param account The address of user to kick. function kick(address account) external; }
{ "optimizer": { "enabled": true, "runs": 200 }, "evmVersion": "cancun", "outputSelection": { "*": { "*": [ "evm.bytecode", "evm.deployedBytecode", "devdoc", "userdoc", "metadata", "abi" ] } } }
Contract Security Audit
- No Contract Security Audit Submitted- Submit Audit Here
[{"inputs":[{"internalType":"address","name":"_poolManager","type":"address"},{"internalType":"address","name":"_stableToken","type":"address"},{"internalType":"address","name":"_pegKeeper","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"ErrorCallerNotPegKeeper","type":"error"},{"inputs":[],"name":"ErrorCallerNotPoolManager","type":"error"},{"inputs":[],"name":"ErrorExceedMintCap","type":"error"},{"inputs":[],"name":"ErrorExceedStableReserve","type":"error"},{"inputs":[],"name":"ErrorInsufficientBuyBack","type":"error"},{"inputs":[],"name":"ErrorInsufficientLiquidity","type":"error"},{"inputs":[],"name":"ErrorInsufficientOutput","type":"error"},{"inputs":[],"name":"ErrorLengthMismatch","type":"error"},{"inputs":[],"name":"ErrorMarketAlreadySupported","type":"error"},{"inputs":[],"name":"ErrorMarketInStabilityMode","type":"error"},{"inputs":[],"name":"ErrorMarketWithInvalidPrice","type":"error"},{"inputs":[],"name":"ErrorUnderCollateral","type":"error"},{"inputs":[],"name":"ErrorUnsupportedMarket","type":"error"},{"inputs":[],"name":"ErrorUnsupportedRebalancePool","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"baseToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"mintCap","type":"uint256"}],"name":"AddMarket","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"baseToken","type":"address"},{"indexed":true,"internalType":"address","name":"pool","type":"address"}],"name":"AddRebalancePool","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amountStable","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountFxUSD","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"bonusFxUSD","type":"uint256"}],"name":"Buyback","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amountStable","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountFxUSD","type":"uint256"}],"name":"RebalanceWithStable","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"baseToken","type":"address"},{"indexed":true,"internalType":"address","name":"pool","type":"address"}],"name":"RemoveRebalancePool","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"previousAdminRole","type":"bytes32"},{"indexed":true,"internalType":"bytes32","name":"newAdminRole","type":"bytes32"}],"name":"RoleAdminChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleGranted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"role","type":"bytes32"},{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":true,"internalType":"address","name":"sender","type":"address"}],"name":"RoleRevoked","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"baseToken","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Unwrap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"baseToken","type":"address"},{"indexed":false,"internalType":"uint256","name":"oldCap","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"newCap","type":"uint256"}],"name":"UpdateMintCap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"baseToken","type":"address"},{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Wrap","type":"event"},{"inputs":[],"name":"DEFAULT_ADMIN_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MIGRATOR_ROLE","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256[]","name":"_minOuts","type":"uint256[]"}],"name":"autoRedeem","outputs":[{"internalType":"address[]","name":"_baseTokens","type":"address[]"},{"internalType":"uint256[]","name":"_amountOuts","type":"uint256[]"},{"internalType":"uint256[]","name":"_bonusOuts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"burn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"buyback","outputs":[{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"uint256","name":"bonusOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"subtractedValue","type":"uint256"}],"name":"decreaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"}],"name":"earn","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMarkets","outputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRebalancePools","outputs":[{"internalType":"address[]","name":"_pools","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"}],"name":"getRoleAdmin","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"grantRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"hasRole","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"addedValue","type":"uint256"}],"name":"increaseAllowance","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"_name","type":"string"},{"internalType":"string","name":"_symbol","type":"string"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initializeV2","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"isUnderCollateral","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"legacyTotalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"markets","outputs":[{"internalType":"address","name":"fToken","type":"address"},{"internalType":"address","name":"treasury","type":"address"},{"internalType":"address","name":"market","type":"address"},{"internalType":"uint256","name":"mintCap","type":"uint256"},{"internalType":"uint256","name":"managed","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"mint","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"mint","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address","name":"","type":"address"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"mintAndEarn","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"nav","outputs":[{"internalType":"uint256","name":"_nav","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"amountStableToken","type":"uint256"},{"internalType":"uint256","name":"amountFxUSD","type":"uint256"}],"name":"onRebalanceWithStable","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"pegKeeper","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"poolManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_baseToken","type":"address"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_minOut","type":"uint256"}],"name":"redeem","outputs":[{"internalType":"uint256","name":"_amountOut","type":"uint256"},{"internalType":"uint256","name":"_bonusOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"},{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_minOut","type":"uint256"}],"name":"redeemFrom","outputs":[{"internalType":"uint256","name":"_amountOut","type":"uint256"},{"internalType":"uint256","name":"_bonusOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"renounceRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"role","type":"bytes32"},{"internalType":"address","name":"account","type":"address"}],"name":"revokeRole","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"stableReserve","outputs":[{"internalType":"uint96","name":"owned","type":"uint96"},{"internalType":"uint96","name":"managed","type":"uint96"},{"internalType":"uint8","name":"decimals","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stableToken","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_baseToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"unwrap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_baseToken","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"wrap","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_pool","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"wrapFrom","outputs":[],"stateMutability":"nonpayable","type":"function"}]
Contract Creation Code
60e060405234801561000f575f80fd5b506040516144e33803806144e383398101604081905261002e91610066565b6001600160a01b0392831660805290821660a0521660c0526100a6565b80516001600160a01b0381168114610061575f80fd5b919050565b5f805f60608486031215610078575f80fd5b6100818461004b565b925061008f6020850161004b565b915061009d6040850161004b565b90509250925092565b60805160a05160c0516143d461010f5f395f818161029f015281816110d2015281816111cc015261121a01525f818161067f015281816111aa01528181611247015261199601525f818161070401528181610f68015281816116e70152611de901526143d45ff3fe608060405234801561000f575f80fd5b5060043610610296575f3560e01c80636fae2e1511610161578063a9059cbb116100ca578063d505accf11610084578063d505accf146106d9578063d547741f146106ec578063dc4c90d3146106ff578063dd62ed3e14610726578063ec2c901614610739578063f3f094a114610741575f80fd5b8063a9059cbb14610667578063a9d75b2b1461067a578063b68f4345146106a1578063b8452aa9146106b4578063c1590cd7146106c9578063cdc6c7cc146106d1575f80fd5b806391d148541161011b57806391d14854146105ca57806395d89b41146105dd5780639a5c6eb6146105e55780639dc29fac1461063a578063a217fddf1461064d578063a457c2d714610654575f80fd5b80636fae2e15146104bf57806370a08231146104e65780637ecebe001461050e57806380b174071461052157806384b0196e1461052b5780638e8f294b14610546575f80fd5b80633644e5151161020357806340c10f19116101bd57806340c10f1914610458578063468a0efd1461046b5780634cd88b761461047e5780635c15155e146104915780635cd8a76b146104a4578063685dc7d3146104ac575f80fd5b80633644e515146103f157806336568abe146103f9578063395093511461040c5780633a4639e11461041f5780633bb03e2d146104325780633c173a4f14610445575f80fd5b80631ae94cde116102545780631ae94cde1461035d57806323b872dd14610372578063248a9ca3146103855780632f2ff15d146103a757806330df8c87146103ba578063313ce567146103e2575f80fd5b8062799a5e1461029a57806301ffc9a7146102de57806306fdde0314610301578063095ea7b31461031657806316c403351461032957806318160ddd1461034b575b5f80fd5b6102c17f000000000000000000000000000000000000000000000000000000000000000081565b6040516001600160a01b0390911681526020015b60405180910390f35b6102f16102ec366004613985565b610754565b60405190151581526020016102d5565b61030961078a565b6040516102d591906139da565b6102f1610324366004613a00565b61081a565b61033c610337366004613a6e565b610831565b6040516102d593929190613ba8565b6099545b6040519081526020016102d5565b61037061036b366004613bea565b610f65565b005b6102f1610380366004613c0a565b611081565b61034f610393366004613c48565b5f9081526065602052604090206001015490565b6103706103b5366004613c5f565b6110a4565b6103cd6103c8366004613c8d565b6110cd565b604080519283526020830191909152016102d5565b604051601281526020016102d5565b61034f61141e565b610370610407366004613c5f565b61142c565b6102f161041a366004613a00565b6114aa565b61037061042d366004613d10565b6114cb565b61034f610440366004613d4f565b61166b565b61034f610453366004613d4f565b6116ac565b610370610466366004613a00565b6116e4565b610370610479366004613d10565b611737565b61037061048c366004613dff565b61176d565b61037061049f366004613d10565b61185f565b61037061192d565b6103cd6104ba366004613d4f565b611a73565b61034f7f600e5f1c60beb469a3fa6dd3814a4ae211cc6259a6d033bae218a742f2af01d381565b61034f6104f4366004613e62565b6001600160a01b03165f9081526097602052604090205490565b61034f61051c366004613e62565b611cf7565b61034f6101355481565b610533611d14565b6040516102d59796959493929190613e7d565b610591610554366004613e62565b6101306020525f9081526040902080546001820154600283015460038401546004909401546001600160a01b039384169492841693909116919085565b604080516001600160a01b039687168152948616602086015292909416918301919091526060820152608081019190915260a0016102d5565b6102f16105d8366004613c5f565b611dad565b610309611dd7565b61013654610611906001600160601b0380821691600160601b810490911690600160c01b900460ff1683565b604080516001600160601b03948516815293909216602084015260ff16908201526060016102d5565b610370610648366004613a00565b611de6565b61034f5f81565b6102f1610662366004613a00565b611e39565b6102f1610675366004613a00565b611eb3565b6102c17f000000000000000000000000000000000000000000000000000000000000000081565b6103706106af366004613d10565b611ec0565b6106bc611faa565b6040516102d59190613eec565b61034f61204b565b6102f1612164565b6103706106e7366004613f0c565b61222c565b6103706106fa366004613c5f565b61238d565b6102c17f000000000000000000000000000000000000000000000000000000000000000081565b61034f610734366004613f78565b6123b1565b6106bc6123db565b6103cd61074f366004613d4f565b612477565b5f6001600160e01b03198216637965db0b60e01b148061078457506301ffc9a760e01b6001600160e01b03198316145b92915050565b6060609a805461079990613fa4565b80601f01602080910402602001604051908101604052809291908181526020018280546107c590613fa4565b80156108105780601f106107e757610100808354040283529160200191610810565b820191905f5260205f20905b8154815290600101906020018083116107f357829003601f168201915b5050505050905090565b5f33610827818585612690565b5060019392505050565b60608060605f6108426101316127b3565b90508085511461086557604051637326389560e01b815260040160405180910390fd5b806001600160401b0381111561087d5761087d613a2a565b6040519080825280602002602001820160405280156108a6578160200160208202803683370190505b509350806001600160401b038111156108c1576108c1613a2a565b6040519080825280602002602001820160405280156108ea578160200160208202803683370190505b509250806001600160401b0381111561090557610905613a2a565b60405190808252806020026020018201604052801561092e578160200160208202803683370190505b5091505f816001600160401b0381111561094a5761094a613a2a565b604051908082528060200260200182016040528015610973578160200160208202803683370190505b5090505f805b83811015610ae75761098d610131826127bc565b87828151811061099f5761099f613fd6565b60200260200101906001600160a01b031690816001600160a01b0316815250506101305f8883815181106109d5576109d5613fd6565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f2060040154838281518110610a1257610a12613fd6565b6020026020010181815250505f6101305f898481518110610a3557610a35613fd6565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f206001015f9054906101000a90046001600160a01b03169050806001600160a01b031663cdc6c7cc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ab0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ad49190613fea565b15610ade57600192505b50600101610979565b5061013554808a1115610b385760405162461bcd60e51b815260206004820152601460248201527372656465656d2065786365656420737570706c7960601b60448201526064015b60405180910390fd5b89810361013555610b49338b6127ce565b8115610bb4575f5b84811015610bae57818b858381518110610b6d57610b6d613fd6565b6020026020010151610b7f919061401d565b610b899190614034565b878281518110610b9b57610b9b613fd6565b6020908102919091010152600101610b51565b50610cdf565b8915610cdf575f835f81518110610bcd57610bcd613fd6565b602002602001015190505f80600190505b86811015610c2f5782868281518110610bf957610bf9613fd6565b60200260200101511115610c2757858181518110610c1957610c19613fd6565b602002602001015192508091505b600101610bde565b50818c1115610c5c5781888281518110610c4b57610c4b613fd6565b602002602001018181525050610c7c565b8b888281518110610c6f57610c6f613fd6565b6020026020010181815250505b878181518110610c8e57610c8e613fd6565b6020026020010151858281518110610ca857610ca8613fd6565b602002602001018181510391508181525050878181518110610ccc57610ccc613fd6565b60200260200101518c039b505050610bb4565b5f5b84811015610f5757868181518110610cfb57610cfb613fd6565b60200260200101515f0315610f4f576001600160a01b038a16336001600160a01b0316898381518110610d3057610d30613fd6565b60200260200101516001600160a01b03167fe173a76508f1af24a0f4f1e0ea3192eb6bbb92d750ad178d4dffe6048d63248d8a8581518110610d7457610d74613fd6565b6020026020010151604051610d8b91815260200190565b60405180910390a4868181518110610da557610da5613fd6565b60200260200101516101305f8a8481518110610dc357610dc3613fd6565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f206004015f828254610dfb9190614053565b925050819055505f6101305f8a8481518110610e1957610e19613fd6565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f206002015f9054906101000a90046001600160a01b03169050806001600160a01b031663d0ce98fa898481518110610e7957610e79613fd6565b60200260200101518d8d8681518110610e9457610e94613fd6565b60200260200101516040518463ffffffff1660e01b8152600401610ed4939291909283526001600160a01b03919091166020830152604082015260600190565b60408051808303815f875af1158015610eef573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f139190614066565b898481518110610f2557610f25613fd6565b60200260200101898581518110610f3e57610f3e613fd6565b602090810291909101019190915252505b600101610ce1565b505050505093509350939050565b337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614610fae57604051630a7c22bf60e11b815260040160405180910390fd5b61013680548391905f90610fcc9084906001600160601b0316614088565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550806101365f01600c8282829054906101000a90046001600160601b03166110179190614088565b92506101000a8154816001600160601b0302191690836001600160601b031602179055507ff1238357f035b5131e14074c5fd13978dd1ef19bda53ca98647b7dbe41d7ed9a8282604051611075929190918252602082015260400190565b60405180910390a15050565b5f3361108e858285612900565b611099858585612978565b506001949350505050565b5f828152606560205260409020600101546110be81612b21565b6110c88383612b2e565b505050565b5f80337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316146111185760405163ef7995db60e01b815260040160405180910390fd5b60408051606081018252610136546001600160601b03808216808452600160601b83049091166020840152600160c01b90910460ff1692820192909252908711156111765760405163101511b360e21b815260040160405180910390fd5b5f61119b8883602001516001600160601b0316845f01516001600160601b0316612bb3565b90506111f16001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000167f00000000000000000000000000000000000000000000000000000000000000008a612be8565b305f908152609760205260408120546040516339a62c0160e21b81529091506001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169063e698b00490611277907f00000000000000000000000000000000000000000000000000000000000000009030908e908d908d906004016140a7565b6020604051808303815f875af1158015611293573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112b791906140f7565b305f908152609760205260409020549095506112d4908290614053565b9050808511156112f75760405163ca1d512b60e01b815260040160405180910390fd5b81851015611318576040516312e2df8560e21b815260040160405180910390fd5b6113228286614053565b935061132e30836127ce565b82516001600160601b03908a900381168452602084015116821015611366576020830180518390036001600160601b0316905261136d565b5f60208401525b825161013680546020860151604087015160ff16600160c01b0260ff60c01b196001600160601b03928316600160601b026001600160c01b03199094169290951691909117919091179290921691909117905583156113d1576113d1308986612978565b604080518a8152602081018790529081018590527fd0b9355d279e4300b9775cdafb83758dd2135999d3842f80bcbad1607b6f432f9060600160405180910390a150505094509492505050565b5f611427612c4b565b905090565b6001600160a01b038116331461149c5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610b2f565b6114a68282612c54565b5050565b5f336108278185856114bc83836123b1565b6114c6919061410e565b612690565b826114d861013382612cba565b6114f557604051630a5af2b960e41b815260040160405180910390fd5b6114fd612164565b1561151b57604051635cbe463960e01b815260040160405180910390fd5b5f846001600160a01b031663c55dae636040518163ffffffff1660e01b8152600401602060405180830381865afa158015611558573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061157c9190614121565b905061158781612cdb565b611591815f612d04565b6001600160a01b03851663e162402f336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018790523060448201526064015f604051808303815f87803b1580156115ec575f80fd5b505af11580156115fe573d5f803e3d5ffd5b5050505061160d818486612eab565b6001600160a01b038316336001600160a01b0316826001600160a01b03167fbfa61fc27bb37f6f94529a9d0f81f1cc1a422648a9febe15aa90d13c33ed9ad78760405161165c91815260200190565b60405180910390a45050505050565b60405162461bcd60e51b81526020600482015260146024820152731b5a5b9d08185b990819585c9b881c185d5cd95960621b60448201525f90606401610b2f565b60405162461bcd60e51b815260206004820152600b60248201526a1b5a5b9d081c185d5cd95960aa1b60448201525f90606401610b2f565b337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161461172d57604051630a7c22bf60e11b815260040160405180910390fd5b6114a68282612edb565b60405162461bcd60e51b815260206004820152600b60248201526a19585c9b881c185d5cd95960aa1b6044820152606401610b2f565b5f54610100900460ff161580801561178b57505f54600160ff909116105b806117a45750303b1580156117a457505f5460ff166001145b6117c05760405162461bcd60e51b8152600401610b2f9061413c565b5f805460ff1916600117905580156117e1575f805461ff0019166101001790555b6117e9612f9a565b6117f1612f9a565b6117f9612f9a565b6118038383612fc2565b61180c83612ff2565b6118165f33612b2e565b80156110c8575f805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050565b8261186981612cdb565b835f6118758282612d04565b61187d612164565b1561189b57604051635cbe463960e01b815260040160405180910390fd5b6001600160a01b038681165f9081526101306020526040902054166118c28133308961303b565b6118cd878688612eab565b6001600160a01b038516336001600160a01b0316886001600160a01b03167fbfa61fc27bb37f6f94529a9d0f81f1cc1a422648a9febe15aa90d13c33ed9ad78960405161191c91815260200190565b60405180910390a450505050505050565b5f54600290610100900460ff1615801561194d57505f5460ff8083169116105b6119695760405162461bcd60e51b8152600401610b2f9061413c565b5f805461ffff191660ff8316176101001790556040805163313ce56760e01b815290516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000169163313ce5679160048083019260209291908290030181865afa1580156119df573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a03919061418a565b610136805460ff92909216600160c01b0260ff60c01b19909216919091179055611a2c60995490565b610135555f805461ff001916905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150565b5f8085611a8261013382612cba565b611a9f57604051630a5af2b960e41b815260040160405180910390fd5b5f876001600160a01b031663c55dae636040518163ffffffff1660e01b8152600401602060405180830381865afa158015611adc573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b009190614121565b6001600160a01b038181165f9081526101306020526040908190206002810154905491516370a0823160e01b815230600482015293945082169291169081906370a0823190602401602060405180830381865afa158015611b63573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b8791906140f7565b95506001600160a01b038a1663e162402f336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018c90523060448201526064015f604051808303815f87803b158015611be4575f80fd5b505af1158015611bf6573d5f803e3d5ffd5b50506040516370a0823160e01b81523060048201528892506001600160a01b03841691506370a0823190602401602060405180830381865afa158015611c3e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c6291906140f7565b611c6c9190614053565b6040516368674c7d60e11b8152600481018290526001600160a01b038a81166024830152604482018a90529197509083169063d0ce98fa9060640160408051808303815f875af1158015611cc2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ce69190614066565b909b909a5098505050505050505050565b6001600160a01b0381165f90815260fd6020526040812054610784565b5f6060805f805f606060c9545f801b148015611d30575060ca54155b611d745760405162461bcd60e51b81526020600482015260156024820152741152540dcc4c8e88155b9a5b9a5d1a585b1a5e9959605a1b6044820152606401610b2f565b611d7c613073565b611d84613082565b604080515f80825260208201909252600f60f81b9b939a50919850469750309650945092509050565b5f9182526065602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6060609b805461079990613fa4565b337f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031614611e2f57604051630a7c22bf60e11b815260040160405180910390fd5b6114a682826127ce565b5f3381611e4682866123b1565b905083811015611ea65760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610b2f565b6110998286868403612690565b5f33610827818585612978565b7f600e5f1c60beb469a3fa6dd3814a4ae211cc6259a6d033bae218a742f2af01d3611eea81612b21565b83611ef481612cdb565b611efc612164565b15611f1a57604051635cbe463960e01b815260040160405180910390fd5b611f25853386613091565b6001600160a01b038086165f908152610130602052604090205416611f4b818587612be8565b6001600160a01b038416336001600160a01b0316876001600160a01b03167fe173a76508f1af24a0f4f1e0ea3192eb6bbb92d750ad178d4dffe6048d63248d88604051611f9a91815260200190565b60405180910390a4505050505050565b60605f611fb86101336127b3565b9050806001600160401b03811115611fd257611fd2613a2a565b604051908082528060200260200182016040528015611ffb578160200160208202803683370190505b5091505f5b8181101561204657612014610133826127bc565b83828151811061202657612026613fd6565b6001600160a01b0390921660209283029190910190910152600101612000565b505090565b5f806120586101316127b3565b610135549091505f81900361207757670de0b6b3a76400009250505090565b5f5b82811015612152575f61208e610131836127bc565b6001600160a01b038082165f9081526101306020908152604080832054815163c1590cd760e01b81529151959650909316939192849263c1590cd7926004808401939192918290030181865afa1580156120ea573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061210e91906140f7565b6001600160a01b0384165f9081526101306020526040902060040154909150612137908261401d565b612141908861410e565b965050600190920191506120799050565b5061215d8184614034565b9250505090565b5f806121716101316127b3565b90505f5b81811015612224575f61218a610131836127bc565b6001600160a01b038082165f9081526101306020908152604091829020600101548251633371b1f360e21b8152925194955090921692839263cdc6c7cc92600480820193918290030181865afa1580156121e6573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061220a9190613fea565b1561221a57600194505050505090565b5050600101612175565b505f91505090565b8342111561227c5760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e650000006044820152606401610b2f565b5f7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886122aa8c613104565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090505f6123048261312b565b90505f61231382878787613157565b9050896001600160a01b0316816001600160a01b0316146123765760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e617475726500006044820152606401610b2f565b6123818a8a8a612690565b50505050505050505050565b5f828152606560205260409020600101546123a781612b21565b6110c88383612c54565b6001600160a01b039182165f90815260986020908152604080832093909416825291909152205490565b60605f6123e96101316127b3565b9050806001600160401b0381111561240357612403613a2a565b60405190808252806020026020018201604052801561242c578160200160208202803683370190505b5091505f5b8181101561204657612445610131826127bc565b83828151811061245757612457613fd6565b6001600160a01b0390921660209283029190910190910152600101612431565b5f808561248381612cdb565b61248b612164565b156124a957604051635cbe463960e01b815260040160405180910390fd5b6001600160a01b038781165f90815261013060205260408082206002810154905491516370a0823160e01b81523060048201529084169391909116919082906370a0823190602401602060405180830381865afa15801561250c573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061253091906140f7565b6040516368674c7d60e11b8152600481018b90526001600160a01b038a81166024830152604482018a90529192509084169063d0ce98fa9060640160408051808303815f875af1158015612586573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125aa9190614066565b6040516370a0823160e01b815230600482015291975095506001600160a01b038316906370a0823190602401602060405180830381865afa1580156125f1573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061261591906140f7565b61261f9082614053565b985061262c8a338b613091565b6001600160a01b038816336001600160a01b03168b6001600160a01b03167fe173a76508f1af24a0f4f1e0ea3192eb6bbb92d750ad178d4dffe6048d63248d8c60405161267b91815260200190565b60405180910390a45050505094509492505050565b6001600160a01b0383166126f25760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610b2f565b6001600160a01b0382166127535760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610b2f565b6001600160a01b038381165f8181526098602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b5f610784825490565b5f6127c7838361317d565b9392505050565b6001600160a01b03821661282e5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610b2f565b6001600160a01b0382165f90815260976020526040902054818110156128a15760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610b2f565b6001600160a01b0383165f8181526097602090815260408083208686039055609980548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b5f61290b84846123b1565b90505f19811461297257818110156129655760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610b2f565b6129728484848403612690565b50505050565b6001600160a01b0383166129dc5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610b2f565b6001600160a01b038216612a3e5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610b2f565b6001600160a01b0383165f9081526097602052604090205481811015612ab55760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610b2f565b6001600160a01b038085165f8181526097602052604080822086860390559286168082529083902080548601905591517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612b149086815260200190565b60405180910390a3612972565b612b2b81336131a3565b50565b612b388282611dad565b6114a6575f8281526065602090815260408083206001600160a01b03851684529091529020805460ff19166001179055612b6f3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b5f81600181612bc2868861401d565b612bcc919061410e565b612bd69190614053565b612be09190614034565b949350505050565b6040516001600160a01b0383166024820152604481018290526110c890849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526131fc565b5f6114276132cf565b612c5e8282611dad565b156114a6575f8281526065602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6001600160a01b0381165f90815260018301602052604081205415156127c7565b612ce761013182612cba565b612b2b576040516338453dd760e01b815260040160405180910390fd5b6001600160a01b038083165f9081526101306020526040902060010154168115612e2e575f816001600160a01b031663b4eae1cb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d65573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d8991906140f7565b6001600160a01b038086165f90815261013060209081526040808320600201548151630cf0b5b360e41b815291519596509294929093169263cf0b5b3092600480830193928290030181865afa158015612de5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e0991906140f7565b9050808211612e2b576040516319168f7960e21b815260040160405180910390fd5b50505b806001600160a01b0316639e3ad8716040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e6a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e8e9190613fea565b6110c857604051631d05894b60e31b815260040160405180910390fd5b6001600160a01b0383165f908152610130602052604090206004018054820190556101358054820190556110c882825b6001600160a01b038216612f315760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610b2f565b8060995f828254612f42919061410e565b90915550506001600160a01b0382165f818152609760209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b5f54610100900460ff16612fc05760405162461bcd60e51b8152600401610b2f906141a5565b565b5f54610100900460ff16612fe85760405162461bcd60e51b8152600401610b2f906141a5565b6114a68282613342565b5f54610100900460ff166130185760405162461bcd60e51b8152600401610b2f906141a5565b612b2b81604051806040016040528060018152602001603160f81b815250613381565b6040516001600160a01b03808516602483015283166044820152606481018290526129729085906323b872dd60e01b90608401612c14565b606060cb805461079990613fa4565b606060cc805461079990613fa4565b6001600160a01b0383165f9081526101306020526040902060040154808211156130ce5760405163b268a92160e01b815260040160405180910390fd5b6001600160a01b0384165f90815261013060205260409020600401805483900390556101358054839003905561297283836127ce565b6001600160a01b0381165f90815260fd602052604090208054600181018255905b50919050565b5f610784613137612c4b565b8360405161190160f01b8152600281019290925260228201526042902090565b5f805f613166878787876133ce565b915091506131738161348b565b5095945050505050565b5f825f01828154811061319257613192613fd6565b905f5260205f200154905092915050565b6131ad8282611dad565b6114a6576131ba816135d4565b6131c58360206135e6565b6040516020016131d6929190614207565b60408051601f198184030181529082905262461bcd60e51b8252610b2f916004016139da565b5f613250826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661377b9092919063ffffffff16565b905080515f14806132705750808060200190518101906132709190613fea565b6110c85760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610b2f565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6132f9613789565b6133016137e1565b60408051602081019490945283019190915260608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b5f54610100900460ff166133685760405162461bcd60e51b8152600401610b2f906141a5565b609a61337483826142b0565b50609b6110c882826142b0565b5f54610100900460ff166133a75760405162461bcd60e51b8152600401610b2f906141a5565b60cb6133b383826142b0565b5060cc6133c082826142b0565b50505f60c981905560ca5550565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561340357505f90506003613482565b604080515f8082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015613454573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b03811661347c575f60019250925050613482565b91505f90505b94509492505050565b5f81600481111561349e5761349e61436a565b036134a65750565b60018160048111156134ba576134ba61436a565b036135075760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610b2f565b600281600481111561351b5761351b61436a565b036135685760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610b2f565b600381600481111561357c5761357c61436a565b03612b2b5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610b2f565b60606107846001600160a01b03831660145b60605f6135f483600261401d565b6135ff90600261410e565b6001600160401b0381111561361657613616613a2a565b6040519080825280601f01601f191660200182016040528015613640576020820181803683370190505b509050600360fc1b815f8151811061365a5761365a613fd6565b60200101906001600160f81b03191690815f1a905350600f60fb1b8160018151811061368857613688613fd6565b60200101906001600160f81b03191690815f1a9053505f6136aa84600261401d565b6136b590600161410e565b90505b600181111561372c576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106136e9576136e9613fd6565b1a60f81b8282815181106136ff576136ff613fd6565b60200101906001600160f81b03191690815f1a90535060049490941c936137258161437e565b90506136b8565b5083156127c75760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610b2f565b6060612be084845f85613811565b5f80613793613073565b8051909150156137aa578051602090910120919050565b60c95480156137b95792915050565b7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4709250505090565b5f806137eb613082565b805190915015613802578051602090910120919050565b60ca5480156137b95792915050565b6060824710156138725760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610b2f565b5f80866001600160a01b0316858760405161388d9190614393565b5f6040518083038185875af1925050503d805f81146138c7576040519150601f19603f3d011682016040523d82523d5f602084013e6138cc565b606091505b50915091506138dd878383876138e8565b979650505050505050565b606083156139565782515f0361394f576001600160a01b0385163b61394f5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610b2f565b5081612be0565b612be0838381511561396b5781518083602001fd5b8060405162461bcd60e51b8152600401610b2f91906139da565b5f60208284031215613995575f80fd5b81356001600160e01b0319811681146127c7575f80fd5b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f6127c760208301846139ac565b6001600160a01b0381168114612b2b575f80fd5b5f8060408385031215613a11575f80fd5b8235613a1c816139ec565b946020939093013593505050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715613a6657613a66613a2a565b604052919050565b5f805f60608486031215613a80575f80fd5b833592506020840135613a92816139ec565b915060408401356001600160401b03811115613aac575f80fd5b8401601f81018613613abc575f80fd5b80356001600160401b03811115613ad557613ad5613a2a565b8060051b613ae560208201613a3e565b91825260208184018101929081019089841115613b00575f80fd5b6020850194505b83851015613b2657843580835260209586019590935090910190613b07565b80955050505050509250925092565b5f8151808452602084019350602083015f5b82811015613b6e5781516001600160a01b0316865260209586019590910190600101613b47565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613b6e578151865260209586019590910190600101613b8a565b606081525f613bba6060830186613b35565b8281036020840152613bcc8186613b78565b90508281036040840152613be08185613b78565b9695505050505050565b5f8060408385031215613bfb575f80fd5b50508035926020909101359150565b5f805f60608486031215613c1c575f80fd5b8335613c27816139ec565b92506020840135613c37816139ec565b929592945050506040919091013590565b5f60208284031215613c58575f80fd5b5035919050565b5f8060408385031215613c70575f80fd5b823591506020830135613c82816139ec565b809150509250929050565b5f805f8060608587031215613ca0575f80fd5b843593506020850135613cb2816139ec565b925060408501356001600160401b03811115613ccc575f80fd5b8501601f81018713613cdc575f80fd5b80356001600160401b03811115613cf1575f80fd5b876020828401011115613d02575f80fd5b949793965060200194505050565b5f805f60608486031215613d22575f80fd5b8335613d2d816139ec565b9250602084013591506040840135613d44816139ec565b809150509250925092565b5f805f8060808587031215613d62575f80fd5b8435613d6d816139ec565b9350602085013592506040850135613d84816139ec565b9396929550929360600135925050565b5f82601f830112613da3575f80fd5b81356001600160401b03811115613dbc57613dbc613a2a565b613dcf601f8201601f1916602001613a3e565b818152846020838601011115613de3575f80fd5b816020850160208301375f918101602001919091529392505050565b5f8060408385031215613e10575f80fd5b82356001600160401b03811115613e25575f80fd5b613e3185828601613d94565b92505060208301356001600160401b03811115613e4c575f80fd5b613e5885828601613d94565b9150509250929050565b5f60208284031215613e72575f80fd5b81356127c7816139ec565b60ff60f81b8816815260e060208201525f613e9b60e08301896139ac565b8281036040840152613ead81896139ac565b606084018890526001600160a01b038716608085015260a0840186905283810360c08501529050613ede8185613b78565b9a9950505050505050505050565b602081525f6127c76020830184613b35565b60ff81168114612b2b575f80fd5b5f805f805f805f60e0888a031215613f22575f80fd5b8735613f2d816139ec565b96506020880135613f3d816139ec565b955060408801359450606088013593506080880135613f5b81613efe565b9699959850939692959460a0840135945060c09093013592915050565b5f8060408385031215613f89575f80fd5b8235613f94816139ec565b91506020830135613c82816139ec565b600181811c90821680613fb857607f821691505b60208210810361312557634e487b7160e01b5f52602260045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613ffa575f80fd5b815180151581146127c7575f80fd5b634e487b7160e01b5f52601160045260245ffd5b808202811582820484141761078457610784614009565b5f8261404e57634e487b7160e01b5f52601260045260245ffd5b500490565b8181038181111561078457610784614009565b5f8060408385031215614077575f80fd5b505080516020909101519092909150565b6001600160601b03818116838216019081111561078457610784614009565b6001600160a01b03868116825285166020820152604081018490526080606082018190528101829052818360a08301375f81830160a090810191909152601f909201601f19160101949350505050565b5f60208284031215614107575f80fd5b5051919050565b8082018082111561078457610784614009565b5f60208284031215614131575f80fd5b81516127c7816139ec565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b5f6020828403121561419a575f80fd5b81516127c781613efe565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b5f81518060208401855e5f93019283525090919050565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081525f61423860178301856141f0565b7001034b99036b4b9b9b4b733903937b6329607d1b815261425c60118201856141f0565b95945050505050565b601f8211156110c857805f5260205f20601f840160051c8101602085101561428a5750805b601f840160051c820191505b818110156142a9575f8155600101614296565b5050505050565b81516001600160401b038111156142c9576142c9613a2a565b6142dd816142d78454613fa4565b84614265565b6020601f82116001811461430f575f83156142f85750848201515b5f19600385901b1c1916600184901b1784556142a9565b5f84815260208120601f198516915b8281101561433e578785015182556020948501946001909201910161431e565b508482101561435b57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b634e487b7160e01b5f52602160045260245ffd5b5f8161438c5761438c614009565b505f190190565b5f6127c782846141f056fea2646970667358221220617511e81f28c01052521a101b2ff57d3a4932f021d942061449acdf047f15d264736f6c634300081a0033000000000000000000000000250893ca4ba5d05626c785e8da758026928fcd24000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000050562fe7e870420f5aae480b7f94eb4ace2fcd70
Deployed Bytecode
0x608060405234801561000f575f80fd5b5060043610610296575f3560e01c80636fae2e1511610161578063a9059cbb116100ca578063d505accf11610084578063d505accf146106d9578063d547741f146106ec578063dc4c90d3146106ff578063dd62ed3e14610726578063ec2c901614610739578063f3f094a114610741575f80fd5b8063a9059cbb14610667578063a9d75b2b1461067a578063b68f4345146106a1578063b8452aa9146106b4578063c1590cd7146106c9578063cdc6c7cc146106d1575f80fd5b806391d148541161011b57806391d14854146105ca57806395d89b41146105dd5780639a5c6eb6146105e55780639dc29fac1461063a578063a217fddf1461064d578063a457c2d714610654575f80fd5b80636fae2e15146104bf57806370a08231146104e65780637ecebe001461050e57806380b174071461052157806384b0196e1461052b5780638e8f294b14610546575f80fd5b80633644e5151161020357806340c10f19116101bd57806340c10f1914610458578063468a0efd1461046b5780634cd88b761461047e5780635c15155e146104915780635cd8a76b146104a4578063685dc7d3146104ac575f80fd5b80633644e515146103f157806336568abe146103f9578063395093511461040c5780633a4639e11461041f5780633bb03e2d146104325780633c173a4f14610445575f80fd5b80631ae94cde116102545780631ae94cde1461035d57806323b872dd14610372578063248a9ca3146103855780632f2ff15d146103a757806330df8c87146103ba578063313ce567146103e2575f80fd5b8062799a5e1461029a57806301ffc9a7146102de57806306fdde0314610301578063095ea7b31461031657806316c403351461032957806318160ddd1461034b575b5f80fd5b6102c17f00000000000000000000000050562fe7e870420f5aae480b7f94eb4ace2fcd7081565b6040516001600160a01b0390911681526020015b60405180910390f35b6102f16102ec366004613985565b610754565b60405190151581526020016102d5565b61030961078a565b6040516102d591906139da565b6102f1610324366004613a00565b61081a565b61033c610337366004613a6e565b610831565b6040516102d593929190613ba8565b6099545b6040519081526020016102d5565b61037061036b366004613bea565b610f65565b005b6102f1610380366004613c0a565b611081565b61034f610393366004613c48565b5f9081526065602052604090206001015490565b6103706103b5366004613c5f565b6110a4565b6103cd6103c8366004613c8d565b6110cd565b604080519283526020830191909152016102d5565b604051601281526020016102d5565b61034f61141e565b610370610407366004613c5f565b61142c565b6102f161041a366004613a00565b6114aa565b61037061042d366004613d10565b6114cb565b61034f610440366004613d4f565b61166b565b61034f610453366004613d4f565b6116ac565b610370610466366004613a00565b6116e4565b610370610479366004613d10565b611737565b61037061048c366004613dff565b61176d565b61037061049f366004613d10565b61185f565b61037061192d565b6103cd6104ba366004613d4f565b611a73565b61034f7f600e5f1c60beb469a3fa6dd3814a4ae211cc6259a6d033bae218a742f2af01d381565b61034f6104f4366004613e62565b6001600160a01b03165f9081526097602052604090205490565b61034f61051c366004613e62565b611cf7565b61034f6101355481565b610533611d14565b6040516102d59796959493929190613e7d565b610591610554366004613e62565b6101306020525f9081526040902080546001820154600283015460038401546004909401546001600160a01b039384169492841693909116919085565b604080516001600160a01b039687168152948616602086015292909416918301919091526060820152608081019190915260a0016102d5565b6102f16105d8366004613c5f565b611dad565b610309611dd7565b61013654610611906001600160601b0380821691600160601b810490911690600160c01b900460ff1683565b604080516001600160601b03948516815293909216602084015260ff16908201526060016102d5565b610370610648366004613a00565b611de6565b61034f5f81565b6102f1610662366004613a00565b611e39565b6102f1610675366004613a00565b611eb3565b6102c17f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4881565b6103706106af366004613d10565b611ec0565b6106bc611faa565b6040516102d59190613eec565b61034f61204b565b6102f1612164565b6103706106e7366004613f0c565b61222c565b6103706106fa366004613c5f565b61238d565b6102c17f000000000000000000000000250893ca4ba5d05626c785e8da758026928fcd2481565b61034f610734366004613f78565b6123b1565b6106bc6123db565b6103cd61074f366004613d4f565b612477565b5f6001600160e01b03198216637965db0b60e01b148061078457506301ffc9a760e01b6001600160e01b03198316145b92915050565b6060609a805461079990613fa4565b80601f01602080910402602001604051908101604052809291908181526020018280546107c590613fa4565b80156108105780601f106107e757610100808354040283529160200191610810565b820191905f5260205f20905b8154815290600101906020018083116107f357829003601f168201915b5050505050905090565b5f33610827818585612690565b5060019392505050565b60608060605f6108426101316127b3565b90508085511461086557604051637326389560e01b815260040160405180910390fd5b806001600160401b0381111561087d5761087d613a2a565b6040519080825280602002602001820160405280156108a6578160200160208202803683370190505b509350806001600160401b038111156108c1576108c1613a2a565b6040519080825280602002602001820160405280156108ea578160200160208202803683370190505b509250806001600160401b0381111561090557610905613a2a565b60405190808252806020026020018201604052801561092e578160200160208202803683370190505b5091505f816001600160401b0381111561094a5761094a613a2a565b604051908082528060200260200182016040528015610973578160200160208202803683370190505b5090505f805b83811015610ae75761098d610131826127bc565b87828151811061099f5761099f613fd6565b60200260200101906001600160a01b031690816001600160a01b0316815250506101305f8883815181106109d5576109d5613fd6565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f2060040154838281518110610a1257610a12613fd6565b6020026020010181815250505f6101305f898481518110610a3557610a35613fd6565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f206001015f9054906101000a90046001600160a01b03169050806001600160a01b031663cdc6c7cc6040518163ffffffff1660e01b8152600401602060405180830381865afa158015610ab0573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ad49190613fea565b15610ade57600192505b50600101610979565b5061013554808a1115610b385760405162461bcd60e51b815260206004820152601460248201527372656465656d2065786365656420737570706c7960601b60448201526064015b60405180910390fd5b89810361013555610b49338b6127ce565b8115610bb4575f5b84811015610bae57818b858381518110610b6d57610b6d613fd6565b6020026020010151610b7f919061401d565b610b899190614034565b878281518110610b9b57610b9b613fd6565b6020908102919091010152600101610b51565b50610cdf565b8915610cdf575f835f81518110610bcd57610bcd613fd6565b602002602001015190505f80600190505b86811015610c2f5782868281518110610bf957610bf9613fd6565b60200260200101511115610c2757858181518110610c1957610c19613fd6565b602002602001015192508091505b600101610bde565b50818c1115610c5c5781888281518110610c4b57610c4b613fd6565b602002602001018181525050610c7c565b8b888281518110610c6f57610c6f613fd6565b6020026020010181815250505b878181518110610c8e57610c8e613fd6565b6020026020010151858281518110610ca857610ca8613fd6565b602002602001018181510391508181525050878181518110610ccc57610ccc613fd6565b60200260200101518c039b505050610bb4565b5f5b84811015610f5757868181518110610cfb57610cfb613fd6565b60200260200101515f0315610f4f576001600160a01b038a16336001600160a01b0316898381518110610d3057610d30613fd6565b60200260200101516001600160a01b03167fe173a76508f1af24a0f4f1e0ea3192eb6bbb92d750ad178d4dffe6048d63248d8a8581518110610d7457610d74613fd6565b6020026020010151604051610d8b91815260200190565b60405180910390a4868181518110610da557610da5613fd6565b60200260200101516101305f8a8481518110610dc357610dc3613fd6565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f206004015f828254610dfb9190614053565b925050819055505f6101305f8a8481518110610e1957610e19613fd6565b60200260200101516001600160a01b03166001600160a01b031681526020019081526020015f206002015f9054906101000a90046001600160a01b03169050806001600160a01b031663d0ce98fa898481518110610e7957610e79613fd6565b60200260200101518d8d8681518110610e9457610e94613fd6565b60200260200101516040518463ffffffff1660e01b8152600401610ed4939291909283526001600160a01b03919091166020830152604082015260600190565b60408051808303815f875af1158015610eef573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f139190614066565b898481518110610f2557610f25613fd6565b60200260200101898581518110610f3e57610f3e613fd6565b602090810291909101019190915252505b600101610ce1565b505050505093509350939050565b337f000000000000000000000000250893ca4ba5d05626c785e8da758026928fcd246001600160a01b031614610fae57604051630a7c22bf60e11b815260040160405180910390fd5b61013680548391905f90610fcc9084906001600160601b0316614088565b92506101000a8154816001600160601b0302191690836001600160601b03160217905550806101365f01600c8282829054906101000a90046001600160601b03166110179190614088565b92506101000a8154816001600160601b0302191690836001600160601b031602179055507ff1238357f035b5131e14074c5fd13978dd1ef19bda53ca98647b7dbe41d7ed9a8282604051611075929190918252602082015260400190565b60405180910390a15050565b5f3361108e858285612900565b611099858585612978565b506001949350505050565b5f828152606560205260409020600101546110be81612b21565b6110c88383612b2e565b505050565b5f80337f00000000000000000000000050562fe7e870420f5aae480b7f94eb4ace2fcd706001600160a01b0316146111185760405163ef7995db60e01b815260040160405180910390fd5b60408051606081018252610136546001600160601b03808216808452600160601b83049091166020840152600160c01b90910460ff1692820192909252908711156111765760405163101511b360e21b815260040160405180910390fd5b5f61119b8883602001516001600160601b0316845f01516001600160601b0316612bb3565b90506111f16001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48167f00000000000000000000000050562fe7e870420f5aae480b7f94eb4ace2fcd708a612be8565b305f908152609760205260408120546040516339a62c0160e21b81529091506001600160a01b037f00000000000000000000000050562fe7e870420f5aae480b7f94eb4ace2fcd70169063e698b00490611277907f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb489030908e908d908d906004016140a7565b6020604051808303815f875af1158015611293573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906112b791906140f7565b305f908152609760205260409020549095506112d4908290614053565b9050808511156112f75760405163ca1d512b60e01b815260040160405180910390fd5b81851015611318576040516312e2df8560e21b815260040160405180910390fd5b6113228286614053565b935061132e30836127ce565b82516001600160601b03908a900381168452602084015116821015611366576020830180518390036001600160601b0316905261136d565b5f60208401525b825161013680546020860151604087015160ff16600160c01b0260ff60c01b196001600160601b03928316600160601b026001600160c01b03199094169290951691909117919091179290921691909117905583156113d1576113d1308986612978565b604080518a8152602081018790529081018590527fd0b9355d279e4300b9775cdafb83758dd2135999d3842f80bcbad1607b6f432f9060600160405180910390a150505094509492505050565b5f611427612c4b565b905090565b6001600160a01b038116331461149c5760405162461bcd60e51b815260206004820152602f60248201527f416363657373436f6e74726f6c3a2063616e206f6e6c792072656e6f756e636560448201526e103937b632b9903337b91039b2b63360891b6064820152608401610b2f565b6114a68282612c54565b5050565b5f336108278185856114bc83836123b1565b6114c6919061410e565b612690565b826114d861013382612cba565b6114f557604051630a5af2b960e41b815260040160405180910390fd5b6114fd612164565b1561151b57604051635cbe463960e01b815260040160405180910390fd5b5f846001600160a01b031663c55dae636040518163ffffffff1660e01b8152600401602060405180830381865afa158015611558573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061157c9190614121565b905061158781612cdb565b611591815f612d04565b6001600160a01b03851663e162402f336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018790523060448201526064015f604051808303815f87803b1580156115ec575f80fd5b505af11580156115fe573d5f803e3d5ffd5b5050505061160d818486612eab565b6001600160a01b038316336001600160a01b0316826001600160a01b03167fbfa61fc27bb37f6f94529a9d0f81f1cc1a422648a9febe15aa90d13c33ed9ad78760405161165c91815260200190565b60405180910390a45050505050565b60405162461bcd60e51b81526020600482015260146024820152731b5a5b9d08185b990819585c9b881c185d5cd95960621b60448201525f90606401610b2f565b60405162461bcd60e51b815260206004820152600b60248201526a1b5a5b9d081c185d5cd95960aa1b60448201525f90606401610b2f565b337f000000000000000000000000250893ca4ba5d05626c785e8da758026928fcd246001600160a01b03161461172d57604051630a7c22bf60e11b815260040160405180910390fd5b6114a68282612edb565b60405162461bcd60e51b815260206004820152600b60248201526a19585c9b881c185d5cd95960aa1b6044820152606401610b2f565b5f54610100900460ff161580801561178b57505f54600160ff909116105b806117a45750303b1580156117a457505f5460ff166001145b6117c05760405162461bcd60e51b8152600401610b2f9061413c565b5f805460ff1916600117905580156117e1575f805461ff0019166101001790555b6117e9612f9a565b6117f1612f9a565b6117f9612f9a565b6118038383612fc2565b61180c83612ff2565b6118165f33612b2e565b80156110c8575f805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a1505050565b8261186981612cdb565b835f6118758282612d04565b61187d612164565b1561189b57604051635cbe463960e01b815260040160405180910390fd5b6001600160a01b038681165f9081526101306020526040902054166118c28133308961303b565b6118cd878688612eab565b6001600160a01b038516336001600160a01b0316886001600160a01b03167fbfa61fc27bb37f6f94529a9d0f81f1cc1a422648a9febe15aa90d13c33ed9ad78960405161191c91815260200190565b60405180910390a450505050505050565b5f54600290610100900460ff1615801561194d57505f5460ff8083169116105b6119695760405162461bcd60e51b8152600401610b2f9061413c565b5f805461ffff191660ff8316176101001790556040805163313ce56760e01b815290516001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48169163313ce5679160048083019260209291908290030181865afa1580156119df573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611a03919061418a565b610136805460ff92909216600160c01b0260ff60c01b19909216919091179055611a2c60995490565b610135555f805461ff001916905560405160ff821681527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150565b5f8085611a8261013382612cba565b611a9f57604051630a5af2b960e41b815260040160405180910390fd5b5f876001600160a01b031663c55dae636040518163ffffffff1660e01b8152600401602060405180830381865afa158015611adc573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b009190614121565b6001600160a01b038181165f9081526101306020526040908190206002810154905491516370a0823160e01b815230600482015293945082169291169081906370a0823190602401602060405180830381865afa158015611b63573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611b8791906140f7565b95506001600160a01b038a1663e162402f336040516001600160e01b031960e084901b1681526001600160a01b039091166004820152602481018c90523060448201526064015f604051808303815f87803b158015611be4575f80fd5b505af1158015611bf6573d5f803e3d5ffd5b50506040516370a0823160e01b81523060048201528892506001600160a01b03841691506370a0823190602401602060405180830381865afa158015611c3e573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611c6291906140f7565b611c6c9190614053565b6040516368674c7d60e11b8152600481018290526001600160a01b038a81166024830152604482018a90529197509083169063d0ce98fa9060640160408051808303815f875af1158015611cc2573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611ce69190614066565b909b909a5098505050505050505050565b6001600160a01b0381165f90815260fd6020526040812054610784565b5f6060805f805f606060c9545f801b148015611d30575060ca54155b611d745760405162461bcd60e51b81526020600482015260156024820152741152540dcc4c8e88155b9a5b9a5d1a585b1a5e9959605a1b6044820152606401610b2f565b611d7c613073565b611d84613082565b604080515f80825260208201909252600f60f81b9b939a50919850469750309650945092509050565b5f9182526065602090815260408084206001600160a01b0393909316845291905290205460ff1690565b6060609b805461079990613fa4565b337f000000000000000000000000250893ca4ba5d05626c785e8da758026928fcd246001600160a01b031614611e2f57604051630a7c22bf60e11b815260040160405180910390fd5b6114a682826127ce565b5f3381611e4682866123b1565b905083811015611ea65760405162461bcd60e51b815260206004820152602560248201527f45524332303a2064656372656173656420616c6c6f77616e63652062656c6f77604482015264207a65726f60d81b6064820152608401610b2f565b6110998286868403612690565b5f33610827818585612978565b7f600e5f1c60beb469a3fa6dd3814a4ae211cc6259a6d033bae218a742f2af01d3611eea81612b21565b83611ef481612cdb565b611efc612164565b15611f1a57604051635cbe463960e01b815260040160405180910390fd5b611f25853386613091565b6001600160a01b038086165f908152610130602052604090205416611f4b818587612be8565b6001600160a01b038416336001600160a01b0316876001600160a01b03167fe173a76508f1af24a0f4f1e0ea3192eb6bbb92d750ad178d4dffe6048d63248d88604051611f9a91815260200190565b60405180910390a4505050505050565b60605f611fb86101336127b3565b9050806001600160401b03811115611fd257611fd2613a2a565b604051908082528060200260200182016040528015611ffb578160200160208202803683370190505b5091505f5b8181101561204657612014610133826127bc565b83828151811061202657612026613fd6565b6001600160a01b0390921660209283029190910190910152600101612000565b505090565b5f806120586101316127b3565b610135549091505f81900361207757670de0b6b3a76400009250505090565b5f5b82811015612152575f61208e610131836127bc565b6001600160a01b038082165f9081526101306020908152604080832054815163c1590cd760e01b81529151959650909316939192849263c1590cd7926004808401939192918290030181865afa1580156120ea573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061210e91906140f7565b6001600160a01b0384165f9081526101306020526040902060040154909150612137908261401d565b612141908861410e565b965050600190920191506120799050565b5061215d8184614034565b9250505090565b5f806121716101316127b3565b90505f5b81811015612224575f61218a610131836127bc565b6001600160a01b038082165f9081526101306020908152604091829020600101548251633371b1f360e21b8152925194955090921692839263cdc6c7cc92600480820193918290030181865afa1580156121e6573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061220a9190613fea565b1561221a57600194505050505090565b5050600101612175565b505f91505090565b8342111561227c5760405162461bcd60e51b815260206004820152601d60248201527f45524332305065726d69743a206578706972656420646561646c696e650000006044820152606401610b2f565b5f7f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98888886122aa8c613104565b6040805160208101969096526001600160a01b0394851690860152929091166060840152608083015260a082015260c0810186905260e0016040516020818303038152906040528051906020012090505f6123048261312b565b90505f61231382878787613157565b9050896001600160a01b0316816001600160a01b0316146123765760405162461bcd60e51b815260206004820152601e60248201527f45524332305065726d69743a20696e76616c6964207369676e617475726500006044820152606401610b2f565b6123818a8a8a612690565b50505050505050505050565b5f828152606560205260409020600101546123a781612b21565b6110c88383612c54565b6001600160a01b039182165f90815260986020908152604080832093909416825291909152205490565b60605f6123e96101316127b3565b9050806001600160401b0381111561240357612403613a2a565b60405190808252806020026020018201604052801561242c578160200160208202803683370190505b5091505f5b8181101561204657612445610131826127bc565b83828151811061245757612457613fd6565b6001600160a01b0390921660209283029190910190910152600101612431565b5f808561248381612cdb565b61248b612164565b156124a957604051635cbe463960e01b815260040160405180910390fd5b6001600160a01b038781165f90815261013060205260408082206002810154905491516370a0823160e01b81523060048201529084169391909116919082906370a0823190602401602060405180830381865afa15801561250c573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061253091906140f7565b6040516368674c7d60e11b8152600481018b90526001600160a01b038a81166024830152604482018a90529192509084169063d0ce98fa9060640160408051808303815f875af1158015612586573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906125aa9190614066565b6040516370a0823160e01b815230600482015291975095506001600160a01b038316906370a0823190602401602060405180830381865afa1580156125f1573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061261591906140f7565b61261f9082614053565b985061262c8a338b613091565b6001600160a01b038816336001600160a01b03168b6001600160a01b03167fe173a76508f1af24a0f4f1e0ea3192eb6bbb92d750ad178d4dffe6048d63248d8c60405161267b91815260200190565b60405180910390a45050505094509492505050565b6001600160a01b0383166126f25760405162461bcd60e51b8152602060048201526024808201527f45524332303a20617070726f76652066726f6d20746865207a65726f206164646044820152637265737360e01b6064820152608401610b2f565b6001600160a01b0382166127535760405162461bcd60e51b815260206004820152602260248201527f45524332303a20617070726f766520746f20746865207a65726f206164647265604482015261737360f01b6064820152608401610b2f565b6001600160a01b038381165f8181526098602090815260408083209487168084529482529182902085905590518481527f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925910160405180910390a3505050565b5f610784825490565b5f6127c7838361317d565b9392505050565b6001600160a01b03821661282e5760405162461bcd60e51b815260206004820152602160248201527f45524332303a206275726e2066726f6d20746865207a65726f206164647265736044820152607360f81b6064820152608401610b2f565b6001600160a01b0382165f90815260976020526040902054818110156128a15760405162461bcd60e51b815260206004820152602260248201527f45524332303a206275726e20616d6f756e7420657863656564732062616c616e604482015261636560f01b6064820152608401610b2f565b6001600160a01b0383165f8181526097602090815260408083208686039055609980548790039055518581529192917fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a3505050565b5f61290b84846123b1565b90505f19811461297257818110156129655760405162461bcd60e51b815260206004820152601d60248201527f45524332303a20696e73756666696369656e7420616c6c6f77616e63650000006044820152606401610b2f565b6129728484848403612690565b50505050565b6001600160a01b0383166129dc5760405162461bcd60e51b815260206004820152602560248201527f45524332303a207472616e736665722066726f6d20746865207a65726f206164604482015264647265737360d81b6064820152608401610b2f565b6001600160a01b038216612a3e5760405162461bcd60e51b815260206004820152602360248201527f45524332303a207472616e7366657220746f20746865207a65726f206164647260448201526265737360e81b6064820152608401610b2f565b6001600160a01b0383165f9081526097602052604090205481811015612ab55760405162461bcd60e51b815260206004820152602660248201527f45524332303a207472616e7366657220616d6f756e7420657863656564732062604482015265616c616e636560d01b6064820152608401610b2f565b6001600160a01b038085165f8181526097602052604080822086860390559286168082529083902080548601905591517fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef90612b149086815260200190565b60405180910390a3612972565b612b2b81336131a3565b50565b612b388282611dad565b6114a6575f8281526065602090815260408083206001600160a01b03851684529091529020805460ff19166001179055612b6f3390565b6001600160a01b0316816001600160a01b0316837f2f8788117e7eff1d82e926ec794901d17c78024a50270940304540a733656f0d60405160405180910390a45050565b5f81600181612bc2868861401d565b612bcc919061410e565b612bd69190614053565b612be09190614034565b949350505050565b6040516001600160a01b0383166024820152604481018290526110c890849063a9059cbb60e01b906064015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b0319909316929092179091526131fc565b5f6114276132cf565b612c5e8282611dad565b156114a6575f8281526065602090815260408083206001600160a01b0385168085529252808320805460ff1916905551339285917ff6391f5c32d9c69d2a47ea670b442974b53935d1edc7fd64eb21e047a839171b9190a45050565b6001600160a01b0381165f90815260018301602052604081205415156127c7565b612ce761013182612cba565b612b2b576040516338453dd760e01b815260040160405180910390fd5b6001600160a01b038083165f9081526101306020526040902060010154168115612e2e575f816001600160a01b031663b4eae1cb6040518163ffffffff1660e01b8152600401602060405180830381865afa158015612d65573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612d8991906140f7565b6001600160a01b038086165f90815261013060209081526040808320600201548151630cf0b5b360e41b815291519596509294929093169263cf0b5b3092600480830193928290030181865afa158015612de5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e0991906140f7565b9050808211612e2b576040516319168f7960e21b815260040160405180910390fd5b50505b806001600160a01b0316639e3ad8716040518163ffffffff1660e01b8152600401602060405180830381865afa158015612e6a573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190612e8e9190613fea565b6110c857604051631d05894b60e31b815260040160405180910390fd5b6001600160a01b0383165f908152610130602052604090206004018054820190556101358054820190556110c882825b6001600160a01b038216612f315760405162461bcd60e51b815260206004820152601f60248201527f45524332303a206d696e7420746f20746865207a65726f2061646472657373006044820152606401610b2f565b8060995f828254612f42919061410e565b90915550506001600160a01b0382165f818152609760209081526040808320805486019055518481527fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef910160405180910390a35050565b5f54610100900460ff16612fc05760405162461bcd60e51b8152600401610b2f906141a5565b565b5f54610100900460ff16612fe85760405162461bcd60e51b8152600401610b2f906141a5565b6114a68282613342565b5f54610100900460ff166130185760405162461bcd60e51b8152600401610b2f906141a5565b612b2b81604051806040016040528060018152602001603160f81b815250613381565b6040516001600160a01b03808516602483015283166044820152606481018290526129729085906323b872dd60e01b90608401612c14565b606060cb805461079990613fa4565b606060cc805461079990613fa4565b6001600160a01b0383165f9081526101306020526040902060040154808211156130ce5760405163b268a92160e01b815260040160405180910390fd5b6001600160a01b0384165f90815261013060205260409020600401805483900390556101358054839003905561297283836127ce565b6001600160a01b0381165f90815260fd602052604090208054600181018255905b50919050565b5f610784613137612c4b565b8360405161190160f01b8152600281019290925260228201526042902090565b5f805f613166878787876133ce565b915091506131738161348b565b5095945050505050565b5f825f01828154811061319257613192613fd6565b905f5260205f200154905092915050565b6131ad8282611dad565b6114a6576131ba816135d4565b6131c58360206135e6565b6040516020016131d6929190614207565b60408051601f198184030181529082905262461bcd60e51b8252610b2f916004016139da565b5f613250826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b031661377b9092919063ffffffff16565b905080515f14806132705750808060200190518101906132709190613fea565b6110c85760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610b2f565b5f7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6132f9613789565b6133016137e1565b60408051602081019490945283019190915260608201524660808201523060a082015260c00160405160208183030381529060405280519060200120905090565b5f54610100900460ff166133685760405162461bcd60e51b8152600401610b2f906141a5565b609a61337483826142b0565b50609b6110c882826142b0565b5f54610100900460ff166133a75760405162461bcd60e51b8152600401610b2f906141a5565b60cb6133b383826142b0565b5060cc6133c082826142b0565b50505f60c981905560ca5550565b5f807f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a083111561340357505f90506003613482565b604080515f8082526020820180845289905260ff881692820192909252606081018690526080810185905260019060a0016020604051602081039080840390855afa158015613454573d5f803e3d5ffd5b5050604051601f1901519150506001600160a01b03811661347c575f60019250925050613482565b91505f90505b94509492505050565b5f81600481111561349e5761349e61436a565b036134a65750565b60018160048111156134ba576134ba61436a565b036135075760405162461bcd60e51b815260206004820152601860248201527f45434453413a20696e76616c6964207369676e617475726500000000000000006044820152606401610b2f565b600281600481111561351b5761351b61436a565b036135685760405162461bcd60e51b815260206004820152601f60248201527f45434453413a20696e76616c6964207369676e6174757265206c656e677468006044820152606401610b2f565b600381600481111561357c5761357c61436a565b03612b2b5760405162461bcd60e51b815260206004820152602260248201527f45434453413a20696e76616c6964207369676e6174757265202773272076616c604482015261756560f01b6064820152608401610b2f565b60606107846001600160a01b03831660145b60605f6135f483600261401d565b6135ff90600261410e565b6001600160401b0381111561361657613616613a2a565b6040519080825280601f01601f191660200182016040528015613640576020820181803683370190505b509050600360fc1b815f8151811061365a5761365a613fd6565b60200101906001600160f81b03191690815f1a905350600f60fb1b8160018151811061368857613688613fd6565b60200101906001600160f81b03191690815f1a9053505f6136aa84600261401d565b6136b590600161410e565b90505b600181111561372c576f181899199a1a9b1b9c1cb0b131b232b360811b85600f16601081106136e9576136e9613fd6565b1a60f81b8282815181106136ff576136ff613fd6565b60200101906001600160f81b03191690815f1a90535060049490941c936137258161437e565b90506136b8565b5083156127c75760405162461bcd60e51b815260206004820181905260248201527f537472696e67733a20686578206c656e67746820696e73756666696369656e746044820152606401610b2f565b6060612be084845f85613811565b5f80613793613073565b8051909150156137aa578051602090910120919050565b60c95480156137b95792915050565b7fc5d2460186f7233c927e7db2dcc703c0e500b653ca82273b7bfad8045d85a4709250505090565b5f806137eb613082565b805190915015613802578051602090910120919050565b60ca5480156137b95792915050565b6060824710156138725760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610b2f565b5f80866001600160a01b0316858760405161388d9190614393565b5f6040518083038185875af1925050503d805f81146138c7576040519150601f19603f3d011682016040523d82523d5f602084013e6138cc565b606091505b50915091506138dd878383876138e8565b979650505050505050565b606083156139565782515f0361394f576001600160a01b0385163b61394f5760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610b2f565b5081612be0565b612be0838381511561396b5781518083602001fd5b8060405162461bcd60e51b8152600401610b2f91906139da565b5f60208284031215613995575f80fd5b81356001600160e01b0319811681146127c7575f80fd5b5f81518084528060208401602086015e5f602082860101526020601f19601f83011685010191505092915050565b602081525f6127c760208301846139ac565b6001600160a01b0381168114612b2b575f80fd5b5f8060408385031215613a11575f80fd5b8235613a1c816139ec565b946020939093013593505050565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f191681016001600160401b0381118282101715613a6657613a66613a2a565b604052919050565b5f805f60608486031215613a80575f80fd5b833592506020840135613a92816139ec565b915060408401356001600160401b03811115613aac575f80fd5b8401601f81018613613abc575f80fd5b80356001600160401b03811115613ad557613ad5613a2a565b8060051b613ae560208201613a3e565b91825260208184018101929081019089841115613b00575f80fd5b6020850194505b83851015613b2657843580835260209586019590935090910190613b07565b80955050505050509250925092565b5f8151808452602084019350602083015f5b82811015613b6e5781516001600160a01b0316865260209586019590910190600101613b47565b5093949350505050565b5f8151808452602084019350602083015f5b82811015613b6e578151865260209586019590910190600101613b8a565b606081525f613bba6060830186613b35565b8281036020840152613bcc8186613b78565b90508281036040840152613be08185613b78565b9695505050505050565b5f8060408385031215613bfb575f80fd5b50508035926020909101359150565b5f805f60608486031215613c1c575f80fd5b8335613c27816139ec565b92506020840135613c37816139ec565b929592945050506040919091013590565b5f60208284031215613c58575f80fd5b5035919050565b5f8060408385031215613c70575f80fd5b823591506020830135613c82816139ec565b809150509250929050565b5f805f8060608587031215613ca0575f80fd5b843593506020850135613cb2816139ec565b925060408501356001600160401b03811115613ccc575f80fd5b8501601f81018713613cdc575f80fd5b80356001600160401b03811115613cf1575f80fd5b876020828401011115613d02575f80fd5b949793965060200194505050565b5f805f60608486031215613d22575f80fd5b8335613d2d816139ec565b9250602084013591506040840135613d44816139ec565b809150509250925092565b5f805f8060808587031215613d62575f80fd5b8435613d6d816139ec565b9350602085013592506040850135613d84816139ec565b9396929550929360600135925050565b5f82601f830112613da3575f80fd5b81356001600160401b03811115613dbc57613dbc613a2a565b613dcf601f8201601f1916602001613a3e565b818152846020838601011115613de3575f80fd5b816020850160208301375f918101602001919091529392505050565b5f8060408385031215613e10575f80fd5b82356001600160401b03811115613e25575f80fd5b613e3185828601613d94565b92505060208301356001600160401b03811115613e4c575f80fd5b613e5885828601613d94565b9150509250929050565b5f60208284031215613e72575f80fd5b81356127c7816139ec565b60ff60f81b8816815260e060208201525f613e9b60e08301896139ac565b8281036040840152613ead81896139ac565b606084018890526001600160a01b038716608085015260a0840186905283810360c08501529050613ede8185613b78565b9a9950505050505050505050565b602081525f6127c76020830184613b35565b60ff81168114612b2b575f80fd5b5f805f805f805f60e0888a031215613f22575f80fd5b8735613f2d816139ec565b96506020880135613f3d816139ec565b955060408801359450606088013593506080880135613f5b81613efe565b9699959850939692959460a0840135945060c09093013592915050565b5f8060408385031215613f89575f80fd5b8235613f94816139ec565b91506020830135613c82816139ec565b600181811c90821680613fb857607f821691505b60208210810361312557634e487b7160e01b5f52602260045260245ffd5b634e487b7160e01b5f52603260045260245ffd5b5f60208284031215613ffa575f80fd5b815180151581146127c7575f80fd5b634e487b7160e01b5f52601160045260245ffd5b808202811582820484141761078457610784614009565b5f8261404e57634e487b7160e01b5f52601260045260245ffd5b500490565b8181038181111561078457610784614009565b5f8060408385031215614077575f80fd5b505080516020909101519092909150565b6001600160601b03818116838216019081111561078457610784614009565b6001600160a01b03868116825285166020820152604081018490526080606082018190528101829052818360a08301375f81830160a090810191909152601f909201601f19160101949350505050565b5f60208284031215614107575f80fd5b5051919050565b8082018082111561078457610784614009565b5f60208284031215614131575f80fd5b81516127c7816139ec565b6020808252602e908201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160408201526d191e481a5b9a5d1a585b1a5e995960921b606082015260800190565b5f6020828403121561419a575f80fd5b81516127c781613efe565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b5f81518060208401855e5f93019283525090919050565b7f416363657373436f6e74726f6c3a206163636f756e742000000000000000000081525f61423860178301856141f0565b7001034b99036b4b9b9b4b733903937b6329607d1b815261425c60118201856141f0565b95945050505050565b601f8211156110c857805f5260205f20601f840160051c8101602085101561428a5750805b601f840160051c820191505b818110156142a9575f8155600101614296565b5050505050565b81516001600160401b038111156142c9576142c9613a2a565b6142dd816142d78454613fa4565b84614265565b6020601f82116001811461430f575f83156142f85750848201515b5f19600385901b1c1916600184901b1784556142a9565b5f84815260208120601f198516915b8281101561433e578785015182556020948501946001909201910161431e565b508482101561435b57868401515f19600387901b60f8161c191681555b50505050600190811b01905550565b634e487b7160e01b5f52602160045260245ffd5b5f8161438c5761438c614009565b505f190190565b5f6127c782846141f056fea2646970667358221220617511e81f28c01052521a101b2ff57d3a4932f021d942061449acdf047f15d264736f6c634300081a0033
Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)
000000000000000000000000250893ca4ba5d05626c785e8da758026928fcd24000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4800000000000000000000000050562fe7e870420f5aae480b7f94eb4ace2fcd70
-----Decoded View---------------
Arg [0] : _poolManager (address): 0x250893CA4Ba5d05626C785e8da758026928FCD24
Arg [1] : _stableToken (address): 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48
Arg [2] : _pegKeeper (address): 0x50562fe7e870420F5AAe480B7F94EB4ace2fcd70
-----Encoded View---------------
3 Constructor Arguments found :
Arg [0] : 000000000000000000000000250893ca4ba5d05626c785e8da758026928fcd24
Arg [1] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [2] : 00000000000000000000000050562fe7e870420f5aae480b7f94eb4ace2fcd70
Loading...
Loading
Loading...
Loading
Multichain Portfolio | 30 Chains
Chain | Token | Portfolio % | Price | Amount | Value |
---|
Loading...
Loading
A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.