Sepolia Testnet

Contract

0xd3EC28AD6D777f5Aa92377294B9B6522C7193079

Overview

ETH Balance

0 ETH

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Swap Exact Token...57539752024-04-22 15:41:12293 days ago1713800472IN
0xd3EC28AD...2C7193079
2 wei0.000663211.51529505
Swap Exact Token...57534362024-04-22 13:52:00293 days ago1713793920IN
0xd3EC28AD...2C7193079
2 wei0.000656751.50058861
Swap Exact Token...57340682024-04-19 20:43:36296 days ago1713559416IN
0xd3EC28AD...2C7193079
2 wei0.00090922.07758061
Swap Exact Token...57106262024-04-16 12:19:36299 days ago1713269976IN
0xd3EC28AD...2C7193079
2 wei0.00044881.01406116
Swap Exact Token...57093392024-04-16 8:02:00299 days ago1713254520IN
0xd3EC28AD...2C7193079
2 wei0.06965959157.47512845
Swap Exact Token...57092882024-04-16 7:51:48299 days ago1713253908IN
0xd3EC28AD...2C7193079
2 wei0.0334072675.51660442
Swap Exact Token...57078872024-04-16 3:11:24299 days ago1713237084IN
0xd3EC28AD...2C7193079
2 wei0.000663951.50039035
Swap Exact Token...57078672024-04-16 3:07:24299 days ago1713236844IN
0xd3EC28AD...2C7193079
2 wei0.000663841.50047443
Swap Exact Token...57045762024-04-15 16:08:00300 days ago1713197280IN
0xd3EC28AD...2C7193079
2 wei0.001634343.55607838
Swap Exact Token...55786472024-03-28 13:57:24318 days ago1711634244IN
0xd3EC28AD...2C7193079
2 wei0.000931832.12925035
Swap Exact Token...55786112024-03-28 13:49:36318 days ago1711633776IN
0xd3EC28AD...2C7193079
2 wei0.000860121.9654293
Swap Exact Token...55785072024-03-28 13:27:36318 days ago1711632456IN
0xd3EC28AD...2C7193079
2 wei0.000890932.03599797
Swap Exact Token...55784982024-03-28 13:25:48318 days ago1711632348IN
0xd3EC28AD...2C7193079
2 wei0.000881412.01384464
Swap Exact Token...55728712024-03-27 17:28:00319 days ago1711560480IN
0xd3EC28AD...2C7193079
2 wei0.000866251.97932125
Swap Exact Token...55728642024-03-27 17:26:36319 days ago1711560396IN
0xd3EC28AD...2C7193079
2 wei0.000856861.95803106
Swap Exact Token...55728392024-03-27 17:21:00319 days ago1711560060IN
0xd3EC28AD...2C7193079
2 wei0.00082311.88056556
Swap Exact Token...55728142024-03-27 17:15:48319 days ago1711559748IN
0xd3EC28AD...2C7193079
2 wei0.00088232.01613517
Swap Exact Token...55728052024-03-27 17:14:00319 days ago1711559640IN
0xd3EC28AD...2C7193079
2 wei0.000917862.09768017
Swap Exact Token...55565292024-03-25 7:44:24321 days ago1711352664IN
0xd3EC28AD...2C7193079
2 wei0.000820071.87271398
Swap Exact Token...55565242024-03-25 7:43:24321 days ago1711352604IN
0xd3EC28AD...2C7193079
2 wei0.000842911.83386812
Swap Exact Token...55556162024-03-25 4:31:48321 days ago1711341108IN
0xd3EC28AD...2C7193079
2 wei0.000695911.61417654
Swap Exact Token...55446152024-03-23 12:29:24323 days ago1711196964IN
0xd3EC28AD...2C7193079
2 wei0.000656361.50000002
Swap Exact Token...55445862024-03-23 12:23:00323 days ago1711196580IN
0xd3EC28AD...2C7193079
2 wei0.000656511.50000001
Swap Exact Token...55445702024-03-23 12:19:48323 days ago1711196388IN
0xd3EC28AD...2C7193079
2 wei0.000663721.50000002
Swap Exact Token...55325182024-03-21 17:42:24325 days ago1711042944IN
0xd3EC28AD...2C7193079
2 wei0.0321498873.44565963
View all transactions

Latest 25 internal transactions (View All)

Advanced mode:
Parent Transaction Hash Block
From
To
57539752024-04-22 15:41:12293 days ago1713800472
0xd3EC28AD...2C7193079
2 wei
57534362024-04-22 13:52:00293 days ago1713793920
0xd3EC28AD...2C7193079
2 wei
57340682024-04-19 20:43:36296 days ago1713559416
0xd3EC28AD...2C7193079
2 wei
57106262024-04-16 12:19:36299 days ago1713269976
0xd3EC28AD...2C7193079
2 wei
57093392024-04-16 8:02:00299 days ago1713254520
0xd3EC28AD...2C7193079
2 wei
57092882024-04-16 7:51:48299 days ago1713253908
0xd3EC28AD...2C7193079
2 wei
57078872024-04-16 3:11:24299 days ago1713237084
0xd3EC28AD...2C7193079
2 wei
57078672024-04-16 3:07:24299 days ago1713236844
0xd3EC28AD...2C7193079
2 wei
57045762024-04-15 16:08:00300 days ago1713197280
0xd3EC28AD...2C7193079
2 wei
55786472024-03-28 13:57:24318 days ago1711634244
0xd3EC28AD...2C7193079
2 wei
55786112024-03-28 13:49:36318 days ago1711633776
0xd3EC28AD...2C7193079
2 wei
55785072024-03-28 13:27:36318 days ago1711632456
0xd3EC28AD...2C7193079
2 wei
55784982024-03-28 13:25:48318 days ago1711632348
0xd3EC28AD...2C7193079
2 wei
55728712024-03-27 17:28:00319 days ago1711560480
0xd3EC28AD...2C7193079
2 wei
55728642024-03-27 17:26:36319 days ago1711560396
0xd3EC28AD...2C7193079
2 wei
55728392024-03-27 17:21:00319 days ago1711560060
0xd3EC28AD...2C7193079
2 wei
55728142024-03-27 17:15:48319 days ago1711559748
0xd3EC28AD...2C7193079
2 wei
55728052024-03-27 17:14:00319 days ago1711559640
0xd3EC28AD...2C7193079
2 wei
55565292024-03-25 7:44:24321 days ago1711352664
0xd3EC28AD...2C7193079
2 wei
55565242024-03-25 7:43:24321 days ago1711352604
0xd3EC28AD...2C7193079
2 wei
55556162024-03-25 4:31:48321 days ago1711341108
0xd3EC28AD...2C7193079
2 wei
55446152024-03-23 12:29:24323 days ago1711196964
0xd3EC28AD...2C7193079
2 wei
55445862024-03-23 12:23:00323 days ago1711196580
0xd3EC28AD...2C7193079
2 wei
55445702024-03-23 12:19:48323 days ago1711196388
0xd3EC28AD...2C7193079
2 wei
55325182024-03-21 17:42:24325 days ago1711042944
0xd3EC28AD...2C7193079
2 wei
View All Internal Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Router

Compiler Version
v0.8.13+commit.abaa5c0e

Optimization Enabled:
Yes with 200 runs

Other Settings:
default evmVersion, Unlicense license

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 44 : Router.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {
    ReentrancyGuard
} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {SafeERC20} from "contracts/@chain/ethereum/token/SafeERC20.sol";
import {IPythAdapter} from "../interfaces/IPythAdapter.sol";
import {ISwapPoolPermissioned} from "../interfaces/ISwapPool.sol";
import "../interfaces/IRouter.sol";

contract Router is Pausable, ReentrancyGuard, Ownable, IRouterPermissioned {
    using SafeERC20 for IERC20;

    IPythAdapter public pythAdapter;

    mapping(address => ISwapPoolPermissioned) public poolByAsset;

    /**
     * @notice Router constructor
     * @param _pythAdapter PythAdapter address
     * @dev Sets the PythAdapter address used for updating price data and fetching prices (price oracle)
     */
    constructor(address _pythAdapter) {
        require(_pythAdapter != address(0), "NO_ADAPTER");

        pythAdapter = IPythAdapter(_pythAdapter);
    }

    /**
     * @notice Sets the address to the new PythAdapter contract
     * @param _newPythAdapter New PythAdapter address
     * @param success Confirmation of success
     */
    function setPythAdapter(
        address _newPythAdapter
    ) external onlyOwner returns (bool success) {
        require(_newPythAdapter != address(0), "NO_ADAPTER");
        require(_newPythAdapter != address(pythAdapter), "SAME_ADAPTER");

        pythAdapter = IPythAdapter(_newPythAdapter);

        emit PythAdapterSet(msg.sender, _newPythAdapter);
        return true;
    }

    /**
     * @notice Registers a newly created swap pool
     * @param _asset ERC-20 token address
     * @param _swapPool Swap pool address
     * @return success Confirmation of success
     */
    function registerPool(
        address _asset,
        address _swapPool
    ) external onlyOwner returns (bool success) {
        require(_asset != address(0), "NO_ASSET");
        require(_swapPool != address(0), "NO_POOL");
        require(pythAdapter.isPriceFeedRegistered(_asset), "NO_PRICE_FEED");

        poolByAsset[_asset] = ISwapPoolPermissioned(_swapPool);
        IERC20(_asset).approve(_swapPool, 2 ** 256 - 1);

        emit SwapPoolRegistered(msg.sender, _swapPool, _asset);
        return true;
    }

    /**
     * @notice Unregisters a swap pool
     * @param _asset ERC-20 token address
     * @return success Confirmation of success
     */
    function unregisterPool(
        address _asset
    ) external onlyOwner returns (bool success) {
        require(_asset != address(0), "NO_ASSET");
        require(address(poolByAsset[_asset]) != address(0), "NO_POOL");

        delete poolByAsset[_asset];

        emit SwapPoolUnregistered(msg.sender, _asset);
        return true;
    }

    /**
     * @notice Disable all swaps
     */
    function pause() external onlyOwner {
        _pause();
    }

    /**
     * @notice Resume all swaps
     */
    function unpause() external onlyOwner {
        _unpause();
    }

    /**
     * @notice Swap some `_fromToken` tokens for `_toToken` tokens,
     *         ensures `_amountOutMin` and `_deadline`, sends funds to `_to` address
     * @notice `msg.sender` needs to grant the chef contract a sufficient allowance beforehand
     * @param _amountIn         The amount of input tokens to swap
     * @param _amountOutMin     The minimum amount that the user will accept
     * @param _tokenInOut       Array of size two, indicating the in and out token
     * @param _to               The recipient of the output tokens
     * @param _deadline         Unix timestamp after which the transaction will revert
     * @param _priceUpdateData  Array of price update data
     * @return _amounts     Array of size two, containing the input and output amount
     * @dev By calling this function the price feed gets be updated (IPythAdapter.updatePriceFeeds)
     */
    function swapExactTokensForTokens(
        uint256 _amountIn,
        uint256 _amountOutMin,
        address[] calldata _tokenInOut,
        address _to,
        uint _deadline,
        bytes[] calldata _priceUpdateData
    ) external payable whenNotPaused returns (uint256[] memory _amounts) {
        require(block.timestamp <= _deadline, "ROUTER: EXPIRED");

        // Update the price feeds
        _updatePriceFeeds(_priceUpdateData);

        uint256 _amountOut = _swapExactTokensForTokens(
            _amountIn,
            _tokenInOut,
            _to
        );
        require(_amountOut >= _amountOutMin, "ROUTER: BELOW_MINIMUM");

        _amounts = new uint256[](2);
        _amounts[0] = _amountIn;
        _amounts[1] = _amountOut;

        emit Swap(
            msg.sender,
            _amountIn,
            _amountOut,
            _tokenInOut[0],
            _tokenInOut[1],
            _to
        );
    }

    function _swapExactTokensForTokens(
        uint256 _amountIn,
        address[] calldata _tokenInOut,
        address _to
    ) internal returns (uint256 _amountOut) {
        require(_tokenInOut.length == 2, "ROUTER: TOKEN_ARRAY_SIZE");
        require(
            _tokenInOut[0] != _tokenInOut[1],
            "ROUTER: TOKEN_ARRAY_DUPLICATE"
        );

        address _fromToken = _tokenInOut[0];
        address _toToken = _tokenInOut[1];

        require(
            address(poolByAsset[_fromToken]) != address(0) &&
                address(poolByAsset[_toToken]) != address(0),
            "ROUTER: ASSET_NOT_REGISTERED"
        );

        uint256 _tokenPriceFrom = pythAdapter.getAssetPrice(_fromToken);
        uint256 _tokenPriceTo = pythAdapter.getAssetPrice(_toToken);

        // send user funds
        IERC20(_fromToken).safeTransferFrom(
            msg.sender,
            address(this),
            _amountIn
        );

        // explicit block scoping to prevent "stack too deep" error when reading `_amountIn`
        {
            // user funds into swap pool
            uint256 _effectiveAmountIn = poolByAsset[_fromToken]
                .swapIntoFromRouter(_amountIn);

            uint256 _rawOutAmount = _convertAmount(
                _effectiveAmountIn,
                _tokenPriceFrom,
                _tokenPriceTo,
                poolByAsset[_fromToken].assetDecimals(),
                poolByAsset[_toToken].assetDecimals()
            );

            // send funds to user
            _amountOut = poolByAsset[_toToken].swapOutFromRouter(_rawOutAmount);
        }

        IERC20(_toToken).safeTransfer(_to, _amountOut);
    }

    /**
     * @notice Get a quote for how many `_toToken` tokens `_amountIn` many `tokenIn`
     *         tokens can currently be swapped for.
     * @param _amountIn     The amount of input tokens to swap
     * @param _tokenInOut   Array of size two, indicating the in and out token
     * @param _tokenPrices  Array of size two, indicating the in and out token prices fetched off-chain from Pyth oracle
     * @return _amountOut   Number of `_toToken` tokens that such a swap would yield right now
     * @return _swapFee     The fee that is charged for the swap (in `_toToken` tokens)
     */
    function getAmountOut(
        uint _amountIn,
        address[] calldata _tokenInOut,
        uint256[] calldata _tokenPrices
    ) external view returns (uint256 _amountOut, uint256 _swapFee) {
        require(_tokenInOut.length == 2, "ROUTER: TOKEN_ARRAY_SIZE");
        require(
            _tokenInOut[0] != _tokenInOut[1],
            "ROUTER: TOKEN_ARRAY_DUPLICATE"
        );
        require(_tokenPrices.length == 2, "ROUTER: TOKEN_PRICE_ARRAY_SIZE");

        address _fromToken = _tokenInOut[0];
        address _toToken = _tokenInOut[1];

        require(
            address(poolByAsset[_fromToken]) != address(0) &&
                address(poolByAsset[_toToken]) != address(0),
            "ROUTER: ASSET_NOT_REGISTERED"
        );

        uint256 _tokenPriceFrom = _tokenPrices[0];
        uint256 _tokenPriceTo = _tokenPrices[1];

        // user funds into swap pool
        uint256 _effectiveAmountIn = poolByAsset[_fromToken].quoteSwapInto(
            _amountIn
        );

        uint256 _rawOutAmount = _convertAmount(
            _effectiveAmountIn,
            _tokenPriceFrom,
            _tokenPriceTo,
            poolByAsset[_fromToken].assetDecimals(),
            poolByAsset[_toToken].assetDecimals()
        );

        uint256 _protocolFeeWithSlippage;
        uint256 _effectiveLpFee;
        uint256 _backstopFee;

        (
            _amountOut,
            _protocolFeeWithSlippage,
            _effectiveLpFee,
            _backstopFee
        ) = poolByAsset[_toToken].quoteSwapOut(_rawOutAmount);

        _swapFee = _protocolFeeWithSlippage + _effectiveLpFee + _backstopFee;
    }

    /**
     * @notice Price convert amounts of tokens
     * @notice The two involved tokens are called "from token" and "to token"
     * @notice Allows that tokens have different numbers of decimals
     * @param _fromAmount The amount of the from token
     * @param _fromPrice The price of the from token (in terms of a reference asset)
     * @param _toPrice The price of the to token (in terms of a reference asset)
     * @param _fromDecimals The number of decimals of the from token
     * @param _toDecimals The number of decimals of the to token
     * @return _toAmount The equivalent amount of to tokens
     */
    function _convertAmount(
        uint256 _fromAmount,
        uint256 _fromPrice,
        uint256 _toPrice,
        uint8 _fromDecimals,
        uint8 _toDecimals
    ) internal pure returns (uint256 _toAmount) {
        if (_fromDecimals > _toDecimals) {
            _toAmount =
                (_fromAmount * _fromPrice) /
                _toPrice /
                (10 ** uint256(_fromDecimals - _toDecimals));
        } else {
            _toAmount =
                (_fromAmount *
                    _fromPrice *
                    (10 ** uint256(_toDecimals - _fromDecimals))) /
                _toPrice;
        }
    }

    /**
     * @notice Update the price feed for an array of assets
     * @param _priceUpdateData Array of price update data
     */
    function _updatePriceFeeds(bytes[] calldata _priceUpdateData) internal {
        pythAdapter.updatePriceFeeds{value: msg.value}(_priceUpdateData);
    }
}

File 2 of 44 : AccessControl.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (access/AccessControl.sol)

pragma solidity ^0.8.0;

import "./IAccessControl.sol";
import "../utils/Context.sol";
import "../utils/Strings.sol";
import "../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 => 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);
        _;
    }

    /**
     * @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 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 ",
                        Strings.toHexString(account),
                        " is missing role ",
                        Strings.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());
        }
    }
}

File 3 of 44 : IAccessControl.sol
// 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 IAccessControl {
    /**
     * @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;
}

File 4 of 44 : Ownable.sol
// 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);
    }
}

File 5 of 44 : Pausable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (security/Pausable.sol)

pragma solidity ^0.8.0;

import "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    bool private _paused;

    /**
     * @dev Initializes the contract in unpaused state.
     */
    constructor() {
        _paused = false;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        require(!paused(), "Pausable: paused");
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        require(paused(), "Pausable: not paused");
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}

File 6 of 44 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant _NOT_ENTERED = 1;
    uint256 private constant _ENTERED = 2;

    uint256 private _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be _NOT_ENTERED
        require(_status != _ENTERED, "ReentrancyGuard: reentrant call");

        // Any calls to nonReentrant after this point will fail
        _status = _ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == _ENTERED;
    }
}

File 7 of 44 : ERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/ERC20.sol)

pragma solidity ^0.8.0;

import "./IERC20.sol";
import "./extensions/IERC20Metadata.sol";
import "../../utils/Context.sol";

/**
 * @dev Implementation of the {IERC20} interface.
 *
 * This implementation is agnostic to the way tokens are created. This means
 * that a supply mechanism has to be added in a derived contract using {_mint}.
 * For a generic mechanism see {ERC20PresetMinterPauser}.
 *
 * TIP: For a detailed writeup see our guide
 * https://forum.openzeppelin.com/t/how-to-implement-erc20-supply-mechanisms/226[How
 * to implement supply mechanisms].
 *
 * The default value of {decimals} is 18. To change this, you should override
 * this function so it returns a different value.
 *
 * We have followed general OpenZeppelin Contracts guidelines: functions revert
 * instead returning `false` on failure. This behavior is nonetheless
 * conventional and does not conflict with the expectations of ERC20
 * applications.
 *
 * Additionally, an {Approval} event is emitted on calls to {transferFrom}.
 * This allows applications to reconstruct the allowance for all accounts just
 * by listening to said events. Other implementations of the EIP may not emit
 * these events, as it isn't required by the specification.
 *
 * Finally, the non-standard {decreaseAllowance} and {increaseAllowance}
 * functions have been added to mitigate the well-known issues around setting
 * allowances. See {IERC20-approve}.
 */
