Arbitrum Sepolia Testnet

Contract

0x20ED655cD693AB3ec122234724b0DE9B365C78da
Source Code Source Code

Overview

ETH Balance

0 ETH

More Info

Multichain Info

N/A
Transaction Hash
Method
Block
From
To
Amount

There are no matching entries

Please try again later

Parent Transaction Hash Block From To Amount
View All Internal Transactions

Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
Vault

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 10 runs

Other Settings:
default evmVersion

Contract Source Code (Solidity Standard Json-Input format)

File 1 of 36 : Vault.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import {MathUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";
import {
    IERC20Upgradeable,
    SafeERC20Upgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import {ReentrancyGuardUpgradeable} from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol";
import {Ownable2StepUpgradeable} from "@openzeppelin/contracts-upgradeable/access/Ownable2StepUpgradeable.sol";
import {PausableUpgradeable} from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol";

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

import {SwapLogic} from "./libraries/logic/SwapLogic.sol";
import {SupplyLogic} from "./libraries/logic/SupplyLogic.sol";
import {PositionLogic} from "./libraries/logic/PositionLogic.sol";
import {GenericLogic} from "./libraries/logic/GenericLogic.sol";
import {ConfigureLogic} from "./libraries/logic/ConfigureLogic.sol";
import {BorrowingFeeLogic} from "./libraries/logic/BorrowingFeeLogic.sol";
import {FundingFeeLogic} from "./libraries/logic/FundingFeeLogic.sol";
import {StorageSlot} from "./libraries/logic/StorageSlot.sol";
import {IVaultPriceFeed} from "./interfaces/IVaultPriceFeed.sol";

import {DataTypes} from "./libraries/types/DataTypes.sol";
import {Constants} from "./libraries/helpers/Constants.sol";

contract Vault is Ownable2StepUpgradeable, PausableUpgradeable, ReentrancyGuardUpgradeable, IVault {
    using MathUpgradeable for uint256;
    using SafeERC20Upgradeable for IERC20Upgradeable;

    event ClaimFundingFee(address indexed account, address token, uint256 amount);
    event SettleFundingFee(address indexed account, uint256 amount);

    function initialize() external initializer {
        __Ownable2Step_init();
        __Pausable_init();
        __ReentrancyGuard_init();
    }

    function setPermissionParams(
        bool _inManagerMode,
        bool _inPrivateLiquidationMode,
        bool _isSwapEnabled,
        bool _isLeverageEnabled
    ) external onlyOwner {
        ConfigureLogic.setPermissionParams(
            _inManagerMode, _inPrivateLiquidationMode, _isSwapEnabled, _isLeverageEnabled
        );
    }

    function getPermissionParams() external view returns (bool, bool, bool, bool) {
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();
        return (ps.inManagerMode, ps.inPrivateLiquidationMode, ps.isSwapEnabled, ps.isLeverageEnabled);
    }

    function setManager(address _manager, bool _isManager) external onlyOwner {
        ConfigureLogic.setManager(_manager, _isManager);
    }

    function isManager(address _manager) external view returns (bool) {
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();
        return ps.isManager[_manager];
    }

    function setLiquidator(address _liquidator, bool _isActive) external onlyOwner {
        ConfigureLogic.setLiquidator(_liquidator, _isActive);
    }

    function isLiquidator(address _liquidator) external view returns (bool) {
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();
        return ps.isLiquidator[_liquidator];
    }

    function setTokenPause(address _token, bool _isPause) external {
        DataTypes.AddressStorage memory addrs = getAddresses();
        require(msg.sender == owner() || msg.sender == addrs.router, "Vault: invalid caller");
        ConfigureLogic.setTokenPause(_token, _isPause);
    }

    function isTokenPaused(address _token) public view returns (bool) {
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();
        return ps.isTokenPaused[_token];
    }

    function setMaxGasPrice(uint256 _maxGasPrice) external onlyOwner {
        ConfigureLogic.setMaxGasPrice(_maxGasPrice);
    }

    function getMaxGasPrice() external view returns (uint256) {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        return ps.maxGasPrice;
    }

    function setAddresses(DataTypes.AddressStorage memory params) external onlyOwner {
        ConfigureLogic.setAddresses(params);
    }

    function getAddresses() public view override returns (DataTypes.AddressStorage memory) {
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();

        DataTypes.AddressStorage memory addrsT;
        addrsT.weth = addrs.weth;
        addrsT.router = addrs.router;
        addrsT.ulpManager = addrs.ulpManager;
        addrsT.priceFeed = addrs.priceFeed;
        addrsT.usdg = addrs.usdg;
        addrsT.collateralToken = addrs.collateralToken;
        addrsT.feesReceiver = addrs.feesReceiver;
        return addrsT;
    }

    function setReserveRatio(uint256 _ratio) external onlyOwner {
        ConfigureLogic.setReserveRatio(_ratio);
    }

    function getReserveRatio() external view override returns (uint256) {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        return ps.reserveRatio;
    }

    function setMaxLeverage(uint256 _maxLeverage) external onlyOwner {
        ConfigureLogic.setMaxLeverage(_maxLeverage);
    }

    function getMaxLeverage() external view returns (uint256) {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        return ps.maxLeverage;
    }

    function setBufferAmount(address _token, uint256 _amount) external onlyOwner {
        ConfigureLogic.setBufferAmount(_token, _amount);
    }

    function getBufferAmount(address _token) external view returns (uint256) {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        return ps.bufferAmounts[_token];
    }

    function setMaxGlobalSizes(address[] memory _tokens, uint256[] memory _longSizes, uint256[] memory _shortSizes)
        external
        onlyOwner
    {
        ConfigureLogic.setMaxGlobalSizes(_tokens, _longSizes, _shortSizes);
    }

    function getGlobalData(address _token) external view override returns (DataTypes.GlobalData memory) {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        DataTypes.GlobalData memory globalData;
        globalData.maxGlobalLongSize = ps.maxGlobalLongSizes[_token];
        globalData.globalLongSize = ps.globalLongSizes[_token];
        globalData.globalLongAveragePrice = ps.globalLongAveragePrices[_token];
        globalData.maxGlobalShortSize = ps.maxGlobalShortSizes[_token];
        globalData.globalShortSize = ps.globalShortSizes[_token];
        globalData.globalShortAveragePrice = ps.globalShortAveragePrices[_token];
        return globalData;
    }

    function setFees(DataTypes.SetFeesParams memory params) external onlyOwner {
        ConfigureLogic.setFees(params);
    }

    function getFees() external view override returns (DataTypes.SetFeesParams memory) {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        DataTypes.SetFeesParams memory fee;
        fee.taxBasisPoints = fs.taxBasisPoints;
        fee.stableTaxBasisPoints = fs.stableTaxBasisPoints;
        fee.mintBurnFeeBasisPoints = fs.mintBurnFeeBasisPoints;
        fee.swapFeeBasisPoints = fs.swapFeeBasisPoints;
        fee.stableSwapFeeBasisPoints = fs.stableSwapFeeBasisPoints;
        fee.marginFeeBasisPoints = fs.marginFeeBasisPoints;
        fee.liquidationFeeUsd = fs.liquidationFeeUsd;
        fee.hasDynamicFees = fs.hasDynamicFees;
        fee.balanceReward = fs.balanceReward;
        return fee;
    }

    function setBorrowingRate(
        uint256 _borrowingInterval,
        uint256 _borrowingRateFactor,
        uint256 _stableBorrowingRateFactor
    ) external onlyOwner {
        (uint256 borrowingInterval, uint256 borrowingRateFactor, uint256 stableBorrowingRateFactor) = getBorrowingRate();
        if (borrowingInterval != 0 || borrowingRateFactor != 0 || stableBorrowingRateFactor != 0) {
            DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
            updateCumulativeBorrowingRate(addrs.collateralToken, addrs.collateralToken);
        }
        ConfigureLogic.setBorrowingRate(_borrowingInterval, _borrowingRateFactor, _stableBorrowingRateFactor);
    }

    function getBorrowingRate() public view override returns (uint256, uint256, uint256) {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        return (fs.borrowingInterval, fs.borrowingRateFactor, fs.stableBorrowingRateFactor);
    }

    function setTokenConfig(
        address _token,
        uint256 _tokenDecimals,
        uint256 _tokenWeight,
        uint256 _maxUsdgAmount,
        bool _isStable,
        bool _isShortable
    ) external onlyOwner {
        ConfigureLogic.setTokenConfig(
            DataTypes.SetTokenConfigParams({
                token: _token,
                tokenDecimals: _tokenDecimals,
                tokenWeight: _tokenWeight,
                maxUsdgAmount: _maxUsdgAmount,
                isStable: _isStable,
                isShortable: _isShortable
            })
        );
    }

    function getTokenConfig(address _token) external view returns (DataTypes.SetTokenConfigParams memory) {
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();

        DataTypes.SetTokenConfigParams memory tokenConfig;
        tokenConfig.token = _token;
        tokenConfig.tokenDecimals = ts.tokenDecimals[_token];
        tokenConfig.tokenWeight = ts.tokenWeights[_token];
        tokenConfig.maxUsdgAmount = ts.maxUsdgAmounts[_token];
        tokenConfig.isStable = ts.stableTokens[_token];
        tokenConfig.isShortable = ts.shortableTokens[_token];
        return tokenConfig;
    }

    function getTokenDecimal(address _token) external view override returns (uint256) {
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        return 10 ** ts.tokenDecimals[_token];
    }

    function clearTokenConfig(address _token) external onlyOwner {
        ConfigureLogic.clearTokenConfig(_token);
    }

    function withdrawFees(address _token) external override returns (uint256) {
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();
        require(ps.isWithdrawFeesAdmin[msg.sender], "not withdraw fee admin");
        return GenericLogic.withdrawFees(_token);
    }

    function setFeesAdmin(address _admin, bool _isAdmin) external onlyOwner {
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();
        ps.isWithdrawFeesAdmin[_admin] = _isAdmin;
    }

    function isFeesAdmin(address _admin) external view returns (bool) {
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();
        return ps.isWithdrawFeesAdmin[_admin];
    }

    function setRouter(address _router, bool _isRouter) external {
        ConfigureLogic.setRouter(_router, _isRouter);
    }

    function isRouter(address _account, address _router) external view returns (bool) {
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();
        return ps.approvedRouters[_account][_router];
    }

    function setUsdgAmount(address _token, uint256 _amount) external onlyOwner {
        ConfigureLogic.setUsdgAmount(_token, _amount);
    }

    function getUsdgAmount(address _token) external view returns (uint256) {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        return ps.usdgAmounts[_token];
    }

    function setFundingFactor(
        address[] memory _tokens,
        uint256[] memory _fundingFactors,
        uint256[] memory _fundingExponentFactors
    ) external onlyOwner {
        require(
            _tokens.length == _fundingFactors.length && _tokens.length == _fundingExponentFactors.length,
            "inconsistent length"
        );
        for (uint256 i = 0; i < _tokens.length; i++) {
            if (_fundingFactors[i] != 0 || _fundingExponentFactors[i] != 0) {
                updateFundingState(_tokens[i]);
            }
        }
        ConfigureLogic.setFundingFactor(_tokens, _fundingFactors, _fundingExponentFactors);
    }

    function getFundingFactor(address _token) public view override returns (uint256, uint256) {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();

        return (fs.fundingFactors[_token], fs.fundingExponentFactors[_token]);
    }

    // deposit into the pool without minting USDG tokens
    // useful in allowing the pool to become over-collaterised
    function directPoolDeposit(address _token) external override nonReentrant whenNotPaused {
        SupplyLogic.directPoolDeposit(_token);
    }

    function buyUSDG(address _token, address _receiver)
        external
        override
        nonReentrant
        whenNotPaused
        returns (uint256)
    {
        require(!isTokenPaused(_token), "Vault: already paused");
        return SupplyLogic.ExecuteBuyUSDG(_token, _receiver);
    }

    function sellUSDG(address _token, address _receiver)
        external
        override
        nonReentrant
        whenNotPaused
        returns (uint256)
    {
        return SupplyLogic.ExecuteSellUSDG(_token, _receiver);
    }

    function swap(address _tokenIn, address _tokenOut, address _receiver)
        external
        override
        nonReentrant
        whenNotPaused
        returns (uint256)
    {
        require(!isTokenPaused(_tokenIn) && !isTokenPaused(_tokenOut), "Vault: already paused");
        uint256 amountIn = GenericLogic.transferIn(_tokenIn);
        return SwapLogic.ExecuteSwap(_tokenIn, _tokenOut, amountIn, _receiver);
    }

    function increasePosition(
        address _account,
        address _collateralToken,
        address _indexToken,
        uint256 _sizeDelta,
        bool _isLong
    ) external override nonReentrant whenNotPaused {
        require(!isTokenPaused(_indexToken), "Vault: already paused");

        PositionLogic.increasePosition(
            DataTypes.IncreasePositionParams({
                account: _account,
                collateralToken: _collateralToken,
                indexToken: _indexToken,
                sizeDelta: _sizeDelta,
                isLong: _isLong
            })
        );
    }

    function decreasePosition(
        address _account,
        address _collateralToken,
        address _indexToken,
        uint256 _collateralDelta,
        uint256 _sizeDelta,
        bool _isLong,
        address _receiver
    ) external override nonReentrant whenNotPaused returns (uint256) {
        return PositionLogic.decreasePosition(
            DataTypes.DecreasePositionParams({
                account: _account,
                collateralToken: _collateralToken,
                indexToken: _indexToken,
                collateralDelta: _collateralDelta,
                sizeDelta: _sizeDelta,
                isLong: _isLong,
                receiver: _receiver
            })
        );
    }

    function liquidatePosition(
        address _account,
        address _collateralToken,
        address _indexToken,
        bool _isLong,
        address _feeReceiver
    ) external override nonReentrant whenNotPaused {
        return PositionLogic.liquidatePosition(
            DataTypes.LiquidatePositionParams({
                account: _account,
                collateralToken: _collateralToken,
                indexToken: _indexToken,
                isLong: _isLong,
                feeReceiver: _feeReceiver
            })
        );
    }

    function validateLiquidation(
        address _account,
        address _collateralToken,
        address _indexToken,
        bool _isLong,
        bool _raise
    ) external view override returns (uint256, uint256) {
        return PositionLogic.validateLiquidation(_account, _collateralToken, _indexToken, _isLong, _raise);
    }

    function updateCumulativeBorrowingRate(address _collateralToken, address _indexToken)
        public
        nonReentrant
        whenNotPaused
    {
        BorrowingFeeLogic.updateCumulativeBorrowingRate(_collateralToken, _indexToken);
    }

    function updateFundingState(address _indexToken) public nonReentrant whenNotPaused {
        FundingFeeLogic.updateFundingState(_indexToken);
    }

    function getBorrowingFeeInfo(address _token) external view returns (uint256, uint256) {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        return (fs.cumulativeBorrowingRates[_token], fs.lastBorrowingTimes[_token]);
    }

    function getMaxPrice(address _token) public view override returns (uint256) {
        return GenericLogic.getMaxPrice(_token);
    }

    function getMinPrice(address _token) external view override returns (uint256) {
        return GenericLogic.getMinPrice(_token);
    }

    function getPoolInfo(address _token) external view override returns (DataTypes.PoolInfo memory) {
        return GenericLogic.getPoolInfo(_token);
    }

    function getWhitelistedToken() external view override returns (uint256, address[] memory) {
        return GenericLogic.getWhitelistedToken();
    }

    function getTokenInfo(address _token) external view override returns (DataTypes.TokenInfo memory) {
        return GenericLogic.getTokenInfo(_token);
    }

    function getFeeReserves(address _token) external view returns (uint256) {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        return fs.feeReserves[_token];
    }

    function getUtilisation(address _token) public view returns (uint256) {
        return GenericLogic.getUtilisation(_token);
    }

    function getPositionLeverage(address _account, address _collateralToken, address _indexToken, bool _isLong)
        external
        view
        returns (uint256)
    {
        DataTypes.Position memory position = getPosition(_account, _collateralToken, _indexToken, _isLong);
        require(position.collateral > 0, "Vault: invalid position");
        return (position.size * Constants.PERCENTAGE_FACTOR) / position.collateral;
    }

    function getGlobalShortDelta(address _token) public view returns (bool, uint256) {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        uint256 size = ps.globalShortSizes[_token];
        if (size == 0) {
            return (false, 0);
        }
        uint256 nextPrice = GenericLogic.getMaxPrice(_token);
        uint256 averagePrice = ps.globalShortAveragePrices[_token];
        uint256 priceDelta = averagePrice > nextPrice ? averagePrice - nextPrice : nextPrice - averagePrice;
        uint256 delta = (size * priceDelta) / averagePrice;
        bool hasProfit = averagePrice > nextPrice;
        return (hasProfit, delta);
    }

    function getPosition(address _account, address _collateralToken, address _indexToken, bool _isLong)
        public
        view
        override
        returns (DataTypes.Position memory)
    {
        return PositionLogic.getPosition(_account, _collateralToken, _indexToken, _isLong);
    }

    function getRedemptionAmount(address _token, uint256 _usdgAmount) external view override returns (uint256) {
        return GenericLogic.getRedemptionAmount(_token, _usdgAmount);
    }

    function getTargetUsdgAmount(address _token) external view returns (uint256) {
        return GenericLogic.getTargetUsdgAmount(_token);
    }

    function getNextTargetUsdgAmount(address _token, uint256 _usdgDelta, bool _increment)
        external
        view
        returns (uint256)
    {
        return GenericLogic.getNextTargetUsdgAmount(_token, _usdgDelta, _increment);
    }

    function getFeeBasisPoints(
        address _token,
        uint256 _usdgDelta,
        uint256 _feeBasisPoints,
        uint256 _taxBasisPoints,
        bool _increment,
        bool _isSwap
    ) external view override returns (uint256, bool, bool) {
        return GenericLogic.getFeeBasisPoints(_token, _usdgDelta, _feeBasisPoints, _taxBasisPoints, _increment, _isSwap);
    }

    function tokenToUsdMin(address _token, uint256 _tokenAmount) external view override returns (uint256) {
        return GenericLogic.tokenToUsdMin(_token, _tokenAmount);
    }

    function getFundingFeeAmount(address _account) external view returns (uint256) {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        return fs.claimableFundingAmount[_account];
    }

    function claimFundingFees() external nonReentrant whenNotPaused returns (uint256) {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();

        uint256 claimableFundingAmount = fs.claimableFundingAmount[msg.sender];

        if (claimableFundingAmount > 0) {
            claimableFundingAmount = GenericLogic.usdToTokenMin(addrs.collateralToken, claimableFundingAmount);
            GenericLogic.transferOut(addrs.collateralToken, claimableFundingAmount, msg.sender);
            fs.claimableFundingAmount[msg.sender] = 0;

            emit ClaimFundingFee(msg.sender, addrs.collateralToken, claimableFundingAmount);
        }
        return claimableFundingAmount;
    }

    function getPositionDeltaAndFees(address _account, address _collateralToken, address _indexToken, bool _isLong)
        external
        view
        returns (bool, uint256, uint256, uint256, uint256)
    {
        DataTypes.Position memory position = getPosition(_account, _collateralToken, _indexToken, _isLong);
        (bool hasProfit, uint256 delta) =
            PositionLogic.getDelta(_indexToken, position.size, position.averagePrice, _isLong);
        uint256 borrowingFee = BorrowingFeeLogic.getBorrowingFee(
            _account, _collateralToken, _indexToken, _isLong, position.size, position.entryBorrowingRate
        );

        DataTypes.PositionFundingFees memory positionFundingFees =
            FundingFeeLogic.getFundingFees(_indexToken, _isLong, position);
        return
            (hasProfit, delta, borrowingFee, positionFundingFees.fundingFeeAmount, positionFundingFees.claimableAmount);
    }

    function getBatchSettleFundingFeeAmount(
        address _account,
        address[] memory _collateralToken,
        address[] memory _indexToken,
        bool[] memory _isLong
    ) external view returns (uint256) {
        require(
            _collateralToken.length == _indexToken.length && _collateralToken.length == _isLong.length,
            "inconsistent length"
        );
        uint256 totalSettleFundingFee;
        for (uint256 i = 0; i < _collateralToken.length; i++) {
            bytes32 key = PositionLogic.getPositionKey(_account, _collateralToken[i], _indexToken[i], _isLong[i]);
            DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
            DataTypes.Position storage position = ps.positions[key];
            if (position.size == 0) {
                continue;
            }

            DataTypes.PositionFundingFees memory positionFundingFees =
                FundingFeeLogic.getFundingFees(_indexToken[i], _isLong[i], position);
            totalSettleFundingFee += positionFundingFees.claimableAmount;
        }
        return totalSettleFundingFee;
    }

    function batchSettleFundingFee(
        address[] memory _collateralToken,
        address[] memory _indexToken,
        bool[] memory _isLong
    ) external nonReentrant whenNotPaused {
        require(
            _collateralToken.length == _indexToken.length && _collateralToken.length == _isLong.length,
            "inconsistent length"
        );
        uint256 totalSettleFundingFee;
        for (uint256 i = 0; i < _collateralToken.length; i++) {
            bytes32 key = PositionLogic.getPositionKey(msg.sender, _collateralToken[i], _indexToken[i], _isLong[i]);
            DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
            DataTypes.Position storage position = ps.positions[key];
            if (position.size == 0) {
                continue;
            }
            DataTypes.PositionFundingFees memory positionFundingFees =
                FundingFeeLogic.getFundingFees(_indexToken[i], _isLong[i], position);
            FundingFeeLogic.incrementClaimableFundingAmount(msg.sender, positionFundingFees);

            position.claimableFundingAmountPerSize = positionFundingFees.latestClaimableFundingAmountPerSize;
            totalSettleFundingFee += positionFundingFees.claimableAmount;
        }
        emit SettleFundingFee(msg.sender, totalSettleFundingFee);
    }

    function getTotalFeesForPositionLiquidation(
        address _account,
        address _collateralToken,
        address _indexToken,
        bool _isLong
    ) external view returns (uint256) {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        DataTypes.Position memory position = getPosition(_account, _collateralToken, _indexToken, _isLong);
        uint256 positionFee = PositionLogic.getPositionFee(position.size);

        uint256 borrowingFee = BorrowingFeeLogic.getBorrowingFee(
            _account, _collateralToken, _indexToken, _isLong, position.size, position.entryBorrowingRate
        );

        DataTypes.PositionFundingFees memory positionFundingFees =
            FundingFeeLogic.getFundingFees(_indexToken, _isLong, position);

        uint256 totalFees = positionFee + borrowingFee + positionFundingFees.fundingFeeAmount + fs.liquidationFeeUsd;
        return totalFees;
    }

    function setPause(bool flag) external onlyOwner {
        if (flag) {
            _pause();
        } else {
            _unpause();
        }
    }

    function getPause() external view override returns (bool) {
        return paused();
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (access/Ownable2Step.sol)

pragma solidity ^0.8.0;

import "./OwnableUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * By default, the owner account will be the one that deploys the contract. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2StepUpgradeable is Initializable, OwnableUpgradeable {
    function __Ownable2Step_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable2Step_init_unchained() internal onlyInitializing {
    }
    address private _pendingOwner;

    event OwnershipTransferStarted(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Returns the address of the pending owner.
     */
    function pendingOwner() public view virtual returns (address) {
        return _pendingOwner;
    }

    /**
     * @dev Starts the ownership transfer of the contract to a new account. Replaces the pending transfer if there is one.
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual override onlyOwner {
        _pendingOwner = newOwner;
        emit OwnershipTransferStarted(owner(), newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`) and deletes any pending owner.
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual override {
        delete _pendingOwner;
        super._transferOwnership(newOwner);
    }

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() external {
        address sender = _msgSender();
        require(pendingOwner() == sender, "Ownable2Step: caller is not the new owner");
        _transferOwnership(sender);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.7.0) (access/Ownable.sol)

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.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 OwnableUpgradeable is Initializable, ContextUpgradeable {
    address private _owner;

    event OwnershipTransferred(address indexed previousOwner, address indexed newOwner);

    /**
     * @dev Initializes the contract setting the deployer as the initial owner.
     */
    function __Ownable_init() internal onlyInitializing {
        __Ownable_init_unchained();
    }

    function __Ownable_init_unchained() internal onlyInitializing {
        _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 anymore. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby removing 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);
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.1) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.2;

import "../../utils/AddressUpgradeable.sol";

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Indicates that the contract has been initialized.
     * @custom:oz-retyped-from bool
     */
    uint8 private _initialized;

    /**
     * @dev Indicates that the contract is in the process of being initialized.
     */
    bool private _initializing;

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint8 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that functions marked with `initializer` can be nested in the context of a
     * constructor.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        bool isTopLevelCall = !_initializing;
        require(
            (isTopLevelCall && _initialized < 1) || (!AddressUpgradeable.isContract(address(this)) && _initialized == 1),
            "Initializable: contract is already initialized"
        );
        _initialized = 1;
        if (isTopLevelCall) {
            _initializing = true;
        }
        _;
        if (isTopLevelCall) {
            _initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: setting the version to 255 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint8 version) {
        require(!_initializing && _initialized < version, "Initializable: contract is already initialized");
        _initialized = version;
        _initializing = true;
        _;
        _initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        require(_initializing, "Initializable: contract is not initializing");
        _;
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        require(!_initializing, "Initializable: contract is initializing");
        if (_initialized < type(uint8).max) {
            _initialized = type(uint8).max;
            emit Initialized(type(uint8).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint8) {
        return _initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _initializing;
    }
}

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

pragma solidity ^0.8.0;

import "../utils/ContextUpgradeable.sol";
import "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract PausableUpgradeable is Initializable, ContextUpgradeable {
    /**
     * @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.
     */
    function __Pausable_init() internal onlyInitializing {
        __Pausable_init_unchained();
    }

    function __Pausable_init_unchained() internal onlyInitializing {
        _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());
    }

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (security/ReentrancyGuard.sol)

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

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

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

    uint256 private _status;

    function __ReentrancyGuard_init() internal onlyInitializing {
        __ReentrancyGuard_init_unchained();
    }

    function __ReentrancyGuard_init_unchained() internal onlyInitializing {
        _status = _NOT_ENTERED;
    }

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

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

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

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

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[49] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts v4.4.1 (token/ERC20/extensions/draft-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 IERC20PermitUpgradeable {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

import "../IERC20Upgradeable.sol";
import "../extensions/draft-IERC20PermitUpgradeable.sol";
import "../../../utils/AddressUpgradeable.sol";

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

    function safeTransfer(
        IERC20Upgradeable token,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transfer.selector, to, value));
    }

    function safeTransferFrom(
        IERC20Upgradeable token,
        address from,
        address to,
        uint256 value
    ) internal {
        _callOptionalReturn(token, abi.encodeWithSelector(token.transferFrom.selector, from, to, value));
    }

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

    function safeIncreaseAllowance(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        uint256 newAllowance = token.allowance(address(this), spender) + value;
        _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
    }

    function safeDecreaseAllowance(
        IERC20Upgradeable token,
        address spender,
        uint256 value
    ) internal {
        unchecked {
            uint256 oldAllowance = token.allowance(address(this), spender);
            require(oldAllowance >= value, "SafeERC20: decreased allowance below zero");
            uint256 newAllowance = oldAllowance - value;
            _callOptionalReturn(token, abi.encodeWithSelector(token.approve.selector, spender, newAllowance));
        }
    }

    function safePermit(
        IERC20PermitUpgradeable token,
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal {
        uint256 nonceBefore = token.nonces(owner);
        token.permit(owner, spender, value, deadline, v, r, s);
        uint256 nonceAfter = token.nonces(owner);
        require(nonceAfter == nonceBefore + 1, "SafeERC20: permit did not succeed");
    }

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/Address.sol)

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library AddressUpgradeable {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     * ====
     *
     * [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://diligence.consensys.net/posts/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.5.11/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 Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and revert if it wasn't, either by bubbling the
     * revert reason or using the provided one.
     *
     * _Available since v4.3._
     */
    function verifyCallResult(
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal pure returns (bytes memory) {
        if (success) {
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

    function _revert(bytes memory returndata, string memory errorMessage) private pure {
        // Look for revert reason and bubble it up if present
        if (returndata.length > 0) {
            // The easiest way to bubble the revert reason is using memory via assembly
            /// @solidity memory-safe-assembly
            assembly {
                let returndata_size := mload(returndata)
                revert(add(32, returndata), returndata_size)
            }
        } else {
            revert(errorMessage);
        }
    }
}

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

pragma solidity ^0.8.0;
import "../proxy/utils/Initializable.sol";

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

    function __Context_init_unchained() internal onlyInitializing {
    }
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

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

    /**
     * @dev This empty reserved space is put in place to allow future versions to add new
     * variables without shifting down storage in the inheritance chain.
     * See https://docs.openzeppelin.com/contracts/4.x/upgradeable#storage_gaps
     */
    uint256[50] private __gap;
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.0) (utils/math/Math.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

            // Handle non-overflow cases, 256 by 256 division.
            if (prod1 == 0) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 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 10, 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 * 8) < value ? 1 : 0);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.8.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) {
                return prod0 / denominator;
            }

            // Make sure the result is less than 2^256. Also prevents denominator == 0.
            require(denominator > prod1);

            ///////////////////////////////////////////////
            // 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 10, 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 * 8) < value ? 1 : 0);
        }
    }
}

File 14 of 36 : 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);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (utils/math/SafeMath.sol)

pragma solidity ^0.8.0;

// CAUTION
// This version of SafeMath should only be used with Solidity 0.8 or later,
// because it relies on the compiler's built in overflow checks.

/**
 * @dev Wrappers over Solidity's arithmetic operations.
 *
 * NOTE: `SafeMath` is generally not needed starting with Solidity 0.8, since the compiler
 * now has built in overflow checking.
 */
library SafeMath {
    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function trySub(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b > a) return (false, 0);
            return (true, a - b);
        }
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, with an overflow flag.
     *
     * _Available since v3.4._
     */
    function tryMul(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            // Gas optimization: this is cheaper than requiring 'a' not being zero, but the
            // benefit is lost if 'b' is also tested.
            // See: https://github.com/OpenZeppelin/openzeppelin-contracts/pull/522
            if (a == 0) return (true, 0);
            uint256 c = a * b;
            if (c / a != b) return (false, 0);
            return (true, c);
        }
    }

    /**
     * @dev Returns the division of two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryDiv(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a / b);
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers, with a division by zero flag.
     *
     * _Available since v3.4._
     */
    function tryMod(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            if (b == 0) return (false, 0);
            return (true, a % b);
        }
    }

    /**
     * @dev Returns the addition of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `+` operator.
     *
     * Requirements:
     *
     * - Addition cannot overflow.
     */
    function add(uint256 a, uint256 b) internal pure returns (uint256) {
        return a + b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting on
     * overflow (when the result is negative).
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(uint256 a, uint256 b) internal pure returns (uint256) {
        return a - b;
    }

    /**
     * @dev Returns the multiplication of two unsigned integers, reverting on
     * overflow.
     *
     * Counterpart to Solidity's `*` operator.
     *
     * Requirements:
     *
     * - Multiplication cannot overflow.
     */
    function mul(uint256 a, uint256 b) internal pure returns (uint256) {
        return a * b;
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator.
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(uint256 a, uint256 b) internal pure returns (uint256) {
        return a / b;
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting when dividing by zero.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(uint256 a, uint256 b) internal pure returns (uint256) {
        return a % b;
    }

    /**
     * @dev Returns the subtraction of two unsigned integers, reverting with custom message on
     * overflow (when the result is negative).
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {trySub}.
     *
     * Counterpart to Solidity's `-` operator.
     *
     * Requirements:
     *
     * - Subtraction cannot overflow.
     */
    function sub(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b <= a, errorMessage);
            return a - b;
        }
    }

    /**
     * @dev Returns the integer division of two unsigned integers, reverting with custom message on
     * division by zero. The result is rounded towards zero.
     *
     * Counterpart to Solidity's `/` operator. Note: this function uses a
     * `revert` opcode (which leaves remaining gas untouched) while Solidity
     * uses an invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function div(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a / b;
        }
    }

    /**
     * @dev Returns the remainder of dividing two unsigned integers. (unsigned integer modulo),
     * reverting with custom message when dividing by zero.
     *
     * CAUTION: This function is deprecated because it requires allocating memory for the error
     * message unnecessarily. For custom revert reasons use {tryMod}.
     *
     * Counterpart to Solidity's `%` operator. This function uses a `revert`
     * opcode (which leaves remaining gas untouched) while Solidity uses an
     * invalid opcode to revert (consuming all remaining gas).
     *
     * Requirements:
     *
     * - The divisor cannot be zero.
     */
    function mod(
        uint256 a,
        uint256 b,
        string memory errorMessage
    ) internal pure returns (uint256) {
        unchecked {
            require(b > 0, errorMessage);
            return a % b;
        }
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import "./IVault.sol";

interface IUlpManager {
    function ulp() external view returns (address);

    function usdg() external view returns (address);

    function vault() external view returns (IVault);

    function cooldownDuration() external returns (uint256);

    function getPrice(bool _maximise) external view returns (uint256);

    function getAum(bool maximise) external view returns (uint256);

    function getAumInUsdg(bool maximise) external view returns (uint256);

    function getTokenInUsdg(address token, uint256 amount, bool maximise) external view returns (uint256);

    function lastAddedAt(address _account) external returns (uint256);

    function addLiquidity(address _token, uint256 _amount, uint256 _minUsdg, uint256 _minUlp)
        external
        returns (uint256);

    function addLiquidityForAccount(
        address _fundingAccount,
        address _account,
        address _token,
        uint256 _amount,
        uint256 _minUsdg,
        uint256 _minUlp
    ) external returns (uint256);

    function removeLiquidity(uint256 _ulpAmount, uint256 _minOut, address _receiver) external returns (uint256);

    function removeLiquidityForAccount(address _account, uint256 _ulpAmount, uint256 _minOut, address _receiver)
        external
        returns (uint256);

    function setCooldownDuration(uint256 _cooldownDuration) external;

    struct TokenAmountOut {
        address token;
        uint256 amountOut;
        uint256 amountOutFormatPrecision;
    }

    function getTokenAmountOut(uint256 _ulpAmount) external view returns (TokenAmountOut[] memory);

    function getUsdgAmount(uint256 _ulpAmount) external view returns (address[] memory, uint256[] memory, uint256);

    function getAumInUsdgWithoutProfit(bool maximise) external view returns (uint256, uint256);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import {DataTypes} from "../libraries/types/DataTypes.sol";

interface IVault {
    function buyUSDG(address _token, address _receiver) external returns (uint256);

    function sellUSDG(address _token, address _receiver) external returns (uint256);

    function swap(address _tokenIn, address _tokenOut, address _receiver) external returns (uint256);

    function increasePosition(
        address _account,
        address _collateralToken,
        address _indexToken,
        uint256 _sizeDelta,
        bool _isLong
    ) external;

    function decreasePosition(
        address _account,
        address _collateralToken,
        address _indexToken,
        uint256 _collateralDelta,
        uint256 _sizeDelta,
        bool _isLong,
        address _receiver
    ) external returns (uint256);

    function liquidatePosition(
        address _account,
        address _collateralToken,
        address _indexToken,
        bool _isLong,
        address _feeReceiver
    ) external;

    function getMaxPrice(address _token) external view returns (uint256);

    function getMinPrice(address _token) external view returns (uint256);

    function getPosition(address _account, address _collateralToken, address _indexToken, bool _isLong)
        external
        view
        returns (DataTypes.Position memory);

    function getRedemptionAmount(address _token, uint256 _usdgAmount) external view returns (uint256);

    function tokenToUsdMin(address _token, uint256 _tokenAmount) external view returns (uint256);

    function getTokenDecimal(address _token) external view returns (uint256);

    function getBorrowingRate() external view returns (uint256, uint256, uint256);

    function getFundingFactor(address _token) external view returns (uint256, uint256);

    function getPoolInfo(address _token) external view returns (DataTypes.PoolInfo memory);

    function getWhitelistedToken() external view returns (uint256, address[] memory);

    function getTokenInfo(address _token) external view returns (DataTypes.TokenInfo memory);

    function directPoolDeposit(address _token) external;

    function getFeeBasisPoints(
        address _token,
        uint256 _usdgDelta,
        uint256 _feeBasisPoints,
        uint256 _taxBasisPoints,
        bool _increment,
        bool _isSwap
    ) external view returns (uint256, bool, bool);

    function getFees() external view returns (DataTypes.SetFeesParams memory);

    function getAddresses() external view returns (DataTypes.AddressStorage memory);

    function isTokenPaused(address _token) external view returns (bool);

    function setTokenPause(address _token, bool _isPause) external;

    function getReserveRatio() external view returns (uint256);

    function getGlobalData(address _token) external view returns (DataTypes.GlobalData memory);

    function getPause() external view returns (bool);

    function validateLiquidation(
        address _account,
        address _collateralToken,
        address _indexToken,
        bool _isLong,
        bool _raise
    ) external view returns (uint256, uint256);

    function withdrawFees(address _token) external returns (uint256);
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

interface IVaultPriceFeed {
    function adjustmentBasisPoints(address _token) external view returns (uint256);

    function isAdjustmentAdditive(address _token) external view returns (bool);

    function getPrice(address _token, bool _maximise) external view returns (uint256);

    function getLatestPrimaryPrice(address _token) external view returns (uint256);

    function getPrimaryPrice(address _token, bool _maximise) external view returns (uint256);

    function setTokenConfig(address _token, address _priceFeed, uint256 _priceDecimals, bool _isStrictStable)
        external;
}

File 20 of 36 : Constants.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

library Constants {
    // public
    uint256 public constant PERCENTAGE_FACTOR = 1e4;
    uint256 public constant PRICE_PRECISION = 1e30;
    uint256 public constant USDG_DECIMALS = 18;
    uint256 public constant ULP_PRECISION = 1e18;
    uint256 public constant COllATERAL_PRECISION = 1e18;

    // Vault
    uint256 public constant BORROWING_RATE_PRECISION = 1000000;
    uint256 public constant MIN_LEVERAGE = 1e4; // 1x
    uint256 public constant MAX_FEE_BASIS_POINTS = 500; // 5%
    uint256 public constant MAX_LIQUIDATION_FEE_USD = 100 * PRICE_PRECISION; // 100 USD
    uint256 public constant MIN_BORROWING_RATE_INTERVAL = 1 hours;
    uint256 public constant MAX_BORROWING_RATE_FACTOR = 1e4; // 1%

    // UlpManager
    uint256 public constant MAX_COOLDOWN_DURATION = 48 hours;

    // VaultPriceFeed
    uint256 public constant ONE_USD = PRICE_PRECISION;
    uint256 public constant MAX_SPREAD_BASIS_POINTS = 50;
    uint256 public constant MAX_ADJUSTMENT_INTERVAL = 2 hours;
    uint256 public constant MAX_ADJUSTMENT_BASIS_POINTS = 20;

    // FastPrieFeed
    uint256 public constant CUMULATIVE_DELTA_PRECISION = 10 * 1000 * 1000;
    uint256 public constant MAX_REF_PRICE = type(uint160).max;
    uint256 public constant MAX_CUMULATIVE_REF_DELTA = type(uint32).max;
    uint256 public constant MAX_CUMULATIVE_FAST_DELTA = type(uint32).max;
    // uint256(~0) is 256 bits of 1s
    // shift the 1s by (256 - 32) to get (256 - 32) 0s followed by 32 1s
    uint256 public constant BITMASK_32 = uint256(int256(~0)) >> (256 - 32);
    uint256 public constant MAX_PRICE_DURATION = 30 minutes;

    // Reward
    uint256 public constant BONUS_DURATION = 365 days;
}

File 21 of 36 : Errors.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

library Errors {
    string public constant VAULT_INVALID_MAXLEVERAGE = "0";
    string public constant VAULT_INVALID_TAX_BASIS_POINTS = "1";
    string public constant VAULT_INVALID_STABLE_TAX_BASIS_POINTS = "2";
    string public constant VAULT_INVALID_MINT_BURN_FEE_BASIS_POINTS = "3";
    string public constant VAULT_INVALID_SWAP_FEE_BASIS_POINTS = "4";
    string public constant VAULT_INVALID_STABLE_SWAP_FEE_BASIS_POINTS = "5";
    string public constant VAULT_INVALID_MARGIN_FEE_BASIS_POINTS = "6";
    string public constant VAULT_INVALID_LIQUIDATION_FEE_USD = "7";
    string public constant VAULT_INVALID_BORROWING_INTERVALE = "8";
    string public constant VAULT_INVALID_BORROWING_RATE_FACTOR = "9";
    string public constant VAULT_INVALID_STABLE_BORROWING_RATE_FACTOR = "10";
    string public constant VAULT_TOKEN_NOT_WHITELISTED = "11";
    string public constant VAULT_INVALID_TOKEN_AMOUNT = "12";
    string public constant VAULT_INVALID_USDG_AMOUNT = "13";
    string public constant VAULT_INVALID_REDEMPTION_AMOUNT = "14";
    string public constant VAULT_INVALID_AMOUNT_OUT = "15";
    string public constant VAULT_SWAPS_NOT_ENABLED = "16";
    string public constant VAULT_TOKEN_IN_NOT_WHITELISTED = "17";
    string public constant VAULT_TOKEN_OUT_NOT_WHITELISTED = "18";
    string public constant VAULT_INVALID_TOKENS = "19";
    string public constant VAULT_INVALID_AMOUNT_IN = "20";
    string public constant VAULT_LEVERAGE_NOT_ENABLED = "21";
    string public constant VAULT_INSUFFICIENT_COLLATERAL_FOR_FEES = "22";
    string public constant VAULT_INVALID_POSITION_SIZE = "23";
    string public constant VAULT_EMPTY_POSITION = "24";
    string public constant VAULT_POSITION_SIZE_EXCEEDED = "25";
    string public constant VAULT_POSITION_COLLATERAL_EXCEEDED = "26";
    string public constant VAULT_INVALID_LIQUIDATOR = "27";
    string public constant VAULT_POSITION_CAN_NOT_BE_LIQUIDATED = "28";
    string public constant VAULT_INVALID_POSITION = "29";
    string public constant VAULT_INVALID_AVERAGE_PRICE = "30";
    string public constant VAULT_COLLATERAL_SHOULD_BE_WITHDRAWN = "31";
    string public constant VAULT_SIZE_MUST_BE_MORE_THAN_COLLATERAL = "32";
    string public constant VAULT_INVALID_MSG_SENDER = "33";
    string public constant VAULT_MISMATCHED_TOKENS = "34";
    string public constant VAULT_COLLATERAL_TOKEN_NOT_WHITELISTED = "35";
    string public constant VAULT_COLLATERAL_TOKEN_MUST_NOT_BE_A_STABLE_TOKEN = "36";
    string public constant VAULT_COLLATERAL_TOKEN_MUST_BE_STABLE_TOKEN = "37";
    string public constant VAULT_INDEX_TOKEN_MUST_NOT_BE_STABLE_TOKEN = "38";
    string public constant VAULT_INDEX_TOKEN_NOT_SHORTABLE = "39";
    string public constant VAULT_INVALID_INCREASE = "40";
    string public constant VAULT_RESERVE_EXCEEDS_POOL = "41";
    string public constant VAULT_MAX_USDG_EXCEEDED = "42";
    string public constant VAULT_FORBIDDEN = "43";
    string public constant VAULT_MAX_GAS_PRICE_EXCEEDED = "44";
    string public constant VAULT_POOL_AMOUNT_LESS_THAN_BUFFER_AMOUNT = "45";
    string public constant VAULT_POOL_AMOUNT_EXCEEDED = "46";
    string public constant VAULT_MAX_SHORTS_EXCEEDED = "47";
    string public constant VAULT_INSUFFICIENT_RESERVE = "48";
    string public constant VAULT_NOT_SWAPER = "51";
    string public constant MATH_MULTIPLICATION_OVERFLOW = "52";
    string public constant MATH_DIVISION_BY_ZERO = "53";
    string public constant INVALID_CALLER = "54";
    string public constant VAULT_MAX_LONG_EXCEEDED = "55";
    string public constant VAULT_INDEX_TOKEN_NOT_WHITELISTED = "56";

    // Funding Fee Error
    string public constant EMPTY_OPEN_INTEREST = "60";
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {DataTypes} from "../types/DataTypes.sol";
import {StorageSlot} from "./StorageSlot.sol";
import {Constants} from "../helpers/Constants.sol";
import {GenericLogic} from "./GenericLogic.sol";

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

library BorrowingFeeLogic {
    event UpdateBorrowingRate(address token, uint256 borrowngRate);

    function getBorrowingFee(
        address, /* _account */
        address _collateralToken,
        address, /* _indexToken */
        bool, /* _isLong */
        uint256 _size,
        uint256 _entryBorrowingRate
    ) internal view returns (uint256) {
        if (_size == 0) {
            return 0;
        }
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        uint256 borrowingRate = fs.cumulativeBorrowingRates[_collateralToken] - _entryBorrowingRate;
        if (borrowingRate == 0) {
            return 0;
        }

        return (_size * borrowingRate) / Constants.BORROWING_RATE_PRECISION;
    }

    function updateCumulativeBorrowingRate(address _collateralToken, address /*_indexToken*/ ) internal {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        if (fs.lastBorrowingTimes[_collateralToken] == 0) {
            fs.lastBorrowingTimes[_collateralToken] = (block.timestamp / fs.borrowingInterval) * fs.borrowingInterval;
            return;
        }

        if (fs.lastBorrowingTimes[_collateralToken] + fs.borrowingInterval > block.timestamp) {
            return;
        }
        uint256 borrowingRate = getNextBorrowingRate(_collateralToken);
        fs.cumulativeBorrowingRates[_collateralToken] = fs.cumulativeBorrowingRates[_collateralToken] + borrowingRate;
        fs.lastBorrowingTimes[_collateralToken] = (block.timestamp / fs.borrowingInterval) * fs.borrowingInterval;

        emit UpdateBorrowingRate(_collateralToken, fs.cumulativeBorrowingRates[_collateralToken]);
    }

    function getNextBorrowingRate(address _token) internal view returns (uint256) {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        if (fs.lastBorrowingTimes[_token] + fs.borrowingInterval > block.timestamp) {
            return 0;
        }

        uint256 intervals = (block.timestamp - fs.lastBorrowingTimes[_token]) / (fs.borrowingInterval);

        uint256 _borrowingRateFactor = ts.stableTokens[_token] ? fs.stableBorrowingRateFactor : fs.borrowingRateFactor;

        uint256 aum = IUlpManager(addrs.ulpManager).getAum(true);

        uint256 price = GenericLogic.getMinPrice(_token);
        uint256 decimals = ts.tokenDecimals[_token];
        uint256 reservedUsd = (price * ps.reservedAmounts[_token]) / 10 ** decimals;

        if (reservedUsd == 0 || aum == 0) {
            return 0;
        }
        return (_borrowingRateFactor * reservedUsd * intervals) / aum;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import {
    IERC20Upgradeable,
    SafeERC20Upgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import {MathUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";

import {ValidationLogic} from "./ValidationLogic.sol";
import {GenericLogic} from "./GenericLogic.sol";
import {StorageSlot} from "./StorageSlot.sol";
import {Errors} from "../helpers/Errors.sol";
import {Constants} from "../helpers/Constants.sol";

import {DataTypes} from "../types/DataTypes.sol";

library ConfigureLogic {
    event SetMaxLeverage(uint256 maxLeverage);
    event SetMaxGlobalSizes(address[] tokens, uint256[] longSizes, uint256[] shortSizes);

    function setBorrowingRate(
        uint256 _borrowingInterval,
        uint256 _borrowingRateFactor,
        uint256 _stableBorrowingRateFactor
    ) external {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        ValidationLogic.validate(
            _borrowingInterval >= Constants.MIN_BORROWING_RATE_INTERVAL, Errors.VAULT_INVALID_BORROWING_INTERVALE
        );
        ValidationLogic.validate(
            _borrowingRateFactor <= Constants.MAX_BORROWING_RATE_FACTOR, Errors.VAULT_INVALID_BORROWING_RATE_FACTOR
        );
        ValidationLogic.validate(
            _stableBorrowingRateFactor <= Constants.MAX_BORROWING_RATE_FACTOR,
            Errors.VAULT_INVALID_STABLE_BORROWING_RATE_FACTOR
        );
        fs.borrowingInterval = _borrowingInterval;
        fs.borrowingRateFactor = _borrowingRateFactor;
        fs.stableBorrowingRateFactor = _stableBorrowingRateFactor;
    }

    function setAddresses(DataTypes.AddressStorage memory params) external {
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        addrs.weth = params.weth;
        addrs.router = params.router;
        addrs.ulpManager = params.ulpManager;
        addrs.priceFeed = params.priceFeed;
        addrs.usdg = params.usdg;
        addrs.collateralToken = params.collateralToken;
        addrs.feesReceiver = params.feesReceiver;
    }

    function setFees(DataTypes.SetFeesParams memory params) external {
        ValidationLogic.validate(
            params.taxBasisPoints <= Constants.MAX_FEE_BASIS_POINTS, Errors.VAULT_INVALID_TAX_BASIS_POINTS
        );
        ValidationLogic.validate(
            params.stableTaxBasisPoints <= Constants.MAX_FEE_BASIS_POINTS, Errors.VAULT_INVALID_STABLE_TAX_BASIS_POINTS
        );
        ValidationLogic.validate(
            params.mintBurnFeeBasisPoints <= Constants.MAX_FEE_BASIS_POINTS,
            Errors.VAULT_INVALID_MINT_BURN_FEE_BASIS_POINTS
        );
        ValidationLogic.validate(
            params.swapFeeBasisPoints <= Constants.MAX_FEE_BASIS_POINTS, Errors.VAULT_INVALID_SWAP_FEE_BASIS_POINTS
        );
        ValidationLogic.validate(
            params.stableSwapFeeBasisPoints <= Constants.MAX_FEE_BASIS_POINTS,
            Errors.VAULT_INVALID_STABLE_SWAP_FEE_BASIS_POINTS
        );
        ValidationLogic.validate(
            params.marginFeeBasisPoints <= Constants.MAX_FEE_BASIS_POINTS, Errors.VAULT_INVALID_MARGIN_FEE_BASIS_POINTS
        );
        ValidationLogic.validate(
            params.liquidationFeeUsd <= Constants.MAX_LIQUIDATION_FEE_USD, Errors.VAULT_INVALID_LIQUIDATION_FEE_USD
        );
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        fs.taxBasisPoints = params.taxBasisPoints;
        fs.stableTaxBasisPoints = params.stableTaxBasisPoints;
        fs.mintBurnFeeBasisPoints = params.mintBurnFeeBasisPoints;
        fs.swapFeeBasisPoints = params.swapFeeBasisPoints;
        fs.stableSwapFeeBasisPoints = params.stableSwapFeeBasisPoints;
        fs.marginFeeBasisPoints = params.marginFeeBasisPoints;
        fs.liquidationFeeUsd = params.liquidationFeeUsd;
        fs.hasDynamicFees = params.hasDynamicFees;
        fs.balanceReward = params.balanceReward;
    }

    function setRouter(address _router, bool _isRouter) external {
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();
        ps.approvedRouters[msg.sender][_router] = _isRouter;
    }

    function setTokenConfig(DataTypes.SetTokenConfigParams memory params) external {
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        // increment token count for the first time
        if (!ts.whitelistedTokens[params.token]) {
            ts.whitelistedTokenCount = ts.whitelistedTokenCount + 1;
            ts.allWhitelistedTokens.push(params.token);
        }
        uint256 totalTokenWeights = ts.totalTokenWeights;
        totalTokenWeights = totalTokenWeights - ts.tokenWeights[params.token];
        ts.whitelistedTokens[params.token] = true;
        ts.tokenDecimals[params.token] = params.tokenDecimals;
        ts.tokenWeights[params.token] = params.tokenWeight;
        ts.maxUsdgAmounts[params.token] = params.maxUsdgAmount;
        ts.stableTokens[params.token] = params.isStable;
        ts.shortableTokens[params.token] = params.isShortable;
        ts.totalTokenWeights = totalTokenWeights + params.tokenWeight;
    }

    function clearTokenConfig(address _token) external {
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        ValidationLogic.validate(ts.whitelistedTokens[_token], Errors.VAULT_TOKEN_NOT_WHITELISTED);
        ts.totalTokenWeights = ts.totalTokenWeights - ts.tokenWeights[_token];
        delete ts.whitelistedTokens[_token];
        delete ts.tokenDecimals[_token];
        delete ts.tokenWeights[_token];
        delete ts.maxUsdgAmounts[_token];
        delete ts.stableTokens[_token];
        delete ts.shortableTokens[_token];
        ts.whitelistedTokenCount = ts.whitelistedTokenCount - 1;
    }

    function setUsdgAmount(address _token, uint256 _amount) external {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        uint256 usdgAmount = ps.usdgAmounts[_token];
        if (_amount > usdgAmount) {
            GenericLogic.increaseUsdgAmount(_token, _amount - usdgAmount);
            return;
        }
        GenericLogic.decreaseUsdgAmount(_token, usdgAmount - _amount);
    }

    function setFundingFactor(
        address[] memory _tokens,
        uint256[] memory _fundingFactors,
        uint256[] memory _fundingExponentFactors
    ) external {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();

        for (uint256 i = 0; i < _tokens.length; i++) {
            fs.fundingFactors[_tokens[i]] = _fundingFactors[i];
            fs.fundingExponentFactors[_tokens[i]] = _fundingExponentFactors[i];
        }
    }

    function setPermissionParams(
        bool _inManagerMode,
        bool _inPrivateLiquidationMode,
        bool _isSwapEnabled,
        bool _isLeverageEnabled
    ) external {
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();
        ps.inManagerMode = _inManagerMode;
        ps.inPrivateLiquidationMode = _inPrivateLiquidationMode;
        ps.isSwapEnabled = _isSwapEnabled;
        ps.isLeverageEnabled = _isLeverageEnabled;
    }

    function setManager(address _manager, bool _isManager) external {
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();

        ps.isManager[_manager] = _isManager;
    }

    function setLiquidator(address _liquidator, bool _isActive) external {
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();
        ps.isLiquidator[_liquidator] = _isActive;
    }

    function setTokenPause(address _token, bool _isPause) external {
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();
        ps.isTokenPaused[_token] = _isPause;
    }

    function setMaxGasPrice(uint256 _maxGasPrice) external {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        ps.maxGasPrice = _maxGasPrice;
    }

    function setMaxLeverage(uint256 _maxLeverage) external {
        ValidationLogic.validate(_maxLeverage > Constants.MIN_LEVERAGE, Errors.VAULT_INVALID_MAXLEVERAGE);
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        ps.maxLeverage = _maxLeverage;
        emit SetMaxLeverage(_maxLeverage);
    }

    function setReserveRatio(uint256 _ratio) external {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        ps.reserveRatio = _ratio;
    }

    function setBufferAmount(address _token, uint256 _amount) external {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        ps.bufferAmounts[_token] = _amount;
    }

    function setMaxGlobalSizes(address[] memory _tokens, uint256[] memory _longSizes, uint256[] memory _shortSizes)
        external
    {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        require(_tokens.length == _longSizes.length, "inconsistent length");
        require(_tokens.length == _shortSizes.length, "inconsistent length");
        for (uint256 i = 0; i < _tokens.length; i++) {
            address token = _tokens[i];
            ps.maxGlobalLongSizes[token] = _longSizes[i];
            ps.maxGlobalShortSizes[token] = _shortSizes[i];
        }
        emit SetMaxGlobalSizes(_tokens, _longSizes, _shortSizes);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {DataTypes} from "../types/DataTypes.sol";
import {Errors} from "../helpers/Errors.sol";
import {Calc} from "../math/Calc.sol";
import {Precision} from "../math/Precision.sol";
import {StorageSlot} from "./StorageSlot.sol";
import {GenericLogic} from "./GenericLogic.sol";
import {Constants} from "../helpers/Constants.sol";

struct PositionType {
    uint256 long;
    uint256 short;
}

struct GetNextFundingAmountPerSizeResult {
    bool longsPayShorts;
    uint256 fundingFactorPerSecond;
    PositionType fundingFeeAmountPerSizeDelta;
    PositionType claimableFundingAmountPerSizeDelta;
}

struct GetNextFundingAmountPerSizeCache {
    uint256 longOpenInterest;
    uint256 shortOpenInterest;
    uint256 durationInSeconds;
    uint256 sizeOfLargerSide;
    uint256 fundingUsd;
}

struct GetNextFundingFactorPerSecondCache {
    uint256 diffUsd;
    uint256 totalOpenInterest;
    uint256 fundingFactor;
    uint256 fundingExponentFactor;
    uint256 diffUsdAfterExponent;
    uint256 diffUsdToOpenInterestFactor;
}

library FundingFeeLogic {
    event UpdateFundingState(address token, uint256 lastFundingTime);

    function getFundingFees(address _indexToken, bool _isLong, DataTypes.Position memory position)
        internal
        view
        returns (DataTypes.PositionFundingFees memory)
    {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        DataTypes.PositionFundingFees memory positionFundingFees;

        positionFundingFees.latestFundingFeeAmountPerSize = fs.fundingFeeAmountPerSizes[_indexToken][_isLong];
        positionFundingFees.latestClaimableFundingAmountPerSize =
            fs.claimableFundingAmountPerSizes[_indexToken][_isLong];

        positionFundingFees.fundingFeeAmount = getFundingAmount(
            positionFundingFees.latestFundingFeeAmountPerSize,
            position.fundingFeeAmountPerSize,
            position.size,
            true // roundUpMagnitude
        );

        positionFundingFees.claimableAmount = getFundingAmount(
            positionFundingFees.latestClaimableFundingAmountPerSize,
            position.claimableFundingAmountPerSize,
            position.size,
            false // roundUpMagnitude
        );

        return positionFundingFees;
    }

    function updateFundingState(address _indexToken) external {
        DataTypes.FeeStorage storage fee = StorageSlot.getVaultFeeStorage();

        GetNextFundingAmountPerSizeResult memory result = getNextFundingAmountPerSize(_indexToken);

        fee.fundingFeeAmountPerSizes[_indexToken][true] += result.fundingFeeAmountPerSizeDelta.long;
        fee.fundingFeeAmountPerSizes[_indexToken][false] += result.fundingFeeAmountPerSizeDelta.short;
        fee.claimableFundingAmountPerSizes[_indexToken][true] += result.claimableFundingAmountPerSizeDelta.long;
        fee.claimableFundingAmountPerSizes[_indexToken][false] += result.claimableFundingAmountPerSizeDelta.short;

        fee.lastFundingTimes[_indexToken] = block.timestamp;

        emit UpdateFundingState(_indexToken, fee.lastFundingTimes[_indexToken]);
    }

    function getNextFundingAmountPerSize(address _indexToken)
        internal
        view
        returns (GetNextFundingAmountPerSizeResult memory)
    {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();

        GetNextFundingAmountPerSizeResult memory result;
        GetNextFundingAmountPerSizeCache memory cache;
        cache.longOpenInterest = getOpenInterest(_indexToken, true);
        cache.shortOpenInterest = getOpenInterest(_indexToken, false);
        if (cache.longOpenInterest == 0 || cache.shortOpenInterest == 0) {
            return result;
        }

        if (fs.lastFundingTimes[_indexToken] == 0) {
            cache.durationInSeconds = 0;
        } else {
            cache.durationInSeconds = block.timestamp - fs.lastFundingTimes[_indexToken];
        }

        cache.sizeOfLargerSide =
            cache.longOpenInterest > cache.shortOpenInterest ? cache.longOpenInterest : cache.shortOpenInterest;

        (result.fundingFactorPerSecond, result.longsPayShorts) =
            getNextFundingFactorPerSecond(_indexToken, cache.longOpenInterest, cache.shortOpenInterest);
        cache.fundingUsd =
            Precision.applyFactor(cache.sizeOfLargerSide, cache.durationInSeconds * result.fundingFactorPerSecond);
        uint256 shortTokenPrice = Constants.ONE_USD;
        if (result.longsPayShorts) {
            result.fundingFeeAmountPerSizeDelta.long = getFundingAmountPerSizeDelta(
                cache.fundingUsd,
                cache.longOpenInterest,
                shortTokenPrice,
                true // roundUpMagnitude
            );

            result.claimableFundingAmountPerSizeDelta.short = getFundingAmountPerSizeDelta(
                cache.fundingUsd,
                cache.shortOpenInterest,
                shortTokenPrice,
                false // roundUpMagnitude
            );
        } else {
            result.fundingFeeAmountPerSizeDelta.short = getFundingAmountPerSizeDelta(
                cache.fundingUsd,
                cache.shortOpenInterest,
                shortTokenPrice,
                true // roundUpMagnitude
            );

            result.claimableFundingAmountPerSizeDelta.long = getFundingAmountPerSizeDelta(
                cache.fundingUsd,
                cache.longOpenInterest,
                shortTokenPrice,
                false // roundUpMagnitude
            );
        }

        return result;
    }

    // @dev get the next funding factor per second
    // @return nextFundingFactorPerSecond, longsPayShorts
    function getNextFundingFactorPerSecond(address _indexToken, uint256 _longOpenInterest, uint256 _shortOpenInterest)
        internal
        view
        returns (uint256, bool)
    {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();

        GetNextFundingFactorPerSecondCache memory cache;

        cache.diffUsd = Calc.diff(_longOpenInterest, _shortOpenInterest);
        cache.totalOpenInterest = _longOpenInterest + _shortOpenInterest;
        if (cache.diffUsd == 0) {
            return (0, true);
        }

        require(cache.totalOpenInterest > 0, Errors.EMPTY_OPEN_INTEREST);

        cache.fundingExponentFactor = fs.fundingExponentFactors[_indexToken];
        cache.diffUsdAfterExponent = Precision.applyExponentFactor(cache.diffUsd, cache.fundingExponentFactor);
        cache.diffUsdToOpenInterestFactor = Precision.toFactor(cache.diffUsdAfterExponent, cache.totalOpenInterest);
        cache.fundingFactor = fs.fundingFactors[_indexToken];

        return (
            Precision.applyFactor(cache.diffUsdToOpenInterestFactor, cache.fundingFactor),
            _longOpenInterest > _shortOpenInterest
        );
    }

    // store funding values as token amount per (Precision.FLOAT_PRECISION_SQRT / Precision.FLOAT_PRECISION) of USD size
    function getFundingAmountPerSizeDelta(
        uint256 fundingUsd,
        uint256 openInterest,
        uint256 tokenPrice,
        bool roundUpMagnitude
    ) internal pure returns (uint256) {
        if (fundingUsd == 0 || openInterest == 0) {
            return 0;
        }
        uint256 fundingUsdPerSize = Precision.mulDiv(
            fundingUsd, Precision.FLOAT_PRECISION * Precision.FLOAT_PRECISION_SQRT, openInterest, roundUpMagnitude
        );

        if (roundUpMagnitude) {
            return Calc.roundUpDivision(fundingUsdPerSize, tokenPrice);
        } else {
            return (fundingUsdPerSize) / tokenPrice;
        }
    }

    function getOpenInterest(address _indexToken, bool _isLong) internal view returns (uint256) {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();

        if (_isLong) {
            return ps.globalLongSizes[_indexToken];
        } else {
            return ps.globalShortSizes[_indexToken];
        }
    }

    // @dev get the funding amount to be deducted or distributed
    //
    // @param latestFundingAmountPerSize the latest funding amount per size
    // @param positionFundingAmountPerSize the funding amount per size for the position
    // @param positionSizeInUsd the position size in USD
    // @param roundUpMagnitude whether the round up the result
    //
    // @return fundingAmount
    function getFundingAmount(
        uint256 latestFundingAmountPerSize,
        uint256 positionFundingAmountPerSize,
        uint256 positionSizeInUsd,
        bool roundUpMagnitude
    ) internal pure returns (uint256) {
        uint256 fundingDiffFactor = (latestFundingAmountPerSize - positionFundingAmountPerSize);

        // a user could avoid paying funding fees by continually updating the position
        // before the funding fee becomes large enough to be chargeable
        // to avoid this, funding fee amounts should be rounded up
        //
        // this could lead to large additional charges if the token has a low number of decimals
        // or if the token's value is very high, so care should be taken to inform users of this
        //
        // if the calculation is for the claimable amount, the amount should be rounded down instead

        // divide the result by Precision.FLOAT_PRECISION * Precision.FLOAT_PRECISION_SQRT as the fundingAmountPerSize values
        // are stored based on FLOAT_PRECISION_SQRT values
        return Precision.mulDiv(positionSizeInUsd, fundingDiffFactor, Precision.FLOAT_PRECISION_SQRT, roundUpMagnitude);
    }

    function incrementClaimableFundingAmount(address _account, DataTypes.PositionFundingFees memory positionFundingFees)
        internal
    {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        // if the position has negative funding fees, distribute it to allow it to be claimable
        if (positionFundingFees.claimableAmount > 0) {
            fs.claimableFundingAmount[_account] += positionFundingFees.claimableAmount;
        }
    }
}

File 25 of 36 : GenericLogic.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {SignedMath} from "@openzeppelin/contracts/utils/math/SignedMath.sol";
import {
    IERC20Upgradeable,
    SafeERC20Upgradeable
} from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol";
import {MathUpgradeable} from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol";

import {ValidationLogic} from "./ValidationLogic.sol";
import {StorageSlot} from "./StorageSlot.sol";

import {Constants} from "../helpers/Constants.sol";
import {Errors} from "../helpers/Errors.sol";
import {DataTypes} from "../types/DataTypes.sol";

import {IVaultPriceFeed} from "../../interfaces/IVaultPriceFeed.sol";
import {IUlpManager} from "../../interfaces/IUlpManager.sol";

library GenericLogic {
    using MathUpgradeable for uint256;
    using SafeCast for uint256;
    using SafeCast for int256;
    using SignedMath for int256;
    using SafeERC20Upgradeable for IERC20Upgradeable;

    event CollectSwapFees(address token, uint256 feeUsd, uint256 feeTokens, bool protocolPayFee);
    event IncreasePoolAmount(address token, uint256 amount);
    event DecreasePoolAmount(address token, uint256 amount);
    event IncreaseUsdgAmount(address token, uint256 amount);
    event DecreaseUsdgAmount(address token, uint256 amount);
    event IncreaseReservedAmount(address token, uint256 amount);
    event DecreaseReservedAmount(address token, uint256 amount);
    event WithdrawFee(address token, uint256 amount, address receiver);

    function collectSwapFees(address _token, uint256 _amount, int256 _feeBasisPoints) internal returns (uint256) {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        uint256 afterFeeAmount;
        uint256 feeAmount;
        if (_feeBasisPoints < 0) {
            afterFeeAmount =
                (_amount * (Constants.PERCENTAGE_FACTOR + _feeBasisPoints.abs())) / Constants.PERCENTAGE_FACTOR;
            emit CollectSwapFees(_token, tokenToUsdMin(_token, feeAmount), feeAmount, true);
            return afterFeeAmount;
        } else {
            afterFeeAmount =
                (_amount * (Constants.PERCENTAGE_FACTOR - _feeBasisPoints.abs())) / Constants.PERCENTAGE_FACTOR;
            feeAmount = _amount - afterFeeAmount;
            fs.feeReserves[_token] = fs.feeReserves[_token] + feeAmount;
            emit CollectSwapFees(_token, tokenToUsdMin(_token, feeAmount), feeAmount, false);
            return afterFeeAmount;
        }
    }

    // cases to consider
    // 1. initialAmount is far from targetAmount, action increases balance slightly => high rebate
    // 2. initialAmount is far from targetAmount, action increases balance largely => high rebate
    // 3. initialAmount is close to targetAmount, action increases balance slightly => low rebate
    // 4. initialAmount is far from targetAmount, action reduces balance slightly => high tax
    // 5. initialAmount is far from targetAmount, action reduces balance largely => high tax
    // 6. initialAmount is close to targetAmount, action reduces balance largely => low tax
    // 7. initialAmount is above targetAmount, nextAmount is below targetAmount and vice versa
    // 8. a large swap should have similar fees as the same trade split into multiple smaller swaps

    // avoid stack too deep
    struct GetFeeBasisPointsCache {
        uint256 fee;
        bool haveBalanceReward;
        bool isCrossBalance;
        uint256 initialAmount;
        uint256 nextAmount;
        uint256 initialTargetAmount;
        int256 initialDiff;
        uint256 nextTargetAmount;
        int256 nextDiff;
    }

    function getFeeBasisPoints(
        address _token,
        uint256 _usdgDelta,
        uint256 _feeBasisPoints,
        uint256 _taxBasisPoints,
        bool _increment,
        bool _isSwap
    ) internal view returns (uint256, bool, bool) {
        GetFeeBasisPointsCache memory cache;
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        if (!fs.hasDynamicFees) {
            return (_feeBasisPoints, false, false);
        }

        cache.initialAmount = ps.usdgAmounts[_token];
        cache.nextAmount = cache.initialAmount + _usdgDelta;
        if (!_increment) {
            if (_usdgDelta > cache.initialAmount) {
                cache.nextAmount = 0;
                _usdgDelta = cache.initialAmount;
            } else {
                cache.nextAmount = cache.initialAmount - _usdgDelta;
            }
        }

        cache.initialTargetAmount = getTargetUsdgAmount(_token);
        if (_isSwap && !_increment) {
            cache.initialTargetAmount = getNextTargetUsdgAmount(_token, _usdgDelta, true);
        }
        if (cache.initialTargetAmount == 0) {
            return (_feeBasisPoints, false, false);
        }
        cache.initialDiff = cache.initialTargetAmount.toInt256() - cache.initialAmount.toInt256();

        cache.nextTargetAmount = getNextTargetUsdgAmount(_token, _usdgDelta, _increment);
        if (_isSwap && !_increment) {
            cache.nextTargetAmount = getTargetUsdgAmount(_token);
        }
        if (cache.nextTargetAmount == 0) {
            return (_feeBasisPoints, false, false);
        }
        cache.nextDiff = cache.nextTargetAmount.toInt256() - cache.nextAmount.toInt256();

        if (_increment) {
            // add liquidity
            if (cache.nextDiff.abs() < cache.initialDiff.abs()) {
                // action improves relative asset balance
                uint256 rebateBps = (_taxBasisPoints * cache.initialDiff.abs()) / cache.nextTargetAmount;
                cache.fee = rebateBps > _feeBasisPoints ? 0 : _feeBasisPoints - rebateBps;

                if ((cache.initialDiff < 0 && cache.nextDiff <= 0) || (cache.initialDiff > 0 && cache.nextDiff >= 0)) {
                    // no cross balance, have balance reward
                    cache.haveBalanceReward = true;
                    cache.isCrossBalance = false;
                } else {
                    // cross balance, no balance reward
                    cache.haveBalanceReward = false;
                    cache.isCrossBalance = true;
                }
            } else {
                // action worsen relative asset balance
                uint256 averageDiff = (cache.initialDiff.abs() + cache.nextDiff.abs()) / 2;
                if (averageDiff > cache.nextTargetAmount) {
                    averageDiff = cache.nextTargetAmount;
                }
                uint256 taxBps = (_taxBasisPoints * averageDiff) / cache.nextTargetAmount;
                if ((cache.initialDiff < 0 && cache.nextDiff <= 0) || (cache.initialDiff > 0 && cache.nextDiff >= 0)) {
                    // no cross balance, no balance reward
                    cache.fee = _feeBasisPoints + taxBps;
                    cache.haveBalanceReward = false;
                    cache.isCrossBalance = false;
                } else {
                    // cross balance, no balance reward
                    cache.fee = _feeBasisPoints + taxBps;
                    cache.haveBalanceReward = false;
                    cache.isCrossBalance = true;
                }
            }
        } else {
            // remove liquidity
            if ((cache.initialDiff < 0 && cache.nextDiff <= 0) || (cache.initialDiff > 0 && cache.nextDiff >= 0)) {
                // no cross balance
                // action improves relative asset balance
                if (cache.nextDiff.abs() < cache.initialDiff.abs()) {
                    uint256 rebateBps = (_taxBasisPoints * cache.initialDiff.abs()) / cache.nextTargetAmount;
                    cache.fee = rebateBps > _feeBasisPoints ? 0 : _feeBasisPoints - rebateBps;
                    cache.haveBalanceReward = false;
                    cache.isCrossBalance = false;
                } else {
                    uint256 averageDiff = (cache.initialDiff.abs() + cache.nextDiff.abs()) / 2;
                    if (averageDiff > cache.nextTargetAmount) {
                        averageDiff = cache.nextTargetAmount;
                    }
                    uint256 taxBps = (_taxBasisPoints * averageDiff) / cache.nextTargetAmount;
                    cache.fee = _feeBasisPoints + taxBps;
                    cache.haveBalanceReward = false;
                    cache.isCrossBalance = false;
                }
            } else {
                // cross balance
                uint256 averageDiff = (cache.initialDiff.abs() + cache.nextDiff.abs()) / 2;
                if (averageDiff > cache.nextTargetAmount) {
                    averageDiff = cache.nextTargetAmount;
                }
                uint256 taxBps = (_taxBasisPoints * averageDiff) / cache.nextTargetAmount;
                cache.fee = _feeBasisPoints + taxBps;
                cache.haveBalanceReward = false;
                cache.isCrossBalance = true;
            }
        }
        if (_token != addrs.collateralToken) {
            cache.haveBalanceReward = false;
        }
        if (!_increment && _token == addrs.collateralToken) {
            cache.fee += fs.balanceReward;
        }
        return (cache.fee, cache.haveBalanceReward, cache.isCrossBalance);
    }

    function adjustForDecimals(uint256 _amount, address _tokenDiv, address _tokenMul) internal view returns (uint256) {
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        uint256 decimalsDiv = _tokenDiv == addrs.usdg ? Constants.USDG_DECIMALS : ts.tokenDecimals[_tokenDiv];
        uint256 decimalsMul = _tokenMul == addrs.usdg ? Constants.USDG_DECIMALS : ts.tokenDecimals[_tokenMul];
        return (_amount * 10 ** decimalsMul) / 10 ** decimalsDiv;
    }

    function adjustFor30Decimals(uint256 _amount, address _tokenMul) internal view returns (uint256) {
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        uint256 decimalsDiv = Constants.PRICE_PRECISION;
        uint256 decimalsMul = ts.tokenDecimals[_tokenMul];
        return (_amount * 10 ** decimalsMul) / decimalsDiv;
    }

    function getMaxPrice(address _token) internal view returns (uint256) {
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        return IVaultPriceFeed(addrs.priceFeed).getPrice(_token, true);
    }

    function getMinPrice(address _token) internal view returns (uint256) {
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        return IVaultPriceFeed(addrs.priceFeed).getPrice(_token, false);
    }

    function tokenToUsdMin(address _token, uint256 _tokenAmount) internal view returns (uint256) {
        if (_tokenAmount == 0) {
            return 0;
        }
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        uint256 price = getMinPrice(_token);
        uint256 decimals = ts.tokenDecimals[_token];
        return (_tokenAmount * price) / 10 ** decimals;
    }

    function usdToTokenMax(address _token, uint256 _usdAmount) internal view returns (uint256) {
        if (_usdAmount == 0) {
            return 0;
        }
        return usdToToken(_token, _usdAmount, getMinPrice(_token));
    }

    function usdToTokenMin(address _token, uint256 _usdAmount) internal view returns (uint256) {
        if (_usdAmount == 0) {
            return 0;
        }
        return usdToToken(_token, _usdAmount, getMaxPrice(_token));
    }

    function usdToToken(address _token, uint256 _usdAmount, uint256 _price) internal view returns (uint256) {
        if (_usdAmount == 0) {
            return 0;
        }
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        uint256 decimals = ts.tokenDecimals[_token];
        return (_usdAmount * 10 ** decimals) / _price;
    }

    function getTargetUsdgAmount(address _token) internal view returns (uint256) {
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        uint256 supply = IERC20Upgradeable(addrs.usdg).totalSupply();
        if (supply == 0) {
            return 0;
        }
        uint256 weight = ts.tokenWeights[_token];
        return (weight * supply) / ts.totalTokenWeights;
    }

    function getNextTargetUsdgAmount(address _token, uint256 _usdgDelta, bool _increment)
        internal
        view
        returns (uint256)
    {
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        uint256 supply = IERC20Upgradeable(addrs.usdg).totalSupply();
        if (_increment) {
            supply += _usdgDelta;
        } else {
            if (_usdgDelta > supply) {
                return 0;
            }
            supply -= _usdgDelta;
        }
        if (supply == 0) {
            return 0;
        }
        uint256 weight = ts.tokenWeights[_token];
        return (weight * supply) / ts.totalTokenWeights;
    }

    function transferIn(address _token) internal returns (uint256) {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        uint256 prevBalance = ps.tokenBalances[_token];
        uint256 nextBalance = IERC20Upgradeable(_token).balanceOf(address(this));
        ps.tokenBalances[_token] = nextBalance;

        return nextBalance - prevBalance;
    }

    function transferOut(address _token, uint256 _amount, address _receiver) internal {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        IERC20Upgradeable(_token).safeTransfer(_receiver, _amount);
        ps.tokenBalances[_token] = IERC20Upgradeable(_token).balanceOf(address(this));
    }

    function getRedemptionAmount(address _token, uint256 _usdgAmount) internal view returns (uint256) {
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        uint256 price = getMaxPrice(_token);
        uint256 redemptionAmount = (_usdgAmount * Constants.PRICE_PRECISION) / price;
        return adjustForDecimals(redemptionAmount, addrs.usdg, _token);
    }

    function increasePoolAmount(address _token, uint256 _amount) internal {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        ps.poolAmounts[_token] += _amount;
        uint256 balance = IERC20Upgradeable(_token).balanceOf(address(this));
        ValidationLogic.validate(ps.poolAmounts[_token] <= balance, Errors.VAULT_POOL_AMOUNT_EXCEEDED);
        emit IncreasePoolAmount(_token, _amount);
    }

    function decreasePoolAmount(address _token, uint256 _amount, bool isDecreasePosition) internal {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        ValidationLogic.validate(ps.poolAmounts[_token] >= _amount, Errors.VAULT_POOL_AMOUNT_EXCEEDED);
        ps.poolAmounts[_token] -= _amount;
        if (!isDecreasePosition) {
            ValidationLogic.validate(
                ps.reservedAmounts[_token] <= ps.poolAmounts[_token], Errors.VAULT_RESERVE_EXCEEDS_POOL
            );
        }
        emit DecreasePoolAmount(_token, _amount);
    }

    function increaseUsdgAmount(address _token, uint256 _amount) internal {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        ps.usdgAmounts[_token] = ps.usdgAmounts[_token] + _amount;
        uint256 maxUsdgAmount = ts.maxUsdgAmounts[_token];
        if (maxUsdgAmount != 0) {
            ValidationLogic.validate(ps.usdgAmounts[_token] <= maxUsdgAmount, Errors.VAULT_MAX_USDG_EXCEEDED);
        }
        emit IncreaseUsdgAmount(_token, _amount);
    }

    function decreaseUsdgAmount(address _token, uint256 _amount) internal {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        uint256 value = ps.usdgAmounts[_token];
        // since USDG can be minted using multiple assets
        // it is possible for the USDG debt for a single asset to be less than zero
        // the USDG debt is capped to zero for this case
        if (value <= _amount) {
            ps.usdgAmounts[_token] = 0;
            emit DecreaseUsdgAmount(_token, value);
            return;
        }
        ps.usdgAmounts[_token] = value - _amount;
        emit DecreaseUsdgAmount(_token, _amount);
    }

    function increaseReservedAmount(address _token, uint256 _amount) internal {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();

        uint256 aum = IUlpManager(addrs.ulpManager).getAum(true);

        ps.reservedAmounts[_token] = ps.reservedAmounts[_token] + _amount;
        uint256 price = getMinPrice(_token);
        uint256 decimals = ts.tokenDecimals[_token];
        uint256 reservedUsd = (price * ps.reservedAmounts[_token]) / 10 ** decimals;
        ValidationLogic.validate(reservedUsd <= aum, Errors.VAULT_RESERVE_EXCEEDS_POOL);
        emit IncreaseReservedAmount(_token, _amount);
    }

    function decreaseReservedAmount(address _token, uint256 _amount) internal {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        ValidationLogic.validate(ps.reservedAmounts[_token] - _amount >= 0, Errors.VAULT_INSUFFICIENT_RESERVE);
        ps.reservedAmounts[_token] -= _amount;
        emit DecreaseReservedAmount(_token, _amount);
    }

    function increaseGlobalLongSize(address _token, uint256 _amount) internal {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        ps.globalLongSizes[_token] = ps.globalLongSizes[_token] + _amount;
        uint256 maxSize = ps.maxGlobalLongSizes[_token];
        if (maxSize != 0) {
            ValidationLogic.validate(ps.globalLongSizes[_token] <= maxSize, Errors.VAULT_MAX_LONG_EXCEEDED);
        }
    }

    function decreaseGlobalLongSize(address _token, uint256 _amount) internal {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        uint256 size = ps.globalLongSizes[_token];
        if (_amount > size) {
            ps.globalLongSizes[_token] = 0;
            return;
        }

        ps.globalLongSizes[_token] = size - _amount;
    }

    function increaseGlobalShortSize(address _token, uint256 _amount) internal {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        ps.globalShortSizes[_token] = ps.globalShortSizes[_token] + _amount;
        uint256 maxSize = ps.maxGlobalShortSizes[_token];
        if (maxSize != 0) {
            ValidationLogic.validate(ps.globalShortSizes[_token] <= maxSize, Errors.VAULT_MAX_SHORTS_EXCEEDED);
        }
    }

    function decreaseGlobalShortSize(address _token, uint256 _amount) internal {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        uint256 size = ps.globalShortSizes[_token];
        if (_amount > size) {
            ps.globalShortSizes[_token] = 0;
            return;
        }

        ps.globalShortSizes[_token] = size - _amount;
    }

    function updateTokenBalance(address _token) internal {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        uint256 nextBalance = IERC20Upgradeable(_token).balanceOf(address(this));
        ps.tokenBalances[_token] = nextBalance;
    }

    function withdrawFees(address _token) external returns (uint256) {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        require(addrs.feesReceiver != address(0), "feesReceiver is 0");
        uint256 amount = fs.feeReserves[_token];
        if (amount == 0) {
            return 0;
        }
        fs.feeReserves[_token] = 0;
        transferOut(_token, amount, addrs.feesReceiver);
        emit WithdrawFee(_token, amount, addrs.feesReceiver);
        return amount;
    }

    function getPoolInfo(address _token) external view returns (DataTypes.PoolInfo memory) {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        DataTypes.PoolInfo memory poolInfo;

        poolInfo.poolAmount = ps.poolAmounts[_token];
        poolInfo.reservedAmount = ps.reservedAmounts[_token];
        poolInfo.bufferAmount = ps.bufferAmounts[_token];
        poolInfo.globalLongSize = ps.globalLongSizes[_token];
        poolInfo.globalLongAveragePrice = ps.globalLongAveragePrices[_token];
        poolInfo.globalShortSize = ps.globalShortSizes[_token];
        poolInfo.globalShortAveragePrice = ps.globalShortAveragePrices[_token];
        poolInfo.usdgAmount = ps.usdgAmounts[_token];

        return poolInfo;
    }

    function getWhitelistedToken() internal view returns (uint256, address[] memory) {
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        return (ts.whitelistedTokenCount, ts.allWhitelistedTokens);
    }

    function getTokenInfo(address _token) internal view returns (DataTypes.TokenInfo memory) {
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();

        return DataTypes.TokenInfo({
            tokenDecimal: ts.tokenDecimals[_token],
            isWhitelistedToken: ts.whitelistedTokens[_token],
            isStableToken: ts.stableTokens[_token],
            maxUsdgAmount: ts.maxUsdgAmounts[_token]
        });
    }

    function getBuyUsdgFeeBasisPoints(address _token, uint256 _usdgAmount) internal view returns (int256) {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        (uint256 fee, bool haveBalanceReward,) =
            getFeeBasisPoints(_token, _usdgAmount, fs.mintBurnFeeBasisPoints, fs.taxBasisPoints, true, false);
        if (haveBalanceReward) {
            return fee.toInt256() - fs.balanceReward.toInt256();
        } else {
            return fee.toInt256();
        }
    }

    function getSellUsdgFeeBasisPoints(address _token, uint256 _usdgAmount) internal view returns (uint256) {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();

        (uint256 fee,,) =
            getFeeBasisPoints(_token, _usdgAmount, fs.mintBurnFeeBasisPoints, fs.taxBasisPoints, false, false);
        return fee;
    }

    function getUtilisation(address _token) public view returns (uint256) {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        uint256 poolAmount = ps.poolAmounts[_token];
        if (poolAmount == 0) {
            return 0;
        }
        return (ps.reservedAmounts[_token] * Constants.BORROWING_RATE_PRECISION) / poolAmount;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";
import {SignedMath} from "@openzeppelin/contracts/utils/math/SignedMath.sol";

import {ValidationLogic} from "./ValidationLogic.sol";
import {GenericLogic} from "./GenericLogic.sol";
import {BorrowingFeeLogic} from "./BorrowingFeeLogic.sol";
import {FundingFeeLogic} from "./FundingFeeLogic.sol";
import {StorageSlot} from "./StorageSlot.sol";
import {DataTypes} from "../types/DataTypes.sol";
import {Errors} from "../helpers/Errors.sol";
import {Calc} from "../math/Calc.sol";
import {Precision} from "../math/Precision.sol";

import {DataTypes} from "../types/DataTypes.sol";

import {Constants} from "../helpers/Constants.sol";

library PositionLogic {
    using SafeCast for uint256;
    using SafeCast for int256;
    using SignedMath for int256;

    event CollectMarginFees(address token, uint256 feeUsd, uint256 feeTokens);
    event UpdatePnl(bytes32 key, bool hasProfit, uint256 delta);
    event ClaimFundingFee(address indexed account, address token, uint256 amount);
    event IncreasePosition(
        bytes32 key, DataTypes.IncreasePositionParams params, uint256 collateralDelta, uint256 price, uint256 fee
    );

    event DecreasePosition(bytes32 key, DataTypes.DecreasePositionParams params, uint256 price, uint256 fee);

    event LiquidatePosition(
        bytes32 key,
        address account,
        address collateralToken,
        address indexToken,
        bool isLong,
        uint256 size,
        uint256 collateral,
        uint256 reserveAmount,
        int256 realisedPnl,
        uint256 markPrice
    );

    event UpdatePosition(
        bytes32 key,
        uint256 size,
        uint256 collateral,
        uint256 averagePrice,
        uint256 entryBorrowingRate,
        uint256 reserveAmount,
        int256 realisedPnl,
        uint256 markPrice
    );
    event ClosePosition(
        bytes32 key,
        uint256 size,
        uint256 collateral,
        uint256 averagePrice,
        uint256 entryBorrowingRate,
        uint256 reserveAmount,
        int256 realisedPnl
    );

    struct CollectMarginFeesParams {
        address account;
        address collateralToken;
        address indexToken;
        bool isLong;
        uint256 sizeDelta;
        DataTypes.Position position;
    }

    struct ReduceCollateralCache {
        uint256 fee;
        bool hasProfit;
        uint256 adjustedDelta;
        uint256 delta;
        uint256 usdOut;
        uint256 usdOutAfterFee;
        DataTypes.PositionFundingFees fundingFees;
    }

    struct IncreasePositionCache {
        uint256 entryPrice;
        uint256 fee;
        DataTypes.PositionFundingFees fundingFees;
        uint256 collateralDelta;
        uint256 collateralDeltaUsd;
        uint256 reserveDelta;
    }

    struct DecreasePositionCache {
        bytes32 key;
        uint256 reservedAmount;
        uint256 reserveDelta;
        uint256 usdOut;
        uint256 usdOutAfterFee;
        DataTypes.PositionFundingFees fundingFees;
        uint256 price;
        uint256 amountOutAfterFees;
    }

    function increasePosition(DataTypes.IncreasePositionParams memory params) external {
        IncreasePositionCache memory cache;
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        DataTypes.PermissionStorage storage permission = StorageSlot.getVaultPermissionStorage();
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();

        ValidationLogic.validateIncreasePositionParams(
            permission.isLeverageEnabled, params.account, params.collateralToken, params.indexToken, params.isLong
        );

        bytes32 key = getPositionKey(params.account, params.collateralToken, params.indexToken, params.isLong);
        DataTypes.Position storage position = ps.positions[key];
        cache.entryPrice =
            params.isLong ? GenericLogic.getMaxPrice(params.indexToken) : GenericLogic.getMinPrice(params.indexToken);

        BorrowingFeeLogic.updateCumulativeBorrowingRate(params.collateralToken, params.indexToken);

        FundingFeeLogic.updateFundingState(params.indexToken);

        if (position.size == 0) {
            position.averagePrice = cache.entryPrice;
            position.fundingFeeAmountPerSize = fs.fundingFeeAmountPerSizes[params.indexToken][params.isLong];
            position.claimableFundingAmountPerSize = fs.claimableFundingAmountPerSizes[params.indexToken][params.isLong];
        }
        if (position.size > 0 && params.sizeDelta > 0) {
            position.averagePrice = getNextAveragePrice(
                params.indexToken,
                position.size,
                position.averagePrice,
                params.isLong,
                cache.entryPrice,
                params.sizeDelta
            );
        }
        (cache.fee, cache.fundingFees) = collectMarginFees(
            CollectMarginFeesParams({
                account: params.account,
                collateralToken: params.collateralToken,
                indexToken: params.indexToken,
                isLong: params.isLong,
                sizeDelta: params.sizeDelta,
                position: position
            })
        );
        {
            FundingFeeLogic.incrementClaimableFundingAmount(params.account, cache.fundingFees);
        }

        cache.collateralDelta = GenericLogic.transferIn(params.collateralToken);
        cache.collateralDeltaUsd = GenericLogic.tokenToUsdMin(params.collateralToken, cache.collateralDelta);

        position.fundingFeeAmountPerSize = cache.fundingFees.latestFundingFeeAmountPerSize;
        position.claimableFundingAmountPerSize = cache.fundingFees.latestClaimableFundingAmountPerSize;
        position.collateral = position.collateral + cache.collateralDeltaUsd;
        ValidationLogic.validate(position.collateral >= cache.fee, Errors.VAULT_INSUFFICIENT_COLLATERAL_FOR_FEES);
        position.collateral = position.collateral - cache.fee;
        position.entryBorrowingRate = fs.cumulativeBorrowingRates[params.collateralToken];
        position.size = position.size + params.sizeDelta;
        position.lastIncreasedTime = block.timestamp;
        ValidationLogic.validate(position.size > 0, Errors.VAULT_INVALID_POSITION_SIZE);
        ValidationLogic.validatePosition(position.size, position.collateral);
        validateLiquidation(params.account, params.collateralToken, params.indexToken, params.isLong, true);

        // reserve tokens to pay profits on the position
        cache.reserveDelta = GenericLogic.usdToTokenMax(params.collateralToken, params.sizeDelta);
        if (ps.reserveRatio != 0) {
            cache.reserveDelta = (cache.reserveDelta * ps.reserveRatio) / Constants.PERCENTAGE_FACTOR;
        }
        position.reserveAmount = position.reserveAmount + cache.reserveDelta;
        GenericLogic.increaseReservedAmount(params.collateralToken, cache.reserveDelta);

        if (params.isLong) {
            if (ps.globalLongSizes[params.indexToken] == 0) {
                ps.globalLongAveragePrices[params.indexToken] = cache.entryPrice;
            } else {
                ps.globalLongAveragePrices[params.indexToken] = getNextGlobalLongData(
                    params.account, params.collateralToken, params.indexToken, cache.entryPrice, params.sizeDelta, true
                );
            }
            GenericLogic.increaseGlobalLongSize(params.indexToken, params.sizeDelta);
        } else {
            if (ps.globalShortSizes[params.indexToken] == 0) {
                ps.globalShortAveragePrices[params.indexToken] = cache.entryPrice;
            } else {
                ps.globalShortAveragePrices[params.indexToken] = getNextGlobalShortData(
                    params.account, params.collateralToken, params.indexToken, cache.entryPrice, params.sizeDelta, true
                );
            }
            GenericLogic.increaseGlobalShortSize(params.indexToken, params.sizeDelta);
        }

        emit IncreasePosition(key, params, cache.collateralDeltaUsd, cache.entryPrice, cache.fee);
        emit UpdatePosition(
            key,
            position.size,
            position.collateral,
            position.averagePrice,
            position.entryBorrowingRate,
            position.reserveAmount,
            position.realisedPnl,
            cache.entryPrice
        );
    }

    function decreasePosition(DataTypes.DecreasePositionParams memory params) external returns (uint256) {
        ValidationLogic.validateDecreasePositionParams(params.account);
        return _decreasePosition(params);
    }

    function _decreasePosition(DataTypes.DecreasePositionParams memory params) internal returns (uint256) {
        DecreasePositionCache memory cache;

        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();

        BorrowingFeeLogic.updateCumulativeBorrowingRate(params.collateralToken, params.indexToken);

        FundingFeeLogic.updateFundingState(params.indexToken);

        cache.key = getPositionKey(params.account, params.collateralToken, params.indexToken, params.isLong);
        DataTypes.Position storage position = ps.positions[cache.key];

        ValidationLogic.validate(position.size > 0, Errors.VAULT_EMPTY_POSITION);
        ValidationLogic.validate(position.size >= params.sizeDelta, Errors.VAULT_POSITION_SIZE_EXCEEDED);
        ValidationLogic.validate(
            position.collateral >= params.collateralDelta, Errors.VAULT_POSITION_COLLATERAL_EXCEEDED
        );

        cache.reservedAmount = ps.reservedAmounts[params.indexToken];

        cache.reserveDelta = (position.reserveAmount * (params.sizeDelta)) / position.size;
        position.reserveAmount = position.reserveAmount - cache.reserveDelta;
        GenericLogic.decreaseReservedAmount(params.collateralToken, cache.reserveDelta);
        (cache.usdOut, cache.usdOutAfterFee, cache.fundingFees) = reduceCollateral(params);

        cache.price =
            params.isLong ? GenericLogic.getMinPrice(params.indexToken) : GenericLogic.getMaxPrice(params.indexToken);
        FundingFeeLogic.incrementClaimableFundingAmount(params.account, cache.fundingFees);
        if (params.isLong) {
            ps.globalLongAveragePrices[params.indexToken] = getNextGlobalLongData(
                params.account, params.collateralToken, params.indexToken, cache.price, params.sizeDelta, false
            );
            GenericLogic.decreaseGlobalLongSize(params.indexToken, params.sizeDelta);
        } else {
            ps.globalShortAveragePrices[params.indexToken] = getNextGlobalShortData(
                params.account, params.collateralToken, params.indexToken, cache.price, params.sizeDelta, false
            );
            GenericLogic.decreaseGlobalShortSize(params.indexToken, params.sizeDelta);
        }

        if (position.size != params.sizeDelta) {
            position.entryBorrowingRate = fs.cumulativeBorrowingRates[params.collateralToken];

            position.fundingFeeAmountPerSize = cache.fundingFees.latestFundingFeeAmountPerSize;
            position.claimableFundingAmountPerSize = cache.fundingFees.latestClaimableFundingAmountPerSize;

            position.size = position.size - params.sizeDelta;
            ValidationLogic.validatePosition(position.size, position.collateral);

            validateLiquidation(params.account, params.collateralToken, params.indexToken, params.isLong, true);
            emit UpdatePosition(
                cache.key,
                position.size,
                position.collateral,
                position.averagePrice,
                position.entryBorrowingRate,
                position.reserveAmount,
                position.realisedPnl,
                cache.price
            );
        } else {
            emit ClosePosition(
                cache.key,
                position.size,
                position.collateral,
                position.averagePrice,
                position.entryBorrowingRate,
                position.reserveAmount,
                position.realisedPnl
            );
            delete ps.positions[cache.key];
        }

        emit DecreasePosition(cache.key, params, cache.price, cache.usdOut - cache.usdOutAfterFee);

        if (cache.usdOutAfterFee > 0) {
            cache.amountOutAfterFees = GenericLogic.usdToTokenMin(params.collateralToken, cache.usdOutAfterFee);
            GenericLogic.transferOut(params.collateralToken, cache.amountOutAfterFees, params.receiver);
            return cache.amountOutAfterFees;
        }

        return 0;
    }

    function liquidatePosition(DataTypes.LiquidatePositionParams memory params) external {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        DataTypes.PermissionStorage storage permission = StorageSlot.getVaultPermissionStorage();
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        if (permission.inPrivateLiquidationMode) {
            ValidationLogic.validate(permission.isLiquidator[msg.sender], Errors.VAULT_INVALID_LIQUIDATOR);
        }
        uint256 markPrice =
            params.isLong ? GenericLogic.getMinPrice(params.indexToken) : GenericLogic.getMaxPrice(params.indexToken);

        BorrowingFeeLogic.updateCumulativeBorrowingRate(params.collateralToken, params.indexToken);
        FundingFeeLogic.updateFundingState(params.indexToken);

        bytes32 key = getPositionKey(params.account, params.collateralToken, params.indexToken, params.isLong);
        DataTypes.Position storage position = ps.positions[key];
        ValidationLogic.validate(position.size > 0, Errors.VAULT_EMPTY_POSITION);
        (uint256 liquidationState, uint256 marginFees) =
            validateLiquidation(params.account, params.collateralToken, params.indexToken, params.isLong, false);
        ValidationLogic.validate(liquidationState != 0, Errors.VAULT_POSITION_CAN_NOT_BE_LIQUIDATED);
        if (liquidationState == 2) {
            // max leverage exceeded but there is collateral remaining after deducting losses so decreasePosition instead
            _decreasePosition(
                DataTypes.DecreasePositionParams({
                    account: params.account,
                    collateralToken: params.collateralToken,
                    indexToken: params.indexToken,
                    collateralDelta: 0,
                    sizeDelta: position.size,
                    isLong: params.isLong,
                    receiver: params.account
                })
            );
            return;
        }
        {
            uint256 feeTokens = GenericLogic.usdToTokenMin(params.collateralToken, marginFees);
            fs.feeReserves[params.collateralToken] = fs.feeReserves[params.collateralToken] + feeTokens;
            emit CollectMarginFees(params.collateralToken, marginFees, feeTokens);
        }
        GenericLogic.decreaseReservedAmount(params.collateralToken, position.reserveAmount);
        emit LiquidatePosition(
            key,
            params.account,
            params.collateralToken,
            params.indexToken,
            params.isLong,
            position.size,
            position.collateral,
            position.reserveAmount,
            position.realisedPnl,
            markPrice
        );
        if (marginFees < position.collateral) {
            uint256 remainingCollateral = position.collateral - marginFees;
            uint256 amount = GenericLogic.usdToTokenMin(params.collateralToken, remainingCollateral);
            GenericLogic.increasePoolAmount(params.collateralToken, amount);
        }
        if (params.isLong) {
            ps.globalLongAveragePrices[params.indexToken] = getNextGlobalLongData(
                params.account, params.collateralToken, params.indexToken, markPrice, position.size, false
            );
            GenericLogic.decreaseGlobalLongSize(params.indexToken, position.size);
        } else {
            ps.globalShortAveragePrices[params.indexToken] = getNextGlobalShortData(
                params.account, params.collateralToken, params.indexToken, markPrice, position.size, false
            );
            GenericLogic.decreaseGlobalShortSize(params.indexToken, position.size);
        }
        delete ps.positions[key];
        // pay the fee receiver using the pool, we assume that in general the liquidated amount should be sufficient to cover
        // the liquidation fees
        GenericLogic.decreasePoolAmount(
            params.collateralToken, GenericLogic.usdToTokenMin(params.collateralToken, fs.liquidationFeeUsd), true
        );
        GenericLogic.transferOut(
            params.collateralToken,
            GenericLogic.usdToTokenMin(params.collateralToken, fs.liquidationFeeUsd),
            params.feeReceiver
        );
    }

    function getPositionKey(address _account, address _collateralToken, address _indexToken, bool _isLong)
        internal
        pure
        returns (bytes32)
    {
        return keccak256(abi.encodePacked(_account, _collateralToken, _indexToken, _isLong));
    }

    // for longs has position profit: nextAveragePrice = (nextPrice * nextSize)/ (nextSize + delta)
    // for longs has negative profit: nextAveragePrice = (nextPrice * nextSize)/ (nextSize - delta)
    // for shorts has position profit: nextAveragePrice = (nextPrice * nextSize) / (nextSize - delta)
    // for shorts has negative profit: nextAveragePrice = (nextPrice * nextSize) / (nextSize + delta)
    function getNextAveragePrice(
        address _indexToken,
        uint256 _size,
        uint256 _averagePrice,
        bool _isLong,
        uint256 _nextPrice,
        uint256 _sizeDelta
    ) internal view returns (uint256) {
        (bool hasProfit, uint256 delta) = getDelta(_indexToken, _size, _averagePrice, _isLong);
        uint256 nextSize = _size + _sizeDelta;
        uint256 divisor;
        if (_isLong) {
            divisor = hasProfit ? nextSize + delta : nextSize - delta;
        } else {
            divisor = hasProfit ? nextSize - delta : nextSize + delta;
        }
        return (_nextPrice * nextSize) / divisor;
    }

    function getDelta(address _indexToken, uint256 _size, uint256 _averagePrice, bool _isLong)
        internal
        view
        returns (bool, uint256)
    {
        ValidationLogic.validate(_averagePrice > 0, Errors.VAULT_INVALID_AVERAGE_PRICE);
        uint256 price = _isLong ? GenericLogic.getMinPrice(_indexToken) : GenericLogic.getMaxPrice(_indexToken);
        uint256 priceDelta = _averagePrice > price ? _averagePrice - price : price - _averagePrice;
        uint256 delta = (_size * priceDelta) / _averagePrice;

        bool hasProfit;

        if (_isLong) {
            hasProfit = price > _averagePrice;
        } else {
            hasProfit = _averagePrice > price;
        }

        return (hasProfit, delta);
    }

    function collectMarginFees(CollectMarginFeesParams memory params)
        internal
        returns (uint256, DataTypes.PositionFundingFees memory)
    {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        uint256 feeUsd = getPositionFee(params.sizeDelta);

        uint256 borrowingFee = BorrowingFeeLogic.getBorrowingFee(
            params.account,
            params.collateralToken,
            params.indexToken,
            params.isLong,
            params.position.size,
            params.position.entryBorrowingRate
        );

        // funding fee
        DataTypes.PositionFundingFees memory positionFundingFees =
            FundingFeeLogic.getFundingFees(params.indexToken, params.isLong, params.position);

        feeUsd += borrowingFee + positionFundingFees.fundingFeeAmount;
        uint256 feeUsdExcludingFunding = feeUsd - positionFundingFees.fundingFeeAmount;

        uint256 feeTokensExcludingFunding = GenericLogic.usdToTokenMin(params.collateralToken, feeUsdExcludingFunding);
        fs.feeReserves[params.collateralToken] = fs.feeReserves[params.collateralToken] + feeTokensExcludingFunding;

        emit CollectMarginFees(params.collateralToken, feeUsd, feeTokensExcludingFunding);
        return (feeUsd, positionFundingFees);
    }

    function getPositionFee(uint256 _sizeDelta) internal view returns (uint256) {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        if (_sizeDelta == 0) {
            return 0;
        }
        uint256 afterFeeUsd =
            (_sizeDelta * (Constants.PERCENTAGE_FACTOR - fs.marginFeeBasisPoints)) / Constants.PERCENTAGE_FACTOR;
        return _sizeDelta - afterFeeUsd;
    }

    // validateLiquidation returns (state, fees)
    function validateLiquidation(
        address _account,
        address _collateralToken,
        address _indexToken,
        bool _isLong,
        bool _raise
    ) internal view returns (uint256, uint256) {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        bytes32 key = getPositionKey(_account, _collateralToken, _indexToken, _isLong);
        DataTypes.Position storage position = ps.positions[key];

        (bool hasProfit, uint256 delta) = getDelta(_indexToken, position.size, position.averagePrice, _isLong);
        uint256 positionFee = getPositionFee(position.size);

        uint256 borrowingFee = BorrowingFeeLogic.getBorrowingFee(
            _account, _collateralToken, _indexToken, _isLong, position.size, position.entryBorrowingRate
        );

        DataTypes.PositionFundingFees memory positionFundingFees =
            FundingFeeLogic.getFundingFees(_indexToken, _isLong, position);

        uint256 marginFees = positionFee + borrowingFee + positionFundingFees.fundingFeeAmount;

        if (!hasProfit && position.collateral < delta) {
            if (_raise) {
                revert("Vault: losses exceed collateral");
            }
            return (1, 0);
        }

        uint256 remainingCollateral = position.collateral;
        if (!hasProfit) {
            remainingCollateral = position.collateral - delta;
        }

        if (remainingCollateral < marginFees) {
            if (_raise) {
                revert("Vault: fees exceed collateral");
            }
            // cap the fees to the remainingCollateral
            return (1, remainingCollateral);
        }

        if (remainingCollateral < marginFees + fs.liquidationFeeUsd) {
            if (_raise) {
                revert("Vault: liquidation fees exceed collateral");
            }
            return (1, marginFees);
        }

        if (remainingCollateral * ps.maxLeverage < position.size * Constants.PERCENTAGE_FACTOR) {
            if (_raise) {
                revert("Vault: maxLeverage exceeded");
            }
            return (2, marginFees);
        }
        return (0, marginFees);
    }

    function reduceCollateral(DataTypes.DecreasePositionParams memory params)
        internal
        returns (uint256, uint256, DataTypes.PositionFundingFees memory)
    {
        ReduceCollateralCache memory cache;
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        bytes32 key = getPositionKey(params.account, params.collateralToken, params.indexToken, params.isLong);
        DataTypes.Position storage position = ps.positions[key];
        (cache.fee, cache.fundingFees) = collectMarginFees(
            CollectMarginFeesParams({
                account: params.account,
                collateralToken: params.collateralToken,
                indexToken: params.indexToken,
                isLong: params.isLong,
                sizeDelta: params.sizeDelta,
                position: position
            })
        );

        (cache.hasProfit, cache.delta) =
            getDelta(params.indexToken, position.size, position.averagePrice, params.isLong);
        // get the proportional change in pnl
        cache.adjustedDelta = (params.sizeDelta * cache.delta) / position.size;

        // transfer profits out
        if (cache.hasProfit && cache.adjustedDelta > 0) {
            cache.usdOut = cache.adjustedDelta;
            position.realisedPnl = position.realisedPnl + cache.adjustedDelta.toInt256();

            // pay out realised profits from the pool amount for positions
            uint256 tokenAmount = GenericLogic.usdToTokenMin(params.collateralToken, cache.adjustedDelta);
            GenericLogic.decreasePoolAmount(params.collateralToken, tokenAmount, true);
        }

        if (!cache.hasProfit && cache.adjustedDelta > 0) {
            position.collateral = position.collateral - cache.adjustedDelta;

            uint256 tokenAmount = GenericLogic.usdToTokenMin(params.collateralToken, cache.adjustedDelta);
            GenericLogic.increasePoolAmount(params.collateralToken, tokenAmount);

            position.realisedPnl = position.realisedPnl - cache.adjustedDelta.toInt256();
        }

        // reduce the position's collateral by _collateralDelta
        // transfer _collateralDelta out
        if (params.collateralDelta > 0) {
            cache.usdOut += params.collateralDelta;
            position.collateral -= params.collateralDelta;
        }

        // if the position will be closed, then transfer the remaining collateral out
        if (position.size == params.sizeDelta) {
            cache.usdOut += position.collateral;
            position.collateral = 0;
        }

        // if the usdOut is more than the fee then deduct the fee from the usdOut directly
        // else deduct the fee from the position's collateral
        cache.usdOutAfterFee = cache.usdOut;
        if (cache.usdOut > cache.fee) {
            cache.usdOutAfterFee = cache.usdOut - cache.fee;
        } else {
            position.collateral -= cache.fee;
        }

        emit UpdatePnl(key, cache.hasProfit, cache.adjustedDelta);

        return (cache.usdOut, cache.usdOutAfterFee, cache.fundingFees);
    }

    function getPosition(address _account, address _collateralToken, address _indexToken, bool _isLong)
        public
        view
        returns (DataTypes.Position memory)
    {
        bytes32 key = getPositionKey(_account, _collateralToken, _indexToken, _isLong);
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        return ps.positions[key];
    }

    function getNextGlobalLongData(
        address _account,
        address _collateralToken,
        address _indexToken,
        uint256 _nextPrice,
        uint256 _sizeDelta,
        bool _isIncrease
    ) internal view returns (uint256) {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();

        int256 realisedPnl = getRealisedPnl(_account, _collateralToken, _indexToken, _sizeDelta, _isIncrease, true);
        uint256 globalLongSize = ps.globalLongSizes[_indexToken];
        uint256 averagePrice = ps.globalLongAveragePrices[_indexToken];
        uint256 priceDelta = averagePrice > _nextPrice ? averagePrice - _nextPrice : _nextPrice - averagePrice;

        uint256 nextSize;
        uint256 delta;
        // avoid stack to deep
        {
            nextSize = _isIncrease ? globalLongSize + _sizeDelta : globalLongSize - _sizeDelta;

            if (nextSize == 0) {
                return 0;
            }

            if (averagePrice == 0) {
                return _nextPrice;
            }

            delta = (globalLongSize * priceDelta) / averagePrice;
        }

        uint256 nextAveragePrice =
            getNextGlobalAveragePrice(averagePrice, _nextPrice, nextSize, delta, realisedPnl, true);

        return nextAveragePrice;
    }

    function getNextGlobalShortData(
        address _account,
        address _collateralToken,
        address _indexToken,
        uint256 _nextPrice,
        uint256 _sizeDelta,
        bool _isIncrease
    ) internal view returns (uint256) {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();

        int256 realisedPnl = getRealisedPnl(_account, _collateralToken, _indexToken, _sizeDelta, _isIncrease, false);
        uint256 globalShortSize = ps.globalShortSizes[_indexToken];
        uint256 averagePrice = ps.globalShortAveragePrices[_indexToken];
        uint256 priceDelta = averagePrice > _nextPrice ? averagePrice - _nextPrice : _nextPrice - averagePrice;

        uint256 nextSize;
        uint256 delta;
        // avoid stack to deep
        {
            nextSize = _isIncrease ? globalShortSize + _sizeDelta : globalShortSize - _sizeDelta;

            if (nextSize == 0) {
                return 0;
            }

            if (averagePrice == 0) {
                return _nextPrice;
            }

            delta = (globalShortSize * priceDelta) / averagePrice;
        }

        uint256 nextAveragePrice =
            getNextGlobalAveragePrice(averagePrice, _nextPrice, nextSize, delta, realisedPnl, false);

        return nextAveragePrice;
    }

    function getRealisedPnl(
        address _account,
        address _collateralToken,
        address _indexToken,
        uint256 _sizeDelta,
        bool _isIncrease,
        bool _isLong
    ) internal view returns (int256) {
        if (_isIncrease) {
            return 0;
        }

        DataTypes.Position memory position = getPosition(_account, _collateralToken, _indexToken, _isLong);

        (bool hasProfit, uint256 delta) = getDelta(_indexToken, position.size, position.averagePrice, _isLong);
        // get the proportional change in pnl
        uint256 adjustedDelta = (_sizeDelta * delta) / position.size;

        return hasProfit ? int256(adjustedDelta) : -int256(adjustedDelta);
    }

    function getNextGlobalAveragePrice(
        uint256 _averagePrice,
        uint256 _nextPrice,
        uint256 _nextSize,
        uint256 _delta,
        int256 _realisedPnl,
        bool _isLong
    ) internal pure returns (uint256) {
        (bool hasProfit, uint256 nextDelta) = getNextDelta(_delta, _averagePrice, _nextPrice, _realisedPnl, _isLong);

        uint256 divisor;
        if (_isLong) {
            divisor = hasProfit ? _nextSize + nextDelta : _nextSize - nextDelta;
        } else {
            divisor = hasProfit ? _nextSize - nextDelta : _nextSize + nextDelta;
        }
        return (_nextPrice * _nextSize) / divisor;
    }

    function getNextDelta(uint256 _delta, uint256 _averagePrice, uint256 _nextPrice, int256 _realisedPnl, bool _isLong)
        internal
        pure
        returns (bool, uint256)
    {
        // global delta 10000, realised pnl 1000 => new pnl 9000
        // global delta 10000, realised pnl -1000 => new pnl 11000
        // global delta -10000, realised pnl 1000 => new pnl -11000
        // global delta -10000, realised pnl -1000 => new pnl -9000
        // global delta 10000, realised pnl 11000 => new pnl -1000 (flips sign)
        // global delta -10000, realised pnl -11000 => new pnl 1000 (flips sign)

        bool hasProfit;
        if (_isLong) {
            hasProfit = _nextPrice > _averagePrice;
        } else {
            hasProfit = _averagePrice > _nextPrice;
        }
        if (hasProfit) {
            // global shorts pnl is positive
            if (_realisedPnl > 0) {
                if (uint256(_realisedPnl) > _delta) {
                    _delta = uint256(_realisedPnl) - _delta;
                    hasProfit = false;
                } else {
                    _delta = _delta - uint256(_realisedPnl);
                }
            } else {
                _delta = _delta + uint256(-_realisedPnl);
            }

            return (hasProfit, _delta);
        }

        if (_realisedPnl > 0) {
            _delta = _delta + uint256(_realisedPnl);
        } else {
            if (uint256(-_realisedPnl) > _delta) {
                _delta = uint256(-_realisedPnl) - _delta;
                hasProfit = true;
            } else {
                _delta = _delta - uint256(-_realisedPnl);
            }
        }
        return (hasProfit, _delta);
    }
}

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import {DataTypes} from "../types/DataTypes.sol";

library StorageSlot {
    bytes32 constant STORAGE_VAULT_ADDRESS = bytes32(uint256(keccak256("gemnify.vault.address.storage")) - 1);
    bytes32 constant STORAGE_VAULT_TOKEN_CONFIG = bytes32(uint256(keccak256("gemnify.vault.token.config.storage")) - 1);
    bytes32 constant STORAGE_VAULT_FEE = bytes32(uint256(keccak256("gemnify.vault.fee.storage")) - 1);
    bytes32 constant STORAGE_VAULT_PERMISSION = bytes32(uint256(keccak256("gemnify.vault.permission.storage")) - 1);
    bytes32 constant STORAGE_VAULT_POSITION = bytes32(uint256(keccak256("gemnify.vault.position.storage")) - 1);

    function getVaultAddressStorage() internal pure returns (DataTypes.AddressStorage storage rs) {
        bytes32 position = STORAGE_VAULT_ADDRESS;
        assembly {
            rs.slot := position
        }
    }

    function getVaultTokenConfigStorage() internal pure returns (DataTypes.TokenConfigStorage storage rs) {
        bytes32 position = STORAGE_VAULT_TOKEN_CONFIG;
        assembly {
            rs.slot := position
        }
    }

    function getVaultFeeStorage() internal pure returns (DataTypes.FeeStorage storage rs) {
        bytes32 position = STORAGE_VAULT_FEE;
        assembly {
            rs.slot := position
        }
    }

    function getVaultPermissionStorage() internal pure returns (DataTypes.PermissionStorage storage rs) {
        bytes32 position = STORAGE_VAULT_PERMISSION;
        assembly {
            rs.slot := position
        }
    }

    function getVaultPositionStorage() internal pure returns (DataTypes.PositionStorage storage rs) {
        bytes32 position = STORAGE_VAULT_POSITION;
        assembly {
            rs.slot := position
        }
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";

import {ValidationLogic} from "./ValidationLogic.sol";
import {GenericLogic} from "./GenericLogic.sol";
import {BorrowingFeeLogic} from "./BorrowingFeeLogic.sol";
import {StorageSlot} from "./StorageSlot.sol";
import {Errors} from "../helpers/Errors.sol";
import {Constants} from "../helpers/Constants.sol";
import {DataTypes} from "../types/DataTypes.sol";

import {IUSDG} from "../../../tokens/interfaces/IUSDG.sol";
import {IVault} from "../../interfaces/IVault.sol";

library SupplyLogic {
    using SafeCast for uint256;
    using SafeCast for int256;

    event DirectPoolDeposit(address token, uint256 amount);

    event BuyUSDG(address account, address token, uint256 tokenAmount, uint256 usdgAmount, int256 feeBasisPoints);
    event SellUSDG(address account, address token, uint256 usdgAmount, uint256 tokenAmount, uint256 feeBasisPoints);

    function ExecuteBuyUSDG(address _token, address _receiver) external returns (uint256) {
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        ValidationLogic.validateManager();
        ValidationLogic.validateWhitelistedToken(_token);

        uint256 tokenAmount;

        tokenAmount = GenericLogic.transferIn(_token);

        ValidationLogic.validate(tokenAmount > 0, Errors.VAULT_INVALID_TOKEN_AMOUNT);

        BorrowingFeeLogic.updateCumulativeBorrowingRate(addrs.collateralToken, addrs.collateralToken);

        uint256 price = GenericLogic.getMinPrice(_token);
        uint256 usdgAmount = (tokenAmount * price) / Constants.PRICE_PRECISION;

        usdgAmount = GenericLogic.adjustForDecimals(usdgAmount, _token, addrs.usdg);

        ValidationLogic.validate(usdgAmount > 0, Errors.VAULT_INVALID_USDG_AMOUNT);

        int256 feeBasisPoints = GenericLogic.getBuyUsdgFeeBasisPoints(_token, usdgAmount);

        uint256 amountAfterFees = GenericLogic.collectSwapFees(_token, tokenAmount, feeBasisPoints);
        uint256 mintAmount = (amountAfterFees * price) / Constants.PRICE_PRECISION;
        mintAmount = GenericLogic.adjustForDecimals(mintAmount, _token, addrs.usdg);
        if (amountAfterFees > tokenAmount) {
            GenericLogic.increaseUsdgAmount(_token, usdgAmount);
            GenericLogic.increasePoolAmount(_token, tokenAmount);
            IUSDG(addrs.usdg).mint(_receiver, usdgAmount);
        } else {
            GenericLogic.increaseUsdgAmount(_token, mintAmount);
            GenericLogic.increasePoolAmount(_token, amountAfterFees);
            IUSDG(addrs.usdg).mint(_receiver, mintAmount);
        }

        emit BuyUSDG(_receiver, _token, tokenAmount, mintAmount, feeBasisPoints);

        return mintAmount;
    }

    function ExecuteSellUSDG(address _token, address _receiver) external returns (uint256) {
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        ValidationLogic.validateManager();
        ValidationLogic.validateWhitelistedToken(_token);

        uint256 usdgAmount = GenericLogic.transferIn(addrs.usdg);
        ValidationLogic.validate(usdgAmount > 0, Errors.VAULT_INVALID_USDG_AMOUNT);

        BorrowingFeeLogic.updateCumulativeBorrowingRate(addrs.collateralToken, addrs.collateralToken);

        uint256 redemptionAmount = GenericLogic.getRedemptionAmount(_token, usdgAmount);
        ValidationLogic.validate(redemptionAmount > 0, Errors.VAULT_INVALID_REDEMPTION_AMOUNT);

        uint256 feeBasisPoints = GenericLogic.getSellUsdgFeeBasisPoints(_token, usdgAmount);
        GenericLogic.decreaseUsdgAmount(_token, usdgAmount);
        GenericLogic.decreasePoolAmount(_token, redemptionAmount, false);

        IUSDG(addrs.usdg).burn(address(this), usdgAmount);

        // the _transferIn call increased the value of tokenBalances[usdg]
        // usually decreases in token balances are synced by calling _transferOut
        // however, for usdg, the tokens are burnt, so _updateTokenBalance should
        // be manually called to record the decrease in tokens
        GenericLogic.updateTokenBalance(addrs.usdg);

        uint256 amountOut = GenericLogic.collectSwapFees(_token, redemptionAmount, feeBasisPoints.toInt256());

        ValidationLogic.validate(amountOut > 0, Errors.VAULT_INVALID_AMOUNT_OUT);

        GenericLogic.transferOut(_token, amountOut, _receiver);

        emit SellUSDG(_receiver, _token, usdgAmount, amountOut, feeBasisPoints);

        return amountOut;
    }

    function directPoolDeposit(address _token) external {
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        ValidationLogic.validate(ts.whitelistedTokens[_token], Errors.VAULT_TOKEN_NOT_WHITELISTED);
        BorrowingFeeLogic.updateCumulativeBorrowingRate(addrs.collateralToken, addrs.collateralToken);
        uint256 tokenAmount = GenericLogic.transferIn(_token);
        ValidationLogic.validate(tokenAmount > 0, Errors.VAULT_INVALID_TOKEN_AMOUNT);
        GenericLogic.increasePoolAmount(_token, tokenAmount);
        emit DirectPoolDeposit(_token, tokenAmount);
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import {SafeCast} from "@openzeppelin/contracts/utils/math/SafeCast.sol";

import {BorrowingFeeLogic} from "./BorrowingFeeLogic.sol";
import {ValidationLogic} from "./ValidationLogic.sol";
import {GenericLogic} from "./GenericLogic.sol";
import {StorageSlot} from "./StorageSlot.sol";

import {DataTypes} from "../types/DataTypes.sol";

import {Constants} from "../helpers/Constants.sol";
import {Errors} from "../helpers/Errors.sol";

library SwapLogic {
    using SafeCast for uint256;
    using SafeCast for int256;

    event Swap(
        address indexed account,
        address tokenIn,
        address tokenOut,
        uint256 amountIn,
        uint256 amountOut,
        uint256 amountOutAfterFees,
        int256 feeBasisPoints
    );

    function ExecuteSwap(address _tokenIn, address _tokenOut, uint256 _amountIn, address _receiver)
        external
        returns (uint256)
    {
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();

        ValidationLogic.validateSwapParams(ps.isSwapEnabled, _tokenIn, _tokenOut, _amountIn, ts.whitelistedTokens);

        BorrowingFeeLogic.updateCumulativeBorrowingRate(addrs.collateralToken, addrs.collateralToken);

        uint256 priceInMin = GenericLogic.getMinPrice(_tokenIn);
        uint256 priceOutMax = GenericLogic.getMaxPrice(_tokenOut);

        // adjust usdgAmounts by the same usdgAmount as debt is shifted between the assets
        uint256 usdgAmount = (_amountIn * priceInMin) / Constants.PRICE_PRECISION;
        usdgAmount = GenericLogic.adjustForDecimals(usdgAmount, _tokenIn, addrs.usdg);

        uint256 amountOut = (_amountIn * priceInMin) / priceOutMax;

        amountOut = GenericLogic.adjustForDecimals(amountOut, _tokenIn, _tokenOut);

        int256 feeBasisPoints = getSwapFeeBasisPoints(_tokenIn, _tokenOut, usdgAmount);
        uint256 amountOutAfterFees = GenericLogic.collectSwapFees(_tokenOut, amountOut, feeBasisPoints);

        GenericLogic.increaseUsdgAmount(_tokenIn, usdgAmount);
        GenericLogic.increasePoolAmount(_tokenIn, _amountIn);
        GenericLogic.decreaseUsdgAmount(_tokenOut, usdgAmount);
        if (amountOutAfterFees > amountOut) {
            GenericLogic.decreasePoolAmount(_tokenOut, amountOutAfterFees, false);
        } else {
            GenericLogic.decreasePoolAmount(_tokenOut, amountOut, false);
        }

        ValidationLogic.validateBufferAmount(_tokenOut);

        GenericLogic.transferOut(_tokenOut, amountOutAfterFees, _receiver);

        emit Swap(_receiver, _tokenIn, _tokenOut, _amountIn, amountOut, amountOutAfterFees, feeBasisPoints);

        return amountOutAfterFees;
    }

    function getSwapFeeBasisPoints(address _tokenIn, address _tokenOut, uint256 _usdgAmount)
        internal
        view
        returns (int256)
    {
        DataTypes.FeeStorage storage fs = StorageSlot.getVaultFeeStorage();
        uint256 baseBps = fs.stableSwapFeeBasisPoints;
        uint256 taxBps = fs.stableTaxBasisPoints;

        (uint256 feesBasisPoints0, bool haveBalanceReward0,) =
            GenericLogic.getFeeBasisPoints(_tokenIn, _usdgAmount, baseBps, taxBps, true, true);
        (uint256 feesBasisPoints1,, bool isCrossBalance1) =
            GenericLogic.getFeeBasisPoints(_tokenOut, _usdgAmount, baseBps, taxBps, false, true);
        // use the higher of the two fee basis points
        uint256 fee = feesBasisPoints0 > feesBasisPoints1 ? feesBasisPoints0 : feesBasisPoints1;
        if (haveBalanceReward0 && !isCrossBalance1) {
            return fee.toInt256() - fs.balanceReward.toInt256();
        } else {
            return fee.toInt256();
        }
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

import {Errors} from "../helpers/Errors.sol";
import {StorageSlot} from "./StorageSlot.sol";
import {DataTypes} from "../types/DataTypes.sol";

library ValidationLogic {
    function validateSwapParams(
        bool _isSwapEnabled,
        address _tokenIn,
        address _tokenOut,
        uint256 _amountIn,
        mapping(address => bool) storage whitelistedTokens
    ) internal view {
        validate(_isSwapEnabled, Errors.VAULT_SWAPS_NOT_ENABLED);
        validate(whitelistedTokens[_tokenIn], Errors.VAULT_TOKEN_IN_NOT_WHITELISTED);
        validate(whitelistedTokens[_tokenOut], Errors.VAULT_TOKEN_OUT_NOT_WHITELISTED);
        validate(_tokenIn != _tokenOut, Errors.VAULT_INVALID_TOKENS);
        validate(_amountIn > 0, Errors.VAULT_INVALID_AMOUNT_IN);
    }

    function validateIncreasePositionParams(
        bool _isLeverageEnabled,
        address _account,
        address _collateralToken,
        address _indexToken,
        bool _isLong
    ) internal view {
        validateLeverage(_isLeverageEnabled);
        validateGasPrice();
        validateRouter(_account);
        validateTokens(_collateralToken, _indexToken, _isLong);
    }

    function validateDecreasePositionParams(address _account) internal view {
        validateGasPrice();
        validateRouter(_account);
    }

    function validateGasPrice() internal view {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        if (ps.maxGasPrice == 0) {
            return;
        }
        validate(tx.gasprice <= ps.maxGasPrice, Errors.VAULT_MAX_GAS_PRICE_EXCEEDED);
    }

    function validateWhitelistedToken(address _token) internal view {
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        validate(ts.whitelistedTokens[_token], Errors.VAULT_TOKEN_IN_NOT_WHITELISTED);
    }

    function validateBufferAmount(address _token) internal view {
        DataTypes.PositionStorage storage ps = StorageSlot.getVaultPositionStorage();
        validate(ps.poolAmounts[_token] >= ps.bufferAmounts[_token], Errors.VAULT_POOL_AMOUNT_LESS_THAN_BUFFER_AMOUNT);
    }

    function validateManager() internal view {
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();
        if (ps.inManagerMode) {
            validate(ps.isManager[msg.sender], Errors.VAULT_FORBIDDEN);
        }
    }

    function validateTokens(address _collateralToken, address _indexToken, bool _isLong) internal view {
        DataTypes.TokenConfigStorage storage ts = StorageSlot.getVaultTokenConfigStorage();
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        validate(_collateralToken == addrs.collateralToken, Errors.VAULT_COLLATERAL_TOKEN_MUST_BE_STABLE_TOKEN);
        validate(ts.whitelistedTokens[_collateralToken], Errors.VAULT_COLLATERAL_TOKEN_NOT_WHITELISTED);
        validate(ts.whitelistedTokens[_indexToken], Errors.VAULT_INDEX_TOKEN_NOT_WHITELISTED);
        validate(ts.stableTokens[_collateralToken], Errors.VAULT_COLLATERAL_TOKEN_MUST_BE_STABLE_TOKEN);
        validate(!ts.stableTokens[_indexToken], Errors.VAULT_INDEX_TOKEN_MUST_NOT_BE_STABLE_TOKEN);
        if (!_isLong) {
            validate(ts.shortableTokens[_indexToken], Errors.VAULT_INDEX_TOKEN_NOT_SHORTABLE);
        }
    }

    function validatePosition(uint256 _size, uint256 _collateral) internal pure {
        if (_size == 0) {
            validate(_collateral == 0, Errors.VAULT_COLLATERAL_SHOULD_BE_WITHDRAWN);
            return;
        }
        validate(_size >= _collateral, Errors.VAULT_SIZE_MUST_BE_MORE_THAN_COLLATERAL);
    }

    function validateRouter(address _account) internal view {
        DataTypes.AddressStorage storage addrs = StorageSlot.getVaultAddressStorage();
        DataTypes.PermissionStorage storage ps = StorageSlot.getVaultPermissionStorage();
        if (msg.sender == addrs.router) {
            return;
        }
        validate(ps.approvedRouters[_account][msg.sender], Errors.VAULT_INVALID_MSG_SENDER);
    }

    function validateLeverage(bool _isLeverageEnabled) internal pure {
        validate(_isLeverageEnabled, Errors.VAULT_LEVERAGE_NOT_ENABLED);
    }

    function validateIncreasePosition(
        address, /* _account */
        address, /* _collateralToken */
        address, /* _indexToken */
        uint256, /* _sizeDelta */
        bool /* _isLong */
    ) internal pure {
        // no additional validations
    }

    function validateDecreasePosition(
        address, /* _account */
        address, /* _collateralToken */
        address, /* _indexToken */
        uint256, /* _collateralDelta */
        uint256, /* _sizeDelta */
        bool, /* _isLong */
        address /* _receiver */
    ) internal pure {
        // no additional validations
    }

    function validate(bool _condition, string memory _errorCode) internal pure {
        require(_condition, _errorCode);
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/math/SignedMath.sol";
import "@openzeppelin/contracts/utils/math/SafeCast.sol";

/**
 * @title Calc
 * @dev Library for math functions
 */
library Calc {
    using SignedMath for int256;
    using SafeCast for uint256;

    // this method assumes that min is less than max
    function boundMagnitude(int256 value, uint256 min, uint256 max) internal pure returns (int256) {
        uint256 magnitude = value.abs();

        if (magnitude < min) {
            magnitude = min;
        }

        if (magnitude > max) {
            magnitude = max;
        }

        int256 sign = value == 0 ? int256(1) : value / value.abs().toInt256();

        return magnitude.toInt256() * sign;
    }

    /**
     * @dev Calculates the result of dividing the first number by the second number,
     * rounded up to the nearest integer.
     *
     * @param a the dividend
     * @param b the divisor
     * @return the result of dividing the first number by the second number, rounded up to the nearest integer
     */
    function roundUpDivision(uint256 a, uint256 b) internal pure returns (uint256) {
        return (a + b - 1) / b;
    }

    /**
     * Calculates the result of dividing the first number by the second number,
     * rounded up to the nearest integer.
     * The rounding is purely on the magnitude of a, if a is negative the result
     * is a larger magnitude negative
     *
     * @param a the dividend
     * @param b the divisor
     * @return the result of dividing the first number by the second number, rounded up to the nearest integer
     */
    function roundUpMagnitudeDivision(int256 a, uint256 b) internal pure returns (int256) {
        if (a < 0) {
            return (a - b.toInt256() + 1) / b.toInt256();
        }

        return (a + b.toInt256() - 1) / b.toInt256();
    }

    /**
     * Adds two numbers together and return a uint256 value, treating the second number as a signed integer.
     *
     * @param a the first number
     * @param b the second number
     * @return the result of adding the two numbers together
     */
    function sumReturnUint256(uint256 a, int256 b) internal pure returns (uint256) {
        if (b > 0) {
            return a + b.abs();
        }

        return a - b.abs();
    }

    /**
     * Adds two numbers together and return an int256 value, treating the second number as a signed integer.
     *
     * @param a the first number
     * @param b the second number
     * @return the result of adding the two numbers together
     */
    function sumReturnInt256(uint256 a, int256 b) internal pure returns (int256) {
        return a.toInt256() + b;
    }

    /**
     * @dev Calculates the absolute difference between two numbers.
     *
     * @param a the first number
     * @param b the second number
     * @return the absolute difference between the two numbers
     */
    function diff(uint256 a, uint256 b) internal pure returns (uint256) {
        return a > b ? a - b : b - a;
    }

    /**
     * Adds two numbers together, the result is bounded to prevent overflows.
     *
     * @param a the first number
     * @param b the second number
     * @return the result of adding the two numbers together
     */
    function boundedAdd(int256 a, int256 b) internal pure returns (int256) {
        // if either a or b is zero or if the signs are different there should not be any overflows
        if (a == 0 || b == 0 || (a < 0 && b > 0) || (a > 0 && b < 0)) {
            return a + b;
        }

        // if adding `b` to `a` would result in a value less than the min int256 value
        // then return the min int256 value
        if (a < 0 && b <= type(int256).min - a) {
            return type(int256).min;
        }

        // if adding `b` to `a` would result in a value more than the max int256 value
        // then return the max int256 value
        if (a > 0 && b >= type(int256).max - a) {
            return type(int256).max;
        }

        return a + b;
    }

    /**
     * Returns a - b, the result is bounded to prevent overflows.
     * Note that this will revert if b is type(int256).min because of the usage of "-b".
     *
     * @param a the first number
     * @param b the second number
     * @return the bounded result of a - b
     */
    function boundedSub(int256 a, int256 b) internal pure returns (int256) {
        // if either a or b is zero or the signs are the same there should not be any overflow
        if (a == 0 || b == 0 || (a > 0 && b > 0) || (a < 0 && b < 0)) {
            return a - b;
        }

        // if adding `-b` to `a` would result in a value greater than the max int256 value
        // then return the max int256 value
        if (a > 0 && -b >= type(int256).max - a) {
            return type(int256).max;
        }

        // if subtracting `b` from `a` would result in a value less than the min int256 value
        // then return the min int256 value
        if (a < 0 && -b <= type(int256).min - a) {
            return type(int256).min;
        }

        return a - b;
    }

    /**
     * Converts the given unsigned integer to a signed integer, using the given
     * flag to determine whether the result should be positive or negative.
     *
     * @param a the unsigned integer to convert
     * @param isPositive whether the result should be positive (if true) or negative (if false)
     * @return the signed integer representation of the given unsigned integer
     */
    function toSigned(uint256 a, bool isPositive) internal pure returns (int256) {
        if (isPositive) {
            return a.toInt256();
        } else {
            return -a.toInt256();
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1

pragma solidity ^0.8.0;

// there is a known issue with prb-math v3.x releases
// https://github.com/PaulRBerg/prb-math/issues/178
// due to this, either prb-math v2.x or v4.x versions should be used instead
import "prb-math/contracts/PRBMathUD60x18.sol";

import "@openzeppelin/contracts/utils/math/SafeCast.sol";
import "@openzeppelin/contracts/utils/math/SafeMath.sol";
import "@openzeppelin/contracts/utils/math/SignedMath.sol";
import "@openzeppelin/contracts/utils/math/Math.sol";

import "./Calc.sol";

/**
 * @title Precision
 * @dev Library for precision values and conversions
 */
library Precision {
    using SafeCast for uint256;
    using SignedMath for int256;

    uint256 public constant FLOAT_PRECISION = 10 ** 30;
    uint256 public constant FLOAT_PRECISION_SQRT = 10 ** 15;

    uint256 public constant WEI_PRECISION = 10 ** 18;
    uint256 public constant BASIS_POINTS_DIVISOR = 10000;

    uint256 public constant FLOAT_TO_WEI_DIVISOR = 10 ** 12;

    /**
     * Applies the given factor to the given value and returns the result.
     *
     * @param value The value to apply the factor to.
     * @param factor The factor to apply.
     * @return The result of applying the factor to the value.
     */
    function applyFactor(uint256 value, uint256 factor) internal pure returns (uint256) {
        return mulDiv(value, factor, FLOAT_PRECISION);
    }

    /**
     * Applies the given factor to the given value and returns the result.
     *
     * @param value The value to apply the factor to.
     * @param factor The factor to apply.
     * @return The result of applying the factor to the value.
     */
    function applyFactor(uint256 value, int256 factor) internal pure returns (int256) {
        return mulDiv(value, factor, FLOAT_PRECISION);
    }

    function applyFactor(uint256 value, int256 factor, bool roundUpMagnitude) internal pure returns (int256) {
        return mulDiv(value, factor, FLOAT_PRECISION, roundUpMagnitude);
    }

    function mulDiv(uint256 value, uint256 numerator, uint256 denominator) internal pure returns (uint256) {
        return Math.mulDiv(value, numerator, denominator);
    }

    function mulDiv(int256 value, uint256 numerator, uint256 denominator) internal pure returns (int256) {
        return mulDiv(numerator, value, denominator);
    }

    function mulDiv(uint256 value, int256 numerator, uint256 denominator) internal pure returns (int256) {
        uint256 result = mulDiv(value, numerator.abs(), denominator);
        return numerator > 0 ? result.toInt256() : -result.toInt256();
    }

    function mulDiv(uint256 value, int256 numerator, uint256 denominator, bool roundUpMagnitude)
        internal
        pure
        returns (int256)
    {
        uint256 result = mulDiv(value, numerator.abs(), denominator, roundUpMagnitude);
        return numerator > 0 ? result.toInt256() : -result.toInt256();
    }

    function mulDiv(uint256 value, uint256 numerator, uint256 denominator, bool roundUpMagnitude)
        internal
        pure
        returns (uint256)
    {
        if (roundUpMagnitude) {
            return Math.mulDiv(value, numerator, denominator, Math.Rounding.Up);
        }

        return Math.mulDiv(value, numerator, denominator);
    }

    function applyExponentFactor(uint256 floatValue, uint256 exponentFactor) internal pure returns (uint256) {
        // `PRBMathUD60x18.pow` doesn't work for `x` less than one
        if (floatValue < FLOAT_PRECISION) {
            return 0;
        }

        if (exponentFactor == FLOAT_PRECISION) {
            return floatValue;
        }

        // `PRBMathUD60x18.pow` accepts 2 fixed point numbers 60x18
        // we need to convert float (30 decimals) to 60x18 (18 decimals) and then back to 30 decimals
        uint256 weiValue = PRBMathUD60x18.pow(floatToWei(floatValue), floatToWei(exponentFactor));

        return weiToFloat(weiValue);
    }

    function toFactor(uint256 value, uint256 divisor, bool roundUpMagnitude) internal pure returns (uint256) {
        if (value == 0) return 0;

        if (roundUpMagnitude) {
            return Math.mulDiv(value, FLOAT_PRECISION, divisor, Math.Rounding.Up);
        }

        return Math.mulDiv(value, FLOAT_PRECISION, divisor);
    }

    function toFactor(uint256 value, uint256 divisor) internal pure returns (uint256) {
        return toFactor(value, divisor, false);
    }

    function toFactor(int256 value, uint256 divisor) internal pure returns (int256) {
        uint256 result = toFactor(value.abs(), divisor);
        return value > 0 ? result.toInt256() : -result.toInt256();
    }

    /**
     * Converts the given value from float to wei.
     *
     * @param value The value to convert.
     * @return The converted value in wei.
     */
    function floatToWei(uint256 value) internal pure returns (uint256) {
        return value / FLOAT_TO_WEI_DIVISOR;
    }

    /**
     * Converts the given value from wei to float.
     *
     * @param value The value to convert.
     * @return The converted value in float.
     */
    function weiToFloat(uint256 value) internal pure returns (uint256) {
        return value * FLOAT_TO_WEI_DIVISOR;
    }

    /**
     * Converts the given number of basis points to float.
     *
     * @param basisPoints The number of basis points to convert.
     * @return The converted value in float.
     */
    function basisPointsToFloat(uint256 basisPoints) internal pure returns (uint256) {
        return basisPoints * FLOAT_PRECISION / BASIS_POINTS_DIVISOR;
    }
}

File 33 of 36 : DataTypes.sol
// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

library DataTypes {
    struct Position {
        uint256 size;
        uint256 collateral;
        uint256 averagePrice;
        uint256 entryBorrowingRate;
        uint256 fundingFeeAmountPerSize;
        uint256 claimableFundingAmountPerSize;
        uint256 reserveAmount;
        int256 realisedPnl;
        uint256 lastIncreasedTime;
    }

    struct SetFeesParams {
        uint256 taxBasisPoints;
        uint256 stableTaxBasisPoints;
        uint256 mintBurnFeeBasisPoints;
        uint256 swapFeeBasisPoints;
        uint256 stableSwapFeeBasisPoints;
        uint256 marginFeeBasisPoints;
        uint256 liquidationFeeUsd;
        bool hasDynamicFees;
        uint256 balanceReward;
    }

    struct SwapParams {
        bool isSwapEnabled;
        address tokenIn;
        address tokenOut;
        address receiver;
        bool isStableSwap;
        address usdg;
        address priceFeed;
        uint256 totalTokenWeights;
    }

    struct IncreasePositionParams {
        address account;
        address collateralToken;
        address indexToken;
        uint256 sizeDelta;
        bool isLong;
    }

    struct DecreasePositionParams {
        address account;
        address collateralToken;
        address indexToken;
        uint256 collateralDelta;
        uint256 sizeDelta;
        bool isLong;
        address receiver;
    }

    struct LiquidatePositionParams {
        address account;
        address collateralToken;
        address indexToken;
        bool isLong;
        address feeReceiver;
    }

    struct SetTokenConfigParams {
        address token;
        uint256 tokenDecimals;
        uint256 tokenWeight;
        uint256 maxUsdgAmount;
        bool isStable;
        bool isShortable;
    }

    // vault storage
    struct AddressStorage {
        address weth;
        address router;
        address ulpManager;
        address priceFeed;
        address usdg;
        address collateralToken;
        address feesReceiver;
    }

    struct TokenConfigStorage {
        uint256 whitelistedTokenCount;
        uint256 totalTokenWeights;
        address[] allWhitelistedTokens;
        mapping(address => bool) whitelistedTokens;
        mapping(address => uint256) tokenDecimals;
        mapping(address => uint256) tokenWeights;
        mapping(address => uint256) maxUsdgAmounts;
        mapping(address => bool) stableTokens;
        mapping(address => bool) shortableTokens;
    }

    struct FeeStorage {
        uint256 taxBasisPoints;
        uint256 stableTaxBasisPoints;
        uint256 mintBurnFeeBasisPoints;
        uint256 swapFeeBasisPoints;
        uint256 stableSwapFeeBasisPoints;
        uint256 marginFeeBasisPoints;
        uint256 liquidationFeeUsd;
        bool hasDynamicFees;
        // encourage arbitrage when token ratios are imbalanced
        uint256 balanceReward;
        mapping(address => uint256) feeReserves;
        // borrowing fee
        uint256 borrowingInterval;
        uint256 borrowingRateFactor;
        uint256 stableBorrowingRateFactor;
        mapping(address => uint256) cumulativeBorrowingRates;
        mapping(address => uint256) lastBorrowingTimes;
        // funding fee
        mapping(address => uint256) fundingFactors;
        mapping(address => uint256) fundingExponentFactors;
        mapping(address => uint256) lastFundingTimes;
        mapping(address => mapping(bool => uint256)) fundingFeeAmountPerSizes; // indexToken -> isLong -> fundingFeeAmountPerSize
        mapping(address => mapping(bool => uint256)) claimableFundingAmountPerSizes; // indexToken -> isLong -> claimableFundingAmountPerSize
        mapping(address => uint256) claimableFundingAmount; // user's account  -> claimableFundingAmount,funding amount always collateralToken
    }

    struct PermissionStorage {
        bool isSwapEnabled;
        bool isLeverageEnabled;
        bool inManagerMode;
        bool inPrivateLiquidationMode;
        mapping(address => mapping(address => bool)) approvedRouters;
        mapping(address => bool) isLiquidator;
        mapping(address => bool) isManager;
        mapping(address => bool) isTokenPaused;
        mapping(address => bool) isWithdrawFeesAdmin;
    }

    struct PositionStorage {
        uint256 maxLeverage; // 1000x
        uint256 maxGasPrice;
        // reserveRatio can increase OI limit.
        uint256 reserveRatio;
        mapping(address => uint256) tokenBalances;
        mapping(address => uint256) usdgAmounts;
        mapping(address => uint256) poolAmounts;
        mapping(address => uint256) reservedAmounts;
        mapping(address => uint256) bufferAmounts;
        mapping(bytes32 => Position) positions;
        mapping(address => uint256) globalLongSizes;
        mapping(address => uint256) globalLongAveragePrices;
        mapping(address => uint256) maxGlobalLongSizes;
        mapping(address => uint256) globalShortSizes;
        mapping(address => uint256) globalShortAveragePrices;
        mapping(address => uint256) maxGlobalShortSizes;
    }

    // tokenInfo
    struct TokenInfo {
        uint256 tokenDecimal;
        bool isWhitelistedToken;
        bool isStableToken;
        uint256 maxUsdgAmount;
    }

    // poolInfo
    struct PoolInfo {
        uint256 poolAmount;
        uint256 reservedAmount;
        uint256 bufferAmount;
        uint256 globalLongSize;
        uint256 globalLongAveragePrice;
        uint256 globalShortSize;
        uint256 globalShortAveragePrice;
        uint256 usdgAmount;
    }

    // funding fee
    // @param fundingFeeAmount the position's funding fee amount
    // @param claimableAmount the negative funding fee that is claimable
    // @param latestFundingAmountPerSize the latest funding
    // amount per size for the market
    struct PositionFundingFees {
        uint256 fundingFeeAmount;
        uint256 claimableAmount;
        uint256 latestFundingFeeAmountPerSize;
        uint256 latestClaimableFundingAmountPerSize;
    }

    // Global Data for long and short position
    struct GlobalData {
        uint256 maxGlobalLongSize;
        uint256 globalLongSize;
        uint256 globalLongAveragePrice;
        uint256 maxGlobalShortSize;
        uint256 globalShortSize;
        uint256 globalShortAveragePrice;
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.19;

interface IUSDG {
    function addVault(address _vault) external;

    function removeVault(address _vault) external;

    function mint(address _account, uint256 _amount) external;

    function burn(address _account, uint256 _amount) external;
}

// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;

/// @notice Emitted when the result overflows uint256.
error PRBMath__MulDivFixedPointOverflow(uint256 prod1);

/// @notice Emitted when the result overflows uint256.
error PRBMath__MulDivOverflow(uint256 prod1, uint256 denominator);

/// @notice Emitted when one of the inputs is type(int256).min.
error PRBMath__MulDivSignedInputTooSmall();

/// @notice Emitted when the intermediary absolute result overflows int256.
error PRBMath__MulDivSignedOverflow(uint256 rAbs);

/// @notice Emitted when the input is MIN_SD59x18.
error PRBMathSD59x18__AbsInputTooSmall();

/// @notice Emitted when ceiling a number overflows SD59x18.
error PRBMathSD59x18__CeilOverflow(int256 x);

/// @notice Emitted when one of the inputs is MIN_SD59x18.
error PRBMathSD59x18__DivInputTooSmall();

/// @notice Emitted when one of the intermediary unsigned results overflows SD59x18.
error PRBMathSD59x18__DivOverflow(uint256 rAbs);

/// @notice Emitted when the input is greater than 133.084258667509499441.
error PRBMathSD59x18__ExpInputTooBig(int256 x);

/// @notice Emitted when the input is greater than 192.
error PRBMathSD59x18__Exp2InputTooBig(int256 x);

/// @notice Emitted when flooring a number underflows SD59x18.
error PRBMathSD59x18__FloorUnderflow(int256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format overflows SD59x18.
error PRBMathSD59x18__FromIntOverflow(int256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format underflows SD59x18.
error PRBMathSD59x18__FromIntUnderflow(int256 x);

/// @notice Emitted when the product of the inputs is negative.
error PRBMathSD59x18__GmNegativeProduct(int256 x, int256 y);

/// @notice Emitted when multiplying the inputs overflows SD59x18.
error PRBMathSD59x18__GmOverflow(int256 x, int256 y);

/// @notice Emitted when the input is less than or equal to zero.
error PRBMathSD59x18__LogInputTooSmall(int256 x);

/// @notice Emitted when one of the inputs is MIN_SD59x18.
error PRBMathSD59x18__MulInputTooSmall();

/// @notice Emitted when the intermediary absolute result overflows SD59x18.
error PRBMathSD59x18__MulOverflow(uint256 rAbs);

/// @notice Emitted when the intermediary absolute result overflows SD59x18.
error PRBMathSD59x18__PowuOverflow(uint256 rAbs);

/// @notice Emitted when the input is negative.
error PRBMathSD59x18__SqrtNegativeInput(int256 x);

/// @notice Emitted when the calculating the square root overflows SD59x18.
error PRBMathSD59x18__SqrtOverflow(int256 x);

/// @notice Emitted when addition overflows UD60x18.
error PRBMathUD60x18__AddOverflow(uint256 x, uint256 y);

/// @notice Emitted when ceiling a number overflows UD60x18.
error PRBMathUD60x18__CeilOverflow(uint256 x);

/// @notice Emitted when the input is greater than 133.084258667509499441.
error PRBMathUD60x18__ExpInputTooBig(uint256 x);

/// @notice Emitted when the input is greater than 192.
error PRBMathUD60x18__Exp2InputTooBig(uint256 x);

/// @notice Emitted when converting a basic integer to the fixed-point format format overflows UD60x18.
error PRBMathUD60x18__FromUintOverflow(uint256 x);

/// @notice Emitted when multiplying the inputs overflows UD60x18.
error PRBMathUD60x18__GmOverflow(uint256 x, uint256 y);

/// @notice Emitted when the input is less than 1.
error PRBMathUD60x18__LogInputTooSmall(uint256 x);

/// @notice Emitted when the calculating the square root overflows UD60x18.
error PRBMathUD60x18__SqrtOverflow(uint256 x);

/// @notice Emitted when subtraction underflows UD60x18.
error PRBMathUD60x18__SubUnderflow(uint256 x, uint256 y);

/// @dev Common mathematical functions used in both PRBMathSD59x18 and PRBMathUD60x18. Note that this shared library
/// does not always assume the signed 59.18-decimal fixed-point or the unsigned 60.18-decimal fixed-point
/// representation. When it does not, it is explicitly mentioned in the NatSpec documentation.
library PRBMath {
    /// STRUCTS ///

    struct SD59x18 {
        int256 value;
    }

    struct UD60x18 {
        uint256 value;
    }

    /// STORAGE ///

    /// @dev How many trailing decimals can be represented.
    uint256 internal constant SCALE = 1e18;

    /// @dev Largest power of two divisor of SCALE.
    uint256 internal constant SCALE_LPOTD = 262144;

    /// @dev SCALE inverted mod 2^256.
    uint256 internal constant SCALE_INVERSE =
        78156646155174841979727994598816262306175212592076161876661_508869554232690281;

    /// FUNCTIONS ///

    /// @notice Calculates the binary exponent of x using the binary fraction method.
    /// @dev Has to use 192.64-bit fixed-point numbers.
    /// See https://ethereum.stackexchange.com/a/96594/24693.
    /// @param x The exponent as an unsigned 192.64-bit fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function exp2(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            // Start from 0.5 in the 192.64-bit fixed-point format.
            result = 0x800000000000000000000000000000000000000000000000;

            // Multiply the result by root(2, 2^-i) when the bit at position i is 1. None of the intermediary results overflows
            // because the initial result is 2^191 and all magic factors are less than 2^65.
            if (x & 0x8000000000000000 > 0) {
                result = (result * 0x16A09E667F3BCC909) >> 64;
            }
            if (x & 0x4000000000000000 > 0) {
                result = (result * 0x1306FE0A31B7152DF) >> 64;
            }
            if (x & 0x2000000000000000 > 0) {
                result = (result * 0x1172B83C7D517ADCE) >> 64;
            }
            if (x & 0x1000000000000000 > 0) {
                result = (result * 0x10B5586CF9890F62A) >> 64;
            }
            if (x & 0x800000000000000 > 0) {
                result = (result * 0x1059B0D31585743AE) >> 64;
            }
            if (x & 0x400000000000000 > 0) {
                result = (result * 0x102C9A3E778060EE7) >> 64;
            }
            if (x & 0x200000000000000 > 0) {
                result = (result * 0x10163DA9FB33356D8) >> 64;
            }
            if (x & 0x100000000000000 > 0) {
                result = (result * 0x100B1AFA5ABCBED61) >> 64;
            }
            if (x & 0x80000000000000 > 0) {
                result = (result * 0x10058C86DA1C09EA2) >> 64;
            }
            if (x & 0x40000000000000 > 0) {
                result = (result * 0x1002C605E2E8CEC50) >> 64;
            }
            if (x & 0x20000000000000 > 0) {
                result = (result * 0x100162F3904051FA1) >> 64;
            }
            if (x & 0x10000000000000 > 0) {
                result = (result * 0x1000B175EFFDC76BA) >> 64;
            }
            if (x & 0x8000000000000 > 0) {
                result = (result * 0x100058BA01FB9F96D) >> 64;
            }
            if (x & 0x4000000000000 > 0) {
                result = (result * 0x10002C5CC37DA9492) >> 64;
            }
            if (x & 0x2000000000000 > 0) {
                result = (result * 0x1000162E525EE0547) >> 64;
            }
            if (x & 0x1000000000000 > 0) {
                result = (result * 0x10000B17255775C04) >> 64;
            }
            if (x & 0x800000000000 > 0) {
                result = (result * 0x1000058B91B5BC9AE) >> 64;
            }
            if (x & 0x400000000000 > 0) {
                result = (result * 0x100002C5C89D5EC6D) >> 64;
            }
            if (x & 0x200000000000 > 0) {
                result = (result * 0x10000162E43F4F831) >> 64;
            }
            if (x & 0x100000000000 > 0) {
                result = (result * 0x100000B1721BCFC9A) >> 64;
            }
            if (x & 0x80000000000 > 0) {
                result = (result * 0x10000058B90CF1E6E) >> 64;
            }
            if (x & 0x40000000000 > 0) {
                result = (result * 0x1000002C5C863B73F) >> 64;
            }
            if (x & 0x20000000000 > 0) {
                result = (result * 0x100000162E430E5A2) >> 64;
            }
            if (x & 0x10000000000 > 0) {
                result = (result * 0x1000000B172183551) >> 64;
            }
            if (x & 0x8000000000 > 0) {
                result = (result * 0x100000058B90C0B49) >> 64;
            }
            if (x & 0x4000000000 > 0) {
                result = (result * 0x10000002C5C8601CC) >> 64;
            }
            if (x & 0x2000000000 > 0) {
                result = (result * 0x1000000162E42FFF0) >> 64;
            }
            if (x & 0x1000000000 > 0) {
                result = (result * 0x10000000B17217FBB) >> 64;
            }
            if (x & 0x800000000 > 0) {
                result = (result * 0x1000000058B90BFCE) >> 64;
            }
            if (x & 0x400000000 > 0) {
                result = (result * 0x100000002C5C85FE3) >> 64;
            }
            if (x & 0x200000000 > 0) {
                result = (result * 0x10000000162E42FF1) >> 64;
            }
            if (x & 0x100000000 > 0) {
                result = (result * 0x100000000B17217F8) >> 64;
            }
            if (x & 0x80000000 > 0) {
                result = (result * 0x10000000058B90BFC) >> 64;
            }
            if (x & 0x40000000 > 0) {
                result = (result * 0x1000000002C5C85FE) >> 64;
            }
            if (x & 0x20000000 > 0) {
                result = (result * 0x100000000162E42FF) >> 64;
            }
            if (x & 0x10000000 > 0) {
                result = (result * 0x1000000000B17217F) >> 64;
            }
            if (x & 0x8000000 > 0) {
                result = (result * 0x100000000058B90C0) >> 64;
            }
            if (x & 0x4000000 > 0) {
                result = (result * 0x10000000002C5C860) >> 64;
            }
            if (x & 0x2000000 > 0) {
                result = (result * 0x1000000000162E430) >> 64;
            }
            if (x & 0x1000000 > 0) {
                result = (result * 0x10000000000B17218) >> 64;
            }
            if (x & 0x800000 > 0) {
                result = (result * 0x1000000000058B90C) >> 64;
            }
            if (x & 0x400000 > 0) {
                result = (result * 0x100000000002C5C86) >> 64;
            }
            if (x & 0x200000 > 0) {
                result = (result * 0x10000000000162E43) >> 64;
            }
            if (x & 0x100000 > 0) {
                result = (result * 0x100000000000B1721) >> 64;
            }
            if (x & 0x80000 > 0) {
                result = (result * 0x10000000000058B91) >> 64;
            }
            if (x & 0x40000 > 0) {
                result = (result * 0x1000000000002C5C8) >> 64;
            }
            if (x & 0x20000 > 0) {
                result = (result * 0x100000000000162E4) >> 64;
            }
            if (x & 0x10000 > 0) {
                result = (result * 0x1000000000000B172) >> 64;
            }
            if (x & 0x8000 > 0) {
                result = (result * 0x100000000000058B9) >> 64;
            }
            if (x & 0x4000 > 0) {
                result = (result * 0x10000000000002C5D) >> 64;
            }
            if (x & 0x2000 > 0) {
                result = (result * 0x1000000000000162E) >> 64;
            }
            if (x & 0x1000 > 0) {
                result = (result * 0x10000000000000B17) >> 64;
            }
            if (x & 0x800 > 0) {
                result = (result * 0x1000000000000058C) >> 64;
            }
            if (x & 0x400 > 0) {
                result = (result * 0x100000000000002C6) >> 64;
            }
            if (x & 0x200 > 0) {
                result = (result * 0x10000000000000163) >> 64;
            }
            if (x & 0x100 > 0) {
                result = (result * 0x100000000000000B1) >> 64;
            }
            if (x & 0x80 > 0) {
                result = (result * 0x10000000000000059) >> 64;
            }
            if (x & 0x40 > 0) {
                result = (result * 0x1000000000000002C) >> 64;
            }
            if (x & 0x20 > 0) {
                result = (result * 0x10000000000000016) >> 64;
            }
            if (x & 0x10 > 0) {
                result = (result * 0x1000000000000000B) >> 64;
            }
            if (x & 0x8 > 0) {
                result = (result * 0x10000000000000006) >> 64;
            }
            if (x & 0x4 > 0) {
                result = (result * 0x10000000000000003) >> 64;
            }
            if (x & 0x2 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }
            if (x & 0x1 > 0) {
                result = (result * 0x10000000000000001) >> 64;
            }

            // We're doing two things at the same time:
            //
            //   1. Multiply the result by 2^n + 1, where "2^n" is the integer part and the one is added to account for
            //      the fact that we initially set the result to 0.5. This is accomplished by subtracting from 191
            //      rather than 192.
            //   2. Convert the result to the unsigned 60.18-decimal fixed-point format.
            //
            // This works because 2^(191-ip) = 2^ip / 2^191, where "ip" is the integer part "2^n".
            result *= SCALE;
            result >>= (191 - (x >> 64));
        }
    }

    /// @notice Finds the zero-based index of the first one in the binary representation of x.
    /// @dev See the note on msb in the "Find First Set" Wikipedia article https://en.wikipedia.org/wiki/Find_first_set
    /// @param x The uint256 number for which to find the index of the most significant bit.
    /// @return msb The index of the most significant bit as an uint256.
    function mostSignificantBit(uint256 x) internal pure returns (uint256 msb) {
        if (x >= 2**128) {
            x >>= 128;
            msb += 128;
        }
        if (x >= 2**64) {
            x >>= 64;
            msb += 64;
        }
        if (x >= 2**32) {
            x >>= 32;
            msb += 32;
        }
        if (x >= 2**16) {
            x >>= 16;
            msb += 16;
        }
        if (x >= 2**8) {
            x >>= 8;
            msb += 8;
        }
        if (x >= 2**4) {
            x >>= 4;
            msb += 4;
        }
        if (x >= 2**2) {
            x >>= 2;
            msb += 2;
        }
        if (x >= 2**1) {
            // No need to shift x any more.
            msb += 1;
        }
    }

    /// @notice Calculates floor(x*y÷denominator) with full precision.
    ///
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
    ///
    /// Requirements:
    /// - The denominator cannot be zero.
    /// - The result must fit within uint256.
    ///
    /// Caveats:
    /// - This function does not work with fixed-point numbers.
    ///
    /// @param x The multiplicand as an uint256.
    /// @param y The multiplier as an uint256.
    /// @param denominator The divisor as an uint256.
    /// @return result The result as an uint256.
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        // 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) {
            unchecked {
                result = prod0 / denominator;
            }
            return result;
        }

        // Make sure the result is less than 2^256. Also prevents denominator == 0.
        if (prod1 >= denominator) {
            revert PRBMath__MulDivOverflow(prod1, denominator);
        }

        ///////////////////////////////////////////////
        // 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.
        unchecked {
            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 lpotdod = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by lpotdod.
                denominator := div(denominator, lpotdod)

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

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

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

            // 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 floor(x*y÷1e18) with full precision.
    ///
    /// @dev Variant of "mulDiv" with constant folding, i.e. in which the denominator is always 1e18. Before returning the
    /// final result, we add 1 if (x * y) % SCALE >= HALF_SCALE. Without this, 6.6e-19 would be truncated to 0 instead of
    /// being rounded to 1e-18.  See "Listing 6" and text above it at https://accu.org/index.php/journals/1717.
    ///
    /// Requirements:
    /// - The result must fit within uint256.
    ///
    /// Caveats:
    /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMath.mulDiv" to understand how this works.
    /// - It is assumed that the result can never be type(uint256).max when x and y solve the following two equations:
    ///     1. x * y = type(uint256).max * SCALE
    ///     2. (x * y) % SCALE >= SCALE / 2
    ///
    /// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function mulDivFixedPoint(uint256 x, uint256 y) internal pure returns (uint256 result) {
        uint256 prod0;
        uint256 prod1;
        assembly {
            let mm := mulmod(x, y, not(0))
            prod0 := mul(x, y)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

        if (prod1 >= SCALE) {
            revert PRBMath__MulDivFixedPointOverflow(prod1);
        }

        uint256 remainder;
        uint256 roundUpUnit;
        assembly {
            remainder := mulmod(x, y, SCALE)
            roundUpUnit := gt(remainder, 499999999999999999)
        }

        if (prod1 == 0) {
            unchecked {
                result = (prod0 / SCALE) + roundUpUnit;
                return result;
            }
        }

        assembly {
            result := add(
                mul(
                    or(
                        div(sub(prod0, remainder), SCALE_LPOTD),
                        mul(sub(prod1, gt(remainder, prod0)), add(div(sub(0, SCALE_LPOTD), SCALE_LPOTD), 1))
                    ),
                    SCALE_INVERSE
                ),
                roundUpUnit
            )
        }
    }

    /// @notice Calculates floor(x*y÷denominator) with full precision.
    ///
    /// @dev An extension of "mulDiv" for signed numbers. Works by computing the signs and the absolute values separately.
    ///
    /// Requirements:
    /// - None of the inputs can be type(int256).min.
    /// - The result must fit within int256.
    ///
    /// @param x The multiplicand as an int256.
    /// @param y The multiplier as an int256.
    /// @param denominator The divisor as an int256.
    /// @return result The result as an int256.
    function mulDivSigned(
        int256 x,
        int256 y,
        int256 denominator
    ) internal pure returns (int256 result) {
        if (x == type(int256).min || y == type(int256).min || denominator == type(int256).min) {
            revert PRBMath__MulDivSignedInputTooSmall();
        }

        // Get hold of the absolute values of x, y and the denominator.
        uint256 ax;
        uint256 ay;
        uint256 ad;
        unchecked {
            ax = x < 0 ? uint256(-x) : uint256(x);
            ay = y < 0 ? uint256(-y) : uint256(y);
            ad = denominator < 0 ? uint256(-denominator) : uint256(denominator);
        }

        // Compute the absolute value of (x*y)÷denominator. The result must fit within int256.
        uint256 rAbs = mulDiv(ax, ay, ad);
        if (rAbs > uint256(type(int256).max)) {
            revert PRBMath__MulDivSignedOverflow(rAbs);
        }

        // Get the signs of x, y and the denominator.
        uint256 sx;
        uint256 sy;
        uint256 sd;
        assembly {
            sx := sgt(x, sub(0, 1))
            sy := sgt(y, sub(0, 1))
            sd := sgt(denominator, sub(0, 1))
        }

        // XOR over sx, sy and sd. This is checking whether there are one or three negative signs in the inputs.
        // If yes, the result should be negative.
        result = sx ^ sy ^ sd == 0 ? -int256(rAbs) : int256(rAbs);
    }

    /// @notice Calculates the square root of x, rounding down.
    /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
    ///
    /// Caveats:
    /// - This function does not work with fixed-point numbers.
    ///
    /// @param x The uint256 number for which to calculate the square root.
    /// @return result The result as an uint256.
    function sqrt(uint256 x) internal pure returns (uint256 result) {
        if (x == 0) {
            return 0;
        }

        // Set the initial guess to the least power of two that is greater than or equal to sqrt(x).
        uint256 xAux = uint256(x);
        result = 1;
        if (xAux >= 0x100000000000000000000000000000000) {
            xAux >>= 128;
            result <<= 64;
        }
        if (xAux >= 0x10000000000000000) {
            xAux >>= 64;
            result <<= 32;
        }
        if (xAux >= 0x100000000) {
            xAux >>= 32;
            result <<= 16;
        }
        if (xAux >= 0x10000) {
            xAux >>= 16;
            result <<= 8;
        }
        if (xAux >= 0x100) {
            xAux >>= 8;
            result <<= 4;
        }
        if (xAux >= 0x10) {
            xAux >>= 4;
            result <<= 2;
        }
        if (xAux >= 0x8) {
            result <<= 1;
        }

        // The operations can never overflow because the result is max 2^127 when it enters this block.
        unchecked {
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1;
            result = (result + x / result) >> 1; // Seven iterations should be enough
            uint256 roundedDownResult = x / result;
            return result >= roundedDownResult ? roundedDownResult : result;
        }
    }
}

// SPDX-License-Identifier: Unlicense
pragma solidity >=0.8.4;

import "./PRBMath.sol";

/// @title PRBMathUD60x18
/// @author Paul Razvan Berg
/// @notice Smart contract library for advanced fixed-point math that works with uint256 numbers considered to have 18
/// trailing decimals. We call this number representation unsigned 60.18-decimal fixed-point, since there can be up to 60
/// digits in the integer part and up to 18 decimals in the fractional part. The numbers are bound by the minimum and the
/// maximum values permitted by the Solidity type uint256.
library PRBMathUD60x18 {
    /// @dev Half the SCALE number.
    uint256 internal constant HALF_SCALE = 5e17;

    /// @dev log2(e) as an unsigned 60.18-decimal fixed-point number.
    uint256 internal constant LOG2_E = 1_442695040888963407;

    /// @dev The maximum value an unsigned 60.18-decimal fixed-point number can have.
    uint256 internal constant MAX_UD60x18 =
        115792089237316195423570985008687907853269984665640564039457_584007913129639935;

    /// @dev The maximum whole value an unsigned 60.18-decimal fixed-point number can have.
    uint256 internal constant MAX_WHOLE_UD60x18 =
        115792089237316195423570985008687907853269984665640564039457_000000000000000000;

    /// @dev How many trailing decimals can be represented.
    uint256 internal constant SCALE = 1e18;

    /// @notice Calculates the arithmetic average of x and y, rounding down.
    /// @param x The first operand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The second operand as an unsigned 60.18-decimal fixed-point number.
    /// @return result The arithmetic average as an unsigned 60.18-decimal fixed-point number.
    function avg(uint256 x, uint256 y) internal pure returns (uint256 result) {
        // The operations can never overflow.
        unchecked {
            // The last operand checks if both x and y are odd and if that is the case, we add 1 to the result. We need
            // to do this because if both numbers are odd, the 0.5 remainder gets truncated twice.
            result = (x >> 1) + (y >> 1) + (x & y & 1);
        }
    }

    /// @notice Yields the least unsigned 60.18 decimal fixed-point number greater than or equal to x.
    ///
    /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
    /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
    ///
    /// Requirements:
    /// - x must be less than or equal to MAX_WHOLE_UD60x18.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number to ceil.
    /// @param result The least integer greater than or equal to x, as an unsigned 60.18-decimal fixed-point number.
    function ceil(uint256 x) internal pure returns (uint256 result) {
        if (x > MAX_WHOLE_UD60x18) {
            revert PRBMathUD60x18__CeilOverflow(x);
        }
        assembly {
            // Equivalent to "x % SCALE" but faster.
            let remainder := mod(x, SCALE)

            // Equivalent to "SCALE - remainder" but faster.
            let delta := sub(SCALE, remainder)

            // Equivalent to "x + delta * (remainder > 0 ? 1 : 0)" but faster.
            result := add(x, mul(delta, gt(remainder, 0)))
        }
    }

    /// @notice Divides two unsigned 60.18-decimal fixed-point numbers, returning a new unsigned 60.18-decimal fixed-point number.
    ///
    /// @dev Uses mulDiv to enable overflow-safe multiplication and division.
    ///
    /// Requirements:
    /// - The denominator cannot be zero.
    ///
    /// @param x The numerator as an unsigned 60.18-decimal fixed-point number.
    /// @param y The denominator as an unsigned 60.18-decimal fixed-point number.
    /// @param result The quotient as an unsigned 60.18-decimal fixed-point number.
    function div(uint256 x, uint256 y) internal pure returns (uint256 result) {
        result = PRBMath.mulDiv(x, SCALE, y);
    }

    /// @notice Returns Euler's number as an unsigned 60.18-decimal fixed-point number.
    /// @dev See https://en.wikipedia.org/wiki/E_(mathematical_constant).
    function e() internal pure returns (uint256 result) {
        result = 2_718281828459045235;
    }

    /// @notice Calculates the natural exponent of x.
    ///
    /// @dev Based on the insight that e^x = 2^(x * log2(e)).
    ///
    /// Requirements:
    /// - All from "log2".
    /// - x must be less than 133.084258667509499441.
    ///
    /// @param x The exponent as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function exp(uint256 x) internal pure returns (uint256 result) {
        // Without this check, the value passed to "exp2" would be greater than 192.
        if (x >= 133_084258667509499441) {
            revert PRBMathUD60x18__ExpInputTooBig(x);
        }

        // Do the fixed-point multiplication inline to save gas.
        unchecked {
            uint256 doubleScaleProduct = x * LOG2_E;
            result = exp2((doubleScaleProduct + HALF_SCALE) / SCALE);
        }
    }

    /// @notice Calculates the binary exponent of x using the binary fraction method.
    ///
    /// @dev See https://ethereum.stackexchange.com/q/79903/24693.
    ///
    /// Requirements:
    /// - x must be 192 or less.
    /// - The result must fit within MAX_UD60x18.
    ///
    /// @param x The exponent as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function exp2(uint256 x) internal pure returns (uint256 result) {
        // 2^192 doesn't fit within the 192.64-bit format used internally in this function.
        if (x >= 192e18) {
            revert PRBMathUD60x18__Exp2InputTooBig(x);
        }

        unchecked {
            // Convert x to the 192.64-bit fixed-point format.
            uint256 x192x64 = (x << 64) / SCALE;

            // Pass x to the PRBMath.exp2 function, which uses the 192.64-bit fixed-point number representation.
            result = PRBMath.exp2(x192x64);
        }
    }

    /// @notice Yields the greatest unsigned 60.18 decimal fixed-point number less than or equal to x.
    /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
    /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
    /// @param x The unsigned 60.18-decimal fixed-point number to floor.
    /// @param result The greatest integer less than or equal to x, as an unsigned 60.18-decimal fixed-point number.
    function floor(uint256 x) internal pure returns (uint256 result) {
        assembly {
            // Equivalent to "x % SCALE" but faster.
            let remainder := mod(x, SCALE)

            // Equivalent to "x - remainder * (remainder > 0 ? 1 : 0)" but faster.
            result := sub(x, mul(remainder, gt(remainder, 0)))
        }
    }

    /// @notice Yields the excess beyond the floor of x.
    /// @dev Based on the odd function definition https://en.wikipedia.org/wiki/Fractional_part.
    /// @param x The unsigned 60.18-decimal fixed-point number to get the fractional part of.
    /// @param result The fractional part of x as an unsigned 60.18-decimal fixed-point number.
    function frac(uint256 x) internal pure returns (uint256 result) {
        assembly {
            result := mod(x, SCALE)
        }
    }

    /// @notice Converts a number from basic integer form to unsigned 60.18-decimal fixed-point representation.
    ///
    /// @dev Requirements:
    /// - x must be less than or equal to MAX_UD60x18 divided by SCALE.
    ///
    /// @param x The basic integer to convert.
    /// @param result The same number in unsigned 60.18-decimal fixed-point representation.
    function fromUint(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            if (x > MAX_UD60x18 / SCALE) {
                revert PRBMathUD60x18__FromUintOverflow(x);
            }
            result = x * SCALE;
        }
    }

    /// @notice Calculates geometric mean of x and y, i.e. sqrt(x * y), rounding down.
    ///
    /// @dev Requirements:
    /// - x * y must fit within MAX_UD60x18, lest it overflows.
    ///
    /// @param x The first operand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The second operand as an unsigned 60.18-decimal fixed-point number.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function gm(uint256 x, uint256 y) internal pure returns (uint256 result) {
        if (x == 0) {
            return 0;
        }

        unchecked {
            // Checking for overflow this way is faster than letting Solidity do it.
            uint256 xy = x * y;
            if (xy / x != y) {
                revert PRBMathUD60x18__GmOverflow(x, y);
            }

            // We don't need to multiply by the SCALE here because the x*y product had already picked up a factor of SCALE
            // during multiplication. See the comments within the "sqrt" function.
            result = PRBMath.sqrt(xy);
        }
    }

    /// @notice Calculates 1 / x, rounding toward zero.
    ///
    /// @dev Requirements:
    /// - x cannot be zero.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the inverse.
    /// @return result The inverse as an unsigned 60.18-decimal fixed-point number.
    function inv(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            // 1e36 is SCALE * SCALE.
            result = 1e36 / x;
        }
    }

    /// @notice Calculates the natural logarithm of x.
    ///
    /// @dev Based on the insight that ln(x) = log2(x) / log2(e).
    ///
    /// Requirements:
    /// - All from "log2".
    ///
    /// Caveats:
    /// - All from "log2".
    /// - This doesn't return exactly 1 for 2.718281828459045235, for that we would need more fine-grained precision.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the natural logarithm.
    /// @return result The natural logarithm as an unsigned 60.18-decimal fixed-point number.
    function ln(uint256 x) internal pure returns (uint256 result) {
        // Do the fixed-point multiplication inline to save gas. This is overflow-safe because the maximum value that log2(x)
        // can return is 196205294292027477728.
        unchecked {
            result = (log2(x) * SCALE) / LOG2_E;
        }
    }

    /// @notice Calculates the common logarithm of x.
    ///
    /// @dev First checks if x is an exact power of ten and it stops if yes. If it's not, calculates the common
    /// logarithm based on the insight that log10(x) = log2(x) / log2(10).
    ///
    /// Requirements:
    /// - All from "log2".
    ///
    /// Caveats:
    /// - All from "log2".
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the common logarithm.
    /// @return result The common logarithm as an unsigned 60.18-decimal fixed-point number.
    function log10(uint256 x) internal pure returns (uint256 result) {
        if (x < SCALE) {
            revert PRBMathUD60x18__LogInputTooSmall(x);
        }

        // Note that the "mul" in this block is the assembly multiplication operation, not the "mul" function defined
        // in this contract.
        // prettier-ignore
        assembly {
            switch x
            case 1 { result := mul(SCALE, sub(0, 18)) }
            case 10 { result := mul(SCALE, sub(1, 18)) }
            case 100 { result := mul(SCALE, sub(2, 18)) }
            case 1000 { result := mul(SCALE, sub(3, 18)) }
            case 10000 { result := mul(SCALE, sub(4, 18)) }
            case 100000 { result := mul(SCALE, sub(5, 18)) }
            case 1000000 { result := mul(SCALE, sub(6, 18)) }
            case 10000000 { result := mul(SCALE, sub(7, 18)) }
            case 100000000 { result := mul(SCALE, sub(8, 18)) }
            case 1000000000 { result := mul(SCALE, sub(9, 18)) }
            case 10000000000 { result := mul(SCALE, sub(10, 18)) }
            case 100000000000 { result := mul(SCALE, sub(11, 18)) }
            case 1000000000000 { result := mul(SCALE, sub(12, 18)) }
            case 10000000000000 { result := mul(SCALE, sub(13, 18)) }
            case 100000000000000 { result := mul(SCALE, sub(14, 18)) }
            case 1000000000000000 { result := mul(SCALE, sub(15, 18)) }
            case 10000000000000000 { result := mul(SCALE, sub(16, 18)) }
            case 100000000000000000 { result := mul(SCALE, sub(17, 18)) }
            case 1000000000000000000 { result := 0 }
            case 10000000000000000000 { result := SCALE }
            case 100000000000000000000 { result := mul(SCALE, 2) }
            case 1000000000000000000000 { result := mul(SCALE, 3) }
            case 10000000000000000000000 { result := mul(SCALE, 4) }
            case 100000000000000000000000 { result := mul(SCALE, 5) }
            case 1000000000000000000000000 { result := mul(SCALE, 6) }
            case 10000000000000000000000000 { result := mul(SCALE, 7) }
            case 100000000000000000000000000 { result := mul(SCALE, 8) }
            case 1000000000000000000000000000 { result := mul(SCALE, 9) }
            case 10000000000000000000000000000 { result := mul(SCALE, 10) }
            case 100000000000000000000000000000 { result := mul(SCALE, 11) }
            case 1000000000000000000000000000000 { result := mul(SCALE, 12) }
            case 10000000000000000000000000000000 { result := mul(SCALE, 13) }
            case 100000000000000000000000000000000 { result := mul(SCALE, 14) }
            case 1000000000000000000000000000000000 { result := mul(SCALE, 15) }
            case 10000000000000000000000000000000000 { result := mul(SCALE, 16) }
            case 100000000000000000000000000000000000 { result := mul(SCALE, 17) }
            case 1000000000000000000000000000000000000 { result := mul(SCALE, 18) }
            case 10000000000000000000000000000000000000 { result := mul(SCALE, 19) }
            case 100000000000000000000000000000000000000 { result := mul(SCALE, 20) }
            case 1000000000000000000000000000000000000000 { result := mul(SCALE, 21) }
            case 10000000000000000000000000000000000000000 { result := mul(SCALE, 22) }
            case 100000000000000000000000000000000000000000 { result := mul(SCALE, 23) }
            case 1000000000000000000000000000000000000000000 { result := mul(SCALE, 24) }
            case 10000000000000000000000000000000000000000000 { result := mul(SCALE, 25) }
            case 100000000000000000000000000000000000000000000 { result := mul(SCALE, 26) }
            case 1000000000000000000000000000000000000000000000 { result := mul(SCALE, 27) }
            case 10000000000000000000000000000000000000000000000 { result := mul(SCALE, 28) }
            case 100000000000000000000000000000000000000000000000 { result := mul(SCALE, 29) }
            case 1000000000000000000000000000000000000000000000000 { result := mul(SCALE, 30) }
            case 10000000000000000000000000000000000000000000000000 { result := mul(SCALE, 31) }
            case 100000000000000000000000000000000000000000000000000 { result := mul(SCALE, 32) }
            case 1000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 33) }
            case 10000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 34) }
            case 100000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 35) }
            case 1000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 36) }
            case 10000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 37) }
            case 100000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 38) }
            case 1000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 39) }
            case 10000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 40) }
            case 100000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 41) }
            case 1000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 42) }
            case 10000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 43) }
            case 100000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 44) }
            case 1000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 45) }
            case 10000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 46) }
            case 100000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 47) }
            case 1000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 48) }
            case 10000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 49) }
            case 100000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 50) }
            case 1000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 51) }
            case 10000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 52) }
            case 100000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 53) }
            case 1000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 54) }
            case 10000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 55) }
            case 100000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 56) }
            case 1000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 57) }
            case 10000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 58) }
            case 100000000000000000000000000000000000000000000000000000000000000000000000000000 { result := mul(SCALE, 59) }
            default {
                result := MAX_UD60x18
            }
        }

        if (result == MAX_UD60x18) {
            // Do the fixed-point division inline to save gas. The denominator is log2(10).
            unchecked {
                result = (log2(x) * SCALE) / 3_321928094887362347;
            }
        }
    }

    /// @notice Calculates the binary logarithm of x.
    ///
    /// @dev Based on the iterative approximation algorithm.
    /// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
    ///
    /// Requirements:
    /// - x must be greater than or equal to SCALE, otherwise the result would be negative.
    ///
    /// Caveats:
    /// - The results are nor perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the binary logarithm.
    /// @return result The binary logarithm as an unsigned 60.18-decimal fixed-point number.
    function log2(uint256 x) internal pure returns (uint256 result) {
        if (x < SCALE) {
            revert PRBMathUD60x18__LogInputTooSmall(x);
        }
        unchecked {
            // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n).
            uint256 n = PRBMath.mostSignificantBit(x / SCALE);

            // The integer part of the logarithm as an unsigned 60.18-decimal fixed-point number. The operation can't overflow
            // because n is maximum 255 and SCALE is 1e18.
            result = n * SCALE;

            // This is y = x * 2^(-n).
            uint256 y = x >> n;

            // If y = 1, the fractional part is zero.
            if (y == SCALE) {
                return result;
            }

            // Calculate the fractional part via the iterative approximation.
            // The "delta >>= 1" part is equivalent to "delta /= 2", but shifting bits is faster.
            for (uint256 delta = HALF_SCALE; delta > 0; delta >>= 1) {
                y = (y * y) / SCALE;

                // Is y^2 > 2 and so in the range [2,4)?
                if (y >= 2 * SCALE) {
                    // Add the 2^(-m) factor to the logarithm.
                    result += delta;

                    // Corresponds to z/2 on Wikipedia.
                    y >>= 1;
                }
            }
        }
    }

    /// @notice Multiplies two unsigned 60.18-decimal fixed-point numbers together, returning a new unsigned 60.18-decimal
    /// fixed-point number.
    /// @dev See the documentation for the "PRBMath.mulDivFixedPoint" function.
    /// @param x The multiplicand as an unsigned 60.18-decimal fixed-point number.
    /// @param y The multiplier as an unsigned 60.18-decimal fixed-point number.
    /// @return result The product as an unsigned 60.18-decimal fixed-point number.
    function mul(uint256 x, uint256 y) internal pure returns (uint256 result) {
        result = PRBMath.mulDivFixedPoint(x, y);
    }

    /// @notice Returns PI as an unsigned 60.18-decimal fixed-point number.
    function pi() internal pure returns (uint256 result) {
        result = 3_141592653589793238;
    }

    /// @notice Raises x to the power of y.
    ///
    /// @dev Based on the insight that x^y = 2^(log2(x) * y).
    ///
    /// Requirements:
    /// - All from "exp2", "log2" and "mul".
    ///
    /// Caveats:
    /// - All from "exp2", "log2" and "mul".
    /// - Assumes 0^0 is 1.
    ///
    /// @param x Number to raise to given power y, as an unsigned 60.18-decimal fixed-point number.
    /// @param y Exponent to raise x to, as an unsigned 60.18-decimal fixed-point number.
    /// @return result x raised to power y, as an unsigned 60.18-decimal fixed-point number.
    function pow(uint256 x, uint256 y) internal pure returns (uint256 result) {
        if (x == 0) {
            result = y == 0 ? SCALE : uint256(0);
        } else {
            result = exp2(mul(log2(x), y));
        }
    }

    /// @notice Raises x (unsigned 60.18-decimal fixed-point number) to the power of y (basic unsigned integer) using the
    /// famous algorithm "exponentiation by squaring".
    ///
    /// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring
    ///
    /// Requirements:
    /// - The result must fit within MAX_UD60x18.
    ///
    /// Caveats:
    /// - All from "mul".
    /// - Assumes 0^0 is 1.
    ///
    /// @param x The base as an unsigned 60.18-decimal fixed-point number.
    /// @param y The exponent as an uint256.
    /// @return result The result as an unsigned 60.18-decimal fixed-point number.
    function powu(uint256 x, uint256 y) internal pure returns (uint256 result) {
        // Calculate the first iteration of the loop in advance.
        result = y & 1 > 0 ? x : SCALE;

        // Equivalent to "for(y /= 2; y > 0; y /= 2)" but faster.
        for (y >>= 1; y > 0; y >>= 1) {
            x = PRBMath.mulDivFixedPoint(x, x);

            // Equivalent to "y % 2 == 1" but faster.
            if (y & 1 > 0) {
                result = PRBMath.mulDivFixedPoint(result, x);
            }
        }
    }

    /// @notice Returns 1 as an unsigned 60.18-decimal fixed-point number.
    function scale() internal pure returns (uint256 result) {
        result = SCALE;
    }

    /// @notice Calculates the square root of x, rounding down.
    /// @dev Uses the Babylonian method https://en.wikipedia.org/wiki/Methods_of_computing_square_roots#Babylonian_method.
    ///
    /// Requirements:
    /// - x must be less than MAX_UD60x18 / SCALE.
    ///
    /// @param x The unsigned 60.18-decimal fixed-point number for which to calculate the square root.
    /// @return result The result as an unsigned 60.18-decimal fixed-point .
    function sqrt(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            if (x > MAX_UD60x18 / SCALE) {
                revert PRBMathUD60x18__SqrtOverflow(x);
            }
            // Multiply x by the SCALE to account for the factor of SCALE that is picked up when multiplying two unsigned
            // 60.18-decimal fixed-point numbers together (in this case, those two numbers are both the square root).
            result = PRBMath.sqrt(x * SCALE);
        }
    }

    /// @notice Converts a unsigned 60.18-decimal fixed-point number to basic integer form, rounding down in the process.
    /// @param x The unsigned 60.18-decimal fixed-point number to convert.
    /// @return result The same number in basic integer form.
    function toUint(uint256 x) internal pure returns (uint256 result) {
        unchecked {
            result = x / SCALE;
        }
    }
}