contract ERC20 is Context, IERC20, IERC20Metadata {
    mapping(address => uint256) private _balances;

    mapping(address => mapping(address => uint256)) private _allowances;

    uint256 private _totalSupply;

    string private _name;
    string private _symbol;

    /**
     * @dev Sets the values for {name} and {symbol}.
     *
     * All two of these values are immutable: they can only be set once during
     * construction.
     */
    constructor(string memory name_, string memory symbol_) {
        _name = name_;
        _symbol = symbol_;
    }

    /**
     * @dev Returns the name of the token.
     */
    function name() public view virtual override returns (string memory) {
        return _name;
    }

    /**
     * @dev Returns the symbol of the token, usually a shorter version of the
     * name.
     */
    function symbol() public view virtual override returns (string memory) {
        return _symbol;
    }

    /**
     * @dev Returns the number of decimals used to get its user representation.
     * For example, if `decimals` equals `2`, a balance of `505` tokens should
     * be displayed to a user as `5.05` (`505 / 10 ** 2`).
     *
     * Tokens usually opt for a value of 18, imitating the relationship between
     * Ether and Wei. This is the default value returned by this function, unless
     * it's overridden.
     *
     * NOTE: This information is only used for _display_ purposes: it in
     * no way affects any of the arithmetic of the contract, including
     * {IERC20-balanceOf} and {IERC20-transfer}.
     */
    function decimals() public view virtual override returns (uint8) {
        return 18;
    }

    /**
     * @dev See {IERC20-totalSupply}.
     */
    function totalSupply() public view virtual override returns (uint256) {
        return _totalSupply;
    }

    /**
     * @dev See {IERC20-balanceOf}.
     */
    function balanceOf(address account) public view virtual override returns (uint256) {
        return _balances[account];
    }

    /**
     * @dev See {IERC20-transfer}.
     *
     * Requirements:
     *
     * - `to` cannot be the zero address.
     * - the caller must have a balance of at least `amount`.
     */
    function transfer(address to, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _transfer(owner, to, amount);
        return true;
    }

    /**
     * @dev See {IERC20-allowance}.
     */
    function allowance(address owner, address spender) public view virtual override returns (uint256) {
        return _allowances[owner][spender];
    }

    /**
     * @dev See {IERC20-approve}.
     *
     * NOTE: If `amount` is the maximum `uint256`, the allowance is not updated on
     * `transferFrom`. This is semantically equivalent to an infinite approval.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function approve(address spender, uint256 amount) public virtual override returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, amount);
        return true;
    }

    /**
     * @dev See {IERC20-transferFrom}.
     *
     * Emits an {Approval} event indicating the updated allowance. This is not
     * required by the EIP. See the note at the beginning of {ERC20}.
     *
     * NOTE: Does not update the allowance if the current allowance
     * is the maximum `uint256`.
     *
     * Requirements:
     *
     * - `from` and `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     * - the caller must have allowance for ``from``'s tokens of at least
     * `amount`.
     */
    function transferFrom(address from, address to, uint256 amount) public virtual override returns (bool) {
        address spender = _msgSender();
        _spendAllowance(from, spender, amount);
        _transfer(from, to, amount);
        return true;
    }

    /**
     * @dev Atomically increases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     */
    function increaseAllowance(address spender, uint256 addedValue) public virtual returns (bool) {
        address owner = _msgSender();
        _approve(owner, spender, allowance(owner, spender) + addedValue);
        return true;
    }

    /**
     * @dev Atomically decreases the allowance granted to `spender` by the caller.
     *
     * This is an alternative to {approve} that can be used as a mitigation for
     * problems described in {IERC20-approve}.
     *
     * Emits an {Approval} event indicating the updated allowance.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `spender` must have allowance for the caller of at least
     * `subtractedValue`.
     */
    function decreaseAllowance(address spender, uint256 subtractedValue) public virtual returns (bool) {
        address owner = _msgSender();
        uint256 currentAllowance = allowance(owner, spender);
        require(currentAllowance >= subtractedValue, "ERC20: decreased allowance below zero");
        unchecked {
            _approve(owner, spender, currentAllowance - subtractedValue);
        }

        return true;
    }

    /**
     * @dev Moves `amount` of tokens from `from` to `to`.
     *
     * This internal function is equivalent to {transfer}, and can be used to
     * e.g. implement automatic token fees, slashing mechanisms, etc.
     *
     * Emits a {Transfer} event.
     *
     * Requirements:
     *
     * - `from` cannot be the zero address.
     * - `to` cannot be the zero address.
     * - `from` must have a balance of at least `amount`.
     */
    function _transfer(address from, address to, uint256 amount) internal virtual {
        require(from != address(0), "ERC20: transfer from the zero address");
        require(to != address(0), "ERC20: transfer to the zero address");

        _beforeTokenTransfer(from, to, amount);

        uint256 fromBalance = _balances[from];
        require(fromBalance >= amount, "ERC20: transfer amount exceeds balance");
        unchecked {
            _balances[from] = fromBalance - amount;
            // Overflow not possible: the sum of all balances is capped by totalSupply, and the sum is preserved by
            // decrementing then incrementing.
            _balances[to] += amount;
        }

        emit Transfer(from, to, amount);

        _afterTokenTransfer(from, to, amount);
    }

    /** @dev Creates `amount` tokens and assigns them to `account`, increasing
     * the total supply.
     *
     * Emits a {Transfer} event with `from` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     */
    function _mint(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: mint to the zero address");

        _beforeTokenTransfer(address(0), account, amount);

        _totalSupply += amount;
        unchecked {
            // Overflow not possible: balance + amount is at most totalSupply + amount, which is checked above.
            _balances[account] += amount;
        }
        emit Transfer(address(0), account, amount);

        _afterTokenTransfer(address(0), account, amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, reducing the
     * total supply.
     *
     * Emits a {Transfer} event with `to` set to the zero address.
     *
     * Requirements:
     *
     * - `account` cannot be the zero address.
     * - `account` must have at least `amount` tokens.
     */
    function _burn(address account, uint256 amount) internal virtual {
        require(account != address(0), "ERC20: burn from the zero address");

        _beforeTokenTransfer(account, address(0), amount);

        uint256 accountBalance = _balances[account];
        require(accountBalance >= amount, "ERC20: burn amount exceeds balance");
        unchecked {
            _balances[account] = accountBalance - amount;
            // Overflow not possible: amount <= accountBalance <= totalSupply.
            _totalSupply -= amount;
        }

        emit Transfer(account, address(0), amount);

        _afterTokenTransfer(account, address(0), amount);
    }

    /**
     * @dev Sets `amount` as the allowance of `spender` over the `owner` s tokens.
     *
     * This internal function is equivalent to `approve`, and can be used to
     * e.g. set automatic allowances for certain subsystems, etc.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `owner` cannot be the zero address.
     * - `spender` cannot be the zero address.
     */
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        require(owner != address(0), "ERC20: approve from the zero address");
        require(spender != address(0), "ERC20: approve to the zero address");

        _allowances[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    /**
     * @dev Updates `owner` s allowance for `spender` based on spent `amount`.
     *
     * Does not update the allowance amount in case of infinite allowance.
     * Revert if not enough allowance is available.
     *
     * Might emit an {Approval} event.
     */
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        uint256 currentAllowance = allowance(owner, spender);
        if (currentAllowance != type(uint256).max) {
            require(currentAllowance >= amount, "ERC20: insufficient allowance");
            unchecked {
                _approve(owner, spender, currentAllowance - amount);
            }
        }
    }

    /**
     * @dev Hook that is called before any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * will be transferred to `to`.
     * - when `from` is zero, `amount` tokens will be minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens will be burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /**
     * @dev Hook that is called after any transfer of tokens. This includes
     * minting and burning.
     *
     * Calling conditions:
     *
     * - when `from` and `to` are both non-zero, `amount` of ``from``'s tokens
     * has been transferred to `to`.
     * - when `from` is zero, `amount` tokens have been minted for `to`.
     * - when `to` is zero, `amount` of ``from``'s tokens have been burned.
     * - `from` and `to` are never both zero.
     *
     * To learn more about hooks, head to xref:ROOT:extending-contracts.adoc#using-hooks[Using Hooks].
     */
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}

File 8 of 44 : ERC20Burnable.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.5.0) (token/ERC20/extensions/ERC20Burnable.sol)

pragma solidity ^0.8.0;

import "../ERC20.sol";
import "../../../utils/Context.sol";

/**
 * @dev Extension of {ERC20} that allows token holders to destroy both their own
 * tokens and those that they have an allowance for, in a way that can be
 * recognized off-chain (via event analysis).
 */
abstract contract ERC20Burnable is Context, ERC20 {
    /**
     * @dev Destroys `amount` tokens from the caller.
     *
     * See {ERC20-_burn}.
     */
    function burn(uint256 amount) public virtual {
        _burn(_msgSender(), amount);
    }

    /**
     * @dev Destroys `amount` tokens from `account`, deducting from the caller's
     * allowance.
     *
     * See {ERC20-_burn} and {ERC20-allowance}.
     *
     * Requirements:
     *
     * - the caller must have allowance for ``accounts``'s tokens of at least
     * `amount`.
     */
    function burnFrom(address account, uint256 amount) public virtual {
        _spendAllowance(account, _msgSender(), amount);
        _burn(account, amount);
    }
}

File 9 of 44 : IERC20Metadata.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";

/**
 * @dev Interface for the optional metadata functions from the ERC20 standard.
 *
 * _Available since v4.1._
 */
interface IERC20Metadata is IERC20 {
    /**
     * @dev Returns the name of the token.
     */
    function name() external view returns (string memory);

    /**
     * @dev Returns the symbol of the token.
     */
    function symbol() external view returns (string memory);

    /**
     * @dev Returns the decimals places of the token.
     */
    function decimals() external view returns (uint8);
}

File 10 of 44 : IERC20Permit.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

    /**
     * @dev Returns the current nonce for `owner`. This value must be
     * included whenever a signature is generated for {permit}.
     *
     * Every successful call to {permit} increases ``owner``'s nonce by one. This
     * prevents a signature from being used multiple times.
     */
    function nonces(address owner) external view returns (uint256);

    /**
     * @dev Returns the domain separator used in the encoding of the signature for {permit}, as defined by {EIP712}.
     */
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view returns (bytes32);
}

File 11 of 44 : IERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the amount of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves `amount` tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 amount) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 amount) external returns (bool);

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
}

File 12 of 44 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.3) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20.sol";
import "../extensions/IERC20Permit.sol";
import "../../../utils/Address.sol";

/**
 * @title SafeERC20
 * @dev Wrappers around ERC20 operations that throw on failure (when the token
 * contract returns false). Tokens that return no value (and instead revert or
 * throw on failure) are also supported, non-reverting calls are assumed to be
 * successful.
 * To use this library you can add a `using SafeERC20 for IERC20;` statement to your contract,
 * which allows you to call the safe operations as `token.safeTransfer(...)`, etc.
 */
library SafeERC20 {
    using Address for address;

    /**
     * @dev Transfer `value` amount of `token` from the calling contract to `to`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeTransfer(IERC20 token, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    /**
     * @dev Transfer `value` amount of `token` from `from` to `to`, spending the approval given by `from` to the
     * calling contract. If `token` returns no value, non-reverting calls are assumed to be successful.
     */
    function safeTransferFrom(IERC20 token, address from, address to, uint256 value) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

    /**
     * @dev Deprecated. This function has issues similar to the ones found in
     * {IERC20-approve}, and its usage is discouraged.
     *
     * Whenever possible, use {safeIncreaseAllowance} and
     * {safeDecreaseAllowance} instead.
     */
    function safeApprove(IERC20 token, address spender, uint256 value) internal {
        // safeApprove should only be called when setting an initial allowance,
        // or when resetting it to zero. To increase and decrease it, use
        // 'safeIncreaseAllowance' and 'safeDecreaseAllowance'
        require(
            (value == 0) || (token.allowance(address(this), spender) == 0),
            "SafeERC20: approve from non-zero to non-zero allowance"
        );
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, value));
    }

    /**
     * @dev Increase the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeIncreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        uint256 oldAllowance = token.allowance(address(this), spender);
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance + value));
    }

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 value) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, oldAllowance - value));
        }
    }

    /**
     * @dev Set the calling contract's allowance toward `spender` to `value`. If `token` returns no value,
     * non-reverting calls are assumed to be successful. Meant to be used with tokens that require the approval
     * to be set to zero before setting it to a non-zero value, such as USDT.
     */
    function forceApprove(IERC20 token, address spender, uint256 value) internal {
        bytes memory approvalCall = abi.encodeWithSelector(token.approve.selector, spender, value);

        if (!_callOptionalReturnBool(token, approvalCall)) {
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, 0));
            _callOptionalReturn(token, approvalCall);
        }
    }

    /**
     * @dev Use a ERC-2612 signature to set the `owner` approval toward `spender` on `token`.
     * Revert on invalid signature.
     */
    function safePermit(
        IERC20Permit token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     */
    function _callOptionalReturn(IERC20 token, bytes memory data) private {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We use {Address-functionCall} to perform this call, which verifies that
        // the target address contains contract code and also asserts for success in the low-level call.

        bytes memory returndata = address(token).functionCall(data, "SafeERC20: low-level call failed");
        require(returndata.length == 0 || abi.decode(returndata, (bool)), "SafeERC20: ERC20 operation did not succeed");
    }

    /**
     * @dev Imitates a Solidity high-level call (i.e. a regular function call to a contract), relaxing the requirement
     * on the return value: the return value is optional (but if data is returned, it must not be false).
     * @param token The token targeted by the call.
     * @param data The call data (encoded using abi.encode or one of its variants).
     *
     * This is a variant of {_callOptionalReturn} that silents catches all reverts and returns a bool instead.
     */
    function _callOptionalReturnBool(IERC20 token, bytes memory data) private returns (bool) {
        // We need to perform a low level call here, to bypass Solidity's return data size checking mechanism, since
        // we're implementing it ourselves. We cannot use {Address-functionCall} here since this should return false
        // and not revert is the subcall reverts.

        (bool success, bytes memory returndata) = address(token).call(data);
        return
            success && (returndata.length == 0 || abi.decode(returndata, (bool))) && Address.isContract(address(token));
    }
}

File 13 of 44 : Address.sol
// 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);
        }
    }
}

File 14 of 44 : Context.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/Context.sol)

pragma solidity ^0.8.0;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }
}

File 15 of 44 : ERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/ERC165.sol)

pragma solidity ^0.8.0;