Settings
{
  "optimizer": {
    "enabled": true,
    "runs": 10
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {
    "contracts/core/libraries/logic/ConfigureLogic.sol": {
      "ConfigureLogic": "0xab4eb71128d543c4beeabf5845d51e2e858abb9e"
    },
    "contracts/core/libraries/logic/FundingFeeLogic.sol": {
      "FundingFeeLogic": "0xa71a515de4acf04fb66e5923e5c0bd3146794c05"
    },
    "contracts/core/libraries/logic/GenericLogic.sol": {
      "GenericLogic": "0x10671bc2f07593e208b1762b101fdfea8689067e"
    },
    "contracts/core/libraries/logic/PositionLogic.sol": {
      "PositionLogic": "0x8d8fb6320ab54f5ba37c532c91978f3b61f14877"
    },
    "contracts/core/libraries/logic/SupplyLogic.sol": {
      "SupplyLogic": "0x77cdd46002cd7ae83a72622127acb2f7ae53a7da"
    },
    "contracts/core/libraries/logic/SwapLogic.sol": {
      "SwapLogic": "0xff6ec5be53b532e4b5aac66dab16fd54575e6323"
    }
  }
}

Contract ABI

API
[{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"token","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"ClaimFundingFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint8","name":"version","type":"uint8"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferStarted","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"previousOwner","type":"address"},{"indexed":true,"internalType":"address","name":"newOwner","type":"address"}],"name":"OwnershipTransferred","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Paused","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"SettleFundingFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"account","type":"address"}],"name":"Unpaused","type":"event"},{"inputs":[],"name":"acceptOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_collateralToken","type":"address[]"},{"internalType":"address[]","name":"_indexToken","type":"address[]"},{"internalType":"bool[]","name":"_isLong","type":"bool[]"}],"name":"batchSettleFundingFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"buyUSDG","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimFundingFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"clearTokenConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_collateralDelta","type":"uint256"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"decreasePosition","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"directPoolDeposit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAddresses","outputs":[{"components":[{"internalType":"address","name":"weth","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"ulpManager","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"},{"internalType":"address","name":"usdg","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"feesReceiver","type":"address"}],"internalType":"struct DataTypes.AddressStorage","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address[]","name":"_collateralToken","type":"address[]"},{"internalType":"address[]","name":"_indexToken","type":"address[]"},{"internalType":"bool[]","name":"_isLong","type":"bool[]"}],"name":"getBatchSettleFundingFeeAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getBorrowingFeeInfo","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getBorrowingRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getBufferAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_usdgDelta","type":"uint256"},{"internalType":"uint256","name":"_feeBasisPoints","type":"uint256"},{"internalType":"uint256","name":"_taxBasisPoints","type":"uint256"},{"internalType":"bool","name":"_increment","type":"bool"},{"internalType":"bool","name":"_isSwap","type":"bool"}],"name":"getFeeBasisPoints","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getFeeReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getFees","outputs":[{"components":[{"internalType":"uint256","name":"taxBasisPoints","type":"uint256"},{"internalType":"uint256","name":"stableTaxBasisPoints","type":"uint256"},{"internalType":"uint256","name":"mintBurnFeeBasisPoints","type":"uint256"},{"internalType":"uint256","name":"swapFeeBasisPoints","type":"uint256"},{"internalType":"uint256","name":"stableSwapFeeBasisPoints","type":"uint256"},{"internalType":"uint256","name":"marginFeeBasisPoints","type":"uint256"},{"internalType":"uint256","name":"liquidationFeeUsd","type":"uint256"},{"internalType":"bool","name":"hasDynamicFees","type":"bool"},{"internalType":"uint256","name":"balanceReward","type":"uint256"}],"internalType":"struct DataTypes.SetFeesParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getFundingFactor","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"}],"name":"getFundingFeeAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getGlobalData","outputs":[{"components":[{"internalType":"uint256","name":"maxGlobalLongSize","type":"uint256"},{"internalType":"uint256","name":"globalLongSize","type":"uint256"},{"internalType":"uint256","name":"globalLongAveragePrice","type":"uint256"},{"internalType":"uint256","name":"maxGlobalShortSize","type":"uint256"},{"internalType":"uint256","name":"globalShortSize","type":"uint256"},{"internalType":"uint256","name":"globalShortAveragePrice","type":"uint256"}],"internalType":"struct DataTypes.GlobalData","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getGlobalShortDelta","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxGasPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaxLeverage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getMaxPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getMinPrice","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_usdgDelta","type":"uint256"},{"internalType":"bool","name":"_increment","type":"bool"}],"name":"getNextTargetUsdgAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPause","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPermissionParams","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"},{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getPoolInfo","outputs":[{"components":[{"internalType":"uint256","name":"poolAmount","type":"uint256"},{"internalType":"uint256","name":"reservedAmount","type":"uint256"},{"internalType":"uint256","name":"bufferAmount","type":"uint256"},{"internalType":"uint256","name":"globalLongSize","type":"uint256"},{"internalType":"uint256","name":"globalLongAveragePrice","type":"uint256"},{"internalType":"uint256","name":"globalShortSize","type":"uint256"},{"internalType":"uint256","name":"globalShortAveragePrice","type":"uint256"},{"internalType":"uint256","name":"usdgAmount","type":"uint256"}],"internalType":"struct DataTypes.PoolInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"}],"name":"getPosition","outputs":[{"components":[{"internalType":"uint256","name":"size","type":"uint256"},{"internalType":"uint256","name":"collateral","type":"uint256"},{"internalType":"uint256","name":"averagePrice","type":"uint256"},{"internalType":"uint256","name":"entryBorrowingRate","type":"uint256"},{"internalType":"uint256","name":"fundingFeeAmountPerSize","type":"uint256"},{"internalType":"uint256","name":"claimableFundingAmountPerSize","type":"uint256"},{"internalType":"uint256","name":"reserveAmount","type":"uint256"},{"internalType":"int256","name":"realisedPnl","type":"int256"},{"internalType":"uint256","name":"lastIncreasedTime","type":"uint256"}],"internalType":"struct DataTypes.Position","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"}],"name":"getPositionDeltaAndFees","outputs":[{"internalType":"bool","name":"","type":"bool"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"}],"name":"getPositionLeverage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_usdgAmount","type":"uint256"}],"name":"getRedemptionAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserveRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getTargetUsdgAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getTokenConfig","outputs":[{"components":[{"internalType":"address","name":"token","type":"address"},{"internalType":"uint256","name":"tokenDecimals","type":"uint256"},{"internalType":"uint256","name":"tokenWeight","type":"uint256"},{"internalType":"uint256","name":"maxUsdgAmount","type":"uint256"},{"internalType":"bool","name":"isStable","type":"bool"},{"internalType":"bool","name":"isShortable","type":"bool"}],"internalType":"struct DataTypes.SetTokenConfigParams","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getTokenDecimal","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getTokenInfo","outputs":[{"components":[{"internalType":"uint256","name":"tokenDecimal","type":"uint256"},{"internalType":"bool","name":"isWhitelistedToken","type":"bool"},{"internalType":"bool","name":"isStableToken","type":"bool"},{"internalType":"uint256","name":"maxUsdgAmount","type":"uint256"}],"internalType":"struct DataTypes.TokenInfo","name":"","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"}],"name":"getTotalFeesForPositionLiquidation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getUsdgAmount","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"getUtilisation","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWhitelistedToken","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"address[]","name":"","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"uint256","name":"_sizeDelta","type":"uint256"},{"internalType":"bool","name":"_isLong","type":"bool"}],"name":"increasePosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"}],"name":"isFeesAdmin","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_liquidator","type":"address"}],"name":"isLiquidator","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_manager","type":"address"}],"name":"isManager","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_router","type":"address"}],"name":"isRouter","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"isTokenPaused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"address","name":"_feeReceiver","type":"address"}],"name":"liquidatePosition","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"owner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"paused","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"pendingOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"renounceOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"sellUSDG","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"address","name":"weth","type":"address"},{"internalType":"address","name":"router","type":"address"},{"internalType":"address","name":"ulpManager","type":"address"},{"internalType":"address","name":"priceFeed","type":"address"},{"internalType":"address","name":"usdg","type":"address"},{"internalType":"address","name":"collateralToken","type":"address"},{"internalType":"address","name":"feesReceiver","type":"address"}],"internalType":"struct DataTypes.AddressStorage","name":"params","type":"tuple"}],"name":"setAddresses","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_borrowingInterval","type":"uint256"},{"internalType":"uint256","name":"_borrowingRateFactor","type":"uint256"},{"internalType":"uint256","name":"_stableBorrowingRateFactor","type":"uint256"}],"name":"setBorrowingRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"setBufferAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"uint256","name":"taxBasisPoints","type":"uint256"},{"internalType":"uint256","name":"stableTaxBasisPoints","type":"uint256"},{"internalType":"uint256","name":"mintBurnFeeBasisPoints","type":"uint256"},{"internalType":"uint256","name":"swapFeeBasisPoints","type":"uint256"},{"internalType":"uint256","name":"stableSwapFeeBasisPoints","type":"uint256"},{"internalType":"uint256","name":"marginFeeBasisPoints","type":"uint256"},{"internalType":"uint256","name":"liquidationFeeUsd","type":"uint256"},{"internalType":"bool","name":"hasDynamicFees","type":"bool"},{"internalType":"uint256","name":"balanceReward","type":"uint256"}],"internalType":"struct DataTypes.SetFeesParams","name":"params","type":"tuple"}],"name":"setFees","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_admin","type":"address"},{"internalType":"bool","name":"_isAdmin","type":"bool"}],"name":"setFeesAdmin","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"uint256[]","name":"_fundingFactors","type":"uint256[]"},{"internalType":"uint256[]","name":"_fundingExponentFactors","type":"uint256[]"}],"name":"setFundingFactor","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_liquidator","type":"address"},{"internalType":"bool","name":"_isActive","type":"bool"}],"name":"setLiquidator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_manager","type":"address"},{"internalType":"bool","name":"_isManager","type":"bool"}],"name":"setManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxGasPrice","type":"uint256"}],"name":"setMaxGasPrice","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address[]","name":"_tokens","type":"address[]"},{"internalType":"uint256[]","name":"_longSizes","type":"uint256[]"},{"internalType":"uint256[]","name":"_shortSizes","type":"uint256[]"}],"name":"setMaxGlobalSizes","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_maxLeverage","type":"uint256"}],"name":"setMaxLeverage","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"flag","type":"bool"}],"name":"setPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bool","name":"_inManagerMode","type":"bool"},{"internalType":"bool","name":"_inPrivateLiquidationMode","type":"bool"},{"internalType":"bool","name":"_isSwapEnabled","type":"bool"},{"internalType":"bool","name":"_isLeverageEnabled","type":"bool"}],"name":"setPermissionParams","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_ratio","type":"uint256"}],"name":"setReserveRatio","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_router","type":"address"},{"internalType":"bool","name":"_isRouter","type":"bool"}],"name":"setRouter","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_tokenDecimals","type":"uint256"},{"internalType":"uint256","name":"_tokenWeight","type":"uint256"},{"internalType":"uint256","name":"_maxUsdgAmount","type":"uint256"},{"internalType":"bool","name":"_isStable","type":"bool"},{"internalType":"bool","name":"_isShortable","type":"bool"}],"name":"setTokenConfig","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"bool","name":"_isPause","type":"bool"}],"name":"setTokenPause","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"}],"name":"setUsdgAmount","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_tokenIn","type":"address"},{"internalType":"address","name":"_tokenOut","type":"address"},{"internalType":"address","name":"_receiver","type":"address"}],"name":"swap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"},{"internalType":"uint256","name":"_tokenAmount","type":"uint256"}],"name":"tokenToUsdMin","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"newOwner","type":"address"}],"name":"transferOwnership","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"}],"name":"updateCumulativeBorrowingRate","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_indexToken","type":"address"}],"name":"updateFundingState","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_account","type":"address"},{"internalType":"address","name":"_collateralToken","type":"address"},{"internalType":"address","name":"_indexToken","type":"address"},{"internalType":"bool","name":"_isLong","type":"bool"},{"internalType":"bool","name":"_raise","type":"bool"}],"name":"validateLiquidation","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_token","type":"address"}],"name":"withdrawFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"}]