import "./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);
 * }
 * ```
 *
 * Alternatively, {ERC165Storage} provides an easier to use but more expensive implementation.
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

File 16 of 44 : IERC165.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (utils/introspection/IERC165.sol)

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC165 standard, as defined in the
 * https://eips.ethereum.org/EIPS/eip-165[EIP].
 *
 * Implementers can declare support of contract interfaces, which can then be
 * queried by others ({ERC165Checker}).
 *
 * For an implementation, see {ERC165}.
 */
interface IERC165 {
    /**
     * @dev Returns true if this contract implements the interface defined by
     * `interfaceId`. See the corresponding
     * https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified[EIP section]
     * to learn more about how these ids are created.
     *
     * This function call must use less than 30 000 gas.
     */
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

File 17 of 44 : Math.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    enum Rounding {
        Down, // Toward negative infinity
        Up, // Toward infinity
        Zero // Toward zero
    }

    /**
     * @dev Returns the largest of two numbers.
     */
    function max(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two numbers.
     */
    function min(uint256 a, uint256 b) internal pure returns (uint256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two numbers. The result is rounded towards
     * zero.
     */
    function average(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b) / 2 can overflow.
        return (a & b) + (a ^ b) / 2;
    }

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds up instead
     * of rounding down.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

    /**
     * @notice Calculates floor(x * y / denominator) with full precision. Throws if result overflows a uint256 or denominator == 0
     * @dev Original credit to Remco Bloemen under MIT license (https://xn--2-umb.com/21/muldiv)
     * with further edits by Uniswap Labs also under MIT license.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator) internal pure returns (uint256 result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
            // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2^256 + prod0.
            uint256 prod0; // Least significant 256 bits of the product
            uint256 prod1; // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(x, y, not(0))
                prod0 := mul(x, y)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                // Solidity will revert if denominator == 0, unlike the div opcode on its own.
                // The surrounding unchecked block does not change this fact.
                // See https://docs.soliditylang.org/en/latest/control-structures.html#checked-or-unchecked-arithmetic.
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1, "Math: mulDiv overflow");

            ///////////////////////////////////////////////
            // 512 by 256 division.
            ///////////////////////////////////////////////

            // Make division exact by subtracting the remainder from [prod1 prod0].
            uint256 remainder;
            assembly {
                // Compute remainder using mulmod.
                remainder := mulmod(x, y, denominator)

                // Subtract 256 bit number from 512 bit number.
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
            // See https://cs.stackexchange.com/q/138556/92363.

            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 twos = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

                // Divide [prod1 prod0] by twos.
                prod0 := div(prod0, twos)

                // Flip twos such that it is 2^256 / twos. If twos is zero, then it becomes one.
                twos := add(div(sub(0, twos), twos), 1)
            }

            // Shift in bits from prod1 into prod0.
            prod0 |= prod1 * twos;

            // Invert denominator mod 2^256. Now that denominator is an odd number, it has an inverse modulo 2^256 such
            // that denominator * inv = 1 mod 2^256. Compute the inverse by starting with a seed that is correct for
            // four bits. That is, denominator * inv = 1 mod 2^4.
            uint256 inverse = (3 * denominator) ^ 2;

            // Use the Newton-Raphson iteration to improve the precision. Thanks to Hensel's lifting lemma, this also works
            // in modular arithmetic, doubling the correct bits in each step.
            inverse *= 2 - denominator * inverse; // inverse mod 2^8
            inverse *= 2 - denominator * inverse; // inverse mod 2^16
            inverse *= 2 - denominator * inverse; // inverse mod 2^32
            inverse *= 2 - denominator * inverse; // inverse mod 2^64
            inverse *= 2 - denominator * inverse; // inverse mod 2^128
            inverse *= 2 - denominator * inverse; // inverse mod 2^256

            // Because the division is now exact we can divide by multiplying with the modular inverse of denominator.
            // This will give us the correct result modulo 2^256. Since the preconditions guarantee that the outcome is
            // less than 2^256, this is the final result. We don't need to compute the high bits of the result and prod1
            // is no longer required.
            result = prod0 * inverse;
            return result;
        }
    }

    /**
     * @notice Calculates x * y / denominator with full precision, following the selected rounding direction.
     */
    function mulDiv(uint256 x, uint256 y, uint256 denominator, Rounding rounding) internal pure returns (uint256) {
        uint256 result = mulDiv(x, y, denominator);
        if (rounding == Rounding.Up && mulmod(x, y, denominator) > 0) {
            result += 1;
        }
        return result;
    }

    /**
     * @dev Returns the square root of a number. If the number is not a perfect square, the value is rounded down.
     *
     * Inspired by Henry S. Warren, Jr.'s "Hacker's Delight" (Chapter 11).
     */
    function sqrt(uint256 a) internal pure returns (uint256) {
        if (a == 0) {
            return 0;
        }

        // For our first guess, we get the biggest power of 2 which is smaller than the square root of the target.
        //
        // We know that the "msb" (most significant bit) of our target number `a` is a power of 2 such that we have
        // `msb(a) <= a < 2*msb(a)`. This value can be written `msb(a)=2**k` with `k=log2(a)`.
        //
        // This can be rewritten `2**log2(a) <= a < 2**(log2(a) + 1)`
        // → `sqrt(2**k) <= sqrt(a) < sqrt(2**(k+1))`
        // → `2**(k/2) <= sqrt(a) < 2**((k+1)/2) <= 2**(k/2 + 1)`
        //
        // Consequently, `2**(log2(a) / 2)` is a good first approximation of `sqrt(a)` with at least 1 correct bit.
        uint256 result = 1 << (log2(a) >> 1);

        // At this point `result` is an estimation with one bit of precision. We know the true value is a uint128,
        // since it is the square root of a uint256. Newton's method converges quadratically (precision doubles at
        // every iteration). We thus need at most 7 iteration to turn our partial result with one bit of precision
        // into the expected uint128 result.
        unchecked {
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            result = (result + a / result) >> 1;
            return min(result, a / result);
        }
    }

    /**
     * @notice Calculates sqrt(a), following the selected rounding direction.
     */
    function sqrt(uint256 a, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = sqrt(a);
            return result + (rounding == Rounding.Up && result * result < a ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 2, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 128;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 64;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 32;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 16;
            }
            if (value >> 8 > 0) {
                value >>= 8;
                result += 8;
            }
            if (value >> 4 > 0) {
                value >>= 4;
                result += 4;
            }
            if (value >> 2 > 0) {
                value >>= 2;
                result += 2;
            }
            if (value >> 1 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 2, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log2(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log2(value);
            return result + (rounding == Rounding.Up && 1 << result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 10, rounded down, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >= 10 ** 64) {
                value /= 10 ** 64;
                result += 64;
            }
            if (value >= 10 ** 32) {
                value /= 10 ** 32;
                result += 32;
            }
            if (value >= 10 ** 16) {
                value /= 10 ** 16;
                result += 16;
            }
            if (value >= 10 ** 8) {
                value /= 10 ** 8;
                result += 8;
            }
            if (value >= 10 ** 4) {
                value /= 10 ** 4;
                result += 4;
            }
            if (value >= 10 ** 2) {
                value /= 10 ** 2;
                result += 2;
            }
            if (value >= 10 ** 1) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 10, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log10(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log10(value);
            return result + (rounding == Rounding.Up && 10 ** result < value ? 1 : 0);
        }
    }

    /**
     * @dev Return the log in base 256, rounded down, of a positive value.
     * Returns 0 if given 0.
     *
     * Adding one to the result gives the number of pairs of hex symbols needed to represent `value` as a hex string.
     */
    function log256(uint256 value) internal pure returns (uint256) {
        uint256 result = 0;
        unchecked {
            if (value >> 128 > 0) {
                value >>= 128;
                result += 16;
            }
            if (value >> 64 > 0) {
                value >>= 64;
                result += 8;
            }
            if (value >> 32 > 0) {
                value >>= 32;
                result += 4;
            }
            if (value >> 16 > 0) {
                value >>= 16;
                result += 2;
            }
            if (value >> 8 > 0) {
                result += 1;
            }
        }
        return result;
    }

    /**
     * @dev Return the log in base 256, following the selected rounding direction, of a positive value.
     * Returns 0 if given 0.
     */
    function log256(uint256 value, Rounding rounding) internal pure returns (uint256) {
        unchecked {
            uint256 result = log256(value);
            return result + (rounding == Rounding.Up && 1 << (result << 3) < value ? 1 : 0);
        }
    }
}

File 18 of 44 : SafeCast.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SafeCast.sol)
// This file was procedurally generated from scripts/generate/templates/SafeCast.js.

pragma solidity ^0.8.0;

/**
 * @dev Wrappers over Solidity's uintXX/intXX casting operators with added overflow
 * checks.
 *
 * Downcasting from uint256/int256 in Solidity does not revert on overflow. This can
 * easily result in undesired exploitation or bugs, since developers usually
 * assume that overflows raise errors. `SafeCast` restores this intuition by
 * reverting the transaction when such an operation overflows.
 *
 * Using this library instead of the unchecked operations eliminates an entire
 * class of bugs, so it's recommended to use it always.
 *
 * Can be combined with {SafeMath} and {SignedSafeMath} to extend it to smaller types, by performing
 * all math on `uint256` and `int256` and then downcasting.
 */
library SafeCast {
    /**
     * @dev Returns the downcasted uint248 from uint256, reverting on
     * overflow (when the input is greater than largest uint248).
     *
     * Counterpart to Solidity's `uint248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toUint248(uint256 value) internal pure returns (uint248) {
        require(value <= type(uint248).max, "SafeCast: value doesn't fit in 248 bits");
        return uint248(value);
    }

    /**
     * @dev Returns the downcasted uint240 from uint256, reverting on
     * overflow (when the input is greater than largest uint240).
     *
     * Counterpart to Solidity's `uint240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toUint240(uint256 value) internal pure returns (uint240) {
        require(value <= type(uint240).max, "SafeCast: value doesn't fit in 240 bits");
        return uint240(value);
    }

    /**
     * @dev Returns the downcasted uint232 from uint256, reverting on
     * overflow (when the input is greater than largest uint232).
     *
     * Counterpart to Solidity's `uint232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toUint232(uint256 value) internal pure returns (uint232) {
        require(value <= type(uint232).max, "SafeCast: value doesn't fit in 232 bits");
        return uint232(value);
    }

    /**
     * @dev Returns the downcasted uint224 from uint256, reverting on
     * overflow (when the input is greater than largest uint224).
     *
     * Counterpart to Solidity's `uint224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.2._
     */
    function toUint224(uint256 value) internal pure returns (uint224) {
        require(value <= type(uint224).max, "SafeCast: value doesn't fit in 224 bits");
        return uint224(value);
    }

    /**
     * @dev Returns the downcasted uint216 from uint256, reverting on
     * overflow (when the input is greater than largest uint216).
     *
     * Counterpart to Solidity's `uint216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toUint216(uint256 value) internal pure returns (uint216) {
        require(value <= type(uint216).max, "SafeCast: value doesn't fit in 216 bits");
        return uint216(value);
    }

    /**
     * @dev Returns the downcasted uint208 from uint256, reverting on
     * overflow (when the input is greater than largest uint208).
     *
     * Counterpart to Solidity's `uint208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toUint208(uint256 value) internal pure returns (uint208) {
        require(value <= type(uint208).max, "SafeCast: value doesn't fit in 208 bits");
        return uint208(value);
    }

    /**
     * @dev Returns the downcasted uint200 from uint256, reverting on
     * overflow (when the input is greater than largest uint200).
     *
     * Counterpart to Solidity's `uint200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toUint200(uint256 value) internal pure returns (uint200) {
        require(value <= type(uint200).max, "SafeCast: value doesn't fit in 200 bits");
        return uint200(value);
    }

    /**
     * @dev Returns the downcasted uint192 from uint256, reverting on
     * overflow (when the input is greater than largest uint192).
     *
     * Counterpart to Solidity's `uint192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toUint192(uint256 value) internal pure returns (uint192) {
        require(value <= type(uint192).max, "SafeCast: value doesn't fit in 192 bits");
        return uint192(value);
    }

    /**
     * @dev Returns the downcasted uint184 from uint256, reverting on
     * overflow (when the input is greater than largest uint184).
     *
     * Counterpart to Solidity's `uint184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toUint184(uint256 value) internal pure returns (uint184) {
        require(value <= type(uint184).max, "SafeCast: value doesn't fit in 184 bits");
        return uint184(value);
    }

    /**
     * @dev Returns the downcasted uint176 from uint256, reverting on
     * overflow (when the input is greater than largest uint176).
     *
     * Counterpart to Solidity's `uint176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toUint176(uint256 value) internal pure returns (uint176) {
        require(value <= type(uint176).max, "SafeCast: value doesn't fit in 176 bits");
        return uint176(value);
    }

    /**
     * @dev Returns the downcasted uint168 from uint256, reverting on
     * overflow (when the input is greater than largest uint168).
     *
     * Counterpart to Solidity's `uint168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toUint168(uint256 value) internal pure returns (uint168) {
        require(value <= type(uint168).max, "SafeCast: value doesn't fit in 168 bits");
        return uint168(value);
    }

    /**
     * @dev Returns the downcasted uint160 from uint256, reverting on
     * overflow (when the input is greater than largest uint160).
     *
     * Counterpart to Solidity's `uint160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toUint160(uint256 value) internal pure returns (uint160) {
        require(value <= type(uint160).max, "SafeCast: value doesn't fit in 160 bits");
        return uint160(value);
    }

    /**
     * @dev Returns the downcasted uint152 from uint256, reverting on
     * overflow (when the input is greater than largest uint152).
     *
     * Counterpart to Solidity's `uint152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toUint152(uint256 value) internal pure returns (uint152) {
        require(value <= type(uint152).max, "SafeCast: value doesn't fit in 152 bits");
        return uint152(value);
    }

    /**
     * @dev Returns the downcasted uint144 from uint256, reverting on
     * overflow (when the input is greater than largest uint144).
     *
     * Counterpart to Solidity's `uint144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toUint144(uint256 value) internal pure returns (uint144) {
        require(value <= type(uint144).max, "SafeCast: value doesn't fit in 144 bits");
        return uint144(value);
    }

    /**
     * @dev Returns the downcasted uint136 from uint256, reverting on
     * overflow (when the input is greater than largest uint136).
     *
     * Counterpart to Solidity's `uint136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toUint136(uint256 value) internal pure returns (uint136) {
        require(value <= type(uint136).max, "SafeCast: value doesn't fit in 136 bits");
        return uint136(value);
    }

    /**
     * @dev Returns the downcasted uint128 from uint256, reverting on
     * overflow (when the input is greater than largest uint128).
     *
     * Counterpart to Solidity's `uint128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v2.5._
     */
    function toUint128(uint256 value) internal pure returns (uint128) {
        require(value <= type(uint128).max, "SafeCast: value doesn't fit in 128 bits");
        return uint128(value);
    }

    /**
     * @dev Returns the downcasted uint120 from uint256, reverting on
     * overflow (when the input is greater than largest uint120).
     *
     * Counterpart to Solidity's `uint120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toUint120(uint256 value) internal pure returns (uint120) {
        require(value <= type(uint120).max, "SafeCast: value doesn't fit in 120 bits");
        return uint120(value);
    }

    /**
     * @dev Returns the downcasted uint112 from uint256, reverting on
     * overflow (when the input is greater than largest uint112).
     *
     * Counterpart to Solidity's `uint112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toUint112(uint256 value) internal pure returns (uint112) {
        require(value <= type(uint112).max, "SafeCast: value doesn't fit in 112 bits");
        return uint112(value);
    }

    /**
     * @dev Returns the downcasted uint104 from uint256, reverting on
     * overflow (when the input is greater than largest uint104).
     *
     * Counterpart to Solidity's `uint104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toUint104(uint256 value) internal pure returns (uint104) {
        require(value <= type(uint104).max, "SafeCast: value doesn't fit in 104 bits");
        return uint104(value);
    }

    /**
     * @dev Returns the downcasted uint96 from uint256, reverting on
     * overflow (when the input is greater than largest uint96).
     *
     * Counterpart to Solidity's `uint96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.2._
     */
    function toUint96(uint256 value) internal pure returns (uint96) {
        require(value <= type(uint96).max, "SafeCast: value doesn't fit in 96 bits");
        return uint96(value);
    }

    /**
     * @dev Returns the downcasted uint88 from uint256, reverting on
     * overflow (when the input is greater than largest uint88).
     *
     * Counterpart to Solidity's `uint88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toUint88(uint256 value) internal pure returns (uint88) {
        require(value <= type(uint88).max, "SafeCast: value doesn't fit in 88 bits");
        return uint88(value);
    }

    /**
     * @dev Returns the downcasted uint80 from uint256, reverting on
     * overflow (when the input is greater than largest uint80).
     *
     * Counterpart to Solidity's `uint80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toUint80(uint256 value) internal pure returns (uint80) {
        require(value <= type(uint80).max, "SafeCast: value doesn't fit in 80 bits");
        return uint80(value);
    }

    /**
     * @dev Returns the downcasted uint72 from uint256, reverting on
     * overflow (when the input is greater than largest uint72).
     *
     * Counterpart to Solidity's `uint72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toUint72(uint256 value) internal pure returns (uint72) {
        require(value <= type(uint72).max, "SafeCast: value doesn't fit in 72 bits");
        return uint72(value);
    }

    /**
     * @dev Returns the downcasted uint64 from uint256, reverting on
     * overflow (when the input is greater than largest uint64).
     *
     * Counterpart to Solidity's `uint64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v2.5._
     */
    function toUint64(uint256 value) internal pure returns (uint64) {
        require(value <= type(uint64).max, "SafeCast: value doesn't fit in 64 bits");
        return uint64(value);
    }

    /**
     * @dev Returns the downcasted uint56 from uint256, reverting on
     * overflow (when the input is greater than largest uint56).
     *
     * Counterpart to Solidity's `uint56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toUint56(uint256 value) internal pure returns (uint56) {
        require(value <= type(uint56).max, "SafeCast: value doesn't fit in 56 bits");
        return uint56(value);
    }

    /**
     * @dev Returns the downcasted uint48 from uint256, reverting on
     * overflow (when the input is greater than largest uint48).
     *
     * Counterpart to Solidity's `uint48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toUint48(uint256 value) internal pure returns (uint48) {
        require(value <= type(uint48).max, "SafeCast: value doesn't fit in 48 bits");
        return uint48(value);
    }

    /**
     * @dev Returns the downcasted uint40 from uint256, reverting on
     * overflow (when the input is greater than largest uint40).
     *
     * Counterpart to Solidity's `uint40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toUint40(uint256 value) internal pure returns (uint40) {
        require(value <= type(uint40).max, "SafeCast: value doesn't fit in 40 bits");
        return uint40(value);
    }

    /**
     * @dev Returns the downcasted uint32 from uint256, reverting on
     * overflow (when the input is greater than largest uint32).
     *
     * Counterpart to Solidity's `uint32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v2.5._
     */
    function toUint32(uint256 value) internal pure returns (uint32) {
        require(value <= type(uint32).max, "SafeCast: value doesn't fit in 32 bits");
        return uint32(value);
    }

    /**
     * @dev Returns the downcasted uint24 from uint256, reverting on
     * overflow (when the input is greater than largest uint24).
     *
     * Counterpart to Solidity's `uint24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toUint24(uint256 value) internal pure returns (uint24) {
        require(value <= type(uint24).max, "SafeCast: value doesn't fit in 24 bits");
        return uint24(value);
    }

    /**
     * @dev Returns the downcasted uint16 from uint256, reverting on
     * overflow (when the input is greater than largest uint16).
     *
     * Counterpart to Solidity's `uint16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v2.5._
     */
    function toUint16(uint256 value) internal pure returns (uint16) {
        require(value <= type(uint16).max, "SafeCast: value doesn't fit in 16 bits");
        return uint16(value);
    }

    /**
     * @dev Returns the downcasted uint8 from uint256, reverting on
     * overflow (when the input is greater than largest uint8).
     *
     * Counterpart to Solidity's `uint8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v2.5._
     */
    function toUint8(uint256 value) internal pure returns (uint8) {
        require(value <= type(uint8).max, "SafeCast: value doesn't fit in 8 bits");
        return uint8(value);
    }

    /**
     * @dev Converts a signed int256 into an unsigned uint256.
     *
     * Requirements:
     *
     * - input must be greater than or equal to 0.
     *
     * _Available since v3.0._
     */
    function toUint256(int256 value) internal pure returns (uint256) {
        require(value >= 0, "SafeCast: value must be positive");
        return uint256(value);
    }

    /**
     * @dev Returns the downcasted int248 from int256, reverting on
     * overflow (when the input is less than smallest int248 or
     * greater than largest int248).
     *
     * Counterpart to Solidity's `int248` operator.
     *
     * Requirements:
     *
     * - input must fit into 248 bits
     *
     * _Available since v4.7._
     */
    function toInt248(int256 value) internal pure returns (int248 downcasted) {
        downcasted = int248(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 248 bits");
    }

    /**
     * @dev Returns the downcasted int240 from int256, reverting on
     * overflow (when the input is less than smallest int240 or
     * greater than largest int240).
     *
     * Counterpart to Solidity's `int240` operator.
     *
     * Requirements:
     *
     * - input must fit into 240 bits
     *
     * _Available since v4.7._
     */
    function toInt240(int256 value) internal pure returns (int240 downcasted) {
        downcasted = int240(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 240 bits");
    }

    /**
     * @dev Returns the downcasted int232 from int256, reverting on
     * overflow (when the input is less than smallest int232 or
     * greater than largest int232).
     *
     * Counterpart to Solidity's `int232` operator.
     *
     * Requirements:
     *
     * - input must fit into 232 bits
     *
     * _Available since v4.7._
     */
    function toInt232(int256 value) internal pure returns (int232 downcasted) {
        downcasted = int232(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 232 bits");
    }

    /**
     * @dev Returns the downcasted int224 from int256, reverting on
     * overflow (when the input is less than smallest int224 or
     * greater than largest int224).
     *
     * Counterpart to Solidity's `int224` operator.
     *
     * Requirements:
     *
     * - input must fit into 224 bits
     *
     * _Available since v4.7._
     */
    function toInt224(int256 value) internal pure returns (int224 downcasted) {
        downcasted = int224(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 224 bits");
    }

    /**
     * @dev Returns the downcasted int216 from int256, reverting on
     * overflow (when the input is less than smallest int216 or
     * greater than largest int216).
     *
     * Counterpart to Solidity's `int216` operator.
     *
     * Requirements:
     *
     * - input must fit into 216 bits
     *
     * _Available since v4.7._
     */
    function toInt216(int256 value) internal pure returns (int216 downcasted) {
        downcasted = int216(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 216 bits");
    }

    /**
     * @dev Returns the downcasted int208 from int256, reverting on
     * overflow (when the input is less than smallest int208 or
     * greater than largest int208).
     *
     * Counterpart to Solidity's `int208` operator.
     *
     * Requirements:
     *
     * - input must fit into 208 bits
     *
     * _Available since v4.7._
     */
    function toInt208(int256 value) internal pure returns (int208 downcasted) {
        downcasted = int208(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 208 bits");
    }

    /**
     * @dev Returns the downcasted int200 from int256, reverting on
     * overflow (when the input is less than smallest int200 or
     * greater than largest int200).
     *
     * Counterpart to Solidity's `int200` operator.
     *
     * Requirements:
     *
     * - input must fit into 200 bits
     *
     * _Available since v4.7._
     */
    function toInt200(int256 value) internal pure returns (int200 downcasted) {
        downcasted = int200(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 200 bits");
    }

    /**
     * @dev Returns the downcasted int192 from int256, reverting on
     * overflow (when the input is less than smallest int192 or
     * greater than largest int192).
     *
     * Counterpart to Solidity's `int192` operator.
     *
     * Requirements:
     *
     * - input must fit into 192 bits
     *
     * _Available since v4.7._
     */
    function toInt192(int256 value) internal pure returns (int192 downcasted) {
        downcasted = int192(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 192 bits");
    }

    /**
     * @dev Returns the downcasted int184 from int256, reverting on
     * overflow (when the input is less than smallest int184 or
     * greater than largest int184).
     *
     * Counterpart to Solidity's `int184` operator.
     *
     * Requirements:
     *
     * - input must fit into 184 bits
     *
     * _Available since v4.7._
     */
    function toInt184(int256 value) internal pure returns (int184 downcasted) {
        downcasted = int184(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 184 bits");
    }

    /**
     * @dev Returns the downcasted int176 from int256, reverting on
     * overflow (when the input is less than smallest int176 or
     * greater than largest int176).
     *
     * Counterpart to Solidity's `int176` operator.
     *
     * Requirements:
     *
     * - input must fit into 176 bits
     *
     * _Available since v4.7._
     */
    function toInt176(int256 value) internal pure returns (int176 downcasted) {
        downcasted = int176(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 176 bits");
    }

    /**
     * @dev Returns the downcasted int168 from int256, reverting on
     * overflow (when the input is less than smallest int168 or
     * greater than largest int168).
     *
     * Counterpart to Solidity's `int168` operator.
     *
     * Requirements:
     *
     * - input must fit into 168 bits
     *
     * _Available since v4.7._
     */
    function toInt168(int256 value) internal pure returns (int168 downcasted) {
        downcasted = int168(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 168 bits");
    }

    /**
     * @dev Returns the downcasted int160 from int256, reverting on
     * overflow (when the input is less than smallest int160 or
     * greater than largest int160).
     *
     * Counterpart to Solidity's `int160` operator.
     *
     * Requirements:
     *
     * - input must fit into 160 bits
     *
     * _Available since v4.7._
     */
    function toInt160(int256 value) internal pure returns (int160 downcasted) {
        downcasted = int160(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 160 bits");
    }

    /**
     * @dev Returns the downcasted int152 from int256, reverting on
     * overflow (when the input is less than smallest int152 or
     * greater than largest int152).
     *
     * Counterpart to Solidity's `int152` operator.
     *
     * Requirements:
     *
     * - input must fit into 152 bits
     *
     * _Available since v4.7._
     */
    function toInt152(int256 value) internal pure returns (int152 downcasted) {
        downcasted = int152(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 152 bits");
    }

    /**
     * @dev Returns the downcasted int144 from int256, reverting on
     * overflow (when the input is less than smallest int144 or
     * greater than largest int144).
     *
     * Counterpart to Solidity's `int144` operator.
     *
     * Requirements:
     *
     * - input must fit into 144 bits
     *
     * _Available since v4.7._
     */
    function toInt144(int256 value) internal pure returns (int144 downcasted) {
        downcasted = int144(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 144 bits");
    }

    /**
     * @dev Returns the downcasted int136 from int256, reverting on
     * overflow (when the input is less than smallest int136 or
     * greater than largest int136).
     *
     * Counterpart to Solidity's `int136` operator.
     *
     * Requirements:
     *
     * - input must fit into 136 bits
     *
     * _Available since v4.7._
     */
    function toInt136(int256 value) internal pure returns (int136 downcasted) {
        downcasted = int136(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 136 bits");
    }

    /**
     * @dev Returns the downcasted int128 from int256, reverting on
     * overflow (when the input is less than smallest int128 or
     * greater than largest int128).
     *
     * Counterpart to Solidity's `int128` operator.
     *
     * Requirements:
     *
     * - input must fit into 128 bits
     *
     * _Available since v3.1._
     */
    function toInt128(int256 value) internal pure returns (int128 downcasted) {
        downcasted = int128(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 128 bits");
    }

    /**
     * @dev Returns the downcasted int120 from int256, reverting on
     * overflow (when the input is less than smallest int120 or
     * greater than largest int120).
     *
     * Counterpart to Solidity's `int120` operator.
     *
     * Requirements:
     *
     * - input must fit into 120 bits
     *
     * _Available since v4.7._
     */
    function toInt120(int256 value) internal pure returns (int120 downcasted) {
        downcasted = int120(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 120 bits");
    }

    /**
     * @dev Returns the downcasted int112 from int256, reverting on
     * overflow (when the input is less than smallest int112 or
     * greater than largest int112).
     *
     * Counterpart to Solidity's `int112` operator.
     *
     * Requirements:
     *
     * - input must fit into 112 bits
     *
     * _Available since v4.7._
     */
    function toInt112(int256 value) internal pure returns (int112 downcasted) {
        downcasted = int112(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 112 bits");
    }

    /**
     * @dev Returns the downcasted int104 from int256, reverting on
     * overflow (when the input is less than smallest int104 or
     * greater than largest int104).
     *
     * Counterpart to Solidity's `int104` operator.
     *
     * Requirements:
     *
     * - input must fit into 104 bits
     *
     * _Available since v4.7._
     */
    function toInt104(int256 value) internal pure returns (int104 downcasted) {
        downcasted = int104(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 104 bits");
    }

    /**
     * @dev Returns the downcasted int96 from int256, reverting on
     * overflow (when the input is less than smallest int96 or
     * greater than largest int96).
     *
     * Counterpart to Solidity's `int96` operator.
     *
     * Requirements:
     *
     * - input must fit into 96 bits
     *
     * _Available since v4.7._
     */
    function toInt96(int256 value) internal pure returns (int96 downcasted) {
        downcasted = int96(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 96 bits");
    }

    /**
     * @dev Returns the downcasted int88 from int256, reverting on
     * overflow (when the input is less than smallest int88 or
     * greater than largest int88).
     *
     * Counterpart to Solidity's `int88` operator.
     *
     * Requirements:
     *
     * - input must fit into 88 bits
     *
     * _Available since v4.7._
     */
    function toInt88(int256 value) internal pure returns (int88 downcasted) {
        downcasted = int88(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 88 bits");
    }

    /**
     * @dev Returns the downcasted int80 from int256, reverting on
     * overflow (when the input is less than smallest int80 or
     * greater than largest int80).
     *
     * Counterpart to Solidity's `int80` operator.
     *
     * Requirements:
     *
     * - input must fit into 80 bits
     *
     * _Available since v4.7._
     */
    function toInt80(int256 value) internal pure returns (int80 downcasted) {
        downcasted = int80(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 80 bits");
    }

    /**
     * @dev Returns the downcasted int72 from int256, reverting on
     * overflow (when the input is less than smallest int72 or
     * greater than largest int72).
     *
     * Counterpart to Solidity's `int72` operator.
     *
     * Requirements:
     *
     * - input must fit into 72 bits
     *
     * _Available since v4.7._
     */
    function toInt72(int256 value) internal pure returns (int72 downcasted) {
        downcasted = int72(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 72 bits");
    }

    /**
     * @dev Returns the downcasted int64 from int256, reverting on
     * overflow (when the input is less than smallest int64 or
     * greater than largest int64).
     *
     * Counterpart to Solidity's `int64` operator.
     *
     * Requirements:
     *
     * - input must fit into 64 bits
     *
     * _Available since v3.1._
     */
    function toInt64(int256 value) internal pure returns (int64 downcasted) {
        downcasted = int64(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 64 bits");
    }

    /**
     * @dev Returns the downcasted int56 from int256, reverting on
     * overflow (when the input is less than smallest int56 or
     * greater than largest int56).
     *
     * Counterpart to Solidity's `int56` operator.
     *
     * Requirements:
     *
     * - input must fit into 56 bits
     *
     * _Available since v4.7._
     */
    function toInt56(int256 value) internal pure returns (int56 downcasted) {
        downcasted = int56(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 56 bits");
    }

    /**
     * @dev Returns the downcasted int48 from int256, reverting on
     * overflow (when the input is less than smallest int48 or
     * greater than largest int48).
     *
     * Counterpart to Solidity's `int48` operator.
     *
     * Requirements:
     *
     * - input must fit into 48 bits
     *
     * _Available since v4.7._
     */
    function toInt48(int256 value) internal pure returns (int48 downcasted) {
        downcasted = int48(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 48 bits");
    }

    /**
     * @dev Returns the downcasted int40 from int256, reverting on
     * overflow (when the input is less than smallest int40 or
     * greater than largest int40).
     *
     * Counterpart to Solidity's `int40` operator.
     *
     * Requirements:
     *
     * - input must fit into 40 bits
     *
     * _Available since v4.7._
     */
    function toInt40(int256 value) internal pure returns (int40 downcasted) {
        downcasted = int40(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 40 bits");
    }

    /**
     * @dev Returns the downcasted int32 from int256, reverting on
     * overflow (when the input is less than smallest int32 or
     * greater than largest int32).
     *
     * Counterpart to Solidity's `int32` operator.
     *
     * Requirements:
     *
     * - input must fit into 32 bits
     *
     * _Available since v3.1._
     */
    function toInt32(int256 value) internal pure returns (int32 downcasted) {
        downcasted = int32(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 32 bits");
    }

    /**
     * @dev Returns the downcasted int24 from int256, reverting on
     * overflow (when the input is less than smallest int24 or
     * greater than largest int24).
     *
     * Counterpart to Solidity's `int24` operator.
     *
     * Requirements:
     *
     * - input must fit into 24 bits
     *
     * _Available since v4.7._
     */
    function toInt24(int256 value) internal pure returns (int24 downcasted) {
        downcasted = int24(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 24 bits");
    }

    /**
     * @dev Returns the downcasted int16 from int256, reverting on
     * overflow (when the input is less than smallest int16 or
     * greater than largest int16).
     *
     * Counterpart to Solidity's `int16` operator.
     *
     * Requirements:
     *
     * - input must fit into 16 bits
     *
     * _Available since v3.1._
     */
    function toInt16(int256 value) internal pure returns (int16 downcasted) {
        downcasted = int16(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 16 bits");
    }

    /**
     * @dev Returns the downcasted int8 from int256, reverting on
     * overflow (when the input is less than smallest int8 or
     * greater than largest int8).
     *
     * Counterpart to Solidity's `int8` operator.
     *
     * Requirements:
     *
     * - input must fit into 8 bits
     *
     * _Available since v3.1._
     */
    function toInt8(int256 value) internal pure returns (int8 downcasted) {
        downcasted = int8(value);
        require(downcasted == value, "SafeCast: value doesn't fit in 8 bits");
    }

    /**
     * @dev Converts an unsigned uint256 into a signed int256.
     *
     * Requirements:
     *
     * - input must be less than or equal to maxInt256.
     *
     * _Available since v3.0._
     */
    function toInt256(uint256 value) internal pure returns (int256) {
        // Note: Unsafe cast below is okay because `type(int256).max` is guaranteed to be positive
        require(value <= uint256(type(int256).max), "SafeCast: value doesn't fit in an int256");
        return int256(value);
    }
}

File 19 of 44 : SignedMath.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/SignedMath.sol)

pragma solidity ^0.8.0;

/**
 * @dev Standard signed math utilities missing in the Solidity language.
 */
library SignedMath {
    /**
     * @dev Returns the largest of two signed numbers.
     */
    function max(int256 a, int256 b) internal pure returns (int256) {
        return a > b ? a : b;
    }

    /**
     * @dev Returns the smallest of two signed numbers.
     */
    function min(int256 a, int256 b) internal pure returns (int256) {
        return a < b ? a : b;
    }

    /**
     * @dev Returns the average of two signed numbers without overflow.
     * The result is rounded towards zero.
     */
    function average(int256 a, int256 b) internal pure returns (int256) {
        // Formula from the book "Hacker's Delight"
        int256 x = (a & b) + ((a ^ b) >> 1);
        return x + (int256(uint256(x) >> 255) & (a ^ b));
    }

    /**
     * @dev Returns the absolute unsigned value of a signed value.
     */
    function abs(int256 n) internal pure returns (uint256) {
        unchecked {
            // must be unchecked in order to support `n = type(int256).min`
            return uint256(n >= 0 ? n : -n);
        }
    }
}

File 20 of 44 : Strings.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (utils/Strings.sol)

pragma solidity ^0.8.0;

import "./math/Math.sol";
import "./math/SignedMath.sol";

/**
 * @dev String operations.
 */
library Strings {
    bytes16 private constant _SYMBOLS = "0123456789abcdef";
    uint8 private constant _ADDRESS_LENGTH = 20;

    /**
     * @dev Converts a `uint256` to its ASCII `string` decimal representation.
     */
    function toString(uint256 value) internal pure returns (string memory) {
        unchecked {
            uint256 length = Math.log10(value) + 1;
            string memory buffer = new string(length);
            uint256 ptr;
            /// @solidity memory-safe-assembly
            assembly {
                ptr := add(buffer, add(32, length))
            }
            while (true) {
                ptr--;
                /// @solidity memory-safe-assembly
                assembly {
                    mstore8(ptr, byte(mod(value, 10), _SYMBOLS))
                }
                value /= 10;
                if (value == 0) break;
            }
            return buffer;
        }
    }

    /**
     * @dev Converts a `int256` to its ASCII `string` decimal representation.
     */
    function toString(int256 value) internal pure returns (string memory) {
        return string(abi.encodePacked(value < 0 ? "-" : "", toString(SignedMath.abs(value))));
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation.
     */
    function toHexString(uint256 value) internal pure returns (string memory) {
        unchecked {
            return toHexString(value, Math.log256(value) + 1);
        }
    }

    /**
     * @dev Converts a `uint256` to its ASCII `string` hexadecimal representation with fixed length.
     */
    function toHexString(uint256 value, uint256 length) internal pure returns (string memory) {
        bytes memory buffer = new bytes(2 * length + 2);
        buffer[0] = "0";
        buffer[1] = "x";
        for (uint256 i = 2 * length + 1; i > 1; --i) {
            buffer[i] = _SYMBOLS[value & 0xf];
            value >>= 4;
        }
        require(value == 0, "Strings: hex length insufficient");
        return string(buffer);
    }

    /**
     * @dev Converts an `address` with fixed length of 20 bytes to its not checksummed ASCII `string` hexadecimal representation.
     */
    function toHexString(address addr) internal pure returns (string memory) {
        return toHexString(uint256(uint160(addr)), _ADDRESS_LENGTH);
    }

    /**
     * @dev Returns true if the two strings are equal.
     */
    function equal(string memory a, string memory b) internal pure returns (bool) {
        return keccak256(bytes(a)) == keccak256(bytes(b));
    }
}

File 21 of 44 : IPyth.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

import "./PythStructs.sol";
import "./IPythEvents.sol";

/// @title Consume prices from the Pyth Network (https://pyth.network/).
/// @dev Please refer to the guidance at https://docs.pyth.network/consumers/best-practices for how to consume prices safely.
/// @author Pyth Data Association
interface IPyth is IPythEvents {
    /// @notice Returns the period (in seconds) that a price feed is considered valid since its publish time
    function getValidTimePeriod() external view returns (uint validTimePeriod);

    /// @notice Returns the price and confidence interval.
    /// @dev Reverts if the price has not been updated within the last `getValidTimePeriod()` seconds.
    /// @param id The Pyth Price Feed ID of which to fetch the price and confidence interval.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getPrice(
        bytes32 id
    ) external view returns (PythStructs.Price memory price);

    /// @notice Returns the exponentially-weighted moving average price and confidence interval.
    /// @dev Reverts if the EMA price is not available.
    /// @param id The Pyth Price Feed ID of which to fetch the EMA price and confidence interval.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getEmaPrice(
        bytes32 id
    ) external view returns (PythStructs.Price memory price);

    /// @notice Returns the price of a price feed without any sanity checks.
    /// @dev This function returns the most recent price update in this contract without any recency checks.
    /// This function is unsafe as the returned price update may be arbitrarily far in the past.
    ///
    /// Users of this function should check the `publishTime` in the price to ensure that the returned price is
    /// sufficiently recent for their application. If you are considering using this function, it may be
    /// safer / easier to use either `getPrice` or `getPriceNoOlderThan`.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getPriceUnsafe(
        bytes32 id
    ) external view returns (PythStructs.Price memory price);

    /// @notice Returns the price that is no older than `age` seconds of the current time.
    /// @dev This function is a sanity-checked version of `getPriceUnsafe` which is useful in
    /// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
    /// recently.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getPriceNoOlderThan(
        bytes32 id,
        uint age
    ) external view returns (PythStructs.Price memory price);

    /// @notice Returns the exponentially-weighted moving average price of a price feed without any sanity checks.
    /// @dev This function returns the same price as `getEmaPrice` in the case where the price is available.
    /// However, if the price is not recent this function returns the latest available price.
    ///
    /// The returned price can be from arbitrarily far in the past; this function makes no guarantees that
    /// the returned price is recent or useful for any particular application.
    ///
    /// Users of this function should check the `publishTime` in the price to ensure that the returned price is
    /// sufficiently recent for their application. If you are considering using this function, it may be
    /// safer / easier to use either `getEmaPrice` or `getEmaPriceNoOlderThan`.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getEmaPriceUnsafe(
        bytes32 id
    ) external view returns (PythStructs.Price memory price);

    /// @notice Returns the exponentially-weighted moving average price that is no older than `age` seconds
    /// of the current time.
    /// @dev This function is a sanity-checked version of `getEmaPriceUnsafe` which is useful in
    /// applications that require a sufficiently-recent price. Reverts if the price wasn't updated sufficiently
    /// recently.
    /// @return price - please read the documentation of PythStructs.Price to understand how to use this safely.
    function getEmaPriceNoOlderThan(
        bytes32 id,
        uint age
    ) external view returns (PythStructs.Price memory price);

    /// @notice Update price feeds with given update messages.
    /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
    /// `getUpdateFee` with the length of the `updateData` array.
    /// Prices will be updated if they are more recent than the current stored prices.
    /// The call will succeed even if the update is not the most recent.
    /// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid.
    /// @param updateData Array of price update data.
    function updatePriceFeeds(bytes[] calldata updateData) external payable;

    /// @notice Wrapper around updatePriceFeeds that rejects fast if a price update is not necessary. A price update is
    /// necessary if the current on-chain publishTime is older than the given publishTime. It relies solely on the
    /// given `publishTimes` for the price feeds and does not read the actual price update publish time within `updateData`.
    ///
    /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
    /// `getUpdateFee` with the length of the `updateData` array.
    ///
    /// `priceIds` and `publishTimes` are two arrays with the same size that correspond to senders known publishTime
    /// of each priceId when calling this method. If all of price feeds within `priceIds` have updated and have
    /// a newer or equal publish time than the given publish time, it will reject the transaction to save gas.
    /// Otherwise, it calls updatePriceFeeds method to update the prices.
    ///
    /// @dev Reverts if update is not needed or the transferred fee is not sufficient or the updateData is invalid.
    /// @param updateData Array of price update data.
    /// @param priceIds Array of price ids.
    /// @param publishTimes Array of publishTimes. `publishTimes[i]` corresponds to known `publishTime` of `priceIds[i]`
    function updatePriceFeedsIfNecessary(
        bytes[] calldata updateData,
        bytes32[] calldata priceIds,
        uint64[] calldata publishTimes
    ) external payable;

    /// @notice Returns the required fee to update an array of price updates.
    /// @param updateData Array of price update data.
    /// @return feeAmount The required fee in Wei.
    function getUpdateFee(
        bytes[] calldata updateData
    ) external view returns (uint feeAmount);

    /// @notice Parse `updateData` and return price feeds of the given `priceIds` if they are all published
    /// within `minPublishTime` and `maxPublishTime`.
    ///
    /// You can use this method if you want to use a Pyth price at a fixed time and not the most recent price;
    /// otherwise, please consider using `updatePriceFeeds`. This method does not store the price updates on-chain.
    ///
    /// This method requires the caller to pay a fee in wei; the required fee can be computed by calling
    /// `getUpdateFee` with the length of the `updateData` array.
    ///
    ///
    /// @dev Reverts if the transferred fee is not sufficient or the updateData is invalid or there is
    /// no update for any of the given `priceIds` within the given time range.
    /// @param updateData Array of price update data.
    /// @param priceIds Array of price ids.
    /// @param minPublishTime minimum acceptable publishTime for the given `priceIds`.
    /// @param maxPublishTime maximum acceptable publishTime for the given `priceIds`.
    /// @return priceFeeds Array of the price feeds corresponding to the given `priceIds` (with the same order).
    function parsePriceFeedUpdates(
        bytes[] calldata updateData,
        bytes32[] calldata priceIds,
        uint64 minPublishTime,
        uint64 maxPublishTime
    ) external payable returns (PythStructs.PriceFeed[] memory priceFeeds);
}

File 22 of 44 : IPythEvents.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

/// @title IPythEvents contains the events that Pyth contract emits.
/// @dev This interface can be used for listening to the updates for off-chain and testing purposes.
interface IPythEvents {
    /// @dev Emitted when the price feed with `id` has received a fresh update.
    /// @param id The Pyth Price Feed ID.
    /// @param publishTime Publish time of the given price update.
    /// @param price Price of the given price update.
    /// @param conf Confidence interval of the given price update.
    event PriceFeedUpdate(
        bytes32 indexed id,
        uint64 publishTime,
        int64 price,
        uint64 conf
    );

    /// @dev Emitted when a batch price update is processed successfully.
    /// @param chainId ID of the source chain that the batch price update comes from.
    /// @param sequenceNumber Sequence number of the batch price update.
    event BatchPriceFeedUpdate(uint16 chainId, uint64 sequenceNumber);
}

File 23 of 44 : PythStructs.sol
// SPDX-License-Identifier: Apache-2.0
pragma solidity ^0.8.0;

contract PythStructs {
    // A price with a degree of uncertainty, represented as a price +- a confidence interval.
    //
    // The confidence interval roughly corresponds to the standard error of a normal distribution.
    // Both the price and confidence are stored in a fixed-point numeric representation,
    // `x * (10^expo)`, where `expo` is the exponent.
    //
    // Please refer to the documentation at https://docs.pyth.network/consumers/best-practices for how
    // to how this price safely.
    struct Price {
        // Price
        int64 price;
        // Confidence interval around the price
        uint64 conf;
        // Price exponent
        int32 expo;
        // Unix timestamp describing when the price was published
        uint publishTime;
    }

    // PriceFeed represents a current aggregate price from pyth publisher feeds.
    struct PriceFeed {
        // The price ID.
        bytes32 id;
        // Latest available price
        Price price;
        // Latest available exponentially-weighted moving average price
        Price emaPrice;
    }
}

File 24 of 44 : SafeERC20.sol
//SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

File 25 of 44 : ChainlinkAdapter.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {AggregatorV3Interface} from "../interfaces/IChainlinkAggregatorV3.sol";
import {IPriceOracleGetter} from "../interfaces/IPriceOracleGetter.sol";

/**
 * @title ChainlinkAdapter
 * @notice Price oracle that uses Chainlink's price feeds
 * @dev This contract is meant to be used with the Router contract
 */
contract ChainlinkAdapter is IPriceOracleGetter, Ownable {
    mapping(address => AggregatorV3Interface) public oracleByAsset;

    /**
     * @notice Registers a new price feed
     * @param _asset ERC-20 token address
     * @param _priceFeed Chainlink's price feed address
     */
    function registerPriceFeed(address _asset, address _priceFeed)
        external
        onlyOwner
    {
        require(_asset != address(0), "NO_ASSET");
        require(_priceFeed != address(0), "NO_FEED");

        // Just to check if the address is a valid price feed
        AggregatorV3Interface(_priceFeed).latestRoundData();

        oracleByAsset[_asset] = AggregatorV3Interface(_priceFeed);
    }

    /**
     * @notice Unregisters a price feed
     * @param _asset ERC-20 token address
     */
    function unregisterPriceFeed(address _asset)
        external
        onlyOwner
    {
        require(_asset != address(0), "NO_ASSET");
        delete oracleByAsset[_asset];
    }

    /**
     * @notice Returns the asset price in USD
     * @param _asset ERC-20 token address
     * @return _price Asset price in USD
     */
    function getAssetPrice(address _asset)
        external
        view
        returns (uint256 _price)
    {
        AggregatorV3Interface _oracle = oracleByAsset[_asset];
        require(address(_oracle) != address(0), "NO_ORACLE");

        (, int256 _signedPrice, , , ) = _oracle.latestRoundData();
        _price = uint256(_signedPrice);
    }
}

File 26 of 44 : PythAdapter.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {IPyth} from "@pythnetwork/pyth-sdk-solidity/IPyth.sol";
import {PythStructs} from "@pythnetwork/pyth-sdk-solidity/PythStructs.sol";

/**
 * @title PythAdapter
 * @notice Price oracle adapter that uses Pyth price feeds (https://pyth.network)
 * @dev This contract is meant to be used with the Router contract
 */
contract PythAdapter is Ownable {
    /**
     * @notice Pyth contract instance
     */
    IPyth private pyth;

    /**
     * @notice Mapping of asset address to Pyth price feed id
     */
    mapping(address => bytes32) private priceFeedIdByAsset;

    // Events
    event PythContractAddressSet(
        address indexed sender,
        address pythContractAddress
    );
    event PriceFeedRegistered(
        address indexed sender,
        address indexed asset,
        bytes32 priceFeedId
    );
    event PriceFeedUnregistered(address indexed sender, address indexed asset);
    event PriceFeedsUpdated(address indexed sender, uint256 updateFee);

    /**
     * @notice PythAdapter constructor
     * @param pythContractAddress Pyth contract address
     */
    constructor(address pythContractAddress) {
        require(
            pythContractAddress != address(0),
            "PYTH_ADAPTER: ZERO_ADDRESS"
        );
        pyth = IPyth(pythContractAddress);
    }

    /**
     * @notice Set new Pyth contract address
     * @param _newPythContractAddress New Pyth contract address
     * @return success Confirmation of success
     */
    function setPythContractAddress(
        address _newPythContractAddress
    ) external onlyOwner returns (bool success) {
        require(
            _newPythContractAddress != address(0),
            "PYTH_ADAPTER: ZERO_ADDRESS"
        );

        pyth = IPyth(_newPythContractAddress);

        emit PythContractAddressSet(msg.sender, _newPythContractAddress);
        return true;
    }

    /**
     * @notice Registers a new Pyth price feed
     * @param _asset ERC-20 token address
     * @param _priceFeedId Pyth price feed id
     * @return success Confirmation of success
     */
    function registerPriceFeed(
        address _asset,
        bytes32 _priceFeedId
    ) external onlyOwner returns (bool success) {
        require(_asset != address(0), "PYTH_ADAPTER: NO_ASSET");
        require(_priceFeedId != bytes32(0), "PYTH_ADAPTER: NO_PRICE_FEED");
        require(
            !isPriceFeedRegistered(_asset),
            "PYTH_ADAPTER: PRICE_FEED_ALREADY_REGISTERED"
        );

        emit PriceFeedRegistered(msg.sender, _asset, _priceFeedId);

        // Check if the Pyth price feed exists
        pyth.getPriceUnsafe(_priceFeedId);

        priceFeedIdByAsset[_asset] = _priceFeedId;

        return true;
    }

    /**
     * @notice Unregisters a Pyth price feed
     * @param _asset ERC-20 token address
     * @return success Confirmation of success
     */
    function unregisterPriceFeed(
        address _asset
    ) external onlyOwner returns (bool success) {
        require(_asset != address(0), "PYTH_ADAPTER: NO_ASSET");
        require(isPriceFeedRegistered(_asset), "PYTH_ADAPTER: NO_PRICE_FEED");

        delete priceFeedIdByAsset[_asset];

        emit PriceFeedUnregistered(msg.sender, _asset);
        return true;
    }

    /**
     * @notice Updates one or more Pyth price feeds
     * @param _priceUpdateData Pyth price update data
     * @return success Confirmation of success
     * @dev This function needs to be called right before calling getAssetPrice()
     */
    function updatePriceFeeds(
        bytes[] calldata _priceUpdateData
    ) external payable returns (bool success) {
        require(_priceUpdateData.length != 0, "PYTH_ADAPTER: NO_UPDATE_DATA");

        // Get the update fee
        uint256 updateFee = getUpdateFee(_priceUpdateData);
        require(msg.value == updateFee, "PYTH_ADAPTER: VALUE_NEQ_UPDATE_FEE");

        emit PriceFeedsUpdated(msg.sender, updateFee);

        // Update the Pyth price feed
        pyth.updatePriceFeeds{value: updateFee}(_priceUpdateData);

        return true;
    }

    /**
     * @notice Returns the asset price
     * @param _asset ERC-20 token address
     * @return assetPrice Asset price in USD with 18 decimal places
     * @dev This function reverts with a StalePrice error if the on-chain price has not been updated within the last getValidTimePeriod() seconds via updatePriceFeeds()
     */
    function getAssetPrice(
        address _asset
    ) external view returns (uint256 assetPrice) {
        require(_asset != address(0), "PYTH_ADAPTER: NO_ASSET");
        require(isPriceFeedRegistered(_asset), "PYTH_ADAPTER: NO_PRICE_FEED");

        // Get the asset price Pyth struct
        PythStructs.Price memory pythStructsPrice = pyth.getPrice(
            priceFeedIdByAsset[_asset]
        );

        (int64 price, int32 expo) = (
            pythStructsPrice.price,
            pythStructsPrice.expo
        );
        // Convert price to a 18 digit precision number and return
        return uint256(uint64(price)) * (10 ** uint256(uint32(expo + 18)));
    }

    /** Getters */

    /**
     * @notice Returns the Pyth contract address
     * @return pythContractAddress Pyth contract address
     */
    function getPythContractAddress()
        external
        view
        returns (address pythContractAddress)
    {
        return address(pyth);
    }

    /**
     * @notice Check if Pyth price feed is registered
     * @param _asset ERC-20 token address
     * @return isRegistered Confirmation of registration
     */
    function isPriceFeedRegistered(
        address _asset
    ) public view returns (bool isRegistered) {
        return (priceFeedIdByAsset[_asset] != bytes32(0));
    }

    /**
     * @notice Returns the Pyth price feed id for an asset
     * @param _asset ERC-20 token address
     * @return priceFeedId Pyth price feed id
     */
    function getPriceFeedIdByAsset(
        address _asset
    ) public view returns (bytes32 priceFeedId) {
        require(isPriceFeedRegistered(_asset), "PYTH_ADAPTER: NO_PRICE_FEED");
        return priceFeedIdByAsset[_asset];
    }

    /**
     * @notice Returns the required fee to update an array of price updates
     * @param _updateData Array of price update data
     * @return updateFee Update fee in Wei
     */
    function getUpdateFee(
        bytes[] calldata _updateData
    ) public view returns (uint256 updateFee) {
        return pyth.getUpdateFee(_updateData);
    }

    /**
     * @notice Returns the period (in seconds) that a price feed is considered valid since its publish time
     * @return validTimePeriod Valid time period in seconds
     */
    function getValidTimePeriod()
        external
        view
        returns (uint256 validTimePeriod)
    {
        return pyth.getValidTimePeriod();
    }
}

File 27 of 44 : BackstopPool.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {
    IERC20Metadata
} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {
    ReentrancyGuard
} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {SafeERC20} from "contracts/@chain/ethereum/token/SafeERC20.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {IRouter} from "../interfaces/IRouter.sol";
import {ISlippageCurve} from "../interfaces/ISlippageCurve.sol";
import {GenericPool} from "./GenericPool.sol";
import "../interfaces/IBackstopPool.sol";
import "../interfaces/ISwapPool.sol";

/**
 * @notice The backstop pool takes most of the risk of a set of swap pools
 *         backed by it. Whenever a swap pool is low on reserves and a LPer
 *         wants to withdraw some liquidity, they can conduct an insurance
 *         withdrawal (burn swap pool shares, reimbursed in backstop liquidity)
 *         to avoid paying a high slippage.
 *         The backstop pool owns all excess liquidity in its swap pools,
 *         but is also liable for potential liquidity gaps.
 *         In return, the backstop pool receives a cut of the swap fees.
 */
contract BackstopPool is
    GenericPool,
    Pausable,
    ReentrancyGuard,
    Ownable,
    IBackstopPool
{
    using SafeCast for int256;
    using SafeCast for uint256;
    using SafeERC20 for IERC20;

    IRouter public immutable router;

    // Dynamic array of swap pools that are covered by this backstop pool
    address[] private swapPools;

    mapping(address => uint256) private swapPoolInsuranceFeeBps;
    mapping(address => bool) private swapPoolCovered;

    constructor(
        address _router,
        address _asset,
        string memory _name,
        string memory _symbol
    ) GenericPool(_asset, _name, _symbol) {
        require(_asset != address(0), "NO_ASSET");
        require(_router != address(0), "NO_ROUTER");

        router = IRouter(_router);
    }

    /**
     * @notice Set new upper limit of pool reserves. Will disable deposits when reached.
     * @notice Can always set to an amount < current reserves to temporarily restrict deposits.
     * @param _maxTokens    New limit how many `poolAsset` tokens can be deposited
     */
    function setPoolCap(uint256 _maxTokens) external onlyOwner {
        poolCap = _maxTokens;
    }

    /**
     * @notice Make this backstop pool cover another swap pool
     * @notice Beware: Adding a swap pool holding the same token as the backstop pool
     *         can easily cause undesirable conditions and must be secured (i.e. long time lock)!
     * @param _swapPool         Swap pool to add
     * @param _insuranceFeeBps  Relative fee on insurance withdrawals, in basis points (0.01%)
     * @return success          Confirmation of success
     */
    function addSwapPool(
        address _swapPool,
        uint256 _insuranceFeeBps
    ) external onlyOwner returns (bool success) {
        require(_swapPool != address(0), "addSwapPool():ZERO_ADDRESS");
        require(!swapPoolCovered[_swapPool], "addSwapPool():DUPLICATE");

        swapPools.push(_swapPool);
        swapPoolCovered[_swapPool] = true;

        _setInsuranceFee(_swapPool, _insuranceFeeBps);

        // Invariant violation
        require(
            address(ISwapPool(_swapPool).backstop()) == address(this),
            "addSwapPool():BACKSTOP_MISMATCH"
        );

        emit SwapPoolAdded(msg.sender, _swapPool);
        return true;
    }

    /**
     * @notice Change a swap pool's insurance withdrawal fee
     * @param _swapPool         Swap pool to add
     * @param _insuranceFeeBps  Relative fee on insurance withdrawals, in basis points (0.01%)
     */
    function setInsuranceFee(
        address _swapPool,
        uint256 _insuranceFeeBps
    ) external onlyOwner returns (bool success) {
        require(_swapPool != address(0), "_setInsuranceFee():NO_POOL");
        
        _setInsuranceFee(_swapPool, _insuranceFeeBps);

        return true;
    }

    /**
     * @notice Change a swap pool's insurance withdrawal fee
     * @param _swapPool         Swap pool to set insurance withdrawal fee for
     * @param _insuranceFeeBps  Relative fee on insurance withdrawals, in basis points (0.01%)
     */
    function _setInsuranceFee(address _swapPool, uint256 _insuranceFeeBps)
        internal
    {
        // reject fees > 30% (essentially fraud)
        require(_insuranceFeeBps <= 30_00, "_setInsuranceFee():EXCESSIVE_FEE");

        swapPoolInsuranceFeeBps[_swapPool] = _insuranceFeeBps;

        emit InsuranceFeeSet(msg.sender, _swapPool, _insuranceFeeBps);
    }

    /**
     * @notice enumerate swap pools backed by this backstop pool
     * @return _swapPool swap pool address
     */
    function getBackedPool(uint256 _index)
        external
        view
        returns (address _swapPool)
    {
        require(
            swapPools.length > 0 && _index < swapPools.length,
            "getBackedPool():INVALID_INDEX"
        );
        return address(swapPools[_index]);
    }

    /**
     * @notice get swap pool count backed by this backstop pool
     * @return _count number of swap pools
     */
    function getBackedPoolCount() external view returns (uint256 _count) {
        return swapPools.length;
    }

    /**
     * @notice get insurance withdrawal fee for a given swap pool
     * @param _swapPool address of the swap pool
     * @return _feeBps  insurance witdrawal fee, in basis points (0.01%)
     */
    function getInsuranceFee(address _swapPool)
        external
        view
        returns (uint256 _feeBps)
    {
        _feeBps = swapPoolInsuranceFeeBps[_swapPool];
    }

    /**
     * @notice Deposits amount of tokens into pool
     * @notice Will change cov ratio of pool, will increase delta to 0
     * @param _depositAmount    The amount to be deposited
     * @param _priceUpdateData  Array of price update data of all assets involved 
     *                          (backstop pool and all swapPools as within swapPools[])
     * @return _sharesToMint    Total number of pool lp tokens minted
     * @return _fee             The fee due to slippage (will be zero)
     * @dev    By calling this function the price feeds get updated (IPythAdapter.updatePriceFeeds)
     */
    function deposit(
        uint256 _depositAmount,
        bytes[] calldata _priceUpdateData    
    )
        external
        payable
        whenNotPaused
        nonReentrant
        returns (uint256 _sharesToMint, int256 _fee)
    {
        // Update the price feeds
        _updatePriceFeeds(_priceUpdateData);

        _sharesToMint = _calculateDepositParameters(_depositAmount);
        _processDeposit(msg.sender, _depositAmount, _sharesToMint);

        _fee = 0;

        emit Mint(msg.sender, _sharesToMint, _depositAmount);
    }

    /**
     * @notice complete calculation involved in a deposit
     */
    function _calculateDepositParameters(uint256 _depositAmount)
        internal
        view
        returns (uint256 _sharesToMint)
    {
        int256 _totalPoolWorth = _getTotalPoolWorth();
        uint256 _totalSupply = totalSupply();

        require(
            _totalPoolWorth >= 0,
            "_calculateDepositParameters():POOLWORTH_NEGATIVE"
        );

        if (_totalPoolWorth == 0 || _totalSupply == 0) {
            require(
                _totalPoolWorth > 0 || _totalSupply == 0,
                "_calculateDepositParameters():POOLWORTH_ZERO"
            );
            _sharesToMint = _depositAmount;
        } else {
            _sharesToMint =
                (_depositAmount * _totalSupply) /
                uint256(_totalPoolWorth);
        }
    }

    /**
     * @notice Withdraws liquidity amount of asset ensuring minimum amount required
     * @notice Slippage is applied (withdrawal fee)
     * @param _sharesToBurn     The liquidity to be withdrawn
     * @param _minimumAmount    Reject withdrawal if resulting amount is below
     * @param _priceUpdateData  Array of price update data of all assets involved 
     *                          (backstop pool and all swapPools as within swapPools[])
     * @return _payoutAmount     Amount withdrawn after applying withdrawal fee
     * @return _fee The fee due to slippage (will be zero)
     * @dev    By calling this function the price feeds get updated (IPythAdapter.updatePriceFeeds)
     */
    function withdraw(
        uint256 _sharesToBurn, 
        uint256 _minimumAmount,
        bytes[] calldata _priceUpdateData
    )
        external
        payable
        nonReentrant
        returns (uint256 _payoutAmount, int256 _fee)
    {
        // Update the price feeds
        _updatePriceFeeds(_priceUpdateData);

        _payoutAmount = _calculateWithdrawalParameters(_sharesToBurn);
        require(_payoutAmount >= _minimumAmount, "withdraw():MINIMUM_AMOUNT");

        _processWithdrawal(msg.sender, _sharesToBurn, _payoutAmount);

        _fee = 0;

        emit Burn(msg.sender, _sharesToBurn, _payoutAmount);
    }

    /**
     * @notice complete calculation involved in a deposit
     */
    function _calculateWithdrawalParameters(uint256 _sharesToBurn)
        internal
        view
        returns (uint256 _payoutAmount)
    {
        _payoutAmount = _sharesTargetWorth(_sharesToBurn);
    }

    /**
     * @notice withdraw from a swap pool using backstop liquidity without slippage
     * @notice only possible if swap pool's coverage ratio < 100%
     * @param _swapPool         swap pool address
     * @param _shares           number of swap pool shares to redeem
     * @param _minAmount        minimum amount of backstop liquidity to receive
     * @param _priceUpdateData  Array of price update data of all assets involved (backstop pool and this swapPool)
     * @return _netAmount       amount of backstop liquidity paid-out
     * @dev    By calling this function the price feeds get updated (IPythAdapter.updatePriceFeeds)
     */
    function redeemSwapPoolShares(
        address _swapPool,
        uint256 _shares,
        uint256 _minAmount,
        bytes[] calldata _priceUpdateData
    ) external payable nonReentrant returns (uint256 _netAmount) {
        require(swapPoolCovered[_swapPool], "redeemSwapPoolShares():NO_COVER");

        // Update the price feeds
        _updatePriceFeeds(_priceUpdateData);

        uint256 _backstopPoolAssetPrice = router.pythAdapter().getAssetPrice(
            address(poolAsset)
        );
        uint256 _swapPoolAssetPrice = router.pythAdapter().getAssetPrice(
            ISwapPool(_swapPool).asset()
        );

        uint256 _swapLiquidity = ISwapPoolPermissioned(_swapPool).backstopBurn(
            msg.sender,
            _shares
        );

        uint256 _amount = _convertAmount(
            _swapLiquidity,
            _swapPoolAssetPrice,
            _backstopPoolAssetPrice,
            ISwapPoolPermissioned(_swapPool).assetDecimals(),
            poolAssetDecimals
        );

        uint256 _fee = (_amount * swapPoolInsuranceFeeBps[_swapPool]) / 100_00;
        _netAmount = _amount - _fee;

        require(_netAmount >= _minAmount, "redeemSwapPoolShares():MIN_AMOUNT");

        poolAsset.safeTransfer(msg.sender, _netAmount);
        emit CoverSwapWithdrawal(
            msg.sender,
            _swapPool,
            _shares,
            _swapLiquidity,
            _amount
        );
    }

    /**
     * @notice withdraw from backstop pool, but receive excess liquidity
     *         of a swap pool without slippage, instead of backstop liquidity
     * @param _swapPool         swap pool address, must have a coverage ratio > 100%
     * @param _shares           number of backstop pool shares to redeem
     * @param _minAmount        minimum amount of swap pool liquidity to receive
     * @param _priceUpdateData  Array of price update data of all assets involved 
     *                          (backstop pool and all swapPools as within swapPools[])
     * @return _swapAmount      amount of swap pool liquidity paid-out
     * @dev    By calling this function the price feeds get updated (IPythAdapter.updatePriceFeeds)
     */
    function withdrawExcessSwapLiquidity(
        address _swapPool,
        uint256 _shares,
        uint256 _minAmount,
        bytes[] calldata _priceUpdateData
    ) external payable nonReentrant returns (uint256 _swapAmount) {
        require(
            swapPoolCovered[_swapPool],
            "withdrawExcessSwapLiquidity():NO_COVER"
        );
        require(
            balanceOf(msg.sender) >= _shares,
            "withdrawExcessSwapLiquidity():BALANCE"
        );

        // Update the price feeds
        _updatePriceFeeds(_priceUpdateData);

        uint256 _backstopPoolAssetPrice = router.pythAdapter().getAssetPrice(
            address(poolAsset)
        );
        uint256 _swapPoolAssetPrice = router.pythAdapter().getAssetPrice(
            ISwapPool(_swapPool).asset()
        );

        uint256 _backstopAmount = _sharesTargetWorth(_shares);

        uint256 _rawSwapAmount = _convertAmount(
            _backstopAmount,
            _backstopPoolAssetPrice,
            _swapPoolAssetPrice,
            poolAssetDecimals,
            ISwapPoolPermissioned(_swapPool).assetDecimals()
        );

        _swapAmount = ISwapPoolPermissioned(_swapPool).backstopDrain(
            _rawSwapAmount,
            msg.sender
        );

        require(
            _swapAmount >= _minAmount,
            "withdrawExcessSwapLiquidity():MIN_AMOUNT"
        );

        _burn(msg.sender, _shares);

        emit WithdrawSwapLiquidity(msg.sender, _swapPool, _swapAmount, _shares);
    }

    /**
     * @notice return worth of the whole backstop pool in `asset()`, incl. all
     *         swap pools' excess liquidity and the backstop pool's liabilities
     * @param _allTokenPrices   array of all token prices ([0] backstop pool token, [1] swap pool token_i,
     *                          [2] swap pool token_i+1, ... [n] swap pool token_n) as within swapPools[]
     * @return _value   total value of all backstop pool shares, in `asset()`
     * @dev    ignoring if pools are paused or not, since liabilities still apply
     *         and we don't want the backstop pool worth to jump
     */
    function getTotalPoolWorth(
        uint256[] memory _allTokenPrices
    ) public view returns (int256 _value) {
        uint256 _reserveTokenPrice = _allTokenPrices[0];

        _value = poolAsset.balanceOf(address(this)).toInt256();

        require(
            _reserveTokenPrice > 0,
            "getTotalPoolWorth(): RESERVE_PRICE_ZERO"
        );

        for (uint256 _index = 0; _index < swapPools.length; _index++) {
            ISwapPool _swapPool = ISwapPool(swapPools[_index]);
            if (address(_swapPool) == address(0)) {
                continue;
            }
            address _swapPoolToken = _swapPool.asset();
            if (_swapPoolToken == address(0)) {
                continue;
            }

            int256 _excessLiquidity = _swapPool.getExcessLiquidity();
            bool _isNegative = false;

            if (_excessLiquidity < 0) {
                _isNegative = true;
                _excessLiquidity = -_excessLiquidity;
            }

            uint256 _excessLiquidityValue = _convertAmount(
                _excessLiquidity.toUint256(),
                _allTokenPrices[_index + 1],
                _reserveTokenPrice,
                _swapPool.assetDecimals(),
                poolAssetDecimals
            );

            if (_isNegative) {
                _value -= _excessLiquidityValue.toInt256();
            } else {
                _value += _excessLiquidityValue.toInt256();
            }
        }
    }
    
    /**
     * @notice Gets the token prices of all pools and returns the worth of the whole backstop pool
     * @return _value   total value of all backstop pool shares, in `asset()` (incl. all swap pools' excess liquidity *                  and the backstop pool's liabilities)
     */
    function _getTotalPoolWorth() internal view returns (int256 _value) {
        _value = getTotalPoolWorth(_getAllTokenPrices());
    }

    /**
     * @notice Returns the worth of an amount of pool shares (LP tokens) in underlying principle
     * @param _sharesToBurn The number of LP tokens to burn
     * @param _allTokenPrices   array of all token prices ([0] backstop pool token, [1] swap pool token_i,
     *                          [2] swap pool token_i+1, ... [n] swap pool token_n) as within swapPools[]
     * @return _amount The amount of `asset()` tokens that the shares are worth
     */
    function sharesTargetWorth(
        uint256 _sharesToBurn,
        uint256[] memory _allTokenPrices
    )
        public
        view
        returns (uint256 _amount)
    {
        int256 _totalPoolWorth = getTotalPoolWorth(_allTokenPrices);

        require(_totalPoolWorth >= 0, "sharesTargetWorth():POOLWORTH_NEGATIVE");

        _amount = (_sharesToBurn * uint256(_totalPoolWorth)) / totalSupply();
    }

    /**
     * @notice Gets the token prices of all pools and returns the worth of an amount of pool shares (LP tokens)
     *         in underlying principle
     * @param _shares The number of LP tokens to burn
     */
    function _sharesTargetWorth(
        uint256 _shares
    ) internal view returns (uint256 _amount) {
        _amount = sharesTargetWorth(_shares, _getAllTokenPrices());
    }

    /**
     * @notice Price convert amounts of tokens
     * @notice The two involved tokens are called "from token" and "to token"
     * @notice Allows that tokens have different numbers of decimals
     * @param _fromAmount The amount of the from token
     * @param _fromPrice The price of the from token (in terms of a reference asset)
     * @param _toPrice The price of the to token (in terms of a reference asset)
     * @param _fromDecimals The number of decimals of the from token
     * @param _toDecimals The number of decimals of the to token
     * @return _toAmount The equivalent amount of to tokens
     */
    function _convertAmount(
        uint256 _fromAmount,
        uint256 _fromPrice,
        uint256 _toPrice,
        uint8 _fromDecimals,
        uint8 _toDecimals
    ) internal pure returns (uint256 _toAmount) {
        if (_fromDecimals > _toDecimals) {
            _toAmount =
                (_fromAmount * _fromPrice) /
                _toPrice /
                (10**uint256(_fromDecimals - _toDecimals));
        } else {
            _toAmount =
                (_fromAmount *
                    _fromPrice *
                    (10**uint256(_toDecimals - _fromDecimals))) /
                _toPrice;
        }
    }

    /**
     * @notice Returns the token prices of all pools
     * @return _allTokenPrices   array of all token prices ([0] backstop pool token, [1] swap pool token_i,
     *                           [2] swap pool token_i+1, ... [n] swap pool token_n) as within swapPools[]
     * @dev    Before calling this function the price feeds must be updated (IPythAdapter.updatePriceFeeds)
     */
    function _getAllTokenPrices() internal view returns (uint256[] memory) {
        uint256 swapPoolsLength = swapPools.length;
        uint256[] memory _allTokenPrices = new uint256[](swapPoolsLength + 1);
        // Backstop pool asset price
        _allTokenPrices[0] = router.pythAdapter().getAssetPrice(
            address(poolAsset)
        );
        // Swap pool asset prices
        for (uint256 i = 0; i < swapPoolsLength; i++) {
            ISwapPool _swapPool = ISwapPool(swapPools[i]);
            if (address(_swapPool) == address(0)) {
                continue;
            }
            address _swapPoolAsset = _swapPool.asset();
            if (_swapPoolAsset == address(0)) {
                continue;
            }
            _allTokenPrices[i + 1] = router.pythAdapter().getAssetPrice(
                _swapPoolAsset
            );
        }
        return _allTokenPrices;
    }

    /**
     * @notice returns the backstop pool state
     * @param _allTokenPrices   array of all token prices ([0] backstop pool token, [1] swap pool token_i,
     *                          [2] swap pool token_i+1, ... [n] swap pool token_n) as within swapPools[]
     * @return _reserves        current amount of `asset` in this pool
     * @return _totalPoolWorth  total worth of the pool in `asset()`
     * @return _totalSupply     total amount of LP tokens minted
     */
    function getPoolState(
        uint256[] memory _allTokenPrices
    ) external view returns(
        uint256 _reserves, 
        int256 _totalPoolWorth, 
        uint256 _totalSupply
    ) {
        _totalPoolWorth = getTotalPoolWorth(_allTokenPrices);
        _reserves = poolAsset.balanceOf(address(this));
        _totalSupply = totalSupply();
    }

    /**
     * @notice Update the price feed for an array of assets
     * @param _priceUpdateData Array of price update data
     */
    function _updatePriceFeeds(bytes[] calldata _priceUpdateData) internal {
        router.pythAdapter().updatePriceFeeds{value: msg.value}(_priceUpdateData);
    }
}

File 28 of 44 : GenericPool.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {
    IERC20Metadata
} from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import {SafeERC20} from "contracts/@chain/ethereum/token/SafeERC20.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {IGenericPoolPermissioned} from "../interfaces/IGenericPool.sol";

/**
 * @notice Abstract contract containing common logic for all pools
           Each pool manages can hold a specific pool asset, which is an ERC20 token
           At the same time the pool is itself an ERC20 contract representing an
           LP token ("pool shares").
           This contract takes care of
            - depositing pool tokens and at the same time minting LP tokens
            - withdrawing pool tokens and at the same time burning LP tokens
           It is abstract and does not take any assumptions about how
           the amount of pool tokens and LP tokens are related
 */
abstract contract GenericPool is ERC20, IGenericPoolPermissioned {
    using SafeCast for int256;
    using SafeCast for uint256;
    using SafeERC20 for IERC20;

    /// @notice Asset held by the pool
    IERC20 internal immutable poolAsset;

    /// @notice The decimals of the pool asset
    uint8 internal immutable poolAssetDecimals;

    /// @notice Maximum amount of `poolAsset` that can be deposited into this pool
    uint256 public poolCap = 2 ** 256 - 1;

    modifier checkPoolCap(uint256 _additionalDeposit) {
        require(
            poolAsset.balanceOf(address(this)) + _additionalDeposit <= poolCap,
            "deposit: CAP_EXCEEDED"
        );
        _;
    }

    constructor(
        address _asset,
        string memory _name,
        string memory _symbol
    ) ERC20(_name, _symbol) {
        poolAsset = IERC20(_asset);
        poolAssetDecimals = IERC20Metadata(address(poolAsset)).decimals();
    }

    /**
     * @notice Returns the pooled token's address
     * @return _token   Address of the pooled asset
     */
    function asset() public view returns (address _token) {
        _token = address(poolAsset);
    }

    /**
     * @notice Returns the decimals of the pool asset
     * @return _decimals The number of decimals
     */
    function assetDecimals() public view returns (uint8 _decimals) {
        _decimals = poolAssetDecimals;
    }

    /**
     * @notice Deposits amount of tokens into pool (low-level)
     * @param _user             Address of the depositing liquidity provider
     * @param _amountPoolTokens The amount to be deposited
     * @param _poolSharesToMint The number of LP tokens to mint and credit to the LPer
     */
    function _processDeposit(
        address _user,
        uint256 _amountPoolTokens,
        uint256 _poolSharesToMint
    ) internal checkPoolCap(_amountPoolTokens) {
        require(_poolSharesToMint > 0, "deposit: ZERO_DEPOSIT");

        _mint(_user, _poolSharesToMint);
        poolAsset.safeTransferFrom(_user, address(this), _amountPoolTokens);
    }

    /**
     * @notice Withdraws liquidity amount of asset (low-level)
     * @param _user             Address of the withdrawing liquidity provider
     * @param _poolSharesToBurn The amount of LP tokens to be burnt
     * @param _amountPoolTokens Amount withdrawn, before applying withdrawal fee
     */
    function _processWithdrawal(
        address _user,
        uint256 _poolSharesToBurn,
        uint256 _amountPoolTokens
    ) internal {
        require(
            balanceOf(_user) >= _poolSharesToBurn,
            "withdraw: INSUFFICIENT_BALANCE"
        );

        _burn(_user, _poolSharesToBurn);
        IERC20(poolAsset).safeTransfer(_user, _amountPoolTokens);
    }
}

File 29 of 44 : SwapPool.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {Pausable} from "@openzeppelin/contracts/security/Pausable.sol";
import {
    ReentrancyGuard
} from "@openzeppelin/contracts/security/ReentrancyGuard.sol";
import {Ownable} from "@openzeppelin/contracts/access/Ownable.sol";
import {SafeERC20} from "contracts/@chain/ethereum/token/SafeERC20.sol";
import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {IRouter} from "../interfaces/IRouter.sol";
import {ISlippageCurve} from "../interfaces/ISlippageCurve.sol";
import {GenericPool} from "./GenericPool.sol";
import "../interfaces/IBackstopPool.sol";
import "../interfaces/ISwapPool.sol";

/**
 * @notice Swap pool contract. May or may not be covered by a backstop pool.
 * @notice Conceptionally, there are two ways to temporarily disable a pool:
 * @notice The owner can either pause the pool, disabling deposits, swaps & backstop,
 * @notice or the owner can set the pool cap to zero which only prevents deposits.
 * @notice The former is for security incidents, the latter for phasing out a pool.
 */
contract SwapPool is
    GenericPool,
    Pausable,
    ReentrancyGuard,
    Ownable,
    ISwapPool,
    ISwapPoolPermissioned
{
    using SafeCast for int256;
    using SafeCast for uint256;
    using SafeERC20 for IERC20;

    struct SwapFees {
        uint32 lpFee;
        uint32 backstopFee;
        uint32 protocolFee;
    }

    /// @notice The liabilities of the pool
    /// @notice Cannot just use totalSupply() as the totalLiabilities will accumulate also
    /// @notice the LP fees from swaps.
    uint256 public totalLiabilities = 0;

    /// @notice The amount of pool tokens usable for swaps
    uint256 public reserve = 0;

    /// @notice The amount of pool tokens plus the slippage
    /// @notice The relationship with reserve is:
    /// @notice     reserveWithSlippage = slippageCurve.psi(reserve, totalLiabilities)
    /// @notice However this relationship will never hold precisely and will deviate slightly for
    /// @notice numerical reasons!! All code needs to work even if this relationship does not hold
    uint256 public reserveWithSlippage = 0;

    // Duration of non-withdrawal after deposit, in blocks
    uint256 public insuranceWithdrawalTimelock = 1_000;

    // Address of the treasury account
    address public immutable protocolTreasury;

    IBackstopPool public immutable backstop;
    IRouter public immutable router;

    // Must be immutable (also includes params!)
    ISlippageCurve public immutable slippageCurve;

    // Used for backstop withdrawal time lock
    mapping(address => uint256) private latestDepositAtBlockNo;

    // Fee charged when swapping tokens out of the pool
    SwapFees private swapFeeConfig;

    // Upper limit of pool coverage ratio (reserves / liabilities) for swap-in | default: CR <= 200%
    uint256 public maxCoverageRatioForSwapIn = 200;

    modifier onlyBackstop() {
        require(msg.sender == address(backstop), "SwapPool: ONLY_BACKSTOP");
        _;
    }

    modifier onlyRouter() {
        require(msg.sender == address(router), "SwapPool: ONLY_ROUTER");
        _;
    }

    constructor(
        address _asset,
        address _slippageCurve,
        address _router,
        address _backstop,
        address _protocolTreasury,
        string memory _name,
        string memory _symbol
    ) GenericPool(_asset, _name, _symbol) {
        backstop = IBackstopPool(_backstop);
        router = IRouter(_router);
        protocolTreasury = _protocolTreasury;
        slippageCurve = ISlippageCurve(_slippageCurve);

        if (_backstop != address(0)) {
            require(
                _router == address(backstop.router()),
                "constructor():BACKSTOP_ROUTER_MISMATCH"
            );
        }
    }

    /**
     * @notice Set new insurance withdrawal time lock.
     * @notice Can only be called by the owner
     * @param _durationInBlocks New time lock duration in blocks
     */
    function setInsuranceWithdrawalTimelock(
        uint256 _durationInBlocks
    ) external onlyOwner {
        require(
            _durationInBlocks > 0,
            "setInsuranceWithdrawalTimelock: INVALID_DURATION"
        );
        insuranceWithdrawalTimelock = _durationInBlocks;
    }

    /**
     * @notice Set new upper limit of pool reserves. Will disable deposits when reached.
     * @notice Can always set to an amount < current reserves to temporarily restrict deposits.
     * @param _maxTokens    New limit how many `poolAsset` tokens can be deposited
     */
    function setPoolCap(uint256 _maxTokens) external onlyOwner {
        poolCap = _maxTokens;
    }

    /**
     * @notice Set new upper limit of pool coverage ratio (reserves / liabilities) for swap-in
     * @param _maxCoverageRatio New upper limit of pool coverage ratio (reserves / liabilities) for swap-in in %
     * @dev   Can always set to an amount < current coverage to temporarily restrict swap-ins
     *        or set to 0 to disable swap-ins
     */
    function setMaxCoverageRatioForSwapIn(
        uint256 _maxCoverageRatio
    ) external onlyOwner {
        maxCoverageRatioForSwapIn = _maxCoverageRatio;
    }

    /**
     * @notice Set swap fees (applied when swapping funds out of the pool)
     * @param _lpFee         Fee that benefits the pool's LPers,    in 0.0001% (e.g. 300 -> 0.0300%)
     * @param _backstopFee   Fee that benefits the backstop pool,   in 0.0001% (e.g. 150 -> 0.0150%)
     * @param _protocolFee   Fee that benefits the protocol,        in 0.0001% (e.g.  50 -> 0.0050%)
     */
    function setSwapFees(
        uint256 _lpFee,
        uint256 _backstopFee,
        uint256 _protocolFee
    ) external onlyOwner {
        // don't allow swap fees >= 30% (essentially fraud)
        require(
            _lpFee + _backstopFee + _protocolFee < 300_000,
            "setSwapFees: FEES_TOO_HIGH"
        );

        swapFeeConfig = SwapFees({
            lpFee: _lpFee.toUint32(),
            backstopFee: _backstopFee.toUint32(),
            protocolFee: _protocolFee.toUint32()
        });
    }

    /**
     * @notice Deposits amount of tokens into pool
     * @notice Will change cov ratio of pool, will increase delta to 0
     * @param _depositAmount The amount to be deposited
     * @return _sharesToMint Total number of pool lp tokens minted
     * @return _fee The slippage reward (a negative fee)
     */
    function deposit(
        uint256 _depositAmount
    )
        external
        whenNotPaused
        nonReentrant
        returns (uint256 _sharesToMint, int256 _fee)
    {
        latestDepositAtBlockNo[msg.sender] = block.number;

        uint256 _reserveIncrement;
        uint256 _slippageReward;
        (
            _reserveIncrement,
            _sharesToMint,
            _slippageReward
        ) = _calculateDepositParameters(_depositAmount);

        reserveWithSlippage += _depositAmount;
        reserve += _reserveIncrement;
        totalLiabilities += _reserveIncrement;

        _processDeposit(msg.sender, _depositAmount, _sharesToMint);

        _fee = -_slippageReward.toInt256();

        emit Mint(msg.sender, _sharesToMint, _depositAmount);
    }

    /**
     * @notice complete calculation involved in a deposit
     */
    function _calculateDepositParameters(
        uint256 _depositAmount
    )
        internal
        view
        returns (
            uint256 _reserveIncrement,
            uint256 _sharesToMint,
            uint256 _slippageReward
        )
    {
        _reserveIncrement = slippageCurve.inverseDiagonal(
            reserve,
            totalLiabilities,
            reserveWithSlippage + _depositAmount,
            poolAssetDecimals
        );

        // fix potential numerical imprecission
        if (_reserveIncrement < _depositAmount) {
            _reserveIncrement = _depositAmount;
        }
        _slippageReward = _reserveIncrement - _depositAmount;

        _sharesToMint = totalLiabilities > 0
            ? (_reserveIncrement * totalSupply()) / totalLiabilities
            : _reserveIncrement;
    }

    /**
     * @notice Get a quote for the effective amount of tokens for a deposit
     * @param _depositAmount The amount to be deposited
     * @return _sharesToMint Total number of pool LP tokens to be minted (incl. slippage)
     */
    function quoteDeposit(
        uint256 _depositAmount
    ) external view returns (uint256 _sharesToMint) {
        (, _sharesToMint, ) = _calculateDepositParameters(_depositAmount);
    }

    /**
     * @notice Withdraws liquidity amount of asset ensuring minimum amount required
     * @param _sharesToBurn The liquidity to be withdrawn
     * @param _minimumAmount The minimum amount that will be accepted by user
     * @return _payoutAmount Amount withdrawn after applying withdrawal fee
     * @return _fee The penalty due to slippage (a positive fee)
     */
    function withdraw(
        uint256 _sharesToBurn,
        uint256 _minimumAmount
    ) external nonReentrant returns (uint256 _payoutAmount, int256 _fee) {
        uint256 _reserveDecrement;
        uint256 _slippagePenalty;
        (
            _reserveDecrement,
            _payoutAmount,
            _slippagePenalty
        ) = _calculateWithdrawalParameters(_sharesToBurn);
        require(_payoutAmount >= _minimumAmount, "withdraw: MINIMUM_AMOUNT");

        totalLiabilities -= _reserveDecrement;
        reserve -= _reserveDecrement;
        reserveWithSlippage -= _payoutAmount;

        _processWithdrawal(msg.sender, _sharesToBurn, _payoutAmount);

        _fee = _slippagePenalty.toInt256();

        emit Burn(msg.sender, _sharesToBurn, _payoutAmount);
    }

    /**
     * @notice complete calculation involved in a deposit
     */
    function _calculateWithdrawalParameters(
        uint256 _sharesToBurn
    )
        internal
        view
        returns (
            uint256 _reserveDecrement,
            uint256 _payoutAmount,
            uint256 _slippagePenalty
        )
    {
        _reserveDecrement = sharesTargetWorth(_sharesToBurn);
        uint256 _newReserveWithSlippage = slippageCurve.psi(
            reserve - _reserveDecrement,
            totalLiabilities - _reserveDecrement,
            poolAssetDecimals
        );
        // fix potential numerical imprecission
        if (_newReserveWithSlippage > reserveWithSlippage) {
            _newReserveWithSlippage = reserveWithSlippage;
        }

        _payoutAmount = reserveWithSlippage - _newReserveWithSlippage;
        // fix potential numerical imprecission
        if (_payoutAmount > _reserveDecrement) {
            _payoutAmount = _reserveDecrement;
        }

        _slippagePenalty = _reserveDecrement - _payoutAmount;
    }

    /**
     * @notice Get a quote for the effective amount of tokens for a withdrawal
     * @param _sharesToBurn Total number of pool LP tokens to be burned
     * @return _payoutAmount The amount to be withdrawn (incl. slippage)
     */
    function quoteWithdraw(
        uint256 _sharesToBurn
    ) external view returns (uint256 _payoutAmount) {
        (, _payoutAmount, ) = _calculateWithdrawalParameters(_sharesToBurn);
    }

    /**
     * @notice Burns LP tokens of owner, will get compensated using backstop liquidity
     * @notice Can only be invoked by backstop pool, disabled when pool is paused
     * @param _owner The LP's address whose LP tokens should be burned
     * @param _sharesToBurn The number of LP tokens to burn
     * @return _amount The amount of `asset()` tokens that the burned shares were worth
     */
    function backstopBurn(
        address _owner,
        uint256 _sharesToBurn
    )
        external
        onlyBackstop
        nonReentrant
        whenNotPaused
        returns (uint256 _amount)
    {
        require(
            balanceOf(_owner) >= _sharesToBurn,
            "SwapPool#backstopBurn: BALANCE_TOO_LOW"
        );
        require(
            block.number - latestDepositAtBlockNo[_owner] >=
                insuranceWithdrawalTimelock,
            "SwapPool#backstopBurn: TIMELOCK"
        );

        _amount = sharesTargetWorth(_sharesToBurn);

        require(
            reserve + _amount <= totalLiabilities,
            "SwapPool#backstopBurn():INSUFFICIENT_COVERAGE"
        );

        _burn(_owner, _sharesToBurn);
        totalLiabilities -= _amount;
        uint256 _newReserveWithSlippage = slippageCurve.psi(
            reserve,
            totalLiabilities,
            poolAssetDecimals
        );

        // fix potential numerical imprecission
        if (_newReserveWithSlippage < reserveWithSlippage) {
            reserveWithSlippage = _newReserveWithSlippage;
        }
    }

    /**
     * @notice For backstop pool to withdraw liquidity if swap pool's coverage ratio > 100%
     * @notice Can only be invoked by backstop pool
     * @param _amount The amount of `asset()` tokens to be moved out of swap pool
     * @param _recipient Address to send the funds to
     */
    function backstopDrain(
        uint256 _amount,
        address _recipient
    ) external onlyBackstop nonReentrant returns (uint256 _swapAmount) {
        uint256 _newReserveWithSlippage;
        (
            _swapAmount,
            _newReserveWithSlippage
        ) = _calculateBackstopDrainParameters(_amount);

        reserve -= _amount;

        if (_swapAmount > 0) {
            poolAsset.safeTransfer(_recipient, _swapAmount);
            reserveWithSlippage = _newReserveWithSlippage;
        }

        emit BackstopDrain(_recipient, _swapAmount);
    }

    /**
     * @notice Calculation involved in a backstop drain
     * @param _amount The amount of tokens to be withdrawn for backstop liquidity
     * @return _swapAmount The amount to be withdrawn (incl. slippage)
     * @return _newReserveWithSlippage The new value of `reserveWithSlippage` after this backstop drain
     */
    function _calculateBackstopDrainParameters(
        uint256 _amount
    )
        internal
        view
        returns (uint256 _swapAmount, uint256 _newReserveWithSlippage)
    {
        require(
            reserve >= totalLiabilities + _amount,
            "SwapPool#backstopDrain():INSUFFICIENT_COVERAGE"
        );

        _newReserveWithSlippage = slippageCurve.psi(
            reserve - _amount,
            totalLiabilities,
            poolAssetDecimals
        );

        // we carefully avoid potential numerical imprecission
        if (_newReserveWithSlippage < reserveWithSlippage) {
            _swapAmount = reserveWithSlippage - _newReserveWithSlippage;
        } else {
            _swapAmount = 0;
        }
    }

    /**
     * @notice Get a quote for the effective amount of tokens for a backstop drain
     * @param _amount The amount of tokens to be withdrawn for backstop liquidity
     * @return _swapAmount The amount to be withdrawn (incl. slippage)
     */
    function quoteBackstopDrain(
        uint256 _amount
    ) external view returns (uint256 _swapAmount) {
        (_swapAmount, ) = _calculateBackstopDrainParameters(_amount);
    }

    /**
     * @notice Get called by Router to deposit an amount of pool asset
     * @notice Can only be called by Router
     * @param _amount The amount of asset to swap into the pool
     * @return _effectiveAmount Effective amount, incl. slippage (rewards or penalties)
     */
    function swapIntoFromRouter(
        uint256 _amount
    )
        external
        onlyRouter
        whenNotPaused
        nonReentrant
        returns (uint256 _effectiveAmount)
    {
        _effectiveAmount = quoteSwapInto(_amount);
        reserve += _effectiveAmount;
        reserveWithSlippage += _amount;

        poolAsset.safeTransferFrom(msg.sender, address(this), _amount);
    }

    /**
     * @notice Get a quote for the effective amount of tokens for a swap into
     * @param _amount The amount of asset to swap into the pool
     * @return _effectiveAmount Effective amount, incl. slippage (penalty or rewards)
     */
    function quoteSwapInto(
        uint256 _amount
    ) public view returns (uint256 _effectiveAmount) {
        _effectiveAmount = slippageCurve.inverseHorizontal(
            reserve,
            totalLiabilities,
            reserveWithSlippage + _amount,
            poolAssetDecimals
        );

        require(
            (reserve + _effectiveAmount) <=
                (maxCoverageRatioForSwapIn * totalLiabilities) / 100,
            "SwapPool: EXCEEDS_MAX_COVERAGE_RATIO"
        );
    }

    /**
     * @notice get called by Router to withdraw amount of pool asset
     * @notice Can only be called by Router
     * @param _amount The amount of asset to withdraw
     * @return _effectiveAmount actual withdraw amount
     */
    function swapOutFromRouter(
        uint256 _amount
    )
        external
        onlyRouter
        whenNotPaused
        nonReentrant
        returns (uint256 _effectiveAmount)
    {
        uint256 _protocolFeeWithSlippage;
        uint256 _effectiveLpFee;
        uint256 _newReserve;
        uint256 _backstopFee;

        (
            _effectiveAmount,
            _protocolFeeWithSlippage,
            _effectiveLpFee,
            _newReserve,
            _backstopFee
        ) = _calculateSwapOutParameters(_amount);

        require(
            _effectiveAmount + _protocolFeeWithSlippage <=
                poolAsset.balanceOf(address(this)),
            "SwapPool#swapOutFromRouter: OUT_OF_FUNDS"
        );

        totalLiabilities += _effectiveLpFee;
        reserve = _newReserve;
        reserveWithSlippage -= _effectiveAmount + _protocolFeeWithSlippage;

        emit ChargedSwapFees(
            _effectiveLpFee,
            _backstopFee,
            _protocolFeeWithSlippage
        );

        if (_effectiveAmount > 0) {
            poolAsset.safeTransfer(msg.sender, _effectiveAmount);
        }

        if (_protocolFeeWithSlippage > 0) {
            poolAsset.safeTransfer(protocolTreasury, _protocolFeeWithSlippage);
        }
    }

    /**
     * @notice Get a quote for the effective amount of tokens, incl. slippage and fees
     * @param _amount The amount of asset to swap out of the pool
     * @return _effectiveAmount Effective amount, incl. slippage and fees
     * @return _protocolFeeWithSlippage The protocol fee that is to be sent to the treasury
     * @return _effectiveLpFee The actual LP fee – totalLiabilities should be incremented by this value
     * @return _backstopFee The effective backstop fee
     */
    function quoteSwapOut(
        uint256 _amount
    )
        public
        view
        returns (
            uint256 _effectiveAmount,
            uint256 _protocolFeeWithSlippage,
            uint256 _effectiveLpFee,
            uint256 _backstopFee
        )
    {
        (
            _effectiveAmount,
            _protocolFeeWithSlippage,
            _effectiveLpFee,
            ,
            _backstopFee
        ) = _calculateSwapOutParameters(_amount);
    }

    /**
     * @notice Complete calculation involved in a swap out operation
     * @param _amount The raw amount of assets to swap out
     * @return _effectiveAmount The actual amount to return to the user
     * @return _protocolFeeWithSlippage The protocol fee that is to be sent to the treasury
     * @return _effectiveLpFee The actual LP fee – totalLiabilities should be incremented by this value
     * @return _newReserve The new value of `reserve` after this swap out
     * @return _backstopFee The effective backstop fee
     */
    function _calculateSwapOutParameters(
        uint256 _amount
    )
        internal
        view
        returns (
            uint256 _effectiveAmount,
            uint256 _protocolFeeWithSlippage,
            uint256 _effectiveLpFee,
            uint256 _newReserve,
            uint256 _backstopFee
        )
    {
        uint256 _lpFee = (_amount * swapFeeConfig.lpFee) / 1_000_000;
        _backstopFee = (_amount * swapFeeConfig.backstopFee) / 1_000_000;
        uint256 _protocolFee = (_amount * swapFeeConfig.protocolFee) /
            1_000_000;

        uint256 _reducedReserve = _backstopFee + _protocolFee < _amount
            ? reserve + _backstopFee + _protocolFee - _amount
            : reserve;

        _effectiveLpFee = slippageCurve.inverseDiagonal(
            _reducedReserve,
            totalLiabilities,
            reserveWithSlippage,
            poolAssetDecimals
        );

        if (_effectiveLpFee > _lpFee) {
            _effectiveLpFee = _lpFee;
        }

        uint256 _reserveWithSlippageAfterAmountOut = slippageCurve.psi(
            _reducedReserve + _effectiveLpFee,
            totalLiabilities + _effectiveLpFee,
            poolAssetDecimals
        );

        if (_reserveWithSlippageAfterAmountOut > reserveWithSlippage) {
            _reserveWithSlippageAfterAmountOut = reserveWithSlippage;
        }

        _effectiveAmount =
            reserveWithSlippage -
            _reserveWithSlippageAfterAmountOut;

        _newReserve = _reducedReserve + _effectiveLpFee - _protocolFee;

        uint256 _newReserveWithSlippage = slippageCurve.psi(
            _newReserve,
            totalLiabilities + _effectiveLpFee,
            poolAssetDecimals
        );

        _protocolFeeWithSlippage = _newReserveWithSlippage >
            _reserveWithSlippageAfterAmountOut
            ? 0
            : _reserveWithSlippageAfterAmountOut - _newReserveWithSlippage;
    }

    /**
     * @notice Pause deposits and swaps
     */
    function pause() external onlyOwner {
        _pause();
    }

    /**
     * @notice Resume deposits and swaps
     */
    function unpause() external onlyOwner {
        _unpause();
    }

    /**
     * @notice returns pool coverage ratio
     * @return _reserves Current amount of usable pool tokens (without slippage)
     * @return _liabilities total amount of `asset` deposited by liquidity providers
     */
    function coverage()
        external
        view
        returns (uint256 _reserves, uint256 _liabilities)
    {
        _reserves = reserve;
        _liabilities = totalLiabilities;
    }

    /**
     * @notice Computes the excess liquidity that forms that valuation of the backstop pool
     * @notice is defined as b + C - B - L where
     * @notice    - b is reserve
     * @notice    - C is the amount of pool tokens in the pool
     * @notice    - B is reserveWithSlippage
     * @notice    - L is totalLiabilities
     * @return _excessLiquidity The total excess liquidity of this pool, can be negative
     */
    function getExcessLiquidity()
        external
        view
        returns (int256 _excessLiquidity)
    {
        _excessLiquidity =
            reserve.toInt256() +
            (poolAsset.balanceOf(address(this))).toInt256() -
            reserveWithSlippage.toInt256() -
            totalLiabilities.toInt256();
    }

    /**
     * @notice Return the earliest block no that insurance withdrawals are possible.
     * @param _liquidityProvider    Address of some account, usually the caller
     * @return _unlockedOnBlockNo   Block number of the first block after time lock runs out
     */
    function insuranceWithdrawalUnlock(
        address _liquidityProvider
    ) external view returns (uint256 _unlockedOnBlockNo) {
        _unlockedOnBlockNo = (latestDepositAtBlockNo[_liquidityProvider] +
            insuranceWithdrawalTimelock);
    }

    /**
     * @notice Returns the worth of an amount of pool shares (LP tokens) in underlying principle
     * @param _sharesToBurn The number of LP tokens to burn
     * @return _amount The amount of `asset()` tokens that the shares are worth
     */
    function sharesTargetWorth(
        uint256 _sharesToBurn
    ) public view returns (uint256 _amount) {
        _amount = (_sharesToBurn * totalLiabilities) / totalSupply();
    }

    /**
     * @notice Return the configured swap fees for this pool
     * @return _lpFee         Fee that benefits the pool's LPers,   in 0.0001% (e.g. 300 -> 0.0300%)
     * @return _backstopFee   Fee that benefits the backstop pool,  in 0.0001% (e.g. 150 -> 0.0150%)
     * @return _protocolFee   Fee that benefits the protocol,       in 0.0001% (e.g.  50 -> 0.0050%)
     */
    function swapFees()
        public
        view
        returns (uint256 _lpFee, uint256 _backstopFee, uint256 _protocolFee)
    {
        SwapFees memory _fees = swapFeeConfig;
        _lpFee = _fees.lpFee;
        _backstopFee = _fees.backstopFee;
        _protocolFee = _fees.protocolFee;
    }
}

File 30 of 44 : DSMath.sol
/// math.sol -- mixin for inline numerical wizardry

// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.

// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.

// You should have received a copy of the GNU General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

// SPDX-License-Identifier: MIT

pragma solidity 0.8.13;

library DSMath {
    uint256 public constant WAD = 10**18;
    uint256 public constant RAY = 10**27;

    //rounds to zero if x*y < WAD / 2
    function wmul(uint256 x, uint256 y) internal pure returns (uint256) {
        return ((x * y) + (WAD / 2)) / WAD;
    }

    //rounds to zero if x*y < WAD / 2
    function wdiv(uint256 x, uint256 y) internal pure returns (uint256) {
        return ((x * WAD) + (y / 2)) / y;
    }

    function reciprocal(uint256 x) internal pure returns (uint256) {
        return wdiv(WAD, x);
    }

    // This famous algorithm is called "exponentiation by squaring"
    // and calculates x^n with x as fixed-point and n as regular unsigned.
    //
    // It's O(log n), instead of O(n) for naive repeated multiplication.
    //
    // These facts are why it works:
    //
    //  If n is even, then x^n = (x^2)^(n/2).
    //  If n is odd,  then x^n = x * x^(n-1),
    //   and applying the equation for even x gives
    //    x^n = x * (x^2)^((n-1) / 2).
    //
    //  Also, EVM division is flooring and
    //    floor[(n-1) / 2] = floor[n / 2].
    //
    function rpow(uint256 x, uint256 n) internal pure returns (uint256 z) {
        z = n % 2 != 0 ? x : RAY;

        for (n /= 2; n != 0; n /= 2) {
            x = rmul(x, x);

            if (n % 2 != 0) {
                z = rmul(z, x);
            }
        }
    }

    //rounds to zero if x*y < WAD / 2
    function rmul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        z = ((x * y) + (RAY / 2)) / RAY;
    }
}

File 31 of 44 : IBackstopPool.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import {IPool} from "./IPool.sol";
import {IRouter} from "./IRouter.sol";

/**
 * @notice Public functions of the backstop pool.
 */
interface IBackstopPool is IPool {
    /**
     * @notice emitted on every withdrawal
     * @notice special case withdrawal using swap liquidiity: amountPrincipleWithdrawn = 0
     */
    event Burn(
        address indexed sender,
        uint256 poolSharesBurned,
        uint256 amountPrincipleWithdrawn
    );

    /**
     * @notice emitted when a swap pool LP withdraws from backstop pool
     */
    event CoverSwapWithdrawal(
        address indexed owner,
        address swapPool,
        uint256 amountSwapShares,
        uint256 amountSwapTokens,
        uint256 amountBackstopTokens
    );

    /**
     * @notice emitted when the insurance fee is set for a swap pool
     */
    event InsuranceFeeSet(
        address indexed sender,
        address indexed swapPool,
        uint256 insuranceFeeBps
    );

    /**
     * @notice emitted on every deposit
     */
    event Mint(
        address indexed sender,
        uint256 poolSharesMinted,
        uint256 amountPrincipleDeposited
    );

    /**
     * @notice emitted when a new swap pool is added to the backstop pool for coverage
     */
    event SwapPoolAdded(
        address indexed sender,
        address swapPool
    );

    /**
     * @notice emitted when a backstop pool LP withdraws liquidity from swap pool
     */
    event WithdrawSwapLiquidity(
        address indexed owner,
        address swapPool,
        uint256 amountSwapTokens,
        uint256 amountBackstopTokens
    );

    function redeemSwapPoolShares(
        address swapPool,
        uint256 shares,
        uint256 minAmount,
        bytes[] calldata _priceUpdateData
    ) external payable returns (uint256 amount);

    function withdrawExcessSwapLiquidity(
        address swapPool,
        uint256 shares,
        uint256 minAmount,
        bytes[] calldata _priceUpdateData
    ) external payable returns (uint256 amount);

    function deposit(
        uint256 _amount,
        bytes[] calldata _priceUpdateData
    ) external payable returns (uint256 poolShares, int256 fee);

    function getBackedPool(
        uint256 index
    ) external view returns (address swapPool);

    function getBackedPoolCount() external view returns (uint256 count);

    function getInsuranceFee(
        address swapPool
    ) external view returns (uint256 feeBps);

    function getTotalPoolWorth(
        uint256[] memory _tokenPrices
    ) external view returns (int256 value);

    function router() external view returns (IRouter _router);

    function withdraw(
        uint256 shares,
        uint256 minimumAmount,
        bytes[] calldata _priceUpdateData
    ) external payable returns (uint256 finalAmount, int256 fee);
}

interface IBackstopPoolPermissioned is IBackstopPool {
    function addSwapPool(address swapPool, uint256 insuranceFeeBps) external;
}

File 32 of 44 : IChainlinkAggregatorV3.sol
// Source: https://github.com/smartcontractkit/chainlink/blob/b88c0e508671aa0ca4f509bc3752f05b8bd6f430/contracts/src/v0.8/interfaces/AggregatorV3Interface.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

interface AggregatorV3Interface {
    function decimals() external view returns (uint8);

    function description() external view returns (string memory);

    function version() external view returns (uint256);

    function getRoundData(uint80 _roundId)
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        );

    function latestRoundData()
        external
        view
        returns (
            uint80 roundId,
            int256 answer,
            uint256 startedAt,
            uint256 updatedAt,
            uint80 answeredInRound
        );
}

File 33 of 44 : IGenericPool.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

/**
 * @notice Generic ERC20 pool interface, public functions.
 */
interface IGenericPool {
    function asset() external view returns (address _token);

    function poolCap() external view returns (uint256 _maxTokens);

    function assetDecimals() external view returns (uint8 _decimals);
}

/**
 * @notice Access-restricted functions of the IGenericPool.
 */
interface IGenericPoolPermissioned is IGenericPool {
    function setPoolCap(uint256 _maxTokens) external;
}

File 34 of 44 : IPool.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {IGenericPoolPermissioned} from "./IGenericPool.sol";

/**
 * @notice Public functions of ERC20 pool interface.
 */
interface IPool is IGenericPoolPermissioned {
    // Shared interface of Backstop Pool and Swap Pools have diverged too much.
    // Keep this file for now, but it should be removed in the future.
}

File 35 of 44 : IPriceOracleGetter.sol
// Based on AAVE protocol
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;

/// @title IPriceOracleGetter interface
interface IPriceOracleGetter {
    /// @dev returns the asset price in USD
    function getAssetPrice(address _asset) external view returns (uint256);
}

File 36 of 44 : IPythAdapter.sol
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.0;

import {IPyth} from "@pythnetwork/pyth-sdk-solidity/IPyth.sol";

/**
 * @title PythAdapter interface
 */
interface IPythAdapter {
    /**
     * @notice Registers a new Pyth price feed
     * @param _asset ERC-20 token address
     * @param _priceFeedId Pyth price feed id
     * @return success Confirmation of success
     */
    function registerPriceFeed(
        address _asset,
        bytes32 _priceFeedId
    ) external returns (bool success);

    /**
     * @notice Updates one or more Pyth price feeds
     * @param _priceUpdateData Pyth price update data
     * @return success Confirmation of success
     * @dev This function needs to be called right before calling getAssetPrice()
     */
    function updatePriceFeeds(
        bytes[] calldata _priceUpdateData
    ) external payable returns (bool success);

    /**
     * @notice Returns the asset price
     * @param _asset ERC-20 token address
     * @return assetPrice Asset price in USD with 18 decimal places
     * @dev This function reverts with a StalePrice error if the on-chain price has not been updated within the last getValidTimePeriod() seconds via updateAssetPrice()
     */
    function getAssetPrice(
        address _asset
    ) external view returns (uint256 assetPrice);

    /**
     * @notice Check if Pyth price feed is registered
     * @param _asset ERC-20 token address
     * @return isRegistered Confirmation of registration
     */
    function isPriceFeedRegistered(
        address _asset
    ) external view returns (bool isRegistered);

    /**
     * @notice Returns the required fee to update an array of price updates
     * @param _updateData Array of price update data
     * @return updateFee Update fee in Wei
     */
    function getUpdateFee(
        bytes[] calldata _updateData
    ) external view returns (uint256 updateFee);
}

File 37 of 44 : IRouter.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import {ISwapPoolPermissioned} from "../interfaces/ISwapPool.sol";
import {IPythAdapter} from "../interfaces/IPythAdapter.sol";

interface IRouter {
    /** Emitted on each swap */
    event Swap(
        address indexed sender,
        uint256 amountIn,
        uint256 amountOut,
        address tokenIn,
        address tokenOut,
        address indexed to
    );

    /** Emitted when a new pool is registered */
    event SwapPoolRegistered(
        address indexed sender,
        address pool,
        address asset
    );

    /** Emitted when a pool gets unregistered */
    event SwapPoolUnregistered(address indexed sender, address asset);

    /** Emitted when a new PythAdapter (price oracle) is set */
    event PythAdapterSet(address indexed sender, address pythAdapter);

    function pythAdapter() external view returns (IPythAdapter);

    function poolByAsset(
        address asset
    ) external view returns (ISwapPoolPermissioned);

    function swapExactTokensForTokens(
        uint256 _amountIn,
        uint256 _amountOutMin,
        address[] calldata _tokenInOut,
        address _to,
        uint _deadline,
        bytes[] calldata _priceUpdateData
    ) external payable returns (uint256[] memory _amounts);

    function getAmountOut(
        uint _amountIn,
        address[] calldata _tokenInOut,
        uint256[] calldata _tokenPrices
    ) external view returns (uint256 _amountOut, uint256 _swapFee);
}

interface IRouterPermissioned is IRouter {
    function pause() external;

    function unpause() external;
}

File 38 of 44 : ISlippageCurve.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

/**
 * @notice This defines a slippage curve using the definition in '23-11-21 Fixed Nabla Spec II'
 */
interface ISlippageCurve {
    /**
     * @notice Calculates the output of the function Psi for input values b and l
     * @param _b the input value `b`
     * @param _l the input value `l`
     * @param _decimals the number of decimals used for _b and _l and also for the return value
     * @return _psi the value Psi(b,l)
     */
    function psi(
        uint256 _b,
        uint256 _l,
        uint8 _decimals
    ) external view returns (uint256 _psi);

    /**
     * @notice Given b,l and B >= Psi(b, l), this function determines the unique t>=0
        such that Psi(b+t, l+t) = B
     * @param _b the input value `b`
     * @param _l the input value `l`
     * @param _capitalB the input value `B`
     * @param _decimals the number of decimals used for _b, _l, _capitalB and also for the return value
     * @return _t the number t
     */
    function inverseDiagonal(
        uint256 _b,
        uint256 _l,
        uint256 _capitalB,
        uint8 _decimals
    ) external view returns (uint256 _t);

    /**
     * @notice Given b,l and B >= Psi(b, l), this function determines the unique t>=0
        such that Psi(b+t, l) = B
     * @param _b the input value `b`
     * @param _l the input value `l`
     * @param _capitalB the input value `B`
     * @param _decimals the number of decimals used for _b, _l, _capitalB and also for the return value
     * @return _t the number t
     */
    function inverseHorizontal(
        uint256 _b,
        uint256 _l,
        uint256 _capitalB,
        uint8 _decimals
    ) external view returns (uint256 _t);
}

File 39 of 44 : ISwapPool.sol
//SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.0;

import {IBackstopPool} from "./IBackstopPool.sol";
import {IPool} from "./IPool.sol";
import {IRouter} from "./IRouter.sol";
import {ISlippageCurve} from "./ISlippageCurve.sol";

/**
 * @notice Public functions of the SwapPool.
 */
interface ISwapPool is IPool {
    /**
     * @notice emitted on every withdrawal
     * @notice special case withdrawal using backstop liquidiity: amountPrincipleWithdrawn = 0
     */
    event Burn(
        address indexed sender,
        uint256 poolSharesBurned,
        uint256 amountPrincipleWithdrawn
    );

    /**
     * @notice Tracks the exact amounts of individual fees paid during a swap
     */
    event ChargedSwapFees(
        uint256 lpFees,
        uint256 backstopFees,
        uint256 protocolFees
    );

    /**
     * @notice emitted on every deposit
     */
    event Mint(
        address indexed sender,
        uint256 poolSharesMinted,
        uint256 amountPrincipleDeposited
    );

    function coverage()
        external
        view
        returns (uint256 reserves, uint256 liabilities);

    function deposit(
        uint256 amount
    ) external returns (uint256 poolShares, int256 fee);

    function totalLiabilities()
        external
        view
        returns (uint256 totalLiabilities);

    function reserve() external view returns (uint256 reserve);

    function reserveWithSlippage()
        external
        view
        returns (uint256 reserveWithSlippage);

    function slippageCurve()
        external
        view
        returns (ISlippageCurve slippageCurve);

    function backstop() external view returns (IBackstopPool _pool);

    function protocolTreasury() external view returns (address);

    function quoteSwapInto(
        uint256 _amount
    ) external view returns (uint256 _effectiveAmount);

    function quoteSwapOut(
        uint256 _amount
    )
        external
        view
        returns (
            uint256 _effectiveAmount,
            uint256 _protocolFeeWithSlippage,
            uint256 _effectiveLpFee,
            uint256 _backstopFee
        );

    function router() external view returns (IRouter _router);

    function sharesTargetWorth(
        uint256 _shares
    ) external view returns (uint256 _amount);

    function getExcessLiquidity()
        external
        view
        returns (int256 _excessLiquidity);
}

/**
 * @notice Access-restricted functions of the SwapPool.
 */
interface ISwapPoolPermissioned is ISwapPool {
    /**
     * @notice emitted when a backstop pool LP withdraws liquidity from swap pool
     * @notice only possible if swap pool coverage ratio remains >= 100%
     */
    event BackstopDrain(address recipient, uint256 amountSwapTokens);

    /// @notice for swap pool LP backstop withdrawal
    /// @param shares    number of lp tokens to burn
    function backstopBurn(
        address owner,
        uint256 shares
    ) external returns (uint256 amount);

    /// @notice for backstop pool to withdraw liquidity if swap pool's coverage ratio > 100%
    /// @param amount   amount of swap pool reserves to withdraw
    function backstopDrain(
        uint256 amount,
        address recipient
    ) external returns (uint256 swapAmount);

    /**
     * @notice Set swap fees (applied when swapping funds out of the pool)
     * @param _lpFee         Fee that benefits the pool's LPers,    in 0.0001% (e.g. 300 -> 0.0300%)
     * @param _backstopFee   Fee that benefits the backstop pool,   in 0.0001% (e.g. 150 -> 0.0150%)
     * @param _protocolFee   Fee that benefits the protocol,        in 0.0001% (e.g.  50 -> 0.0050%)
     */
    function setSwapFees(
        uint256 _lpFee,
        uint256 _backstopFee,
        uint256 _protocolFee
    ) external;

    function swapIntoFromRouter(
        uint256 amount
    ) external returns (uint256 effectiveAmount);

    function swapOutFromRouter(
        uint256 amount
    ) external returns (uint256 effectiveAmount);

    function pause() external;

    function unpause() external;
}

File 40 of 44 : AccessControlERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import {
    ERC20Burnable
} from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import {AccessControl} from "@openzeppelin/contracts/access/AccessControl.sol";

contract AccessControlERC20 is ERC20, ERC20Burnable, AccessControl {
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");

    constructor(
        string memory _name,
        string memory _symbol
    ) ERC20(_name, _symbol) {
        _grantRole(DEFAULT_ADMIN_ROLE, msg.sender);
        _grantRole(MINTER_ROLE, msg.sender);
    }

    function mint(address _to, uint256 _amount) public onlyRole(MINTER_ROLE) {
        _mint(_to, _amount);
    }
}

File 41 of 44 : MockERC20.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.13;

import {ERC20} from "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MockERC20 is ERC20 {
    constructor(string memory _name, string memory _symbol)
        ERC20(_name, _symbol)
    {}

    function mint(address _to, uint256 _amount) public virtual {
        _mint(_to, _amount);
    }

    function burn(address from, uint256 _amount) public virtual {
        _burn(from, _amount);
    }
}

File 42 of 44 : MockOracle.sol
// SPDX-License-Identifier: UNLICENSED
pragma solidity 0.8.13;

import {IPriceOracleGetter} from "../interfaces/IPriceOracleGetter.sol";

contract MockOracle is IPriceOracleGetter {
    mapping(address => uint256) currentPrices;

    constructor(address _asset, uint256 _price) {
        currentPrices[_asset] = _price;
    }

    function updateCurrentPrices(address _asset, uint256 _price)
        external
        returns (uint256)
    {
        return currentPrices[_asset] = _price;
    }

    /// @dev returns the asset price in ETH
    function getAssetPrice(address _asset) external view returns (uint256) {
        return currentPrices[_asset];
    }
}

File 43 of 44 : MockPythAdapter.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import {IPythAdapter} from "../../src/interfaces/IPythAdapter.sol";

contract MockPythAdapter is IPythAdapter {
    uint256 public immutable UPDATE_FEE;

    mapping(address => bytes32) private priceFeedIdByAsset;
    mapping(address => uint256) private assetPrices;

    /**
     * @notice MockPythAdapter: Constructor
     * @param updateFee Update fee in Wei
     */
    constructor(uint256 updateFee) {
        UPDATE_FEE = updateFee;
    }

    /**
     * @notice MockPythAdapter: Registers a new Pyth price feed
     * @param _asset ERC-20 token address
     * @param _priceFeedId Pyth price feed id
     * @return success Confirmation of success
     */
    function registerPriceFeed(
        address _asset,
        bytes32 _priceFeedId
    ) external returns (bool success) {
        require(_asset != address(0), "MOCK_PYTH_ADAPTER: NO_ASSET");
        require(_priceFeedId != bytes32(0), "MOCK_PYTH_ADAPTER: NO_PRICE_FEED");
        require(
            !isPriceFeedRegistered(_asset),
            "MOCK_PYTH_ADAPTER: PRICE_FEED_ALREADY_REGISTERED"
        );

        priceFeedIdByAsset[_asset] = _priceFeedId;

        return true;
    }

    /**
     * @notice MockPythAdapter: Mock function that updates one or more Pyth price feeds
     * @param _priceUpdateData Pyth price update data
     * @return success Confirmation of success
     * @dev This function needs to be called right before calling getAssetPrice()
     */
    function updatePriceFeeds(
        bytes[] calldata _priceUpdateData
    ) external payable returns (bool success) {
        require(
            _priceUpdateData.length != 0,
            "MOCK_PYTH_ADAPTER: NO_UPDATE_DATA"
        );

        // Get the update fee
        uint256 updateFee = getUpdateFee(_priceUpdateData);
        require(msg.value >= updateFee, "MOCK_PYTH_ADAPTER: INSUFFICIENT_FEE");

        return true;
    }

    /**
     * @notice MockPythAdapter: Returns the asset price that was manually set
     * @param _asset ERC-20 token address
     * @return assetPrice Asset price
     */
    function getAssetPrice(
        address _asset
    ) external view returns (uint256 assetPrice) {
        require(_asset != address(0), "MOCK_PYTH_ADAPTER: NO_ASSET");
        require(
            isPriceFeedRegistered(_asset),
            "MOCK_PYTH_ADAPTER: PRICE_FEED_NOT_REGISTERED"
        );

        return assetPrices[_asset];
    }

    /**
     * @notice MockPythAdapter: Check if Pyth price feed is registered
     * @param _asset ERC-20 token address
     * @return isRegistered Confirmation of registration
     */
    function isPriceFeedRegistered(
        address _asset
    ) public view returns (bool isRegistered) {
        return (priceFeedIdByAsset[_asset] != bytes32(0));
    }

    /**
     * @notice MockPythAdapter: Returns the required fee to update an array of price updates
     * @param _updateData Array of price update data
     * @return updateFee Update fee in Wei
     */
    function getUpdateFee(
        bytes[] calldata _updateData
    ) public view returns (uint256 updateFee) {
        return (_updateData.length * UPDATE_FEE);
    }

    /**
     * @notice MockPythAdapter: Sets the asset price for registered price feeds
     * @param _asset ERC-20 token address
     * @param _price Asset price
     * @return success Confirmation of success
     */
    function setAssetPrice(
        address _asset,
        uint256 _price
    ) external returns (bool success) {
        require(_asset != address(0), "MOCK_PYTH_ADAPTER: NO_ASSET");
        require(
            isPriceFeedRegistered(_asset),
            "MOCK_PYTH_ADAPTER: PRICE_FEED_NOT_REGISTERED"
        );
        require(_price > 0, "MOCK_PYTH_ADAPTER: INVALID_PRICE");

        assetPrices[_asset] = _price;

        return true;
    }
}

File 44 of 44 : MultiCall.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

contract MultiCall {
    function multiCall(
        address[] calldata targets,
        bytes[] calldata data,
        uint256[] calldata values
    ) external payable returns (bytes[] memory results) {
        require(
            targets.length == data.length && targets.length == values.length,
            "Input array lengths must be equal"
        );

        results = new bytes[](data.length);

        for (uint i = 0; i < targets.length; i++) {
            // Execute the transaction and store the result
            (bool success, bytes memory result) = payable(targets[i]).call{
                value: values[i]
            }(data[i]);
            require(success, "call failed");
            results[i] = result;
        }
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "metadata": {
    "useLiteralContent": true
  },
  "libraries": {
    "": {
      "__CACHE_BREAKER__": "0x0000000000000031373036393835303939393839"
    }
  }
}

Contract ABI

[{"inputs":[{"internalType":"address","name":"_pythAdapter","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"pythAdapter","type":"address"}],"name":"PythAdapterSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"},{"indexed":false,"internalType":"address","name":"tokenIn","type":"address"},{"indexed":false,"internalType":"address","name":"tokenOut","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"pool","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"}],"name":"SwapPoolRegistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"address","name":"asset","type":"address"}],"name":"SwapPoolUnregistered","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"address[]","name":"_tokenInOut","type":"address[]"},{"internalType":"uint256[]","name":"_tokenPrices","type":"uint256[]"}],"name":"getAmountOut","outputs":[{"internalType":"uint256","name":"_amountOut","type":"uint256"},{"internalType":"uint256","name":"_swapFee","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"poolByAsset","outputs":[{"internalType":"contract ISwapPoolPermissioned","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pythAdapter","outputs":[{"internalType":"contract IPythAdapter","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"},{"internalType":"address","name":"_swapPool","type":"address"}],"name":"registerPool","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_newPythAdapter","type":"address"}],"name":"setPythAdapter","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amountIn","type":"uint256"},{"internalType":"uint256","name":"_amountOutMin","type":"uint256"},{"internalType":"address[]","name":"_tokenInOut","type":"address[]"},{"internalType":"address","name":"_to","type":"address"},{"internalType":"uint256","name":"_deadline","type":"uint256"},{"internalType":"bytes[]","name":"_priceUpdateData","type":"bytes[]"}],"name":"swapExactTokensForTokens","outputs":[{"internalType":"uint256[]","name":"_amounts","type":"uint256[]"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"unpause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_asset","type":"address"}],"name":"unregisterPool","outputs":[{"internalType":"bool","name":"success","type":"bool"}],"stateMutability":"nonpayable","type":"function"}]

60806040523480156200001157600080fd5b5060405162001f7038038062001f7083398101604081905262000034916200010d565b6000805460ff19169055600180556200004d33620000bb565b6001600160a01b038116620000955760405162461bcd60e51b815260206004820152600a6024820152692727afa0a220a82a22a960b11b604482015260640160405180910390fd5b600380546001600160a01b0319166001600160a01b03929092169190911790556200013f565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6000602082840312156200012057600080fd5b81516001600160a01b03811681146200013857600080fd5b9392505050565b611e21806200014f6000396000f3fe6080604052600436106100c25760003560e01c8063715018a61161007f5780638da5cb5b116100595780638da5cb5b14610214578063aa260a4d14610232578063ada61cc314610252578063f2fde38b1461027257600080fd5b8063715018a6146101ca5780637286e5e5146101df5780638456cb59146101ff57600080fd5b806306de94d8146100c7578063259198e31461011a57806337e301df1461013a5780633f4ba83a1461015a5780635c975abb14610171578063708d4d9414610195575b600080fd5b3480156100d357600080fd5b506100fd6100e2366004611881565b6004602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b61012d6101283660046118ef565b610292565b604051610111919061198f565b34801561014657600080fd5b506003546100fd906001600160a01b031681565b34801561016657600080fd5b5061016f61045b565b005b34801561017d57600080fd5b5060005460ff165b6040519015158152602001610111565b3480156101a157600080fd5b506101b56101b03660046119d3565b61046d565b60408051928352602083019190915201610111565b3480156101d657600080fd5b5061016f61095f565b3480156101eb57600080fd5b506101856101fa366004611a4d565b610971565b34801561020b57600080fd5b5061016f610b8f565b34801561022057600080fd5b506002546001600160a01b03166100fd565b34801561023e57600080fd5b5061018561024d366004611881565b610b9f565b34801561025e57600080fd5b5061018561026d366004611881565b610c96565b34801561027e57600080fd5b5061016f61028d366004611881565b610d8b565b606061029c610e04565b834211156102e35760405162461bcd60e51b815260206004820152600f60248201526e1493d55511548e8811561412549151608a1b60448201526064015b60405180910390fd5b6102ed8383610e4a565b60006102fb8a898989610ec6565b9050888110156103455760405162461bcd60e51b8152602060048201526015602482015274524f555445523a2042454c4f575f4d494e494d554d60581b60448201526064016102da565b6040805160028082526060820183529091602083019080368337019050509150898260008151811061037957610379611a80565b602002602001018181525050808260018151811061039957610399611a80565b60209081029190910101526001600160a01b038616337f1621cb2414b25cfb014ed2e1e8051310c0f691ac8d2ed92928e804595df0553b8c848c8c6000816103e3576103e3611a80565b90506020020160208101906103f89190611881565b8d8d600181811061040b5761040b611a80565b90506020020160208101906104209190611881565b6040805194855260208501939093526001600160a01b0391821684840152166060830152519081900360800190a35098975050505050505050565b61046361134f565b61046b6113a9565b565b600080600285146104bb5760405162461bcd60e51b8152602060048201526018602482015277524f555445523a20544f4b454e5f41525241595f53495a4560401b60448201526064016102da565b858560018181106104ce576104ce611a80565b90506020020160208101906104e39190611881565b6001600160a01b0316868660008181106104ff576104ff611a80565b90506020020160208101906105149190611881565b6001600160a01b03160361056a5760405162461bcd60e51b815260206004820152601d60248201527f524f555445523a20544f4b454e5f41525241595f4455504c494341544500000060448201526064016102da565b600283146105ba5760405162461bcd60e51b815260206004820152601e60248201527f524f555445523a20544f4b454e5f50524943455f41525241595f53495a45000060448201526064016102da565b6000868660008181106105cf576105cf611a80565b90506020020160208101906105e49190611881565b90506000878760018181106105fb576105fb611a80565b90506020020160208101906106109190611881565b6001600160a01b03838116600090815260046020526040902054919250161580159061065557506001600160a01b038181166000908152600460205260409020541615155b6106a15760405162461bcd60e51b815260206004820152601c60248201527f524f555445523a2041535345545f4e4f545f524547495354455245440000000060448201526064016102da565b6000868660008181106106b6576106b6611a80565b9050602002013590506000878760018181106106d4576106d4611a80565b905060200201359050600060046000866001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a90046001600160a01b03166001600160a01b0316633c9452488d6040518263ffffffff1660e01b815260040161074591815260200190565b602060405180830381865afa158015610762573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107869190611a96565b905060006108a6828585600460008b6001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a90046001600160a01b03166001600160a01b031663c2d416016040518163ffffffff1660e01b8152600401602060405180830381865afa158015610806573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061082a9190611aaf565b6001600160a01b03808b1660009081526004602081815260409283902054835163c2d4160160e01b8152935194169363c2d416019380840193908290030181865afa15801561087d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a19190611aaf565b6113fb565b6001600160a01b03868116600090815260046020819052604080832054905163439ae12360e11b8152918201859052939450909283928392911690638735c24690602401608060405180830381865afa158015610907573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061092b9190611ad2565b929d5090945092509050806109408385611b1e565b61094a9190611b1e565b99505050505050505050509550959350505050565b61096761134f565b61046b600061148c565b600061097b61134f565b6001600160a01b0383166109bc5760405162461bcd60e51b81526020600482015260086024820152671393d7d054d4d15560c21b60448201526064016102da565b6001600160a01b0382166109fc5760405162461bcd60e51b81526020600482015260076024820152661393d7d413d3d360ca1b60448201526064016102da565b60035460405163d908f13f60e01b81526001600160a01b0385811660048301529091169063d908f13f90602401602060405180830381865afa158015610a46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a6a9190611b36565b610aa65760405162461bcd60e51b815260206004820152600d60248201526c1393d7d4149250d157d1915151609a1b60448201526064016102da565b6001600160a01b0383811660008181526004602081905260409182902080546001600160a01b0319169487169485179055905163095ea7b360e01b81529081019290925260001960248301529063095ea7b3906044016020604051808303816000875af1158015610b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3f9190611b36565b50604080516001600160a01b0384811682528516602082015233917f5d267b3a9e782bfece1ff5a68fca3aaad5ea1ea0696eab2e640c1b36a18e0c6b910160405180910390a25060015b92915050565b610b9761134f565b61046b6114de565b6000610ba961134f565b6001600160a01b038216610bec5760405162461bcd60e51b815260206004820152600a6024820152692727afa0a220a82a22a960b11b60448201526064016102da565b6003546001600160a01b0390811690831603610c395760405162461bcd60e51b815260206004820152600c60248201526b29a0a6a2afa0a220a82a22a960a11b60448201526064016102da565b600380546001600160a01b0319166001600160a01b03841690811790915560405190815233907f1b9c79d7491cbc0bf5e5a58168be17e4bedca267716c3a70eaa995f3579fa7d9906020015b60405180910390a25060015b919050565b6000610ca061134f565b6001600160a01b038216610ce15760405162461bcd60e51b81526020600482015260086024820152671393d7d054d4d15560c21b60448201526064016102da565b6001600160a01b0382811660009081526004602052604090205416610d325760405162461bcd60e51b81526020600482015260076024820152661393d7d413d3d360ca1b60448201526064016102da565b6001600160a01b03821660008181526004602090815260409182902080546001600160a01b0319169055905191825233917f7d89353254fbd375c01812e8bfeb3222f21c3e3c55f92a9040a9d8c6955428219101610c85565b610d9361134f565b6001600160a01b038116610df85760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016102da565b610e018161148c565b50565b60005460ff161561046b5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064016102da565b600354604051631df3cbc560e31b81526001600160a01b039091169063ef9e5e28903490610e7e9086908690600401611b81565b60206040518083038185885af1158015610e9c573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610ec19190611b36565b505050565b600060028314610f135760405162461bcd60e51b8152602060048201526018602482015277524f555445523a20544f4b454e5f41525241595f53495a4560401b60448201526064016102da565b83836001818110610f2657610f26611a80565b9050602002016020810190610f3b9190611881565b6001600160a01b031684846000818110610f5757610f57611a80565b9050602002016020810190610f6c9190611881565b6001600160a01b031603610fc25760405162461bcd60e51b815260206004820152601d60248201527f524f555445523a20544f4b454e5f41525241595f4455504c494341544500000060448201526064016102da565b600084846000818110610fd757610fd7611a80565b9050602002016020810190610fec9190611881565b905060008585600181811061100357611003611a80565b90506020020160208101906110189190611881565b6001600160a01b03838116600090815260046020526040902054919250161580159061105d57506001600160a01b038181166000908152600460205260409020541615155b6110a95760405162461bcd60e51b815260206004820152601c60248201527f524f555445523a2041535345545f4e4f545f524547495354455245440000000060448201526064016102da565b60035460405163b3596f0760e01b81526001600160a01b038481166004830152600092169063b3596f0790602401602060405180830381865afa1580156110f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111189190611a96565b60035460405163b3596f0760e01b81526001600160a01b0385811660048301529293506000929091169063b3596f0790602401602060405180830381865afa158015611168573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061118c9190611a96565b90506111a36001600160a01b03851633308c61151b565b6001600160a01b038085166000908152600460208190526040808320549051634d8ea83f60e01b815292931691634d8ea83f916111e6918e910190815260200190565b6020604051808303816000875af1158015611205573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112299190611a96565b905060006112a9828585600460008b6001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a90046001600160a01b03166001600160a01b031663c2d416016040518163ffffffff1660e01b8152600401602060405180830381865afa158015610806573d6000803e3d6000fd5b6001600160a01b03868116600090815260046020819052604091829020549151635f79d44f60e01b81529081018490529293501690635f79d44f906024016020604051808303816000875af1158015611306573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132a9190611a96565b96506113439150506001600160a01b038416878761158c565b50505050949350505050565b6002546001600160a01b0316331461046b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016102da565b6113b16115bc565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b60008160ff168360ff161115611449576114158284611c1c565b6114239060ff16600a611d23565b8461142e8789611d2f565b6114389190611d4e565b6114429190611d4e565b9050611483565b836114548484611c1c565b6114629060ff16600a611d23565b61146c8789611d2f565b6114769190611d2f565b6114809190611d4e565b90505b95945050505050565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6114e6610e04565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586113de3390565b6040516001600160a01b03808516602483015283166044820152606481018290526115869085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611605565b50505050565b6040516001600160a01b038316602482015260448101829052610ec190849063a9059cbb60e01b9060640161154f565b60005460ff1661046b5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b60448201526064016102da565b600061165a826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166116da9092919063ffffffff16565b905080516000148061167b57508080602001905181019061167b9190611b36565b610ec15760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016102da565b60606116e984846000856116f1565b949350505050565b6060824710156117525760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016102da565b600080866001600160a01b0316858760405161176e9190611d9c565b60006040518083038185875af1925050503d80600081146117ab576040519150601f19603f3d011682016040523d82523d6000602084013e6117b0565b606091505b50915091506117c1878383876117cc565b979650505050505050565b6060831561183b578251600003611834576001600160a01b0385163b6118345760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016102da565b50816116e9565b6116e983838151156118505781518083602001fd5b8060405162461bcd60e51b81526004016102da9190611db8565b80356001600160a01b0381168114610c9157600080fd5b60006020828403121561189357600080fd5b61189c8261186a565b9392505050565b60008083601f8401126118b557600080fd5b50813567ffffffffffffffff8111156118cd57600080fd5b6020830191508360208260051b85010111156118e857600080fd5b9250929050565b60008060008060008060008060c0898b03121561190b57600080fd5b8835975060208901359650604089013567ffffffffffffffff8082111561193157600080fd5b61193d8c838d016118a3565b909850965086915061195160608c0161186a565b955060808b0135945060a08b013591508082111561196e57600080fd5b5061197b8b828c016118a3565b999c989b5096995094979396929594505050565b6020808252825182820181905260009190848201906040850190845b818110156119c7578351835292840192918401916001016119ab565b50909695505050505050565b6000806000806000606086880312156119eb57600080fd5b85359450602086013567ffffffffffffffff80821115611a0a57600080fd5b611a1689838a016118a3565b90965094506040880135915080821115611a2f57600080fd5b50611a3c888289016118a3565b969995985093965092949392505050565b60008060408385031215611a6057600080fd5b611a698361186a565b9150611a776020840161186a565b90509250929050565b634e487b7160e01b600052603260045260246000fd5b600060208284031215611aa857600080fd5b5051919050565b600060208284031215611ac157600080fd5b815160ff8116811461189c57600080fd5b60008060008060808587031215611ae857600080fd5b505082516020840151604085015160609095015191969095509092509050565b634e487b7160e01b600052601160045260246000fd5b60008219821115611b3157611b31611b08565b500190565b600060208284031215611b4857600080fd5b8151801515811461189c57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60208082528181018390526000906040600585901b8401810190840186845b87811015611c0f57868403603f190183528135368a9003601e19018112611bc657600080fd5b8901803567ffffffffffffffff811115611bdf57600080fd5b8036038b1315611bee57600080fd5b611bfb8682898501611b58565b955050509184019190840190600101611ba0565b5091979650505050505050565b600060ff821660ff841680821015611c3657611c36611b08565b90039392505050565b600181815b80851115611c7a578160001904821115611c6057611c60611b08565b80851615611c6d57918102915b93841c9390800290611c44565b509250929050565b600082611c9157506001610b89565b81611c9e57506000610b89565b8160018114611cb45760028114611cbe57611cda565b6001915050610b89565b60ff841115611ccf57611ccf611b08565b50506001821b610b89565b5060208310610133831016604e8410600b8410161715611cfd575081810a610b89565b611d078383611c3f565b8060001904821115611d1b57611d1b611b08565b029392505050565b600061189c8383611c82565b6000816000190483118215151615611d4957611d49611b08565b500290565b600082611d6b57634e487b7160e01b600052601260045260246000fd5b500490565b60005b83811015611d8b578181015183820152602001611d73565b838111156115865750506000910152565b60008251611dae818460208701611d70565b9190910192915050565b6020815260008251806020840152611dd7816040850160208701611d70565b601f01601f1916919091016040019291505056fea264697066735822122076c633503a49c914f8f8cc7121fe6393c1415a006f76f9968ec3f84271a8b3a664736f6c634300080d0033000000000000000000000000cb76bd6aeddaddecb583b9ab3d91f9e548153ce5

Deployed Bytecode

0x6080604052600436106100c25760003560e01c8063715018a61161007f5780638da5cb5b116100595780638da5cb5b14610214578063aa260a4d14610232578063ada61cc314610252578063f2fde38b1461027257600080fd5b8063715018a6146101ca5780637286e5e5146101df5780638456cb59146101ff57600080fd5b806306de94d8146100c7578063259198e31461011a57806337e301df1461013a5780633f4ba83a1461015a5780635c975abb14610171578063708d4d9414610195575b600080fd5b3480156100d357600080fd5b506100fd6100e2366004611881565b6004602052600090815260409020546001600160a01b031681565b6040516001600160a01b0390911681526020015b60405180910390f35b61012d6101283660046118ef565b610292565b604051610111919061198f565b34801561014657600080fd5b506003546100fd906001600160a01b031681565b34801561016657600080fd5b5061016f61045b565b005b34801561017d57600080fd5b5060005460ff165b6040519015158152602001610111565b3480156101a157600080fd5b506101b56101b03660046119d3565b61046d565b60408051928352602083019190915201610111565b3480156101d657600080fd5b5061016f61095f565b3480156101eb57600080fd5b506101856101fa366004611a4d565b610971565b34801561020b57600080fd5b5061016f610b8f565b34801561022057600080fd5b506002546001600160a01b03166100fd565b34801561023e57600080fd5b5061018561024d366004611881565b610b9f565b34801561025e57600080fd5b5061018561026d366004611881565b610c96565b34801561027e57600080fd5b5061016f61028d366004611881565b610d8b565b606061029c610e04565b834211156102e35760405162461bcd60e51b815260206004820152600f60248201526e1493d55511548e8811561412549151608a1b60448201526064015b60405180910390fd5b6102ed8383610e4a565b60006102fb8a898989610ec6565b9050888110156103455760405162461bcd60e51b8152602060048201526015602482015274524f555445523a2042454c4f575f4d494e494d554d60581b60448201526064016102da565b6040805160028082526060820183529091602083019080368337019050509150898260008151811061037957610379611a80565b602002602001018181525050808260018151811061039957610399611a80565b60209081029190910101526001600160a01b038616337f1621cb2414b25cfb014ed2e1e8051310c0f691ac8d2ed92928e804595df0553b8c848c8c6000816103e3576103e3611a80565b90506020020160208101906103f89190611881565b8d8d600181811061040b5761040b611a80565b90506020020160208101906104209190611881565b6040805194855260208501939093526001600160a01b0391821684840152166060830152519081900360800190a35098975050505050505050565b61046361134f565b61046b6113a9565b565b600080600285146104bb5760405162461bcd60e51b8152602060048201526018602482015277524f555445523a20544f4b454e5f41525241595f53495a4560401b60448201526064016102da565b858560018181106104ce576104ce611a80565b90506020020160208101906104e39190611881565b6001600160a01b0316868660008181106104ff576104ff611a80565b90506020020160208101906105149190611881565b6001600160a01b03160361056a5760405162461bcd60e51b815260206004820152601d60248201527f524f555445523a20544f4b454e5f41525241595f4455504c494341544500000060448201526064016102da565b600283146105ba5760405162461bcd60e51b815260206004820152601e60248201527f524f555445523a20544f4b454e5f50524943455f41525241595f53495a45000060448201526064016102da565b6000868660008181106105cf576105cf611a80565b90506020020160208101906105e49190611881565b90506000878760018181106105fb576105fb611a80565b90506020020160208101906106109190611881565b6001600160a01b03838116600090815260046020526040902054919250161580159061065557506001600160a01b038181166000908152600460205260409020541615155b6106a15760405162461bcd60e51b815260206004820152601c60248201527f524f555445523a2041535345545f4e4f545f524547495354455245440000000060448201526064016102da565b6000868660008181106106b6576106b6611a80565b9050602002013590506000878760018181106106d4576106d4611a80565b905060200201359050600060046000866001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a90046001600160a01b03166001600160a01b0316633c9452488d6040518263ffffffff1660e01b815260040161074591815260200190565b602060405180830381865afa158015610762573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906107869190611a96565b905060006108a6828585600460008b6001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a90046001600160a01b03166001600160a01b031663c2d416016040518163ffffffff1660e01b8152600401602060405180830381865afa158015610806573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061082a9190611aaf565b6001600160a01b03808b1660009081526004602081815260409283902054835163c2d4160160e01b8152935194169363c2d416019380840193908290030181865afa15801561087d573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906108a19190611aaf565b6113fb565b6001600160a01b03868116600090815260046020819052604080832054905163439ae12360e11b8152918201859052939450909283928392911690638735c24690602401608060405180830381865afa158015610907573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061092b9190611ad2565b929d5090945092509050806109408385611b1e565b61094a9190611b1e565b99505050505050505050509550959350505050565b61096761134f565b61046b600061148c565b600061097b61134f565b6001600160a01b0383166109bc5760405162461bcd60e51b81526020600482015260086024820152671393d7d054d4d15560c21b60448201526064016102da565b6001600160a01b0382166109fc5760405162461bcd60e51b81526020600482015260076024820152661393d7d413d3d360ca1b60448201526064016102da565b60035460405163d908f13f60e01b81526001600160a01b0385811660048301529091169063d908f13f90602401602060405180830381865afa158015610a46573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610a6a9190611b36565b610aa65760405162461bcd60e51b815260206004820152600d60248201526c1393d7d4149250d157d1915151609a1b60448201526064016102da565b6001600160a01b0383811660008181526004602081905260409182902080546001600160a01b0319169487169485179055905163095ea7b360e01b81529081019290925260001960248301529063095ea7b3906044016020604051808303816000875af1158015610b1b573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b3f9190611b36565b50604080516001600160a01b0384811682528516602082015233917f5d267b3a9e782bfece1ff5a68fca3aaad5ea1ea0696eab2e640c1b36a18e0c6b910160405180910390a25060015b92915050565b610b9761134f565b61046b6114de565b6000610ba961134f565b6001600160a01b038216610bec5760405162461bcd60e51b815260206004820152600a6024820152692727afa0a220a82a22a960b11b60448201526064016102da565b6003546001600160a01b0390811690831603610c395760405162461bcd60e51b815260206004820152600c60248201526b29a0a6a2afa0a220a82a22a960a11b60448201526064016102da565b600380546001600160a01b0319166001600160a01b03841690811790915560405190815233907f1b9c79d7491cbc0bf5e5a58168be17e4bedca267716c3a70eaa995f3579fa7d9906020015b60405180910390a25060015b919050565b6000610ca061134f565b6001600160a01b038216610ce15760405162461bcd60e51b81526020600482015260086024820152671393d7d054d4d15560c21b60448201526064016102da565b6001600160a01b0382811660009081526004602052604090205416610d325760405162461bcd60e51b81526020600482015260076024820152661393d7d413d3d360ca1b60448201526064016102da565b6001600160a01b03821660008181526004602090815260409182902080546001600160a01b0319169055905191825233917f7d89353254fbd375c01812e8bfeb3222f21c3e3c55f92a9040a9d8c6955428219101610c85565b610d9361134f565b6001600160a01b038116610df85760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b60648201526084016102da565b610e018161148c565b50565b60005460ff161561046b5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b60448201526064016102da565b600354604051631df3cbc560e31b81526001600160a01b039091169063ef9e5e28903490610e7e9086908690600401611b81565b60206040518083038185885af1158015610e9c573d6000803e3d6000fd5b50505050506040513d601f19601f82011682018060405250810190610ec19190611b36565b505050565b600060028314610f135760405162461bcd60e51b8152602060048201526018602482015277524f555445523a20544f4b454e5f41525241595f53495a4560401b60448201526064016102da565b83836001818110610f2657610f26611a80565b9050602002016020810190610f3b9190611881565b6001600160a01b031684846000818110610f5757610f57611a80565b9050602002016020810190610f6c9190611881565b6001600160a01b031603610fc25760405162461bcd60e51b815260206004820152601d60248201527f524f555445523a20544f4b454e5f41525241595f4455504c494341544500000060448201526064016102da565b600084846000818110610fd757610fd7611a80565b9050602002016020810190610fec9190611881565b905060008585600181811061100357611003611a80565b90506020020160208101906110189190611881565b6001600160a01b03838116600090815260046020526040902054919250161580159061105d57506001600160a01b038181166000908152600460205260409020541615155b6110a95760405162461bcd60e51b815260206004820152601c60248201527f524f555445523a2041535345545f4e4f545f524547495354455245440000000060448201526064016102da565b60035460405163b3596f0760e01b81526001600160a01b038481166004830152600092169063b3596f0790602401602060405180830381865afa1580156110f4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906111189190611a96565b60035460405163b3596f0760e01b81526001600160a01b0385811660048301529293506000929091169063b3596f0790602401602060405180830381865afa158015611168573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061118c9190611a96565b90506111a36001600160a01b03851633308c61151b565b6001600160a01b038085166000908152600460208190526040808320549051634d8ea83f60e01b815292931691634d8ea83f916111e6918e910190815260200190565b6020604051808303816000875af1158015611205573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906112299190611a96565b905060006112a9828585600460008b6001600160a01b03166001600160a01b0316815260200190815260200160002060009054906101000a90046001600160a01b03166001600160a01b031663c2d416016040518163ffffffff1660e01b8152600401602060405180830381865afa158015610806573d6000803e3d6000fd5b6001600160a01b03868116600090815260046020819052604091829020549151635f79d44f60e01b81529081018490529293501690635f79d44f906024016020604051808303816000875af1158015611306573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061132a9190611a96565b96506113439150506001600160a01b038416878761158c565b50505050949350505050565b6002546001600160a01b0316331461046b5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e657260448201526064016102da565b6113b16115bc565b6000805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa335b6040516001600160a01b03909116815260200160405180910390a1565b60008160ff168360ff161115611449576114158284611c1c565b6114239060ff16600a611d23565b8461142e8789611d2f565b6114389190611d4e565b6114429190611d4e565b9050611483565b836114548484611c1c565b6114629060ff16600a611d23565b61146c8789611d2f565b6114769190611d2f565b6114809190611d4e565b90505b95945050505050565b600280546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b6114e6610e04565b6000805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a2586113de3390565b6040516001600160a01b03808516602483015283166044820152606481018290526115869085906323b872dd60e01b906084015b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b031990931692909217909152611605565b50505050565b6040516001600160a01b038316602482015260448101829052610ec190849063a9059cbb60e01b9060640161154f565b60005460ff1661046b5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b60448201526064016102da565b600061165a826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166116da9092919063ffffffff16565b905080516000148061167b57508080602001905181019061167b9190611b36565b610ec15760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b60648201526084016102da565b60606116e984846000856116f1565b949350505050565b6060824710156117525760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b60648201526084016102da565b600080866001600160a01b0316858760405161176e9190611d9c565b60006040518083038185875af1925050503d80600081146117ab576040519150601f19603f3d011682016040523d82523d6000602084013e6117b0565b606091505b50915091506117c1878383876117cc565b979650505050505050565b6060831561183b578251600003611834576001600160a01b0385163b6118345760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e747261637400000060448201526064016102da565b50816116e9565b6116e983838151156118505781518083602001fd5b8060405162461bcd60e51b81526004016102da9190611db8565b80356001600160a01b0381168114610c9157600080fd5b60006020828403121561189357600080fd5b61189c8261186a565b9392505050565b60008083601f8401126118b557600080fd5b50813567ffffffffffffffff8111156118cd57600080fd5b6020830191508360208260051b85010111156118e857600080fd5b9250929050565b60008060008060008060008060c0898b03121561190b57600080fd5b8835975060208901359650604089013567ffffffffffffffff8082111561193157600080fd5b61193d8c838d016118a3565b909850965086915061195160608c0161186a565b955060808b0135945060a08b013591508082111561196e57600080fd5b5061197b8b828c016118a3565b999c989b5096995094979396929594505050565b6020808252825182820181905260009190848201906040850190845b818110156119c7578351835292840192918401916001016119ab565b50909695505050505050565b6000806000806000606086880312156119eb57600080fd5b85359450602086013567ffffffffffffffff80821115611a0a57600080fd5b611a1689838a016118a3565b90965094506040880135915080821115611a2f57600080fd5b50611a3c888289016118a3565b969995985093965092949392505050565b60008060408385031215611a6057600080fd5b611a698361186a565b9150611a776020840161186a565b90509250929050565b634e487b7160e01b600052603260045260246000fd5b600060208284031215611aa857600080fd5b5051919050565b600060208284031215611ac157600080fd5b815160ff8116811461189c57600080fd5b60008060008060808587031215611ae857600080fd5b505082516020840151604085015160609095015191969095509092509050565b634e487b7160e01b600052601160045260246000fd5b60008219821115611b3157611b31611b08565b500190565b600060208284031215611b4857600080fd5b8151801515811461189c57600080fd5b81835281816020850137506000828201602090810191909152601f909101601f19169091010190565b60208082528181018390526000906040600585901b8401810190840186845b87811015611c0f57868403603f190183528135368a9003601e19018112611bc657600080fd5b8901803567ffffffffffffffff811115611bdf57600080fd5b8036038b1315611bee57600080fd5b611bfb8682898501611b58565b955050509184019190840190600101611ba0565b5091979650505050505050565b600060ff821660ff841680821015611c3657611c36611b08565b90039392505050565b600181815b80851115611c7a578160001904821115611c6057611c60611b08565b80851615611c6d57918102915b93841c9390800290611c44565b509250929050565b600082611c9157506001610b89565b81611c9e57506000610b89565b8160018114611cb45760028114611cbe57611cda565b6001915050610b89565b60ff841115611ccf57611ccf611b08565b50506001821b610b89565b5060208310610133831016604e8410600b8410161715611cfd575081810a610b89565b611d078383611c3f565b8060001904821115611d1b57611d1b611b08565b029392505050565b600061189c8383611c82565b6000816000190483118215151615611d4957611d49611b08565b500290565b600082611d6b57634e487b7160e01b600052601260045260246000fd5b500490565b60005b83811015611d8b578181015183820152602001611d73565b838111156115865750506000910152565b60008251611dae818460208701611d70565b9190910192915050565b6020815260008251806020840152611dd7816040850160208701611d70565b601f01601f1916919091016040019291505056fea264697066735822122076c633503a49c914f8f8cc7121fe6393c1415a006f76f9968ec3f84271a8b3a664736f6c634300080d0033

Constructor Arguments (ABI-Encoded and is the last bytes of the Contract Creation Code above)

000000000000000000000000cb76bd6aeddaddecb583b9ab3d91f9e548153ce5

-----Decoded View---------------
Arg [0] : _pythAdapter (address): 0xcb76Bd6AeddAdDeCb583b9Ab3D91F9E548153CE5

-----Encoded View---------------
1 Constructor Arguments found :
Arg [0] : 000000000000000000000000cb76bd6aeddaddecb583b9ab3d91f9e548153ce5


Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ Download: CSV Export  ]
[ Download: CSV Export  ]

A contract address hosts a smart contract, which is a set of code stored on the blockchain that runs when predetermined conditions are met. Learn more about addresses in our Knowledge Base.