608060405234801561001057600080fd5b50615a6180620000216000396000f3fe608060405234801561001057600080fd5b506004361061038a5760003560e01c806381a612d6116101dc57806381a612d6146107f957806382a084901461080c5780638585f4d21461081f57806387ed714414610832578063891c5a731461084557806389201c761461085857806389c98c061461086b5780638b4f3b2c146108735780638da5cb5b14610886578063933162121461089b5780639498a8ca146108ae57806397a6760c146108c1578063a39fac12146108d4578063a5e90eee146108e9578063b364accb146108fc578063b375d49214610926578063bd2521ac14610939578063bedb86fb1461094c578063c14bfd361461095f578063c3c6467414610972578063cb67e3b114610985578063cb8d3b1f146109a5578063d2fa635e146109b8578063d3127e63146109cb578063d3405289146109de578063d54d5a9f146109f1578063d66b000d14610a04578063d91ed30a14610a17578063db8d55f114610a2a578063de2ea94814610a3f578063dfa2ce1c14610a52578063e124e6d214610a65578063e30c397814610a78578063e67f59a714610a80578063ef12c67e14610a93578063f2fde38b14610aa6578063f36b242514610ab9578063f3ae241514610adc578063fe75443314610aef57600080fd5b806304fef1db1461038f57806306bfa938146103b557806308a99df9146104295780630a48d5a9146104595780630ea8b3bf1461046c578063164e68de146104745780631f69565f1461048757806321f9dace146104d15780632c668ec1146104e65780632ef94257146104f9578063322b3d9814610501578063336e94d3146105295780633459a82f1461053c5780633a05dcc1146105525780633cb6b783146105655780633e3002b6146105a25780633e3ca9d3146105b5578063417584ee146105cd5780634453a374146105e057806348d91abf146105f35780634a3f088d1461060657806351723e8214610686578063529a356f146106995780635534aa5f146106ac57806356ac1d5c146106bf57806356b6d0d5146106d757806358e86625146106df5780635c401439146106f25780635c975abb146107055780635e76ad54146107105780635f7bc1191461072357806360986cef1461073657806362883ccd14610749578063655a62571461075c578063711e6190146107bb578063715018a6146107ce57806379ba5097146107d65780638129fc1c146107de578063817bb857146107e6575b600080fd5b6103a261039d366004614a81565b610b02565b6040519081526020015b60405180910390f35b6103c86103c3366004614a81565b610b83565b6040516103ac9190600061010082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b61043c610437366004614ab5565b610c43565b6040805193845291151560208401521515908201526060016103ac565b6103a2610467366004614b1a565b610c69565b6103a2610c7c565b6103a2610482366004614a81565b610c8e565b61049a610495366004614a81565b610d73565b6040516103ac9190815181526020808301511515908201526040808301511515908201526060918201519181019190915260800190565b6104e46104df366004614b44565b610d84565b005b6103a26104f4366004614b1a565b610dc6565b6103a2610dd2565b61051461050f366004614a81565b610eb9565b604080519283526020830191909152016103ac565b6104e4610537366004614ab5565b610ef8565b610544610fa8565b6040516103ac929190614b7b565b6103a2610560366004614a81565b610fbc565b610578610573366004614bd2565b610fc7565b6040805195151586526020860194909452928401919091526060830152608082015260a0016103ac565b6103a26105b0366004614c28565b611040565b6105bd611055565b60405190151581526020016103ac565b6104e46105db366004614dc7565b611068565b6104e46105ee366004614b44565b611197565b6104e4610601366004614e4e565b61120c565b610619610614366004614bd2565b611314565b6040516103ac9190600061012082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525092915050565b6103a2610694366004614bd2565b611401565b6105bd6106a7366004614a81565b611488565b6103a26106ba366004614a81565b6114b8565b6106c76114e5565b6040516103ac9493929190614eb0565b6103a261151f565b6103a26106ed366004614a81565b611534565b6105bd610700366004614a81565b611568565b60975460ff166105bd565b6105bd61071e366004614a81565b611598565b6104e4610731366004614a81565b6115c8565b6104e4610744366004614f35565b61164c565b6103a2610757366004614a81565b611849565b61076f61076a366004614a81565b611876565b6040516103ac9190600060c082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015292915050565b6103a26107c9366004614fb2565b611910565b6104e46119a8565b6104e46119bc565b6104e4611a37565b6103a26107f4366004614fb2565b611b5e565b6103a2610807366004614a81565b611bcf565b6103a261081a366004614fe5565b611bda565b6104e461082d366004614b1a565b611cf4565b6103a2610840366004614a81565b611d35565b6103a2610853366004614a81565b611d62565b6103a261086636600461505f565b611d8f565b6103a2611eae565b6104e46108813660046150f7565b611ec3565b61088e611f2b565b6040516103ac9190615110565b6103a26108a9366004615124565b611f3a565b6105146108bc366004614a81565b612035565b6104e46108cf366004614a81565b612074565b6108dc6120bb565b6040516103ac9190615167565b6104e46108f7366004614b44565b612135565b61090f61090a366004614a81565b612176565b6040805192151583526020830191909152016103ac565b6104e46109343660046151c7565b612229565b6103a2610947366004614bd2565b612268565b6104e461095a366004615276565b6122f7565b6104e461096d366004615293565b612315565b6104e4610980366004614b44565b612354565b610998610993366004614a81565b61238d565b6040516103ac9190615313565b6104e46109b3366004614b44565b612426565b6104e46109c63660046150f7565b6124eb565b6104e46109d93660046150f7565b61252b565b6104e46109ec366004614fb2565b61256b565b6105146109ff36600461535f565b612593565b6104e4610a12366004614b1a565b6125b2565b6104e4610a253660046153bc565b6125f3565b610a3261266e565b6040516103ac91906153fd565b6104e4610a4d366004615463565b6126ea565b6105bd610a60366004614fb2565b612794565b6103a2610a73366004614a81565b6127d3565b61088e6127de565b6104e4610a8e366004614a81565b6127ed565b6104e4610aa1366004614dc7565b61282c565b6104e4610ab4366004614a81565b61286f565b610ac16128d5565b604080519384526020840192909252908201526060016103ac565b6105bd610aea366004614a81565b6128ff565b6104e4610afd3660046154ca565b61292f565b6040516304fef1db60e01b81526000907310671bc2f07593e208b1762b101fdfea8689067e906304fef1db90610b3c908590600401615110565b602060405180830381865af4158015610b59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7d91906154f6565b92915050565b610bcb60405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60405162d7f52760e31b81527310671bc2f07593e208b1762b101fdfea8689067e906306bfa93890610c01908590600401615110565b61010060405180830381865af4158015610c1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7d919061550f565b6000806000610c568989898989896129d5565b9250925092505b96509650969350505050565b6000610c758383612fc4565b9392505050565b600080610c87613020565b5492915050565b600080610c9961304e565b33600090815260058201602052604090205490915060ff16610cfb5760405162461bcd60e51b81526020600482015260166024820152753737ba103bb4ba34323930bb903332b29030b236b4b760511b60448201526064015b60405180910390fd5b604051630b27346f60e11b81527310671bc2f07593e208b1762b101fdfea8689067e9063164e68de90610d32908690600401615110565b602060405180830381865af4158015610d4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c7591906154f6565b610d7b61490e565b610b7d8261307c565b610d8c6130fc565b6000610d9661304e565b6001600160a01b039390931660009081526005909301602052506040909120805460ff1916911515919091179055565b6000610c75838361315b565b6000610ddc6131b5565b610de461320e565b6000610dee613254565b90506000610dfa613282565b3360009081526014840160205260409020549091508015610ea8576005820154610e2d906001600160a01b0316826132b0565b6005830154909150610e49906001600160a01b031682336132d5565b33600081815260148501602052604080822091909155600584015490517f80814eb170ced28dd8eb312180365462e4b9878baf473b6d4302dde21011a89f91610e9f916001600160a01b0390911690859061559e565b60405180910390a25b92505050610eb6600160c955565b90565b6000806000610ec6613254565b6001600160a01b039094166000908152600f850160209081526040808320546010909701909152902054939492505050565b610f006130fc565b73ab4eb71128d543c4beeabf5845d51e2e858abb9e63669c5bfe6040518060c00160405280896001600160a01b0316815260200188815260200187815260200186815260200185151581526020018415158152506040518263ffffffff1660e01b8152600401610f709190615313565b60006040518083038186803b158015610f8857600080fd5b505af4158015610f9c573d6000803e3d6000fd5b50505050505050505050565b60006060610fb461338d565b915091509091565b6000610b7d8261340b565b600080600080600080610fdc8a8a8a8a611314565b9050600080610ff58a846000015185604001518c6134de565b9150915060006110118d8d8d8d88600001518960600151613589565b905060006110208c8c87613609565b8051602090910151949f939e50919c50909a509198509650505050505050565b600061104d8484846136ad565b949350505050565b600061106360975460ff1690565b905090565b6110706130fc565b81518351148015611082575080518351145b61109e5760405162461bcd60e51b8152600401610cf2906155b7565b60005b8351811015611126578281815181106110bc576110bc6155e4565b602002602001015160001415806110ed57508181815181106110e0576110e06155e4565b6020026020010151600014155b1561111457611114848281518110611107576111076155e4565b6020026020010151612074565b8061111e81615610565b9150506110a1565b506040516320bac27760e11b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063417584ee9061116290869086908690600401615664565b60006040518083038186803b15801561117a57600080fd5b505af415801561118e573d6000803e3d6000fd5b50505050505050565b61119f6130fc565b604051631114e8dd60e21b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e90634453a374906111d890859085906004016156cf565b60006040518083038186803b1580156111f057600080fd5b505af4158015611204573d6000803e3d6000fd5b505050505050565b6112146131b5565b61121c61320e565b61122583611598565b156112425760405162461bcd60e51b8152600401610cf2906156ea565b6040805160a0810182526001600160a01b03878116825286811660208301908152868216838501908152606084018781528615156080860190815295516306109c4f60e31b8152945184166004860152915183166024850152519091166044830152516064820152905115156084820152738d8fb6320ab54f5ba37c532c91978f3b61f1487790633084e2789060a4015b60006040518083038186803b1580156112eb57600080fd5b505af41580156112ff573d6000803e3d6000fd5b5050505061130d600160c955565b5050505050565b6113636040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b604051634a3f088d60e01b81526001600160a01b0380871660048301528086166024830152841660448201528215156064820152738d8fb6320ab54f5ba37c532c91978f3b61f1487790634a3f088d9060840161012060405180830381865af41580156113d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f89190615719565b95945050505050565b60008061141086868686611314565b905060008160200151116114605760405162461bcd60e51b81526020600482015260176024820152762b30bab63a1d1034b73b30b634b2103837b9b4ba34b7b760491b6044820152606401610cf2565b602081015181516114749061271090615794565b61147e91906157c1565b9695505050505050565b60008061149361304e565b6001600160a01b03909316600090815260029093016020525050604090205460ff1690565b6000806114c3613020565b6001600160a01b03909316600090815260049093016020525050604090205490565b60008060008060006114f561304e565b5460ff62010000820481169763010000008304821697508183169650610100909204169350915050565b60008061152a613020565b6002015492915050565b60008061153f6137cf565b6001600160a01b0384166000908152600482016020526040902054909150610c7590600a6158c7565b60008061157361304e565b6001600160a01b03909316600090815260059093016020525050604090205460ff1690565b6000806115a361304e565b6001600160a01b03909316600090815260049093016020525050604090205460ff1690565b6115d06131b5565b6115d861320e565b604051635f7bc11960e01b81527377cdd46002cd7ae83a72622127acb2f7ae53a7da90635f7bc1199061160f908490600401615110565b60006040518083038186803b15801561162757600080fd5b505af415801561163b573d6000803e3d6000fd5b50505050611649600160c955565b50565b6116546131b5565b61165c61320e565b8151835114801561166e575080518351145b61168a5760405162461bcd60e51b8152600401610cf2906155b7565b6000805b84518110156118035760006116f0338784815181106116af576116af6155e4565b60200260200101518785815181106116c9576116c96155e4565b60200260200101518786815181106116e3576116e36155e4565b60200260200101516137fd565b905060006116fc613020565b600083815260088201602052604081208054929350919003611720575050506117f1565b60006117c5888681518110611737576117376155e4565b6020026020010151888781518110611751576117516155e4565b602002602001015184604051806101200160405290816000820154815260200160018201548152602001600282015481526020016003820154815260200160048201548152602001600582015481526020016006820154815260200160078201548152602001600882015481525050613609565b90506117d13382613853565b6060810151600583015560208101516117ea90876158d3565b9550505050505b806117fb81615610565b91505061168e565b5060405181815233907f953615e1b3502c33b28f9e7bab58b4a721d74651f2a323a199ba094aae3b2f4c9060200160405180910390a250611844600160c955565b505050565b600080611854613254565b6001600160a01b03909316600090815260149093016020525050604090205490565b61187e61493a565b6000611888613020565b905061189261493a565b6001600160a01b039093166000818152600b8301602090815260408083205487528383526009850182528083205487830152838352600a850182528083205487820152838352600e85018252808320546060880152838352600c85018252808320546080880152928252600d90930190925290205460a08301525090565b600061191a6131b5565b61192261320e565b60405163234a0b6b60e01b81527377cdd46002cd7ae83a72622127acb2f7ae53a7da9063234a0b6b9061195b90869086906004016158e6565b602060405180830381865af4158015611978573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061199c91906154f6565b9050610b7d600160c955565b6119b06130fc565b6119ba60006138af565b565b33806119c66127de565b6001600160a01b031614611a2e5760405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b6064820152608401610cf2565b611649816138af565b600054610100900460ff1615808015611a575750600054600160ff909116105b80611a785750611a66306138c8565b158015611a78575060005460ff166001145b611adb5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610cf2565b6000805460ff191660011790558015611afe576000805461ff0019166101001790555b611b066138d7565b611b0e613906565b611b16613935565b8015611649576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150565b6000611b686131b5565b611b7061320e565b611b7983611598565b15611b965760405162461bcd60e51b8152600401610cf2906156ea565b604051639f2a556160e01b81527377cdd46002cd7ae83a72622127acb2f7ae53a7da90639f2a55619061195b90869086906004016158e6565b6000610b7d82613964565b6000611be46131b5565b611bec61320e565b6040805160e0810182526001600160a01b038a8116825289811660208301908152898216838501908152606084018a8152608085018a815289151560a0870190815289861660c0880190815297516335dde3a160e11b815296518616600488015293518516602487015291518416604486015251606485015251608484015251151560a4830152915190911660c4820152738d8fb6320ab54f5ba37c532c91978f3b61f1487790636bbbc7429060e401602060405180830381865af4158015611cb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cdd91906154f6565b9050611ce9600160c955565b979650505050505050565b611cfc6130fc565b6040516342c2fa6960e11b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e90638585f4d2906111d8908590859060040161559e565b600080611d40613020565b6001600160a01b03909316600090815260079093016020525050604090205490565b600080611d6d613254565b6001600160a01b03909316600090815260099093016020525050604090205490565b600082518451148015611da3575081518451145b611dbf5760405162461bcd60e51b8152600401610cf2906155b7565b6000805b8551811015611ea4576000611e1888888481518110611de457611de46155e4565b6020026020010151888581518110611dfe57611dfe6155e4565b60200260200101518886815181106116e3576116e36155e4565b90506000611e24613020565b600083815260088201602052604081208054929350919003611e4857505050611e92565b6000611e79898681518110611e5f57611e5f6155e4565b6020026020010151898781518110611751576117516155e4565b9050806020015186611e8b91906158d3565b9550505050505b80611e9c81615610565b915050611dc3565b5095945050505050565b600080611eb9613020565b6001015492915050565b611ecb6130fc565b6040516322d3cecb60e21b81526004810182905273ab4eb71128d543c4beeabf5845d51e2e858abb9e90638b4f3b2c906024015b60006040518083038186803b158015611f1757600080fd5b505af415801561130d573d6000803e3d6000fd5b6033546001600160a01b031690565b6000611f446131b5565b611f4c61320e565b611f5584611598565b158015611f685750611f6683611598565b155b611f845760405162461bcd60e51b8152600401610cf2906156ea565b6000611f8f856139c2565b60405160016276c04560e11b031981526001600160a01b0380881660048301528087166024830152604482018390528516606482015290915073ff6ec5be53b532e4b5aac66dab16fd54575e63239063ff127f7690608401602060405180830381865af4158015612004573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061202891906154f6565b915050610c75600160c955565b6000806000612042613254565b6001600160a01b039094166000908152600d85016020908152604080832054600e909701909152902054939492505050565b61207c6131b5565b61208461320e565b6040516325e99d8360e21b815273a71a515de4acf04fb66e5923e5c0bd3146794c05906397a6760c9061160f908490600401615110565b6120c3614970565b60006120cd613282565b90506120d7614970565b81546001600160a01b0390811682526001830154811660208301526002830154811660408301526003830154811660608301526004830154811660808301526005830154811660a083015260069092015490911660c0820152919050565b61213d6130fc565b6040516352f4877760e11b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063a5e90eee906111d890859085906004016156cf565b6000806000612183613020565b6001600160a01b0385166000908152600c820160205260408120549192508190036121b5575060009485945092505050565b60006121c086613a7a565b6001600160a01b0387166000908152600d850160205260408120549192508282116121f4576121ef8284615900565b6121fe565b6121fe8383615900565b905060008261220d8387615794565b61221791906157c1565b93909211989297509195505050505050565b6122316130fc565b604051631395c1d560e31b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e90639cae0ea890611eff908490600401615167565b600080612273613254565b9050600061228387878787611314565b905060006122948260000151613abb565b905060006122ae8989898987600001518860600151613589565b905060006122bd888886613609565b600686015481519192506000916122d485876158d3565b6122de91906158d3565b6122e891906158d3565b9b9a5050505050505050505050565b6122ff6130fc565b801561230d57611649613b10565b611649613b64565b61231d6130fc565b60405163903fe8f160e01b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063903fe8f190611eff9084906004016153fd565b6040516330f1919d60e21b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063c3c64674906111d890859085906004016156cf565b6123956149ac565b600061239f6137cf565b90506123a96149ac565b6001600160a01b0390931680845260008181526004830160209081526040808320548288015283835260058501825280832054818801528383526006850182528083205460608801528383526007850182528083205460ff9081161515608089015293835260089094019052919091205416151560a08301525090565b60006124306120bb565b905061243a611f2b565b6001600160a01b0316336001600160a01b0316148061246e575080602001516001600160a01b0316336001600160a01b0316145b6124b25760405162461bcd60e51b81526020600482015260156024820152742b30bab63a1d1034b73b30b634b21031b0b63632b960591b6044820152606401610cf2565b60405163cb8d3b1f60e01b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063cb8d3b1f9061116290869086906004016156cf565b6124f36130fc565b60405163697d31af60e11b81526004810182905273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063d2fa635e90602401611eff565b6125336130fc565b60405163d3127e6360e01b81526004810182905273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063d3127e6390602401611eff565b6125736131b5565b61257b61320e565b6125858282613b9d565b61258f600160c955565b5050565b6000806125a38787878787613d0d565b915091505b9550959350505050565b6125ba6130fc565b60405163d66b000d60e01b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063d66b000d906111d8908590859060040161559e565b6125fb6130fc565b604051636c8f698560e11b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063d91ed30a90612638908790879087908790600401614eb0565b60006040518083038186803b15801561265057600080fd5b505af4158015612664573d6000803e3d6000fd5b5050505050505050565b6126766149ef565b6000612680613254565b905061268a6149ef565b8154815260018201546020820152600282015460408201526003820154606082015260048201546080820152600582015460a0820152600682015460c0820152600782015460ff16151560e0820152600890910154610100820152919050565b6126f26131b5565b6126fa61320e565b6040805160a0810182526001600160a01b03878116825286811660208301908152868216838501908152861515606085019081528684166080860190815295516317fd4e1d60e01b81529451841660048601529151831660248501525182166044840152511515606483015291519091166084820152738d8fb6320ab54f5ba37c532c91978f3b61f14877906317fd4e1d9060a4016112d3565b60008061279f61304e565b6001600160a01b03948516600090815260019190910160209081526040808320959096168252939093525050205460ff1690565b6000610b7d82613a7a565b6065546001600160a01b031690565b6127f56130fc565b60405163e67f59a760e01b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063e67f59a790611eff908490600401615110565b6128346130fc565b604051637789633f60e11b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063ef12c67e9061116290869086908690600401615664565b6128776130fc565b606580546001600160a01b0319166001600160a01b03831690811790915561289d611f2b565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6000806000806128e3613254565b600a810154600b820154600c9092015490969195509350915050565b60008061290a61304e565b6001600160a01b03909316600090815260039093016020525050604090205460ff1690565b6129376130fc565b60008060006129446128d5565b92509250925082600014158061295957508115155b8061296357508015155b1561298f576000612972613282565b600581015490915061298d906001600160a01b03168061256b565b505b60405163fe75443360e01b815260048101879052602481018690526044810185905273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063fe75443390606401610f70565b6000806000612a2d604051806101200160405280600081526020016000151581526020016000151581526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6000612a37613254565b90506000612a43613020565b90506000612a4f613282565b600784015490915060ff16612a71578a60008096509650965050505050610c5d565b6001600160a01b038d16600090815260048301602052604090205460608501819052612a9e908d906158d3565b608085015288612add5783606001518c1115612ac7576000608085015260608401519b50612add565b8b8460600151612ad79190615900565b60808501525b612ae68d61340b565b60a0850152878015612af6575088155b15612b0d57612b078d8d60016136ad565b60a08501525b8360a00151600003612b2c578a60008096509650965050505050610c5d565b612b39846060015161404e565b612b468560a0015161404e565b612b509190615913565b60c0850152612b608d8d8b6136ad565b60e0850152878015612b70575088155b15612b8457612b7e8d61340b565b60e08501525b8360e00151600003612ba3578a60008096509650965050505050610c5d565b612bb0846080015161404e565b612bbd8560e0015161404e565b612bc79190615913565b6101008501528815612d8657612be08460c001516140bc565b612bee8561010001516140bc565b1015612ca05760008460e00151612c088660c001516140bc565b612c12908d615794565b612c1c91906157c1565b90508b8111612c3457612c2f818d615900565b612c37565b60005b855260c08501516000138015612c535750600085610100015113155b80612c73575060008560c00151138015612c735750600085610100015112155b15612c8b576001602086015260006040860152612c9a565b60006020860152600160408601525b50612f49565b60006002612cb28661010001516140bc565b612cbf8760c001516140bc565b612cc991906158d3565b612cd391906157c1565b90508460e00151811115612ce8575060e08401515b60e0850151600090612cfa838e615794565b612d0491906157c1565b905060008660c00151128015612d205750600086610100015113155b80612d40575060008660c00151138015612d405750600086610100015112155b15612d6457612d4f818e6158d3565b86526000602087018190526040870152612d7f565b612d6e818e6158d3565b865260006020870152600160408701525b5050612f49565b60008460c00151128015612da05750600084610100015113155b80612dc0575060008460c00151138015612dc05750600084610100015112155b15612ec657612dd28460c001516140bc565b612de08561010001516140bc565b1015612e3f5760008460e00151612dfa8660c001516140bc565b612e04908d615794565b612e0e91906157c1565b90508b8111612e2657612e21818d615900565b612e29565b60005b8552506000602085018190526040850152612f49565b60006002612e518661010001516140bc565b612e5e8760c001516140bc565b612e6891906158d3565b612e7291906157c1565b90508460e00151811115612e87575060e08401515b60e0850151600090612e99838e615794565b612ea391906157c1565b9050612eaf818e6158d3565b865250506000602085018190526040850152612f49565b60006002612ed88661010001516140bc565b612ee58760c001516140bc565b612eef91906158d3565b612ef991906157c1565b90508460e00151811115612f0e575060e08401515b60e0850151600090612f20838e615794565b612f2a91906157c1565b9050612f36818e6158d3565b8652505060006020850152600160408501525b60058101546001600160a01b038e8116911614612f6857600060208501525b88158015612f85575060058101546001600160a01b038e81169116145b15612fa257600883015484518590612f9e9083906158d3565b9052505b505081516020830151604090930151909c929b50995090975050505050505050565b600081600003612fd657506000610b7d565b6000612fe06137cf565b90506000612fed85613964565b6001600160a01b038616600090815260048401602052604090205490915061301681600a6158c7565b6114748387615794565b600080610b7d60017f0a6857889562f1d40df97cc84c0aafc7cd2f70e5ce2a65223fd89163f728a8d0615900565b600080610b7d60017fec44c653f648cabed39c0c4eda0c4e30c82628a3e671ccda53ec8daf9f3b79c7615900565b61308461490e565b600061308e6137cf565b604080516080810182526001600160a01b0390951660008181526004840160209081528382205488528282526003850181528382205460ff9081161515828a015283835260078601825284832054161515888501529181526006909301905290205460608401525090919050565b33613105611f2b565b6001600160a01b0316146119ba5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610cf2565b600080613166613282565b9050600061317385613a7a565b905060008161318e68327cb2734119d3b7a9601e1b87615794565b61319891906157c1565b600484015490915061147e9082906001600160a01b0316886140cf565b600260c954036132075760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610cf2565b600260c955565b60975460ff16156119ba5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610cf2565b600080610b7d60017fd5ec53cc5875be7de9fa162234144bc5c71b4ebad2607fd6e72e68038a0e4eca615900565b600080610b7d60017ff2b245feeb42e92aedfd7870377f08ee43c2d83f1add271009599c7c6f1e7f04615900565b6000816000036132c257506000610b7d565b610c7583836132d086613a7a565b614188565b60006132df613020565b90506132f56001600160a01b03851683856141d8565b6040516370a0823160e01b81526001600160a01b038516906370a0823190613321903090600401615110565b602060405180830381865afa15801561333e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061336291906154f6565b6001600160a01b039094166000908152600390910160205260409020929092555050565b600160c955565b60006060600061339b6137cf565b9050806000015481600201808054806020026020016040519081016040528092919081815260200182805480156133fb57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116133dd575b5050505050905092509250509091565b600080613416613282565b905060006134226137cf565b905060008260040160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561347b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061349f91906154f6565b9050806000036134b457506000949350505050565b6001600160a01b038516600090815260058301602052604090205460018301546114748383615794565b6000806135086000851160405180604001604052806002815260200161033360f41b81525061422e565b60008361351d5761351887613a7a565b613526565b61352687613964565b905060008186116135405761353b8683615900565b61354a565b61354a8287615900565b9050600086613559838a615794565b61356391906157c1565b905060008615613576575086831161357b565b508287115b999098509650505050505050565b60008260000361359b5750600061147e565b60006135a5613254565b6001600160a01b0388166000908152600d82016020526040812054919250906135cf908590615900565b9050806000036135e45760009250505061147e565b620f42406135f28287615794565b6135fc91906157c1565b9998505050505050505050565b613611614a3d565b600061361b613254565b9050613625614a3d565b6001600160a01b038616600081815260128401602090815260408083208915158085529083528184205486830190815294845260138701835281842090845290915290205460608301525160808501518551613684929190600161424d565b8152606081015160a0850151855161369f929190600061424d565b602082015295945050505050565b6000806136b8613282565b905060006136c46137cf565b905060008260040160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561371d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061374191906154f6565b9050841561375a5761375386826158d3565b905061377b565b8086111561376e5760009350505050610c75565b6137788682615900565b90505b8060000361378f5760009350505050610c75565b6001600160a01b038716600090815260058301602052604090205460018301546137b98383615794565b6137c391906157c1565b98975050505050505050565b600080610b7d60017f715b167b7990ed658f7189ec1ff00e4d03196ef2816956a21e1391dfc17fa859615900565b604080516001600160601b0319606096871b811660208084019190915295871b811660348301529390951b9092166048850152151560f81b605c8401528051808403603d018152605d9093019052815191012090565b600061385d613254565b602083015190915015611844578160200151816014016000856001600160a01b03166001600160a01b0316815260200190815260200160002060008282546138a591906158d3565b9091555050505050565b606580546001600160a01b03191690556116498161426f565b6001600160a01b03163b151590565b600054610100900460ff166138fe5760405162461bcd60e51b8152600401610cf29061593a565b6119ba6142c1565b600054610100900460ff1661392d5760405162461bcd60e51b8152600401610cf29061593a565b6119ba6142f1565b600054610100900460ff1661395c5760405162461bcd60e51b8152600401610cf29061593a565b6119ba614324565b60008061396f613282565b60038101546040516303b6b4bb60e51b81529192506001600160a01b0316906376d69760906139a59086906000906004016156cf565b602060405180830381865afa158015610d4f573d6000803e3d6000fd5b6000806139cd613020565b6001600160a01b03841660008181526003830160205260408082205490516370a0823160e01b8152939450929091906370a0823190613a10903090600401615110565b602060405180830381865afa158015613a2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a5191906154f6565b6001600160a01b0386166000908152600385016020526040902081905590506113f88282615900565b600080613a85613282565b60038101546040516303b6b4bb60e51b81529192506001600160a01b0316906376d69760906139a59086906001906004016156cf565b600080613ac6613254565b905082600003613ad95750600092915050565b60006127108260050154612710613af09190615900565b613afa9086615794565b613b0491906157c1565b905061104d8185615900565b613b1861320e565b6097805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258613b4d3390565b604051613b5a9190615110565b60405180910390a1565b613b6c61434b565b6097805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33613b4d565b6000613ba7613254565b6001600160a01b0384166000908152600e8201602052604081205491925003613c0657600a810154613bd981426157c1565b613be39190615794565b6001600160a01b039093166000908152600e909101602052604090209190915550565b600a8101546001600160a01b0384166000908152600e830160205260409020544291613c31916158d3565b1115613c3c57505050565b6000613c4784614394565b6001600160a01b0385166000908152600d84016020526040902054909150613c709082906158d3565b6001600160a01b0385166000908152600d84016020526040902055600a820154613c9a81426157c1565b613ca49190615794565b6001600160a01b0385166000908152600e84016020908152604080832093909355600d850190528190205490517fb3adf30b9391ba27493eb9bebf178ba6471ef14184e403152c47ab1d6a5844b091613cff9187919061559e565b60405180910390a150505050565b6000806000613d1a613020565b90506000613d26613254565b90506000613d368a8a8a8a6137fd565b600081815260088501602052604081208054600282015493945090928291613d61918d91908d6134de565b915091506000613d748460000154613abb565b90506000613d8e8f8f8f8f89600001548a60030154613589565b90506000613e038e8e88604051806101200160405290816000820154815260200160018201548152602001600282015481526020016003820154815260200160048201548152602001600582015481526020016006820154815260200160078201548152602001600882015481525050613609565b8051909150600090613e1584866158d3565b613e1f91906158d3565b905085158015613e325750848760010154105b15613e9c578c15613e855760405162461bcd60e51b815260206004820152601f60248201527f5661756c743a206c6f737365732065786365656420636f6c6c61746572616c006044820152606401610cf2565b600160009b509b50505050505050505050506125a8565b600187015486613eb957858860010154613eb69190615900565b90505b81811015613f25578d15613f0f5760405162461bcd60e51b815260206004820152601d60248201527f5661756c743a20666565732065786365656420636f6c6c61746572616c0000006044820152606401610cf2565b60019c509a506125a89950505050505050505050565b60068a0154613f3490836158d3565b811015613fb1578d15613f9b5760405162461bcd60e51b815260206004820152602960248201527f5661756c743a206c69717569646174696f6e20666565732065786365656420636044820152681bdb1b185d195c985b60ba1b6064820152608401610cf2565b5060019b5099506125a898505050505050505050565b8754613fc09061271090615794565b8b54613fcc9083615794565b1015614034578d1561401e5760405162461bcd60e51b815260206004820152601b60248201527a15985d5b1d0e881b585e13195d995c9859d948195e18d959591959602a1b6044820152606401610cf2565b5060029b5099506125a898505050505050505050565b5060009b5099505050505050505050509550959350505050565b60006001600160ff1b038211156140b85760405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e2061604482015267371034b73a191a9b60c11b6064820152608401610cf2565b5090565b6000808212156140b85781600003610b7d565b6000806140da613282565b905060006140e66137cf565b60048301549091506000906001600160a01b03878116911614614123576001600160a01b0386166000908152600483016020526040902054614126565b60125b60048401549091506000906001600160a01b03878116911614614163576001600160a01b0386166000908152600484016020526040902054614166565b60125b905061417382600a6158c7565b61417e82600a6158c7565b6137b9908a615794565b60008260000361419a57506000610c75565b60006141a46137cf565b6001600160a01b0386166000908152600482016020526040902054909150836141ce82600a6158c7565b6114749087615794565b6118448363a9059cbb60e01b84846040516024016141f792919061559e565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261459f565b80826118445760405162461bcd60e51b8152600401610cf291906159a9565b60008061425a8587615900565b905061147e848266038d7ea4c6800086614671565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff166142e85760405162461bcd60e51b8152600401610cf29061593a565b6119ba336138af565b600054610100900460ff166143185760405162461bcd60e51b8152600401610cf29061593a565b6097805460ff19169055565b600054610100900460ff166133865760405162461bcd60e51b8152600401610cf29061593a565b60975460ff166119ba5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610cf2565b60008061439f613254565b905060006143ab613020565b905060006143b76137cf565b905060006143c3613282565b600a8501546001600160a01b0388166000908152600e8701602052604090205491925042916143f291906158d3565b11156144045750600095945050505050565b600a8401546001600160a01b0387166000908152600e860160205260408120549091906144319042615900565b61443b91906157c1565b6001600160a01b03881660009081526007850160205260408120549192509060ff1661446b5785600b0154614471565b85600c01545b600284015460405163019c8a3b60e11b8152600160048201529192506000916001600160a01b0390911690630339147690602401602060405180830381865afa1580156144c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144e691906154f6565b905060006144f38a613964565b6001600160a01b038b16600090815260048801602052604081205491925061451c82600a6158c7565b6001600160a01b038d16600090815260068b0160205260409020546145419085615794565b61454b91906157c1565b9050801580614558575083155b1561456f575060009b9a5050505050505050505050565b838661457b8388615794565b6145859190615794565b61458f91906157c1565b9c9b505050505050505050505050565b60006145f4826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166146989092919063ffffffff16565b805190915015611844578080602001905181019061461291906159dc565b6118445760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610cf2565b6000811561468d5761468685858560016146a7565b905061104d565b6113f88585856146f8565b606061104d84846000856147a7565b6000806146b58686866146f8565b905060018360028111156146cb576146cb6159f9565b1480156146e85750600084806146e3576146e36157ab565b868809115b156113f85761147e6001826158d3565b600080806000198587098587029250828110838203039150508060000361473257838281614728576147286157ab565b0492505050610c75565b80841161473e57600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6060824710156148085760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610cf2565b600080866001600160a01b031685876040516148249190615a0f565b60006040518083038185875af1925050503d8060008114614861576040519150601f19603f3d011682016040523d82523d6000602084013e614866565b606091505b5091509150611ce987838387606083156148df5782516000036148d85761488c856138c8565b6148d85760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610cf2565b508161104d565b61104d83838151156148f45781518083602001fd5b8060405162461bcd60e51b8152600401610cf291906159a9565b604051806080016040528060008152602001600015158152602001600015158152602001600081525090565b6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b6040518060c0016040528060006001600160a01b031681526020016000815260200160008152602001600081526020016000151581526020016000151581525090565b60405180610120016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600015158152602001600081525090565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b80356001600160a01b0381168114614a7c57600080fd5b919050565b600060208284031215614a9357600080fd5b610c7582614a65565b801515811461164957600080fd5b8035614a7c81614a9c565b60008060008060008060c08789031215614ace57600080fd5b614ad787614a65565b95506020870135945060408701359350606087013592506080870135614afc81614a9c565b915060a0870135614b0c81614a9c565b809150509295509295509295565b60008060408385031215614b2d57600080fd5b614b3683614a65565b946020939093013593505050565b60008060408385031215614b5757600080fd5b614b6083614a65565b91506020830135614b7081614a9c565b809150509250929050565b6000604082018483526020604081850152818551808452606086019150828701935060005b81811015614bc55784516001600160a01b031683529383019391830191600101614ba0565b5090979650505050505050565b60008060008060808587031215614be857600080fd5b614bf185614a65565b9350614bff60208601614a65565b9250614c0d60408601614a65565b91506060850135614c1d81614a9c565b939692955090935050565b600080600060608486031215614c3d57600080fd5b614c4684614a65565b9250602084013591506040840135614c5d81614a9c565b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b60405161012081016001600160401b0381118282101715614ca157614ca1614c68565b60405290565b604051601f8201601f191681016001600160401b0381118282101715614ccf57614ccf614c68565b604052919050565b60006001600160401b03821115614cf057614cf0614c68565b5060051b60200190565b600082601f830112614d0b57600080fd5b81356020614d20614d1b83614cd7565b614ca7565b82815260059290921b84018101918181019086841115614d3f57600080fd5b8286015b84811015614d6157614d5481614a65565b8352918301918301614d43565b509695505050505050565b600082601f830112614d7d57600080fd5b81356020614d8d614d1b83614cd7565b82815260059290921b84018101918181019086841115614dac57600080fd5b8286015b84811015614d615780358352918301918301614db0565b600080600060608486031215614ddc57600080fd5b83356001600160401b0380821115614df357600080fd5b614dff87838801614cfa565b94506020860135915080821115614e1557600080fd5b614e2187838801614d6c565b93506040860135915080821115614e3757600080fd5b50614e4486828701614d6c565b9150509250925092565b600080600080600060a08688031215614e6657600080fd5b614e6f86614a65565b9450614e7d60208701614a65565b9350614e8b60408701614a65565b9250606086013591506080860135614ea281614a9c565b809150509295509295909350565b93151584529115156020840152151560408301521515606082015260800190565b600082601f830112614ee257600080fd5b81356020614ef2614d1b83614cd7565b82815260059290921b84018101918181019086841115614f1157600080fd5b8286015b84811015614d61578035614f2881614a9c565b8352918301918301614f15565b600080600060608486031215614f4a57600080fd5b83356001600160401b0380821115614f6157600080fd5b614f6d87838801614cfa565b94506020860135915080821115614f8357600080fd5b614f8f87838801614cfa565b93506040860135915080821115614fa557600080fd5b50614e4486828701614ed1565b60008060408385031215614fc557600080fd5b614fce83614a65565b9150614fdc60208401614a65565b90509250929050565b600080600080600080600060e0888a03121561500057600080fd5b61500988614a65565b965061501760208901614a65565b955061502560408901614a65565b9450606088013593506080880135925060a088013561504381614a9c565b915061505160c08901614a65565b905092959891949750929550565b6000806000806080858703121561507557600080fd5b61507e85614a65565b935060208501356001600160401b038082111561509a57600080fd5b6150a688838901614cfa565b945060408701359150808211156150bc57600080fd5b6150c888838901614cfa565b935060608701359150808211156150de57600080fd5b506150eb87828801614ed1565b91505092959194509250565b60006020828403121561510957600080fd5b5035919050565b6001600160a01b0391909116815260200190565b60008060006060848603121561513957600080fd5b61514284614a65565b925061515060208501614a65565b915061515e60408501614a65565b90509250925092565b60e08101610b7d828480516001600160a01b03908116835260208083015182169084015260408083015182169084015260608083015182169084015260808083015182169084015260a08281015182169084015260c09182015116910152565b600060e082840312156151d957600080fd5b60405160e081016001600160401b03811182821017156151fb576151fb614c68565b60405261520783614a65565b815261521560208401614a65565b602082015261522660408401614a65565b604082015261523760608401614a65565b606082015261524860808401614a65565b608082015261525960a08401614a65565b60a082015261526a60c08401614a65565b60c08201529392505050565b60006020828403121561528857600080fd5b8135610c7581614a9c565b600061012082840312156152a657600080fd5b6152ae614c7e565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c08201526152fa60e08401614aaa565b60e0820152610100928301359281019290925250919050565b81516001600160a01b0316815260208083015190820152604080830151908201526060808301519082015260808083015115159082015260a08083015115159082015260c08101610b7d565b600080600080600060a0868803121561537757600080fd5b61538086614a65565b945061538e60208701614a65565b935061539c60408701614a65565b925060608601356153ac81614a9c565b91506080860135614ea281614a9c565b600080600080608085870312156153d257600080fd5b84356153dd81614a9c565b935060208501356153ed81614a9c565b92506040850135614c0d81614a9c565b6101208101610b7d8284805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e0810151151560e08301526101008082015181840152505050565b600080600080600060a0868803121561547b57600080fd5b61548486614a65565b945061549260208701614a65565b93506154a060408701614a65565b925060608601356154b081614a9c565b91506154be60808701614a65565b90509295509295909350565b6000806000606084860312156154df57600080fd5b505081359360208301359350604090920135919050565b60006020828403121561550857600080fd5b5051919050565b600061010080838503121561552357600080fd5b604051908101906001600160401b038211818310171561554557615545614c68565b81604052835181526020840151602082015260408401516040820152606084015160608201526080840151608082015260a084015160a082015260c084015160c082015260e084015160e0820152809250505092915050565b6001600160a01b03929092168252602082015260400190565b6020808252601390820152720d2dcc6dedce6d2e6e8cadce840d8cadccee8d606b1b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201615622576156226155fa565b5060010190565b600081518084526020808501945080840160005b838110156156595781518752958201959082019060010161563d565b509495945050505050565b606080825284519082018190526000906020906080840190828801845b828110156156a65781516001600160a01b031684529284019290840190600101615681565b505050838103828501526156ba8187615629565b915050828103604084015261147e8185615629565b6001600160a01b039290921682521515602082015260400190565b60208082526015908201527415985d5b1d0e88185b1c9958591e481c185d5cd959605a1b604082015260600190565b6000610120828403121561572c57600080fd5b615734614c7e565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152508091505092915050565b8082028115828204841417610b7d57610b7d6155fa565b634e487b7160e01b600052601260045260246000fd5b6000826157de57634e487b7160e01b600052601260045260246000fd5b500490565b600181815b8085111561581e578160001904821115615804576158046155fa565b8085161561581157918102915b93841c93908002906157e8565b509250929050565b60008261583557506001610b7d565b8161584257506000610b7d565b816001811461585857600281146158625761587e565b6001915050610b7d565b60ff841115615873576158736155fa565b50506001821b610b7d565b5060208310610133831016604e8410600b84101617156158a1575081810a610b7d565b6158ab83836157e3565b80600019048211156158bf576158bf6155fa565b029392505050565b6000610c758383615826565b80820180821115610b7d57610b7d6155fa565b6001600160a01b0392831681529116602082015260400190565b81810381811115610b7d57610b7d6155fa565b8181036000831280158383131683831282161715615933576159336155fa565b5092915050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b60005b838110156159a0578181015183820152602001615988565b50506000910152565b60208152600082518060208401526159c8816040850160208701615985565b601f01601f19169190910160400192915050565b6000602082840312156159ee57600080fd5b8151610c7581614a9c565b634e487b7160e01b600052602160045260246000fd5b60008251615a21818460208701615985565b919091019291505056fea26469706673582212207d0ff70aea193d611058345ada9a87535f03c734d7175dd3fc0237ad2bae319d64736f6c63430008130033

Deployed Bytecode

0x608060405234801561001057600080fd5b506004361061038a5760003560e01c806381a612d6116101dc57806381a612d6146107f957806382a084901461080c5780638585f4d21461081f57806387ed714414610832578063891c5a731461084557806389201c761461085857806389c98c061461086b5780638b4f3b2c146108735780638da5cb5b14610886578063933162121461089b5780639498a8ca146108ae57806397a6760c146108c1578063a39fac12146108d4578063a5e90eee146108e9578063b364accb146108fc578063b375d49214610926578063bd2521ac14610939578063bedb86fb1461094c578063c14bfd361461095f578063c3c6467414610972578063cb67e3b114610985578063cb8d3b1f146109a5578063d2fa635e146109b8578063d3127e63146109cb578063d3405289146109de578063d54d5a9f146109f1578063d66b000d14610a04578063d91ed30a14610a17578063db8d55f114610a2a578063de2ea94814610a3f578063dfa2ce1c14610a52578063e124e6d214610a65578063e30c397814610a78578063e67f59a714610a80578063ef12c67e14610a93578063f2fde38b14610aa6578063f36b242514610ab9578063f3ae241514610adc578063fe75443314610aef57600080fd5b806304fef1db1461038f57806306bfa938146103b557806308a99df9146104295780630a48d5a9146104595780630ea8b3bf1461046c578063164e68de146104745780631f69565f1461048757806321f9dace146104d15780632c668ec1146104e65780632ef94257146104f9578063322b3d9814610501578063336e94d3146105295780633459a82f1461053c5780633a05dcc1146105525780633cb6b783146105655780633e3002b6146105a25780633e3ca9d3146105b5578063417584ee146105cd5780634453a374146105e057806348d91abf146105f35780634a3f088d1461060657806351723e8214610686578063529a356f146106995780635534aa5f146106ac57806356ac1d5c146106bf57806356b6d0d5146106d757806358e86625146106df5780635c401439146106f25780635c975abb146107055780635e76ad54146107105780635f7bc1191461072357806360986cef1461073657806362883ccd14610749578063655a62571461075c578063711e6190146107bb578063715018a6146107ce57806379ba5097146107d65780638129fc1c146107de578063817bb857146107e6575b600080fd5b6103a261039d366004614a81565b610b02565b6040519081526020015b60405180910390f35b6103c86103c3366004614a81565b610b83565b6040516103ac9190600061010082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015292915050565b61043c610437366004614ab5565b610c43565b6040805193845291151560208401521515908201526060016103ac565b6103a2610467366004614b1a565b610c69565b6103a2610c7c565b6103a2610482366004614a81565b610c8e565b61049a610495366004614a81565b610d73565b6040516103ac9190815181526020808301511515908201526040808301511515908201526060918201519181019190915260800190565b6104e46104df366004614b44565b610d84565b005b6103a26104f4366004614b1a565b610dc6565b6103a2610dd2565b61051461050f366004614a81565b610eb9565b604080519283526020830191909152016103ac565b6104e4610537366004614ab5565b610ef8565b610544610fa8565b6040516103ac929190614b7b565b6103a2610560366004614a81565b610fbc565b610578610573366004614bd2565b610fc7565b6040805195151586526020860194909452928401919091526060830152608082015260a0016103ac565b6103a26105b0366004614c28565b611040565b6105bd611055565b60405190151581526020016103ac565b6104e46105db366004614dc7565b611068565b6104e46105ee366004614b44565b611197565b6104e4610601366004614e4e565b61120c565b610619610614366004614bd2565b611314565b6040516103ac9190600061012082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015260c083015160c083015260e083015160e083015261010080840151818401525092915050565b6103a2610694366004614bd2565b611401565b6105bd6106a7366004614a81565b611488565b6103a26106ba366004614a81565b6114b8565b6106c76114e5565b6040516103ac9493929190614eb0565b6103a261151f565b6103a26106ed366004614a81565b611534565b6105bd610700366004614a81565b611568565b60975460ff166105bd565b6105bd61071e366004614a81565b611598565b6104e4610731366004614a81565b6115c8565b6104e4610744366004614f35565b61164c565b6103a2610757366004614a81565b611849565b61076f61076a366004614a81565b611876565b6040516103ac9190600060c082019050825182526020830151602083015260408301516040830152606083015160608301526080830151608083015260a083015160a083015292915050565b6103a26107c9366004614fb2565b611910565b6104e46119a8565b6104e46119bc565b6104e4611a37565b6103a26107f4366004614fb2565b611b5e565b6103a2610807366004614a81565b611bcf565b6103a261081a366004614fe5565b611bda565b6104e461082d366004614b1a565b611cf4565b6103a2610840366004614a81565b611d35565b6103a2610853366004614a81565b611d62565b6103a261086636600461505f565b611d8f565b6103a2611eae565b6104e46108813660046150f7565b611ec3565b61088e611f2b565b6040516103ac9190615110565b6103a26108a9366004615124565b611f3a565b6105146108bc366004614a81565b612035565b6104e46108cf366004614a81565b612074565b6108dc6120bb565b6040516103ac9190615167565b6104e46108f7366004614b44565b612135565b61090f61090a366004614a81565b612176565b6040805192151583526020830191909152016103ac565b6104e46109343660046151c7565b612229565b6103a2610947366004614bd2565b612268565b6104e461095a366004615276565b6122f7565b6104e461096d366004615293565b612315565b6104e4610980366004614b44565b612354565b610998610993366004614a81565b61238d565b6040516103ac9190615313565b6104e46109b3366004614b44565b612426565b6104e46109c63660046150f7565b6124eb565b6104e46109d93660046150f7565b61252b565b6104e46109ec366004614fb2565b61256b565b6105146109ff36600461535f565b612593565b6104e4610a12366004614b1a565b6125b2565b6104e4610a253660046153bc565b6125f3565b610a3261266e565b6040516103ac91906153fd565b6104e4610a4d366004615463565b6126ea565b6105bd610a60366004614fb2565b612794565b6103a2610a73366004614a81565b6127d3565b61088e6127de565b6104e4610a8e366004614a81565b6127ed565b6104e4610aa1366004614dc7565b61282c565b6104e4610ab4366004614a81565b61286f565b610ac16128d5565b604080519384526020840192909252908201526060016103ac565b6105bd610aea366004614a81565b6128ff565b6104e4610afd3660046154ca565b61292f565b6040516304fef1db60e01b81526000907310671bc2f07593e208b1762b101fdfea8689067e906304fef1db90610b3c908590600401615110565b602060405180830381865af4158015610b59573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7d91906154f6565b92915050565b610bcb60405180610100016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b60405162d7f52760e31b81527310671bc2f07593e208b1762b101fdfea8689067e906306bfa93890610c01908590600401615110565b61010060405180830381865af4158015610c1f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610b7d919061550f565b6000806000610c568989898989896129d5565b9250925092505b96509650969350505050565b6000610c758383612fc4565b9392505050565b600080610c87613020565b5492915050565b600080610c9961304e565b33600090815260058201602052604090205490915060ff16610cfb5760405162461bcd60e51b81526020600482015260166024820152753737ba103bb4ba34323930bb903332b29030b236b4b760511b60448201526064015b60405180910390fd5b604051630b27346f60e11b81527310671bc2f07593e208b1762b101fdfea8689067e9063164e68de90610d32908690600401615110565b602060405180830381865af4158015610d4f573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610c7591906154f6565b610d7b61490e565b610b7d8261307c565b610d8c6130fc565b6000610d9661304e565b6001600160a01b039390931660009081526005909301602052506040909120805460ff1916911515919091179055565b6000610c75838361315b565b6000610ddc6131b5565b610de461320e565b6000610dee613254565b90506000610dfa613282565b3360009081526014840160205260409020549091508015610ea8576005820154610e2d906001600160a01b0316826132b0565b6005830154909150610e49906001600160a01b031682336132d5565b33600081815260148501602052604080822091909155600584015490517f80814eb170ced28dd8eb312180365462e4b9878baf473b6d4302dde21011a89f91610e9f916001600160a01b0390911690859061559e565b60405180910390a25b92505050610eb6600160c955565b90565b6000806000610ec6613254565b6001600160a01b039094166000908152600f850160209081526040808320546010909701909152902054939492505050565b610f006130fc565b73ab4eb71128d543c4beeabf5845d51e2e858abb9e63669c5bfe6040518060c00160405280896001600160a01b0316815260200188815260200187815260200186815260200185151581526020018415158152506040518263ffffffff1660e01b8152600401610f709190615313565b60006040518083038186803b158015610f8857600080fd5b505af4158015610f9c573d6000803e3d6000fd5b50505050505050505050565b60006060610fb461338d565b915091509091565b6000610b7d8261340b565b600080600080600080610fdc8a8a8a8a611314565b9050600080610ff58a846000015185604001518c6134de565b9150915060006110118d8d8d8d88600001518960600151613589565b905060006110208c8c87613609565b8051602090910151949f939e50919c50909a509198509650505050505050565b600061104d8484846136ad565b949350505050565b600061106360975460ff1690565b905090565b6110706130fc565b81518351148015611082575080518351145b61109e5760405162461bcd60e51b8152600401610cf2906155b7565b60005b8351811015611126578281815181106110bc576110bc6155e4565b602002602001015160001415806110ed57508181815181106110e0576110e06155e4565b6020026020010151600014155b1561111457611114848281518110611107576111076155e4565b6020026020010151612074565b8061111e81615610565b9150506110a1565b506040516320bac27760e11b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063417584ee9061116290869086908690600401615664565b60006040518083038186803b15801561117a57600080fd5b505af415801561118e573d6000803e3d6000fd5b50505050505050565b61119f6130fc565b604051631114e8dd60e21b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e90634453a374906111d890859085906004016156cf565b60006040518083038186803b1580156111f057600080fd5b505af4158015611204573d6000803e3d6000fd5b505050505050565b6112146131b5565b61121c61320e565b61122583611598565b156112425760405162461bcd60e51b8152600401610cf2906156ea565b6040805160a0810182526001600160a01b03878116825286811660208301908152868216838501908152606084018781528615156080860190815295516306109c4f60e31b8152945184166004860152915183166024850152519091166044830152516064820152905115156084820152738d8fb6320ab54f5ba37c532c91978f3b61f1487790633084e2789060a4015b60006040518083038186803b1580156112eb57600080fd5b505af41580156112ff573d6000803e3d6000fd5b5050505061130d600160c955565b5050505050565b6113636040518061012001604052806000815260200160008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b604051634a3f088d60e01b81526001600160a01b0380871660048301528086166024830152841660448201528215156064820152738d8fb6320ab54f5ba37c532c91978f3b61f1487790634a3f088d9060840161012060405180830381865af41580156113d4573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906113f89190615719565b95945050505050565b60008061141086868686611314565b905060008160200151116114605760405162461bcd60e51b81526020600482015260176024820152762b30bab63a1d1034b73b30b634b2103837b9b4ba34b7b760491b6044820152606401610cf2565b602081015181516114749061271090615794565b61147e91906157c1565b9695505050505050565b60008061149361304e565b6001600160a01b03909316600090815260029093016020525050604090205460ff1690565b6000806114c3613020565b6001600160a01b03909316600090815260049093016020525050604090205490565b60008060008060006114f561304e565b5460ff62010000820481169763010000008304821697508183169650610100909204169350915050565b60008061152a613020565b6002015492915050565b60008061153f6137cf565b6001600160a01b0384166000908152600482016020526040902054909150610c7590600a6158c7565b60008061157361304e565b6001600160a01b03909316600090815260059093016020525050604090205460ff1690565b6000806115a361304e565b6001600160a01b03909316600090815260049093016020525050604090205460ff1690565b6115d06131b5565b6115d861320e565b604051635f7bc11960e01b81527377cdd46002cd7ae83a72622127acb2f7ae53a7da90635f7bc1199061160f908490600401615110565b60006040518083038186803b15801561162757600080fd5b505af415801561163b573d6000803e3d6000fd5b50505050611649600160c955565b50565b6116546131b5565b61165c61320e565b8151835114801561166e575080518351145b61168a5760405162461bcd60e51b8152600401610cf2906155b7565b6000805b84518110156118035760006116f0338784815181106116af576116af6155e4565b60200260200101518785815181106116c9576116c96155e4565b60200260200101518786815181106116e3576116e36155e4565b60200260200101516137fd565b905060006116fc613020565b600083815260088201602052604081208054929350919003611720575050506117f1565b60006117c5888681518110611737576117376155e4565b6020026020010151888781518110611751576117516155e4565b602002602001015184604051806101200160405290816000820154815260200160018201548152602001600282015481526020016003820154815260200160048201548152602001600582015481526020016006820154815260200160078201548152602001600882015481525050613609565b90506117d13382613853565b6060810151600583015560208101516117ea90876158d3565b9550505050505b806117fb81615610565b91505061168e565b5060405181815233907f953615e1b3502c33b28f9e7bab58b4a721d74651f2a323a199ba094aae3b2f4c9060200160405180910390a250611844600160c955565b505050565b600080611854613254565b6001600160a01b03909316600090815260149093016020525050604090205490565b61187e61493a565b6000611888613020565b905061189261493a565b6001600160a01b039093166000818152600b8301602090815260408083205487528383526009850182528083205487830152838352600a850182528083205487820152838352600e85018252808320546060880152838352600c85018252808320546080880152928252600d90930190925290205460a08301525090565b600061191a6131b5565b61192261320e565b60405163234a0b6b60e01b81527377cdd46002cd7ae83a72622127acb2f7ae53a7da9063234a0b6b9061195b90869086906004016158e6565b602060405180830381865af4158015611978573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061199c91906154f6565b9050610b7d600160c955565b6119b06130fc565b6119ba60006138af565b565b33806119c66127de565b6001600160a01b031614611a2e5760405162461bcd60e51b815260206004820152602960248201527f4f776e61626c6532537465703a2063616c6c6572206973206e6f7420746865206044820152683732bb9037bbb732b960b91b6064820152608401610cf2565b611649816138af565b600054610100900460ff1615808015611a575750600054600160ff909116105b80611a785750611a66306138c8565b158015611a78575060005460ff166001145b611adb5760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b6064820152608401610cf2565b6000805460ff191660011790558015611afe576000805461ff0019166101001790555b611b066138d7565b611b0e613906565b611b16613935565b8015611649576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a150565b6000611b686131b5565b611b7061320e565b611b7983611598565b15611b965760405162461bcd60e51b8152600401610cf2906156ea565b604051639f2a556160e01b81527377cdd46002cd7ae83a72622127acb2f7ae53a7da90639f2a55619061195b90869086906004016158e6565b6000610b7d82613964565b6000611be46131b5565b611bec61320e565b6040805160e0810182526001600160a01b038a8116825289811660208301908152898216838501908152606084018a8152608085018a815289151560a0870190815289861660c0880190815297516335dde3a160e11b815296518616600488015293518516602487015291518416604486015251606485015251608484015251151560a4830152915190911660c4820152738d8fb6320ab54f5ba37c532c91978f3b61f1487790636bbbc7429060e401602060405180830381865af4158015611cb9573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190611cdd91906154f6565b9050611ce9600160c955565b979650505050505050565b611cfc6130fc565b6040516342c2fa6960e11b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e90638585f4d2906111d8908590859060040161559e565b600080611d40613020565b6001600160a01b03909316600090815260079093016020525050604090205490565b600080611d6d613254565b6001600160a01b03909316600090815260099093016020525050604090205490565b600082518451148015611da3575081518451145b611dbf5760405162461bcd60e51b8152600401610cf2906155b7565b6000805b8551811015611ea4576000611e1888888481518110611de457611de46155e4565b6020026020010151888581518110611dfe57611dfe6155e4565b60200260200101518886815181106116e3576116e36155e4565b90506000611e24613020565b600083815260088201602052604081208054929350919003611e4857505050611e92565b6000611e79898681518110611e5f57611e5f6155e4565b6020026020010151898781518110611751576117516155e4565b9050806020015186611e8b91906158d3565b9550505050505b80611e9c81615610565b915050611dc3565b5095945050505050565b600080611eb9613020565b6001015492915050565b611ecb6130fc565b6040516322d3cecb60e21b81526004810182905273ab4eb71128d543c4beeabf5845d51e2e858abb9e90638b4f3b2c906024015b60006040518083038186803b158015611f1757600080fd5b505af415801561130d573d6000803e3d6000fd5b6033546001600160a01b031690565b6000611f446131b5565b611f4c61320e565b611f5584611598565b158015611f685750611f6683611598565b155b611f845760405162461bcd60e51b8152600401610cf2906156ea565b6000611f8f856139c2565b60405160016276c04560e11b031981526001600160a01b0380881660048301528087166024830152604482018390528516606482015290915073ff6ec5be53b532e4b5aac66dab16fd54575e63239063ff127f7690608401602060405180830381865af4158015612004573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061202891906154f6565b915050610c75600160c955565b6000806000612042613254565b6001600160a01b039094166000908152600d85016020908152604080832054600e909701909152902054939492505050565b61207c6131b5565b61208461320e565b6040516325e99d8360e21b815273a71a515de4acf04fb66e5923e5c0bd3146794c05906397a6760c9061160f908490600401615110565b6120c3614970565b60006120cd613282565b90506120d7614970565b81546001600160a01b0390811682526001830154811660208301526002830154811660408301526003830154811660608301526004830154811660808301526005830154811660a083015260069092015490911660c0820152919050565b61213d6130fc565b6040516352f4877760e11b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063a5e90eee906111d890859085906004016156cf565b6000806000612183613020565b6001600160a01b0385166000908152600c820160205260408120549192508190036121b5575060009485945092505050565b60006121c086613a7a565b6001600160a01b0387166000908152600d850160205260408120549192508282116121f4576121ef8284615900565b6121fe565b6121fe8383615900565b905060008261220d8387615794565b61221791906157c1565b93909211989297509195505050505050565b6122316130fc565b604051631395c1d560e31b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e90639cae0ea890611eff908490600401615167565b600080612273613254565b9050600061228387878787611314565b905060006122948260000151613abb565b905060006122ae8989898987600001518860600151613589565b905060006122bd888886613609565b600686015481519192506000916122d485876158d3565b6122de91906158d3565b6122e891906158d3565b9b9a5050505050505050505050565b6122ff6130fc565b801561230d57611649613b10565b611649613b64565b61231d6130fc565b60405163903fe8f160e01b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063903fe8f190611eff9084906004016153fd565b6040516330f1919d60e21b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063c3c64674906111d890859085906004016156cf565b6123956149ac565b600061239f6137cf565b90506123a96149ac565b6001600160a01b0390931680845260008181526004830160209081526040808320548288015283835260058501825280832054818801528383526006850182528083205460608801528383526007850182528083205460ff9081161515608089015293835260089094019052919091205416151560a08301525090565b60006124306120bb565b905061243a611f2b565b6001600160a01b0316336001600160a01b0316148061246e575080602001516001600160a01b0316336001600160a01b0316145b6124b25760405162461bcd60e51b81526020600482015260156024820152742b30bab63a1d1034b73b30b634b21031b0b63632b960591b6044820152606401610cf2565b60405163cb8d3b1f60e01b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063cb8d3b1f9061116290869086906004016156cf565b6124f36130fc565b60405163697d31af60e11b81526004810182905273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063d2fa635e90602401611eff565b6125336130fc565b60405163d3127e6360e01b81526004810182905273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063d3127e6390602401611eff565b6125736131b5565b61257b61320e565b6125858282613b9d565b61258f600160c955565b5050565b6000806125a38787878787613d0d565b915091505b9550959350505050565b6125ba6130fc565b60405163d66b000d60e01b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063d66b000d906111d8908590859060040161559e565b6125fb6130fc565b604051636c8f698560e11b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063d91ed30a90612638908790879087908790600401614eb0565b60006040518083038186803b15801561265057600080fd5b505af4158015612664573d6000803e3d6000fd5b5050505050505050565b6126766149ef565b6000612680613254565b905061268a6149ef565b8154815260018201546020820152600282015460408201526003820154606082015260048201546080820152600582015460a0820152600682015460c0820152600782015460ff16151560e0820152600890910154610100820152919050565b6126f26131b5565b6126fa61320e565b6040805160a0810182526001600160a01b03878116825286811660208301908152868216838501908152861515606085019081528684166080860190815295516317fd4e1d60e01b81529451841660048601529151831660248501525182166044840152511515606483015291519091166084820152738d8fb6320ab54f5ba37c532c91978f3b61f14877906317fd4e1d9060a4016112d3565b60008061279f61304e565b6001600160a01b03948516600090815260019190910160209081526040808320959096168252939093525050205460ff1690565b6000610b7d82613a7a565b6065546001600160a01b031690565b6127f56130fc565b60405163e67f59a760e01b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063e67f59a790611eff908490600401615110565b6128346130fc565b604051637789633f60e11b815273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063ef12c67e9061116290869086908690600401615664565b6128776130fc565b606580546001600160a01b0319166001600160a01b03831690811790915561289d611f2b565b6001600160a01b03167f38d16b8cac22d99fc7c124b9cd0de2d3fa1faef420bfe791d8c362d765e2270060405160405180910390a350565b6000806000806128e3613254565b600a810154600b820154600c9092015490969195509350915050565b60008061290a61304e565b6001600160a01b03909316600090815260039093016020525050604090205460ff1690565b6129376130fc565b60008060006129446128d5565b92509250925082600014158061295957508115155b8061296357508015155b1561298f576000612972613282565b600581015490915061298d906001600160a01b03168061256b565b505b60405163fe75443360e01b815260048101879052602481018690526044810185905273ab4eb71128d543c4beeabf5845d51e2e858abb9e9063fe75443390606401610f70565b6000806000612a2d604051806101200160405280600081526020016000151581526020016000151581526020016000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6000612a37613254565b90506000612a43613020565b90506000612a4f613282565b600784015490915060ff16612a71578a60008096509650965050505050610c5d565b6001600160a01b038d16600090815260048301602052604090205460608501819052612a9e908d906158d3565b608085015288612add5783606001518c1115612ac7576000608085015260608401519b50612add565b8b8460600151612ad79190615900565b60808501525b612ae68d61340b565b60a0850152878015612af6575088155b15612b0d57612b078d8d60016136ad565b60a08501525b8360a00151600003612b2c578a60008096509650965050505050610c5d565b612b39846060015161404e565b612b468560a0015161404e565b612b509190615913565b60c0850152612b608d8d8b6136ad565b60e0850152878015612b70575088155b15612b8457612b7e8d61340b565b60e08501525b8360e00151600003612ba3578a60008096509650965050505050610c5d565b612bb0846080015161404e565b612bbd8560e0015161404e565b612bc79190615913565b6101008501528815612d8657612be08460c001516140bc565b612bee8561010001516140bc565b1015612ca05760008460e00151612c088660c001516140bc565b612c12908d615794565b612c1c91906157c1565b90508b8111612c3457612c2f818d615900565b612c37565b60005b855260c08501516000138015612c535750600085610100015113155b80612c73575060008560c00151138015612c735750600085610100015112155b15612c8b576001602086015260006040860152612c9a565b60006020860152600160408601525b50612f49565b60006002612cb28661010001516140bc565b612cbf8760c001516140bc565b612cc991906158d3565b612cd391906157c1565b90508460e00151811115612ce8575060e08401515b60e0850151600090612cfa838e615794565b612d0491906157c1565b905060008660c00151128015612d205750600086610100015113155b80612d40575060008660c00151138015612d405750600086610100015112155b15612d6457612d4f818e6158d3565b86526000602087018190526040870152612d7f565b612d6e818e6158d3565b865260006020870152600160408701525b5050612f49565b60008460c00151128015612da05750600084610100015113155b80612dc0575060008460c00151138015612dc05750600084610100015112155b15612ec657612dd28460c001516140bc565b612de08561010001516140bc565b1015612e3f5760008460e00151612dfa8660c001516140bc565b612e04908d615794565b612e0e91906157c1565b90508b8111612e2657612e21818d615900565b612e29565b60005b8552506000602085018190526040850152612f49565b60006002612e518661010001516140bc565b612e5e8760c001516140bc565b612e6891906158d3565b612e7291906157c1565b90508460e00151811115612e87575060e08401515b60e0850151600090612e99838e615794565b612ea391906157c1565b9050612eaf818e6158d3565b865250506000602085018190526040850152612f49565b60006002612ed88661010001516140bc565b612ee58760c001516140bc565b612eef91906158d3565b612ef991906157c1565b90508460e00151811115612f0e575060e08401515b60e0850151600090612f20838e615794565b612f2a91906157c1565b9050612f36818e6158d3565b8652505060006020850152600160408501525b60058101546001600160a01b038e8116911614612f6857600060208501525b88158015612f85575060058101546001600160a01b038e81169116145b15612fa257600883015484518590612f9e9083906158d3565b9052505b505081516020830151604090930151909c929b50995090975050505050505050565b600081600003612fd657506000610b7d565b6000612fe06137cf565b90506000612fed85613964565b6001600160a01b038616600090815260048401602052604090205490915061301681600a6158c7565b6114748387615794565b600080610b7d60017f0a6857889562f1d40df97cc84c0aafc7cd2f70e5ce2a65223fd89163f728a8d0615900565b600080610b7d60017fec44c653f648cabed39c0c4eda0c4e30c82628a3e671ccda53ec8daf9f3b79c7615900565b61308461490e565b600061308e6137cf565b604080516080810182526001600160a01b0390951660008181526004840160209081528382205488528282526003850181528382205460ff9081161515828a015283835260078601825284832054161515888501529181526006909301905290205460608401525090919050565b33613105611f2b565b6001600160a01b0316146119ba5760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610cf2565b600080613166613282565b9050600061317385613a7a565b905060008161318e68327cb2734119d3b7a9601e1b87615794565b61319891906157c1565b600484015490915061147e9082906001600160a01b0316886140cf565b600260c954036132075760405162461bcd60e51b815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c006044820152606401610cf2565b600260c955565b60975460ff16156119ba5760405162461bcd60e51b815260206004820152601060248201526f14185d5cd8589b194e881c185d5cd95960821b6044820152606401610cf2565b600080610b7d60017fd5ec53cc5875be7de9fa162234144bc5c71b4ebad2607fd6e72e68038a0e4eca615900565b600080610b7d60017ff2b245feeb42e92aedfd7870377f08ee43c2d83f1add271009599c7c6f1e7f04615900565b6000816000036132c257506000610b7d565b610c7583836132d086613a7a565b614188565b60006132df613020565b90506132f56001600160a01b03851683856141d8565b6040516370a0823160e01b81526001600160a01b038516906370a0823190613321903090600401615110565b602060405180830381865afa15801561333e573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061336291906154f6565b6001600160a01b039094166000908152600390910160205260409020929092555050565b600160c955565b60006060600061339b6137cf565b9050806000015481600201808054806020026020016040519081016040528092919081815260200182805480156133fb57602002820191906000526020600020905b81546001600160a01b031681526001909101906020018083116133dd575b5050505050905092509250509091565b600080613416613282565b905060006134226137cf565b905060008260040160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561347b573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061349f91906154f6565b9050806000036134b457506000949350505050565b6001600160a01b038516600090815260058301602052604090205460018301546114748383615794565b6000806135086000851160405180604001604052806002815260200161033360f41b81525061422e565b60008361351d5761351887613a7a565b613526565b61352687613964565b905060008186116135405761353b8683615900565b61354a565b61354a8287615900565b9050600086613559838a615794565b61356391906157c1565b905060008615613576575086831161357b565b508287115b999098509650505050505050565b60008260000361359b5750600061147e565b60006135a5613254565b6001600160a01b0388166000908152600d82016020526040812054919250906135cf908590615900565b9050806000036135e45760009250505061147e565b620f42406135f28287615794565b6135fc91906157c1565b9998505050505050505050565b613611614a3d565b600061361b613254565b9050613625614a3d565b6001600160a01b038616600081815260128401602090815260408083208915158085529083528184205486830190815294845260138701835281842090845290915290205460608301525160808501518551613684929190600161424d565b8152606081015160a0850151855161369f929190600061424d565b602082015295945050505050565b6000806136b8613282565b905060006136c46137cf565b905060008260040160009054906101000a90046001600160a01b03166001600160a01b03166318160ddd6040518163ffffffff1660e01b8152600401602060405180830381865afa15801561371d573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061374191906154f6565b9050841561375a5761375386826158d3565b905061377b565b8086111561376e5760009350505050610c75565b6137788682615900565b90505b8060000361378f5760009350505050610c75565b6001600160a01b038716600090815260058301602052604090205460018301546137b98383615794565b6137c391906157c1565b98975050505050505050565b600080610b7d60017f715b167b7990ed658f7189ec1ff00e4d03196ef2816956a21e1391dfc17fa859615900565b604080516001600160601b0319606096871b811660208084019190915295871b811660348301529390951b9092166048850152151560f81b605c8401528051808403603d018152605d9093019052815191012090565b600061385d613254565b602083015190915015611844578160200151816014016000856001600160a01b03166001600160a01b0316815260200190815260200160002060008282546138a591906158d3565b9091555050505050565b606580546001600160a01b03191690556116498161426f565b6001600160a01b03163b151590565b600054610100900460ff166138fe5760405162461bcd60e51b8152600401610cf29061593a565b6119ba6142c1565b600054610100900460ff1661392d5760405162461bcd60e51b8152600401610cf29061593a565b6119ba6142f1565b600054610100900460ff1661395c5760405162461bcd60e51b8152600401610cf29061593a565b6119ba614324565b60008061396f613282565b60038101546040516303b6b4bb60e51b81529192506001600160a01b0316906376d69760906139a59086906000906004016156cf565b602060405180830381865afa158015610d4f573d6000803e3d6000fd5b6000806139cd613020565b6001600160a01b03841660008181526003830160205260408082205490516370a0823160e01b8152939450929091906370a0823190613a10903090600401615110565b602060405180830381865afa158015613a2d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190613a5191906154f6565b6001600160a01b0386166000908152600385016020526040902081905590506113f88282615900565b600080613a85613282565b60038101546040516303b6b4bb60e51b81529192506001600160a01b0316906376d69760906139a59086906001906004016156cf565b600080613ac6613254565b905082600003613ad95750600092915050565b60006127108260050154612710613af09190615900565b613afa9086615794565b613b0491906157c1565b905061104d8185615900565b613b1861320e565b6097805460ff191660011790557f62e78cea01bee320cd4e420270b5ea74000d11b0c9f74754ebdbfc544b05a258613b4d3390565b604051613b5a9190615110565b60405180910390a1565b613b6c61434b565b6097805460ff191690557f5db9ee0a495bf2e6ff9c91a7834c1ba4fdd244a5e8aa4e537bd38aeae4b073aa33613b4d565b6000613ba7613254565b6001600160a01b0384166000908152600e8201602052604081205491925003613c0657600a810154613bd981426157c1565b613be39190615794565b6001600160a01b039093166000908152600e909101602052604090209190915550565b600a8101546001600160a01b0384166000908152600e830160205260409020544291613c31916158d3565b1115613c3c57505050565b6000613c4784614394565b6001600160a01b0385166000908152600d84016020526040902054909150613c709082906158d3565b6001600160a01b0385166000908152600d84016020526040902055600a820154613c9a81426157c1565b613ca49190615794565b6001600160a01b0385166000908152600e84016020908152604080832093909355600d850190528190205490517fb3adf30b9391ba27493eb9bebf178ba6471ef14184e403152c47ab1d6a5844b091613cff9187919061559e565b60405180910390a150505050565b6000806000613d1a613020565b90506000613d26613254565b90506000613d368a8a8a8a6137fd565b600081815260088501602052604081208054600282015493945090928291613d61918d91908d6134de565b915091506000613d748460000154613abb565b90506000613d8e8f8f8f8f89600001548a60030154613589565b90506000613e038e8e88604051806101200160405290816000820154815260200160018201548152602001600282015481526020016003820154815260200160048201548152602001600582015481526020016006820154815260200160078201548152602001600882015481525050613609565b8051909150600090613e1584866158d3565b613e1f91906158d3565b905085158015613e325750848760010154105b15613e9c578c15613e855760405162461bcd60e51b815260206004820152601f60248201527f5661756c743a206c6f737365732065786365656420636f6c6c61746572616c006044820152606401610cf2565b600160009b509b50505050505050505050506125a8565b600187015486613eb957858860010154613eb69190615900565b90505b81811015613f25578d15613f0f5760405162461bcd60e51b815260206004820152601d60248201527f5661756c743a20666565732065786365656420636f6c6c61746572616c0000006044820152606401610cf2565b60019c509a506125a89950505050505050505050565b60068a0154613f3490836158d3565b811015613fb1578d15613f9b5760405162461bcd60e51b815260206004820152602960248201527f5661756c743a206c69717569646174696f6e20666565732065786365656420636044820152681bdb1b185d195c985b60ba1b6064820152608401610cf2565b5060019b5099506125a898505050505050505050565b8754613fc09061271090615794565b8b54613fcc9083615794565b1015614034578d1561401e5760405162461bcd60e51b815260206004820152601b60248201527a15985d5b1d0e881b585e13195d995c9859d948195e18d959591959602a1b6044820152606401610cf2565b5060029b5099506125a898505050505050505050565b5060009b5099505050505050505050509550959350505050565b60006001600160ff1b038211156140b85760405162461bcd60e51b815260206004820152602860248201527f53616665436173743a2076616c756520646f65736e27742066697420696e2061604482015267371034b73a191a9b60c11b6064820152608401610cf2565b5090565b6000808212156140b85781600003610b7d565b6000806140da613282565b905060006140e66137cf565b60048301549091506000906001600160a01b03878116911614614123576001600160a01b0386166000908152600483016020526040902054614126565b60125b60048401549091506000906001600160a01b03878116911614614163576001600160a01b0386166000908152600484016020526040902054614166565b60125b905061417382600a6158c7565b61417e82600a6158c7565b6137b9908a615794565b60008260000361419a57506000610c75565b60006141a46137cf565b6001600160a01b0386166000908152600482016020526040902054909150836141ce82600a6158c7565b6114749087615794565b6118448363a9059cbb60e01b84846040516024016141f792919061559e565b60408051601f198184030181529190526020810180516001600160e01b03166001600160e01b03199093169290921790915261459f565b80826118445760405162461bcd60e51b8152600401610cf291906159a9565b60008061425a8587615900565b905061147e848266038d7ea4c6800086614671565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff166142e85760405162461bcd60e51b8152600401610cf29061593a565b6119ba336138af565b600054610100900460ff166143185760405162461bcd60e51b8152600401610cf29061593a565b6097805460ff19169055565b600054610100900460ff166133865760405162461bcd60e51b8152600401610cf29061593a565b60975460ff166119ba5760405162461bcd60e51b815260206004820152601460248201527314185d5cd8589b194e881b9bdd081c185d5cd95960621b6044820152606401610cf2565b60008061439f613254565b905060006143ab613020565b905060006143b76137cf565b905060006143c3613282565b600a8501546001600160a01b0388166000908152600e8701602052604090205491925042916143f291906158d3565b11156144045750600095945050505050565b600a8401546001600160a01b0387166000908152600e860160205260408120549091906144319042615900565b61443b91906157c1565b6001600160a01b03881660009081526007850160205260408120549192509060ff1661446b5785600b0154614471565b85600c01545b600284015460405163019c8a3b60e11b8152600160048201529192506000916001600160a01b0390911690630339147690602401602060405180830381865afa1580156144c2573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906144e691906154f6565b905060006144f38a613964565b6001600160a01b038b16600090815260048801602052604081205491925061451c82600a6158c7565b6001600160a01b038d16600090815260068b0160205260409020546145419085615794565b61454b91906157c1565b9050801580614558575083155b1561456f575060009b9a5050505050505050505050565b838661457b8388615794565b6145859190615794565b61458f91906157c1565b9c9b505050505050505050505050565b60006145f4826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b03166146989092919063ffffffff16565b805190915015611844578080602001905181019061461291906159dc565b6118445760405162461bcd60e51b815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e6044820152691bdd081cdd58d8d9595960b21b6064820152608401610cf2565b6000811561468d5761468685858560016146a7565b905061104d565b6113f88585856146f8565b606061104d84846000856147a7565b6000806146b58686866146f8565b905060018360028111156146cb576146cb6159f9565b1480156146e85750600084806146e3576146e36157ab565b868809115b156113f85761147e6001826158d3565b600080806000198587098587029250828110838203039150508060000361473257838281614728576147286157ab565b0492505050610c75565b80841161473e57600080fd5b60008486880960026001871981018816978890046003810283188082028403028082028403028082028403028082028403028082028403029081029092039091026000889003889004909101858311909403939093029303949094049190911702949350505050565b6060824710156148085760405162461bcd60e51b815260206004820152602660248201527f416464726573733a20696e73756666696369656e742062616c616e636520666f6044820152651c8818d85b1b60d21b6064820152608401610cf2565b600080866001600160a01b031685876040516148249190615a0f565b60006040518083038185875af1925050503d8060008114614861576040519150601f19603f3d011682016040523d82523d6000602084013e614866565b606091505b5091509150611ce987838387606083156148df5782516000036148d85761488c856138c8565b6148d85760405162461bcd60e51b815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401610cf2565b508161104d565b61104d83838151156148f45781518083602001fd5b8060405162461bcd60e51b8152600401610cf291906159a9565b604051806080016040528060008152602001600015158152602001600015158152602001600081525090565b6040518060c001604052806000815260200160008152602001600081526020016000815260200160008152602001600081525090565b6040805160e081018252600080825260208201819052918101829052606081018290526080810182905260a0810182905260c081019190915290565b6040518060c0016040528060006001600160a01b031681526020016000815260200160008152602001600081526020016000151581526020016000151581525090565b60405180610120016040528060008152602001600081526020016000815260200160008152602001600081526020016000815260200160008152602001600015158152602001600081525090565b6040518060800160405280600081526020016000815260200160008152602001600081525090565b80356001600160a01b0381168114614a7c57600080fd5b919050565b600060208284031215614a9357600080fd5b610c7582614a65565b801515811461164957600080fd5b8035614a7c81614a9c565b60008060008060008060c08789031215614ace57600080fd5b614ad787614a65565b95506020870135945060408701359350606087013592506080870135614afc81614a9c565b915060a0870135614b0c81614a9c565b809150509295509295509295565b60008060408385031215614b2d57600080fd5b614b3683614a65565b946020939093013593505050565b60008060408385031215614b5757600080fd5b614b6083614a65565b91506020830135614b7081614a9c565b809150509250929050565b6000604082018483526020604081850152818551808452606086019150828701935060005b81811015614bc55784516001600160a01b031683529383019391830191600101614ba0565b5090979650505050505050565b60008060008060808587031215614be857600080fd5b614bf185614a65565b9350614bff60208601614a65565b9250614c0d60408601614a65565b91506060850135614c1d81614a9c565b939692955090935050565b600080600060608486031215614c3d57600080fd5b614c4684614a65565b9250602084013591506040840135614c5d81614a9c565b809150509250925092565b634e487b7160e01b600052604160045260246000fd5b60405161012081016001600160401b0381118282101715614ca157614ca1614c68565b60405290565b604051601f8201601f191681016001600160401b0381118282101715614ccf57614ccf614c68565b604052919050565b60006001600160401b03821115614cf057614cf0614c68565b5060051b60200190565b600082601f830112614d0b57600080fd5b81356020614d20614d1b83614cd7565b614ca7565b82815260059290921b84018101918181019086841115614d3f57600080fd5b8286015b84811015614d6157614d5481614a65565b8352918301918301614d43565b509695505050505050565b600082601f830112614d7d57600080fd5b81356020614d8d614d1b83614cd7565b82815260059290921b84018101918181019086841115614dac57600080fd5b8286015b84811015614d615780358352918301918301614db0565b600080600060608486031215614ddc57600080fd5b83356001600160401b0380821115614df357600080fd5b614dff87838801614cfa565b94506020860135915080821115614e1557600080fd5b614e2187838801614d6c565b93506040860135915080821115614e3757600080fd5b50614e4486828701614d6c565b9150509250925092565b600080600080600060a08688031215614e6657600080fd5b614e6f86614a65565b9450614e7d60208701614a65565b9350614e8b60408701614a65565b9250606086013591506080860135614ea281614a9c565b809150509295509295909350565b93151584529115156020840152151560408301521515606082015260800190565b600082601f830112614ee257600080fd5b81356020614ef2614d1b83614cd7565b82815260059290921b84018101918181019086841115614f1157600080fd5b8286015b84811015614d61578035614f2881614a9c565b8352918301918301614f15565b600080600060608486031215614f4a57600080fd5b83356001600160401b0380821115614f6157600080fd5b614f6d87838801614cfa565b94506020860135915080821115614f8357600080fd5b614f8f87838801614cfa565b93506040860135915080821115614fa557600080fd5b50614e4486828701614ed1565b60008060408385031215614fc557600080fd5b614fce83614a65565b9150614fdc60208401614a65565b90509250929050565b600080600080600080600060e0888a03121561500057600080fd5b61500988614a65565b965061501760208901614a65565b955061502560408901614a65565b9450606088013593506080880135925060a088013561504381614a9c565b915061505160c08901614a65565b905092959891949750929550565b6000806000806080858703121561507557600080fd5b61507e85614a65565b935060208501356001600160401b038082111561509a57600080fd5b6150a688838901614cfa565b945060408701359150808211156150bc57600080fd5b6150c888838901614cfa565b935060608701359150808211156150de57600080fd5b506150eb87828801614ed1565b91505092959194509250565b60006020828403121561510957600080fd5b5035919050565b6001600160a01b0391909116815260200190565b60008060006060848603121561513957600080fd5b61514284614a65565b925061515060208501614a65565b915061515e60408501614a65565b90509250925092565b60e08101610b7d828480516001600160a01b03908116835260208083015182169084015260408083015182169084015260608083015182169084015260808083015182169084015260a08281015182169084015260c09182015116910152565b600060e082840312156151d957600080fd5b60405160e081016001600160401b03811182821017156151fb576151fb614c68565b60405261520783614a65565b815261521560208401614a65565b602082015261522660408401614a65565b604082015261523760608401614a65565b606082015261524860808401614a65565b608082015261525960a08401614a65565b60a082015261526a60c08401614a65565b60c08201529392505050565b60006020828403121561528857600080fd5b8135610c7581614a9c565b600061012082840312156152a657600080fd5b6152ae614c7e565b823581526020830135602082015260408301356040820152606083013560608201526080830135608082015260a083013560a082015260c083013560c08201526152fa60e08401614aaa565b60e0820152610100928301359281019290925250919050565b81516001600160a01b0316815260208083015190820152604080830151908201526060808301519082015260808083015115159082015260a08083015115159082015260c08101610b7d565b600080600080600060a0868803121561537757600080fd5b61538086614a65565b945061538e60208701614a65565b935061539c60408701614a65565b925060608601356153ac81614a9c565b91506080860135614ea281614a9c565b600080600080608085870312156153d257600080fd5b84356153dd81614a9c565b935060208501356153ed81614a9c565b92506040850135614c0d81614a9c565b6101208101610b7d8284805182526020810151602083015260408101516040830152606081015160608301526080810151608083015260a081015160a083015260c081015160c083015260e0810151151560e08301526101008082015181840152505050565b600080600080600060a0868803121561547b57600080fd5b61548486614a65565b945061549260208701614a65565b93506154a060408701614a65565b925060608601356154b081614a9c565b91506154be60808701614a65565b90509295509295909350565b6000806000606084860312156154df57600080fd5b505081359360208301359350604090920135919050565b60006020828403121561550857600080fd5b5051919050565b600061010080838503121561552357600080fd5b604051908101906001600160401b038211818310171561554557615545614c68565b81604052835181526020840151602082015260408401516040820152606084015160608201526080840151608082015260a084015160a082015260c084015160c082015260e084015160e0820152809250505092915050565b6001600160a01b03929092168252602082015260400190565b6020808252601390820152720d2dcc6dedce6d2e6e8cadce840d8cadccee8d606b1b604082015260600190565b634e487b7160e01b600052603260045260246000fd5b634e487b7160e01b600052601160045260246000fd5b600060018201615622576156226155fa565b5060010190565b600081518084526020808501945080840160005b838110156156595781518752958201959082019060010161563d565b509495945050505050565b606080825284519082018190526000906020906080840190828801845b828110156156a65781516001600160a01b031684529284019290840190600101615681565b505050838103828501526156ba8187615629565b915050828103604084015261147e8185615629565b6001600160a01b039290921682521515602082015260400190565b60208082526015908201527415985d5b1d0e88185b1c9958591e481c185d5cd959605a1b604082015260600190565b6000610120828403121561572c57600080fd5b615734614c7e565b825181526020830151602082015260408301516040820152606083015160608201526080830151608082015260a083015160a082015260c083015160c082015260e083015160e08201526101008084015181830152508091505092915050565b8082028115828204841417610b7d57610b7d6155fa565b634e487b7160e01b600052601260045260246000fd5b6000826157de57634e487b7160e01b600052601260045260246000fd5b500490565b600181815b8085111561581e578160001904821115615804576158046155fa565b8085161561581157918102915b93841c93908002906157e8565b509250929050565b60008261583557506001610b7d565b8161584257506000610b7d565b816001811461585857600281146158625761587e565b6001915050610b7d565b60ff841115615873576158736155fa565b50506001821b610b7d565b5060208310610133831016604e8410600b84101617156158a1575081810a610b7d565b6158ab83836157e3565b80600019048211156158bf576158bf6155fa565b029392505050565b6000610c758383615826565b80820180821115610b7d57610b7d6155fa565b6001600160a01b0392831681529116602082015260400190565b81810381811115610b7d57610b7d6155fa565b8181036000831280158383131683831282161715615933576159336155fa565b5092915050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b60005b838110156159a0578181015183820152602001615988565b50506000910152565b60208152600082518060208401526159c8816040850160208701615985565b601f01601f19169190910160400192915050565b6000602082840312156159ee57600080fd5b8151610c7581614a9c565b634e487b7160e01b600052602160045260246000fd5b60008251615a21818460208701615985565b919091019291505056fea26469706673582212207d0ff70aea193d611058345ada9a87535f03c734d7175dd3fc0237ad2bae319d64736f6c63430008130033

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
0x20ED655cD693AB3ec122234724b0DE9B365C78da
Loading...
Loading
Loading...
Loading

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