ETH Price: $3,271.45 (+0.68%)
Gas: 1 Gwei

Contract

0x9C05bDCc909c2b190837E8Fe71619cf389598C2c
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

Token Holdings

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Swap204053362024-07-28 13:26:3510 hrs ago1722173195IN
0x9C05bDCc...389598C2c
0 ETH0.00031311.50136359
Swap203983962024-07-27 14:10:4733 hrs ago1722089447IN
0x9C05bDCc...389598C2c
0 ETH0.000866514.15519479
Swap203907462024-07-26 12:32:352 days ago1721997155IN
0x9C05bDCc...389598C2c
0 ETH0.000535252.56665801
Swap203906942024-07-26 12:22:112 days ago1721996531IN
0x9C05bDCc...389598C2c
0 ETH0.000477842.29139101
Swap203906282024-07-26 12:08:592 days ago1721995739IN
0x9C05bDCc...389598C2c
0 ETH0.000600982.88167471
Swap203854062024-07-25 18:38:353 days ago1721932715IN
0x9C05bDCc...389598C2c
0 ETH0.001106535.30622818
Swap203853562024-07-25 18:28:353 days ago1721932115IN
0x9C05bDCc...389598C2c
0 ETH0.001374966.59302931
Swap203853482024-07-25 18:26:593 days ago1721932019IN
0x9C05bDCc...389598C2c
0 ETH0.001451816.96190775
Swap203836952024-07-25 12:54:593 days ago1721912099IN
0x9C05bDCc...389598C2c
0 ETH0.001533747.35470419
Swap203836322024-07-25 12:42:233 days ago1721911343IN
0x9C05bDCc...389598C2c
0 ETH0.001455926.98076336
Swap203783192024-07-24 18:53:594 days ago1721847239IN
0x9C05bDCc...389598C2c
0 ETH0.001406116.74307373
Swap203782822024-07-24 18:46:354 days ago1721846795IN
0x9C05bDCc...389598C2c
0 ETH0.001339596.42409245
Swap203763502024-07-24 12:17:354 days ago1721823455IN
0x9C05bDCc...389598C2c
0 ETH0.000837934.01789632
Swap203703632024-07-23 16:15:235 days ago1721751323IN
0x9C05bDCc...389598C2c
0 ETH0.001360396.52374423
Swap203703172024-07-23 16:06:115 days ago1721750771IN
0x9C05bDCc...389598C2c
0 ETH0.001259636.04020305
Swap203702812024-07-23 15:58:475 days ago1721750327IN
0x9C05bDCc...389598C2c
0 ETH0.001326456.36099956
Swap203701562024-07-23 15:33:475 days ago1721748827IN
0x9C05bDCc...389598C2c
0 ETH0.001330456.38022098
Swap203695412024-07-23 13:30:235 days ago1721741423IN
0x9C05bDCc...389598C2c
0 ETH0.001131525.4259772
Swap203551502024-07-21 13:15:597 days ago1721567759IN
0x9C05bDCc...389598C2c
0 ETH0.000600772.8808691
Swap203470392024-07-20 10:07:118 days ago1721470031IN
0x9C05bDCc...389598C2c
0 ETH0.000601882.88599671
Swap203404382024-07-19 12:00:119 days ago1721390411IN
0x9C05bDCc...389598C2c
0 ETH0.001193315.72284717
Swap203338692024-07-18 14:01:2310 days ago1721311283IN
0x9C05bDCc...389598C2c
0 ETH0.0028459113.64537299
Swap203333772024-07-18 12:22:3510 days ago1721305355IN
0x9C05bDCc...389598C2c
0 ETH0.0025221212.09280536
Swap203261912024-07-17 12:16:4711 days ago1721218607IN
0x9C05bDCc...389598C2c
0 ETH0.001738048.33486339
Swap203206272024-07-16 17:40:3512 days ago1721151635IN
0x9C05bDCc...389598C2c
0 ETH0.0030399214.57645824
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
199113342024-05-20 13:07:5969 days ago1716210479  Contract Creation0 ETH
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
SovereignPool

Compiler Version
v0.8.19+commit.7dd6d404

Optimization Enabled:
Yes with 20000 runs

Other Settings:
paris EvmVersion
File 1 of 20 : SovereignPool.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import { Math } from '../../lib/openzeppelin-contracts/contracts/utils/math/Math.sol';
import { IERC20 } from '../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol';
import { SafeERC20 } from '../../lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol';

import { Constants } from '../utils/Constants.sol';
import { ReentrancyGuard } from '../utils/ReentrancyGuard.sol';
import { ISwapFeeModule, SwapFeeModuleData } from '../swap-fee-modules/interfaces/ISwapFeeModule.sol';
import { ISovereignPool } from './interfaces/ISovereignPool.sol';
import { ISovereignPoolSwapCallback } from './interfaces/ISovereignPoolSwapCallback.sol';
import { IVerifierModule } from './interfaces/IVerifierModule.sol';
import { ALMLiquidityQuoteInput, ALMLiquidityQuote } from '../ALM/structs/SovereignALMStructs.sol';
import { ISovereignVaultMinimal } from './interfaces/ISovereignVaultMinimal.sol';
import { ISovereignALM } from '../ALM/interfaces/ISovereignALM.sol';
import { ISovereignOracle } from '../oracles/interfaces/ISovereignOracle.sol';
import { SovereignPoolConstructorArgs, SwapCache, SovereignPoolSwapParams } from './structs/SovereignPoolStructs.sol';
import { IFlashBorrower } from './interfaces/IFlashBorrower.sol';

/**
    @notice Valantis Sovereign Pool
    @dev Sovereign Pools contain the following Modules:
        - Swap Fee Module (Optional): Calculates swap fees.
        - Algorithmic Liquidity Module (ALM): Contains any kind of DEX logic.
        - Oracle Module (Optional): Can checkpoint swap data in order to
            build time-weighted price and/or volatility estimates.
        - Verifier Module (Optional): Manages custom access conditions for swaps, deposits and withdrawals.
        - Sovereign Vault (Optional): Allows LPs to store the funds in this contract instead of the pool.
            This allows for easier interoperability with other protocols and multi-token pool support.
            If not specified, the pool itself will hold the LPs' reserves.
 */
contract SovereignPool is ISovereignPool, ReentrancyGuard {
    using SafeERC20 for IERC20;

    /************************************************
     *  ENUMS
     ***********************************************/

    /**
        @notice Verifier access types. 
     */
    enum AccessType {
        SWAP,
        DEPOSIT,
        WITHDRAW
    }

    /************************************************
     *  CUSTOM ERRORS
     ***********************************************/

    error SovereignPool__ALMAlreadySet();
    error SovereignPool__excessiveToken0AbsErrorTolerance();
    error SovereignPool__excessiveToken1AbsErrorTolerance();
    error SovereignPool__onlyALM();
    error SovereignPool__onlyGauge();
    error SovereignPool__onlyPoolManager();
    error SovereignPool__onlyProtocolFactory();
    error SovereignPool__sameTokenNotAllowed();
    error SovereignPool__ZeroAddress();
    error SovereignPool__depositLiquidity_depositDisabled();
    error SovereignPool__depositLiquidity_excessiveToken0ErrorOnTransfer();
    error SovereignPool__depositLiquidity_excessiveToken1ErrorOnTransfer();
    error SovereignPool__depositLiquidity_incorrectTokenAmount();
    error SovereignPool__depositLiquidity_insufficientToken0Amount();
    error SovereignPool__depositLiquidity_insufficientToken1Amount();
    error SovereignPool__depositLiquidity_zeroTotalDepositAmount();
    error SovereignPool__getReserves_invalidReservesLength();
    error SovereignPool__setGauge_gaugeAlreadySet();
    error SovereignPool__setGauge_invalidAddress();
    error SovereignPool__setPoolManagerFeeBips_excessivePoolManagerFee();
    error SovereignPool__setSovereignOracle_oracleDisabled();
    error SovereignPool__setSovereignOracle_sovereignOracleAlreadySet();
    error SovereignPool__swap_excessiveSwapFee();
    error SovereignPool__swap_expired();
    error SovereignPool__swap_invalidLiquidityQuote();
    error SovereignPool__swap_invalidPoolTokenOut();
    error SovereignPool__swap_invalidRecipient();
    error SovereignPool__swap_insufficientAmountIn();
    error SovereignPool__swap_invalidSwapTokenOut();
    error SovereignPool__swap_zeroAmountInOrOut();
    error SovereignPool__setSwapFeeModule_timelock();
    error SovereignPool__withdrawLiquidity_withdrawDisabled();
    error SovereignPool__withdrawLiquidity_insufficientReserve0();
    error SovereignPool__withdrawLiquidity_insufficientReserve1();
    error SovereignPool__withdrawLiquidity_invalidRecipient();
    error SovereignPool___claimPoolManagerFees_invalidFeeReceived();
    error SovereignPool___claimPoolManagerFees_invalidProtocolFee();
    error SovereignPool___handleTokenInOnSwap_excessiveTokenInErrorOnTransfer();
    error SovereignPool___handleTokenInOnSwap_invalidTokenInAmount();
    error SovereignPool___verifyPermission_onlyPermissionedAccess(address sender, uint8 accessType);

    /************************************************
     *  CONSTANTS
     ***********************************************/

    /**
        @notice Maximum swap fee is 50% of input amount. 
        @dev See docs for a more detailed explanation about how swap fees are applied.
     */
    uint256 private constant _MAX_SWAP_FEE_BIPS = 10_000;

    /**
        @notice Factor of one or 100% representation in Basis points
     */
    uint256 private constant _FACTOR_ONE = 10_000;

    /**
        @notice `poolManager` can collect up to 50% of swap fees.
     */
    uint256 private constant _MAX_POOL_MANAGER_FEE_BIPS = 5_000;

    /**
        @notice Maximum allowed error tolerance on rebase token transfers.
        @dev    See:  https://github.com/lidofinance/lido-dao/issues/442.
     */
    uint256 private constant _MAX_ABS_ERROR_TOLERANCE = 10;

    /**
        @dev ERC-3156 onFlashLoan callback return data on success. 
     */
    bytes32 private constant _CALLBACK_SUCCESS = keccak256('ERC3156FlashBorrower.onFlashLoan');

    /************************************************
     *  IMMUTABLES
     ***********************************************/

    /**
        @notice Address of Sovereign Vault (Optional), where token reserves will be kept.
        @dev If set as this pool's address, it will work as a typical two token pool.
             Otherwise it can be set as any external vault or singleton. 
        @dev When sovereignVault is not this pool's address:
             - Reserves cannot be kept in the pool, hence `depositLiquidity` and `flashLoan` are disabled.
             - During swaps, input token must be transferred to `sovereignVault`.
             - During swaps, input token can only be token0 or token1.
               But if `sovereignVault != address(this)`, output token can be any other token.
     */
    address public immutable sovereignVault;

    /**
        @notice Address of Protocol Factory. 
     */
    address public immutable protocolFactory;

    /**
        @notice Default pool swap fee in basis-points (bips).
        @dev Can be overriden by whitelisting a Swap Fee Module.
        @dev See docs for a more detailed explanation about how swap fees are applied.
     */
    uint256 public immutable defaultSwapFeeBips;

    /**
        @notice Verifier Module (Optional).
        @dev Verifies custom authentication conditions on deposits, withdrawals and swaps. 
     */
    IVerifierModule private immutable _verifierModule;

    /**
        @notice Tokens supported by this pool.
        @dev These are not necessarily the only tokens
             available to trade against this pool:

             If `sovereignVault` == address(this):
               In this case only `_token0` and `_token1` can be exchanged.
             If `sovereignVault` != address(this):
               In this case `_token0` and `token1` can be the input token for a swap,
               but any other token can be quoted as the output token (given by calling `getTokens`).
     */
    IERC20 private immutable _token0;
    IERC20 private immutable _token1;

    /**
        @notice True if token0 is a rebase token. 
     */
    bool public immutable isToken0Rebase;

    /**
        @notice True if token1 is a rebase token. 
     */
    bool public immutable isToken1Rebase;

    /**
        @notice Maximum absolute error allowed on token0 transfers.
        @dev Only relevant if token0 is a rebase token.
             See: https://github.com/lidofinance/lido-dao/issues/442.
     */
    uint256 public immutable token0AbsErrorTolerance;

    /**
        @notice Maximum absolute error allowed on token1 transfers.
        @dev Only relevant if token1 is a rebase token.
             See: https://github.com/lidofinance/lido-dao/issues/442.
     */
    uint256 public immutable token1AbsErrorTolerance;

    /************************************************
     *  STORAGE
     ***********************************************/

    /**
        @notice Address of Sovereign ALM position bound to this pool.
        @dev Settable by `poolManager` only once. 
     */
    address public alm;

    /**
        @notice Address of Gauge bound to this pool. 
        @dev Settable by `protocolFactory` only once.
     */
    address public gauge;

    /**
        @notice Address of Pool Manager.
        @dev Can optionally set modules and parameters in this pool. 
     */
    address public poolManager;

    /**
        @notice Fraction of swap fees that go into `poolManager`, in bips.
        @dev Remaining fraction goes to LPs.
     */
    uint256 public poolManagerFeeBips;

    /**
        @notice Total token0 and token1 fees accrued by `poolManager`. 
     */
    uint256 public feePoolManager0;
    uint256 public feePoolManager1;

    /**
        @notice token0 and token1 fees donated to Gauges by `poolManager`.
     */
    uint256 public feeProtocol0;
    uint256 public feeProtocol1;

    /**
        @notice Block timestamp at or after which Swap Fee Module can be updated by `poolManager`.
        @dev This is meant to function as a time-lock to prevent `poolManager` from front-run user swaps,
             which could rapidly increase swap fees at arbitrary block times. 
     */
    uint256 public swapFeeModuleUpdateTimestamp;

    /**
        @notice token0 and token1 LP reserves.
     */
    uint256 private _reserve0;
    uint256 private _reserve1;

    /**
        @notice Sovereign Oracle Module (Optional).
        @dev Can accumulate swap data checkpoints and act as an on-chain price or volatility oracle.
     */
    ISovereignOracle private _sovereignOracleModule;

    /**
        @notice Swap Fee Module (Optional).
        @dev Defines custom logic to compute swap fees.
        @dev If not specified, a constant `defaultSwapFeeBips` will be used.
        @dev See docs for a more detailed explanation about how swap fees are applied.
     */
    ISwapFeeModule private _swapFeeModule;

    /************************************************
     *  MODIFIERS
     ***********************************************/

    modifier onlyALM() {
        _onlyALM();
        _;
    }

    modifier onlyProtocolFactory() {
        _onlyProtocolFactory();
        _;
    }

    modifier onlyPoolManager() {
        _onlyPoolManager();
        _;
    }

    modifier onlyGauge() {
        _onlyGauge();
        _;
    }

    /************************************************
     *  CONSTRUCTOR
     ***********************************************/

    constructor(SovereignPoolConstructorArgs memory args) {
        if (args.token0 == args.token1) {
            revert SovereignPool__sameTokenNotAllowed();
        }

        if (args.token0 == address(0) || args.token1 == address(0)) {
            revert SovereignPool__ZeroAddress();
        }

        sovereignVault = args.sovereignVault == address(0) ? address(this) : args.sovereignVault;

        _verifierModule = IVerifierModule(args.verifierModule);

        _token0 = IERC20(args.token0);
        _token1 = IERC20(args.token1);

        protocolFactory = args.protocolFactory;

        poolManager = args.poolManager;

        isToken0Rebase = args.isToken0Rebase;
        isToken1Rebase = args.isToken1Rebase;

        // Irrelevant in case of non-rebase tokens
        if (args.token0AbsErrorTolerance > _MAX_ABS_ERROR_TOLERANCE) {
            revert SovereignPool__excessiveToken0AbsErrorTolerance();
        }

        if (args.token1AbsErrorTolerance > _MAX_ABS_ERROR_TOLERANCE) {
            revert SovereignPool__excessiveToken1AbsErrorTolerance();
        }

        token0AbsErrorTolerance = args.token0AbsErrorTolerance;
        token1AbsErrorTolerance = args.token1AbsErrorTolerance;

        defaultSwapFeeBips = args.defaultSwapFeeBips <= _MAX_SWAP_FEE_BIPS
            ? args.defaultSwapFeeBips
            : _MAX_SWAP_FEE_BIPS;

        // Initialize timestamp at which Swap Fee Module can be set
        swapFeeModuleUpdateTimestamp = block.timestamp;
    }

    /************************************************
     *  VIEW FUNCTIONS
     ***********************************************/

    /**
        @notice Returns array of tokens available to be swapped against this Sovereign Pool (as tokenOut).
        @dev In case `sovereignVault == address(this)`, only token0 and token1 are available.
             Otherwise, the pool queries `sovereignVault` to retrieve them.
     */
    function getTokens() external view override returns (address[] memory tokens) {
        if (sovereignVault == address(this)) {
            // In this case only token0 and token1 can be swapped
            tokens = new address[](2);

            tokens[0] = address(_token0);
            tokens[1] = address(_token1);
        } else {
            // Data validation should be performed by either caller or `sovereignVault`
            tokens = ISovereignVaultMinimal(sovereignVault).getTokensForPool(address(this));
        }
    }

    /**
        @notice Returns `token0` and `token1` reserves, respectively.
        @dev Reserves are measured differently in case of rebase tokens.
             WARNING: With rebase tokens, balances (hence reserves) can be easily manipulated.
             External contracts MUST be aware and take the right precautions.
        @dev In case `sovereignVault` is not the pool, reserves are queried from `sovereignVault`.
        @dev This function only queries reserves for `token0` and `token1`.
             In case `sovereignVault` supports other tokens, reserves should be queried from it directly.
        @dev This is exposed for convenience. The pool makes no assumptions regarding the way an
             external `sovereignVault` updates reserves internally.
     */
    function getReserves() public view override returns (uint256, uint256) {
        if (sovereignVault == address(this)) {
            return (_getReservesForToken(true), _getReservesForToken(false));
        } else {
            address[] memory tokens = new address[](2);
            tokens[0] = address(_token0);
            tokens[1] = address(_token1);

            uint256[] memory reserves = ISovereignVaultMinimal(sovereignVault).getReservesForPool(
                address(this),
                tokens
            );

            // Only token0 and token1 reserves should be returned
            if (reserves.length != 2) {
                revert SovereignPool__getReserves_invalidReservesLength();
            }

            return (reserves[0], reserves[1]);
        }
    }

    /**
        @notice Returns pool manager fee in amount of token0 and token1.
     */
    function getPoolManagerFees() public view override returns (uint256, uint256) {
        return (feePoolManager0, feePoolManager1);
    }

    /**
        @notice Returns True if this pool contains at least one rebase token. 
     */
    function isRebaseTokenPool() external view override returns (bool) {
        return isToken0Rebase || isToken1Rebase;
    }

    /**
        @notice Returns the address of token0.
     */
    function token0() external view override returns (address) {
        return address(_token0);
    }

    /**
        @notice Returns the address of token1.
     */
    function token1() external view override returns (address) {
        return address(_token1);
    }

    /**
        @notice Returns address of Oracle module in this pool. 
     */
    function sovereignOracleModule() external view override returns (address) {
        return address(_sovereignOracleModule);
    }

    /**
        @notice Returns the address of the swapFeeModule in this pool.
     */
    function swapFeeModule() external view override returns (address) {
        return address(_swapFeeModule);
    }

    /**
        @notice Returns the address of the verifier module in this pool.
     */
    function verifierModule() external view override returns (address) {
        return address(_verifierModule);
    }

    /**
        @notice Exposes the status of reentrancy lock.
        @dev ALMs and other external smart contracts can use it for reentrancy protection. 
             Mainly useful for read-only reentrancy protection.
     */
    function isLocked() external view override returns (bool) {
        return _status == _ENTERED;
    }

    /************************************************
     *  EXTERNAL FUNCTIONS
     ***********************************************/

    /**
        @notice Sets address of `poolManager`.
        @dev Settable by `poolManager`.
        @param _manager Address of new pool manager. 
     */
    function setPoolManager(address _manager) external override onlyPoolManager nonReentrant {
        poolManager = _manager;

        if (_manager == address(0)) {
            poolManagerFeeBips = 0;
            // It will be assumed pool is not going to contribute anything to protocol fees.
            _claimPoolManagerFees(0, 0, msg.sender);
            emit PoolManagerFeeSet(0);
        }

        emit PoolManagerSet(_manager);
    }

    /**
        @notice Set fee in BIPS for `poolManager`.
        @dev Must not be greater than MAX_POOL_MANAGER_FEE_BIPS.
        @dev Settable by `poolManager`.
        @param _poolManagerFeeBips fee to set in BIPS.
     */
    function setPoolManagerFeeBips(uint256 _poolManagerFeeBips) external override onlyPoolManager nonReentrant {
        if (_poolManagerFeeBips > _MAX_POOL_MANAGER_FEE_BIPS) {
            revert SovereignPool__setPoolManagerFeeBips_excessivePoolManagerFee();
        }

        poolManagerFeeBips = _poolManagerFeeBips;

        emit PoolManagerFeeSet(_poolManagerFeeBips);
    }

    /**
        @notice Set Sovereign Oracle Module in this pool.
        @dev Can only be set once by `poolManager`.
        @param sovereignOracle Address of Sovereign Oracle Module instance. 
     */
    function setSovereignOracle(address sovereignOracle) external override onlyPoolManager nonReentrant {
        if (sovereignOracle == address(0)) {
            revert SovereignPool__ZeroAddress();
        }

        if (address(sovereignVault) != address(this)) revert SovereignPool__setSovereignOracle_oracleDisabled();

        if (address(_sovereignOracleModule) != address(0)) {
            revert SovereignPool__setSovereignOracle_sovereignOracleAlreadySet();
        }

        _sovereignOracleModule = ISovereignOracle(sovereignOracle);

        emit SovereignOracleSet(sovereignOracle);
    }

    /**
        @notice Set Gauge in this pool.
        @dev Can only be set once by `protocolFactory`. 
        @param _gauge Address of Gauge instance.
     */
    function setGauge(address _gauge) external override onlyProtocolFactory nonReentrant {
        if (gauge != address(0)) {
            revert SovereignPool__setGauge_gaugeAlreadySet();
        }

        if (_gauge == address(0)) {
            revert SovereignPool__setGauge_invalidAddress();
        }

        gauge = _gauge;

        emit GaugeSet(_gauge);
    }

    /**
        @notice Set Swap Fee Module for this pool.
        @dev Only callable by `poolManager`.
        @dev If set as address(0), a constant default swap fee will be applied.
        @dev It contains a 3 days timelock, to prevent `poolManager` from front-running
             swaps by rapidly increasing swap fees too frequently.
        @param swapFeeModule_ Address of Swap Fee Module to whitelist.
     */
    function setSwapFeeModule(address swapFeeModule_) external override onlyPoolManager nonReentrant {
        // Swap Fee Module cannot be updated too frequently (at most once every 3 days)
        if (block.timestamp < swapFeeModuleUpdateTimestamp) {
            revert SovereignPool__setSwapFeeModule_timelock();
        }

        _swapFeeModule = ISwapFeeModule(swapFeeModule_);
        // Update timestamp at which the next Swap Fee Module update can occur
        swapFeeModuleUpdateTimestamp = block.timestamp + 3 days;

        emit SwapFeeModuleSet(swapFeeModule_);
    }

    /**
        @notice Set ALM for this pool.
        @dev Only callable by `poolManager`.
        @dev Can only be called once.
        @param _alm Address of ALM to whitelist. 
     */
    function setALM(address _alm) external override onlyPoolManager nonReentrant {
        if (_alm == address(0)) {
            revert SovereignPool__ZeroAddress();
        }

        if (alm != address(0)) {
            revert SovereignPool__ALMAlreadySet();
        }

        alm = _alm;

        emit ALMSet(_alm);
    }

    function flashLoan(
        bool _isTokenZero,
        IFlashBorrower _receiver,
        uint256 _amount,
        bytes calldata _data
    ) external nonReentrant {
        // We disable flash-loans,
        // since reserves are not meant to be stored in the pool
        if (sovereignVault != address(this)) revert ValantisPool__flashLoan_flashLoanDisabled();

        IERC20 flashToken = _isTokenZero ? _token0 : _token1;
        bool isRebaseFlashToken = _isTokenZero ? isToken0Rebase : isToken1Rebase;

        // Flash-loans for rebase tokens are disabled.
        // Easy to manipulate token reserves would significantly
        // increase the attack surface for contracts that rely on this pool
        if (isRebaseFlashToken) {
            revert ValantisPool__flashLoan_rebaseTokenNotAllowed();
        }

        uint256 poolPreBalance = flashToken.balanceOf(address(this));

        flashToken.safeTransfer(address(_receiver), _amount);
        if (_receiver.onFlashLoan(msg.sender, address(flashToken), _amount, _data) != _CALLBACK_SUCCESS) {
            revert ValantisPool__flashloan_callbackFailed();
        }
        flashToken.safeTransferFrom(address(_receiver), address(this), _amount);

        if (flashToken.balanceOf(address(this)) != poolPreBalance) {
            revert ValantisPool__flashLoan_flashLoanNotRepaid();
        }

        emit Flashloan(msg.sender, address(_receiver), _amount, address(flashToken));
    }

    /**
        @notice Claim share of fees accrued by this pool
                And optionally share some with the protocol.
        @dev Only callable by `poolManager`.
        @param _feeProtocol0Bips Amount of `token0` fees to be shared with protocol.
        @param _feeProtocol1Bips Amount of `token1` fees to be shared with protocol.
     */
    function claimPoolManagerFees(
        uint256 _feeProtocol0Bips,
        uint256 _feeProtocol1Bips
    )
        external
        override
        nonReentrant
        onlyPoolManager
        returns (uint256 feePoolManager0Received, uint256 feePoolManager1Received)
    {
        (feePoolManager0Received, feePoolManager1Received) = _claimPoolManagerFees(
            _feeProtocol0Bips,
            _feeProtocol1Bips,
            msg.sender
        );
    }

    /**
        @notice Claim accrued protocol fees, if any.
        @dev Only callable by `gauge`. 
     */
    function claimProtocolFees() external override nonReentrant onlyGauge returns (uint256, uint256) {
        uint256 feeProtocol0Cache = feeProtocol0;
        uint256 feeProtocol1Cache = feeProtocol1;

        if (feeProtocol0Cache > 0) {
            feeProtocol0 = 0;
            _token0.safeTransfer(msg.sender, feeProtocol0Cache);
        }

        if (feeProtocol1Cache > 0) {
            feeProtocol1 = 0;
            _token1.safeTransfer(msg.sender, feeProtocol1Cache);
        }

        return (feeProtocol0Cache, feeProtocol1Cache);
    }

    /**
        @notice Swap against the ALM Position in this pool.
        @param _swapParams Struct containing all params.
               * isSwapCallback If this swap should claim funds using a callback.
               * isZeroToOne Direction of the swap.
               * amountIn Input amount to swap.
               * amountOutMin Minimum output token amount required.
               * deadline Block timestamp after which the swap is no longer valid.
               * recipient Recipient address for output token.
               * swapTokenOut Address of output token.
                 If `sovereignVault != address(this)` it can be other tokens apart from token0 or token1.
               * swapContext Struct containing ALM's external, Verifier's and Swap Callback's context data.
        @return amountInUsed Amount of input token filled by this swap.
        @return amountOut Amount of output token provided by this swap.
     */
    function swap(
        SovereignPoolSwapParams calldata _swapParams
    ) external override nonReentrant returns (uint256 amountInUsed, uint256 amountOut) {
        if (block.timestamp > _swapParams.deadline) {
            revert SovereignPool__swap_expired();
        }

        // Cannot swap zero input token amount
        if (_swapParams.amountIn == 0) {
            revert SovereignPool__swap_insufficientAmountIn();
        }

        if (_swapParams.recipient == address(0)) {
            revert SovereignPool__swap_invalidRecipient();
        }

        SwapCache memory swapCache = SwapCache({
            swapFeeModule: _swapFeeModule,
            tokenInPool: _swapParams.isZeroToOne ? _token0 : _token1,
            tokenOutPool: _swapParams.isZeroToOne ? _token1 : _token0,
            amountInWithoutFee: 0
        });

        if (_swapParams.swapTokenOut == address(0) || _swapParams.swapTokenOut == address(swapCache.tokenInPool)) {
            revert SovereignPool__swap_invalidSwapTokenOut();
        }

        // If reserves are kept in the pool, only token0 <-> token1 swaps are allowed
        if (sovereignVault == address(this) && _swapParams.swapTokenOut != address(swapCache.tokenOutPool)) {
            revert SovereignPool__swap_invalidPoolTokenOut();
        }

        bytes memory verifierData;
        if (address(_verifierModule) != address(0)) {
            // Query Verifier Module to authenticate the swap
            verifierData = _verifyPermission(
                msg.sender,
                _swapParams.swapContext.verifierContext,
                uint8(AccessType.SWAP)
            );
        }

        // Calculate swap fee in bips

        SwapFeeModuleData memory swapFeeModuleData;

        if (address(swapCache.swapFeeModule) != address(0)) {
            swapFeeModuleData = swapCache.swapFeeModule.getSwapFeeInBips(
                address(swapCache.tokenInPool),
                address(swapCache.tokenOutPool),
                _swapParams.amountIn,
                msg.sender,
                _swapParams.swapContext.swapFeeModuleContext
            );
            if (swapFeeModuleData.feeInBips > _MAX_SWAP_FEE_BIPS) {
                revert SovereignPool__swap_excessiveSwapFee();
            }
        } else {
            swapFeeModuleData = SwapFeeModuleData({ feeInBips: defaultSwapFeeBips, internalContext: new bytes(0) });
        }

        // Since we do not yet know how much of `amountIn` will be filled,
        // this quantity is calculated in such a way that `msg.sender`
        // will be charged `feeInBips` of whatever the amount of tokenIn filled
        // ends up being (see docs for more details)
        swapCache.amountInWithoutFee = Math.mulDiv(
            _swapParams.amountIn,
            _MAX_SWAP_FEE_BIPS,
            _MAX_SWAP_FEE_BIPS + swapFeeModuleData.feeInBips
        );

        ALMLiquidityQuote memory liquidityQuote = ISovereignALM(alm).getLiquidityQuote(
            ALMLiquidityQuoteInput({
                isZeroToOne: _swapParams.isZeroToOne,
                amountInMinusFee: swapCache.amountInWithoutFee,
                feeInBips: swapFeeModuleData.feeInBips,
                sender: msg.sender,
                recipient: _swapParams.recipient,
                tokenOutSwap: _swapParams.swapTokenOut
            }),
            _swapParams.swapContext.externalContext,
            verifierData
        );

        amountOut = liquidityQuote.amountOut;

        if (
            !_checkLiquidityQuote(
                _swapParams.isZeroToOne,
                swapCache.amountInWithoutFee,
                liquidityQuote.amountInFilled,
                amountOut,
                _swapParams.amountOutMin
            )
        ) {
            revert SovereignPool__swap_invalidLiquidityQuote();
        }

        // If amountOut or amountInFilled is 0, we do not transfer any input token
        if (amountOut == 0 || liquidityQuote.amountInFilled == 0) {
            revert SovereignPool__swap_zeroAmountInOrOut();
        }

        // Calculate the actual swap fee to be charged in input token (`effectiveFee`),
        // now that we know the tokenIn amount filled
        uint256 effectiveFee;
        if (liquidityQuote.amountInFilled != swapCache.amountInWithoutFee) {
            effectiveFee = Math.mulDiv(
                liquidityQuote.amountInFilled,
                swapFeeModuleData.feeInBips,
                _MAX_SWAP_FEE_BIPS,
                Math.Rounding.Up
            );

            amountInUsed = liquidityQuote.amountInFilled + effectiveFee;
        } else {
            // Using above formula in case amountInWithoutFee == amountInFilled introduces rounding errors
            effectiveFee = _swapParams.amountIn - swapCache.amountInWithoutFee;
            amountInUsed = _swapParams.amountIn;
        }

        _handleTokenInTransfersOnSwap(
            _swapParams.isZeroToOne,
            _swapParams.isSwapCallback,
            swapCache.tokenInPool,
            amountInUsed,
            effectiveFee,
            _swapParams.swapContext.swapCallbackContext
        );

        // Update internal state and oracle module.
        // In case of rebase tokens, `amountInUsed` and `amountOut` might not match
        // the exact balance deltas due to rounding errors.
        _updatePoolStateOnSwap(_swapParams.isZeroToOne, amountInUsed, amountOut, effectiveFee);

        if (
            address(_sovereignOracleModule) != address(0) &&
            _swapParams.swapTokenOut == address(swapCache.tokenOutPool) &&
            amountInUsed > 0
        ) {
            _sovereignOracleModule.writeOracleUpdate(_swapParams.isZeroToOne, amountInUsed, effectiveFee, amountOut);
        }

        // Transfer `amountOut to recipient
        _handleTokenOutTransferOnSwap(IERC20(_swapParams.swapTokenOut), _swapParams.recipient, amountOut);

        // Update state for Swap fee module,
        // only performed if internalContext is non-empty
        if (
            address(swapCache.swapFeeModule) != address(0) &&
            keccak256(swapFeeModuleData.internalContext) != keccak256(new bytes(0))
        ) {
            swapCache.swapFeeModule.callbackOnSwapEnd(effectiveFee, amountInUsed, amountOut, swapFeeModuleData);
        }

        // Perform post-swap callback to liquidity module if necessary
        if (liquidityQuote.isCallbackOnSwap) {
            ISovereignALM(alm).onSwapCallback(_swapParams.isZeroToOne, amountInUsed, amountOut);
        }

        emit Swap(msg.sender, _swapParams.isZeroToOne, amountInUsed, effectiveFee, amountOut);
    }

    /**
        @notice Deposit liquidity into an ALM Position.
        @dev Only callable by its respective active ALM Position.
        @param _amount0 Amount of token0 to deposit.
        @param _amount1 Amount of token1 to deposit. 
        @param _verificationContext Bytes containing verification data required in case of permissioned pool.
        @param _depositData Bytes encoded data for deposit callback.
        @return amount0Deposited Amount of token0 deposited.
        @return amount1Deposited Amount of token1 deposited.
     */
    function depositLiquidity(
        uint256 _amount0,
        uint256 _amount1,
        address _sender,
        bytes calldata _verificationContext,
        bytes calldata _depositData
    ) external override onlyALM nonReentrant returns (uint256 amount0Deposited, uint256 amount1Deposited) {
        // We disable deposits,
        // since reserves are not meant to be stored in the pool
        if (sovereignVault != address(this)) revert SovereignPool__depositLiquidity_depositDisabled();

        // At least one token amount must be positive
        if (_amount0 | _amount1 == 0) {
            revert SovereignPool__depositLiquidity_zeroTotalDepositAmount();
        }

        if (address(_verifierModule) != address(0)) {
            _verifyPermission(_sender, _verificationContext, uint8(AccessType.DEPOSIT));
        }

        uint256 token0PreBalance = _token0.balanceOf(address(this));
        uint256 token1PreBalance = _token1.balanceOf(address(this));

        ISovereignALM(msg.sender).onDepositLiquidityCallback(_amount0, _amount1, _depositData);

        amount0Deposited = _token0.balanceOf(address(this)) - token0PreBalance;
        amount1Deposited = _token1.balanceOf(address(this)) - token1PreBalance;

        // Post-deposit checks for token0
        // _amount0 == 0 is interpreted as not depositing token0
        if (_amount0 != 0) {
            if (isToken0Rebase) {
                uint256 amount0AbsDiff = amount0Deposited < _amount0
                    ? _amount0 - amount0Deposited
                    : amount0Deposited - _amount0;

                if (amount0AbsDiff > token0AbsErrorTolerance) {
                    revert SovereignPool__depositLiquidity_excessiveToken0ErrorOnTransfer();
                }
            } else {
                if (amount0Deposited != _amount0) revert SovereignPool__depositLiquidity_insufficientToken0Amount();

                _reserve0 += amount0Deposited;
            }
        } else if (amount0Deposited > 0) {
            revert SovereignPool__depositLiquidity_incorrectTokenAmount();
        }

        // Post-deposit checks for token1
        // _amount1 == 0 is interpreted as not depositing token1
        if (_amount1 != 0) {
            if (isToken1Rebase) {
                uint256 amount1AbsDiff = amount1Deposited < _amount1
                    ? _amount1 - amount1Deposited
                    : amount1Deposited - _amount1;

                if (amount1AbsDiff > token1AbsErrorTolerance) {
                    revert SovereignPool__depositLiquidity_excessiveToken1ErrorOnTransfer();
                }
            } else {
                if (amount1Deposited != _amount1) revert SovereignPool__depositLiquidity_insufficientToken1Amount();

                _reserve1 += amount1Deposited;
            }
        } else if (amount1Deposited > 0) {
            revert SovereignPool__depositLiquidity_incorrectTokenAmount();
        }

        emit DepositLiquidity(amount0Deposited, amount1Deposited);
    }

    /**
        @notice Withdraw liquidity from this pool to an ALM Position.
        @dev Only callable by ALM Position.
        @param _amount0 Amount of token0 reserves to withdraw.
        @param _amount1 Amount of token1 reserves to withdraw.
        @param _recipient Address of recipient.
        @param _verificationContext Bytes containing verfication data required in case of permissioned pool.
     */
    function withdrawLiquidity(
        uint256 _amount0,
        uint256 _amount1,
        address _sender,
        address _recipient,
        bytes calldata _verificationContext
    ) external override nonReentrant onlyALM {
        if (_recipient == address(0)) {
            revert SovereignPool__withdrawLiquidity_invalidRecipient();
        }

        // We disable withdrawals,
        // since reserves are not meant to be stored in the pool
        if (sovereignVault != address(this)) revert SovereignPool__withdrawLiquidity_withdrawDisabled();

        if (address(_verifierModule) != address(0)) {
            _verifyPermission(_sender, _verificationContext, uint8(AccessType.WITHDRAW));
        }

        if (_amount0 > _getReservesForToken(true)) {
            revert SovereignPool__withdrawLiquidity_insufficientReserve0();
        }

        if (_amount1 > _getReservesForToken(false)) {
            revert SovereignPool__withdrawLiquidity_insufficientReserve1();
        }

        // Already checked above
        unchecked {
            if (!isToken0Rebase) _reserve0 -= _amount0;

            if (!isToken1Rebase) _reserve1 -= _amount1;
        }

        if (_amount0 > 0) {
            _token0.safeTransfer(_recipient, _amount0);
        }

        if (_amount1 > 0) {
            _token1.safeTransfer(_recipient, _amount1);
        }

        emit WithdrawLiquidity(_recipient, _amount0, _amount1);
    }

    /************************************************
     *  PRIVATE FUNCTIONS
     ***********************************************/

    function _claimPoolManagerFees(
        uint256 _feeProtocol0Bips,
        uint256 _feeProtocol1Bips,
        address _recipient
    ) private returns (uint256 feePoolManager0Received, uint256 feePoolManager1Received) {
        if (_feeProtocol0Bips > _FACTOR_ONE || _feeProtocol1Bips > _FACTOR_ONE) {
            revert SovereignPool___claimPoolManagerFees_invalidProtocolFee();
        }

        (feePoolManager0Received, feePoolManager1Received) = getPoolManagerFees();

        // Attempt to claim pool manager fees from `sovereignVault`
        // This is necessary since in this case reserves are not kept in this pool
        if (sovereignVault != address(this)) {
            uint256 token0PreBalance = _token0.balanceOf(address(this));
            uint256 token1PreBalance = _token1.balanceOf(address(this));

            ISovereignVaultMinimal(sovereignVault).claimPoolManagerFees(
                feePoolManager0Received,
                feePoolManager1Received
            );

            uint256 fee0ReceivedCache = _token0.balanceOf(address(this)) - token0PreBalance;
            uint256 fee1ReceivedCache = _token1.balanceOf(address(this)) - token1PreBalance;

            // Cannot transfer in excess, otherwise it would be possible to manipulate this pool's
            // fair share of earned swap fees
            if (fee0ReceivedCache > feePoolManager0Received || fee1ReceivedCache > feePoolManager1Received) {
                revert SovereignPool___claimPoolManagerFees_invalidFeeReceived();
            }

            feePoolManager0Received = fee0ReceivedCache;
            feePoolManager1Received = fee1ReceivedCache;
        }

        uint256 protocolFee0 = Math.mulDiv(_feeProtocol0Bips, feePoolManager0Received, _FACTOR_ONE);
        uint256 protocolFee1 = Math.mulDiv(_feeProtocol1Bips, feePoolManager1Received, _FACTOR_ONE);

        feeProtocol0 += protocolFee0;
        feeProtocol1 += protocolFee1;

        feePoolManager0 = 0;
        feePoolManager1 = 0;

        feePoolManager0Received -= protocolFee0;
        feePoolManager1Received -= protocolFee1;

        if (feePoolManager0Received > 0) {
            _token0.safeTransfer(_recipient, feePoolManager0Received);
        }

        if (feePoolManager1Received > 0) {
            _token1.safeTransfer(_recipient, feePoolManager1Received);
        }

        emit PoolManagerFeesClaimed(feePoolManager0Received, feePoolManager1Received);
    }

    function _verifyPermission(
        address sender,
        bytes calldata verificationContext,
        uint8 accessType
    ) private returns (bytes memory verifierData) {
        bool success;

        (success, verifierData) = _verifierModule.verify(sender, verificationContext, accessType);

        if (!success) {
            revert SovereignPool___verifyPermission_onlyPermissionedAccess(sender, accessType);
        }
    }

    function _handleTokenInTransfersOnSwap(
        bool isZeroToOne,
        bool isSwapCallback,
        IERC20 token,
        uint256 amountInUsed,
        uint256 effectiveFee,
        bytes calldata _swapCallbackContext
    ) private {
        uint256 preBalance = token.balanceOf(sovereignVault);

        if (isSwapCallback) {
            ISovereignPoolSwapCallback(msg.sender).sovereignPoolSwapCallback(
                address(token),
                amountInUsed,
                _swapCallbackContext
            );
        } else {
            token.safeTransferFrom(msg.sender, sovereignVault, amountInUsed);
        }

        uint256 amountInReceived = token.balanceOf(sovereignVault) - preBalance;

        bool isTokenInRebase = isZeroToOne ? isToken0Rebase : isToken1Rebase;

        if (isTokenInRebase) {
            uint256 tokenInAbsDiff = amountInUsed > amountInReceived
                ? amountInUsed - amountInReceived
                : amountInReceived - amountInUsed;

            uint256 tokenInAbsErrorTolerance = isZeroToOne ? token0AbsErrorTolerance : token1AbsErrorTolerance;
            if (tokenInAbsDiff > tokenInAbsErrorTolerance)
                revert SovereignPool___handleTokenInOnSwap_excessiveTokenInErrorOnTransfer();
        } else {
            if (amountInReceived != amountInUsed) revert SovereignPool___handleTokenInOnSwap_invalidTokenInAmount();
        }

        if (isTokenInRebase && sovereignVault == address(this) && poolManager != address(0)) {
            // We transfer manager fee to `poolManager`
            uint256 poolManagerFee = Math.mulDiv(effectiveFee, poolManagerFeeBips, _FACTOR_ONE);
            if (poolManagerFee > 0) {
                token.safeTransfer(poolManager, poolManagerFee);
            }
        }
    }

    function _handleTokenOutTransferOnSwap(IERC20 swapTokenOut, address recipient, uint256 amountOut) private {
        if (sovereignVault == address(this)) {
            // In this case, tokenOut should be transferred from this pool to `recipient`
            swapTokenOut.safeTransfer(recipient, amountOut);
        } else {
            // If `sovereignVault` is not this pool,
            // ALM must have already approved this pool to send `amountOut` to `recipient`
            swapTokenOut.safeTransferFrom(sovereignVault, recipient, amountOut);
        }
    }

    function _updatePoolStateOnSwap(
        bool isZeroToOne,
        uint256 amountInUsed,
        uint256 amountOut,
        uint256 effectiveFee
    ) private {
        if (isZeroToOne) {
            if (!isToken0Rebase) {
                uint256 poolManagerFee = Math.mulDiv(effectiveFee, poolManagerFeeBips, _FACTOR_ONE);

                if (sovereignVault == address(this)) _reserve0 += (amountInUsed - poolManagerFee);
                if (poolManagerFee > 0) feePoolManager0 += poolManagerFee;
            }

            if (sovereignVault == address(this) && !isToken1Rebase) {
                _reserve1 -= amountOut;
            }
        } else {
            if (sovereignVault == address(this) && !isToken0Rebase) {
                _reserve0 -= amountOut;
            }

            if (!isToken1Rebase) {
                uint256 poolManagerFee = Math.mulDiv(effectiveFee, poolManagerFeeBips, _FACTOR_ONE);

                if (sovereignVault == address(this)) _reserve1 += (amountInUsed - poolManagerFee);
                if (poolManagerFee > 0) feePoolManager1 += poolManagerFee;
            }
        }
    }

    function _onlyALM() private view {
        if (msg.sender != alm) {
            revert SovereignPool__onlyALM();
        }
    }

    function _onlyProtocolFactory() private view {
        if (msg.sender != protocolFactory) {
            revert SovereignPool__onlyProtocolFactory();
        }
    }

    function _onlyPoolManager() private view {
        if (msg.sender != poolManager) {
            revert SovereignPool__onlyPoolManager();
        }
    }

    function _onlyGauge() private view {
        if (msg.sender != gauge) {
            revert SovereignPool__onlyGauge();
        }
    }

    function _getReservesForToken(bool isToken0) private view returns (uint256 reserve) {
        if (isToken0) {
            if (isToken0Rebase) {
                reserve = _token0.balanceOf(address(this));
            } else {
                reserve = _reserve0;
            }
        } else {
            if (isToken1Rebase) {
                reserve = _token1.balanceOf(address(this));
            } else {
                reserve = _reserve1;
            }
        }
    }

    function _checkLiquidityQuote(
        bool isZeroToOne,
        uint256 amountInWithoutFee,
        uint256 amountInFilled,
        uint256 amountOut,
        uint256 amountOutMin
    ) private view returns (bool) {
        // We only compare against pool reserves if they are meant to be stored in it
        if (sovereignVault == address(this)) {
            if (amountOut > _getReservesForToken(!isZeroToOne)) {
                return false;
            }
        }

        if (amountOut < amountOutMin) {
            return false;
        }

        if (amountInFilled > amountInWithoutFee) {
            return false;
        }

        return true;
    }
}

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

File 4 of 20 : SafeERC20.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.9.0) (token/ERC20/utils/SafeERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

library Constants {
    uint256 public constant Q128 = 1 << 128;

    uint256 public constant Q64 = 1 << 64;
}

File 6 of 20 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

/**
    @notice Adapted from:
            https://github.com/OpenZeppelin/openzeppelin-contracts/blob/v4.8.3/contracts/security/ReentrancyGuard.sol
    @dev Uses internal variables and functions
         so that child contracts can be explicit about view-function reentrancy risk.
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

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

    uint256 internal _status;

    constructor() {
        _status = _NOT_ENTERED;
    }

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

    function _nonReentrantBefore() internal {
        // 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() internal {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = _NOT_ENTERED;
    }
}

File 7 of 20 : ISwapFeeModule.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

/**
    @notice Struct returned by the swapFeeModule during the getSwapFeeInBips call.
    * feeInBips: The swap fee in bips.
    * internalContext: Arbitrary bytes context data.
 */
struct SwapFeeModuleData {
    uint256 feeInBips;
    bytes internalContext;
}

interface ISwapFeeModuleMinimal {
    /**
        @notice Returns the swap fee in bips for both Universal & Sovereign Pools.
        @param _tokenIn The address of the token that the user wants to swap.
        @param _tokenOut The address of the token that the user wants to receive.
        @param _amountIn The amount of tokenIn being swapped.
        @param _user The address of the user.
        @param _swapFeeModuleContext Arbitrary bytes data which can be sent to the swap fee module.
        @return swapFeeModuleData A struct containing the swap fee in bips, and internal context data.
     */
    function getSwapFeeInBips(
        address _tokenIn,
        address _tokenOut,
        uint256 _amountIn,
        address _user,
        bytes memory _swapFeeModuleContext
    ) external returns (SwapFeeModuleData memory swapFeeModuleData);
}

interface ISwapFeeModule is ISwapFeeModuleMinimal {
    /**
        @notice Callback function called by the pool after the swap has finished. ( Universal Pools )
        @param _effectiveFee The effective fee charged for the swap.
        @param _spotPriceTick The spot price tick after the swap.
        @param _amountInUsed The amount of tokenIn used for the swap.
        @param _amountOut The amount of the tokenOut transferred to the user.
        @param _swapFeeModuleData The context data returned by getSwapFeeInBips.
     */
    function callbackOnSwapEnd(
        uint256 _effectiveFee,
        int24 _spotPriceTick,
        uint256 _amountInUsed,
        uint256 _amountOut,
        SwapFeeModuleData memory _swapFeeModuleData
    ) external;

    /**
        @notice Callback function called by the pool after the swap has finished. ( Sovereign Pools )
        @param _effectiveFee The effective fee charged for the swap.
        @param _amountInUsed The amount of tokenIn used for the swap.
        @param _amountOut The amount of the tokenOut transferred to the user.
        @param _swapFeeModuleData The context data returned by getSwapFeeInBips.
     */
    function callbackOnSwapEnd(
        uint256 _effectiveFee,
        uint256 _amountInUsed,
        uint256 _amountOut,
        SwapFeeModuleData memory _swapFeeModuleData
    ) external;
}

File 8 of 20 : ISovereignPool.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import { IValantisPool } from '../interfaces/IValantisPool.sol';
import { PoolLocks } from '../structs/ReentrancyGuardStructs.sol';
import { SovereignPoolSwapContextData, SovereignPoolSwapParams } from '../structs/SovereignPoolStructs.sol';

interface ISovereignPool is IValantisPool {
    event SwapFeeModuleSet(address swapFeeModule);
    event ALMSet(address alm);
    event GaugeSet(address gauge);
    event PoolManagerSet(address poolManager);
    event PoolManagerFeeSet(uint256 poolManagerFeeBips);
    event SovereignOracleSet(address sovereignOracle);
    event PoolManagerFeesClaimed(uint256 amount0, uint256 amount1);
    event DepositLiquidity(uint256 amount0, uint256 amount1);
    event WithdrawLiquidity(address indexed recipient, uint256 amount0, uint256 amount1);
    event Swap(address indexed sender, bool isZeroToOne, uint256 amountIn, uint256 fee, uint256 amountOut);

    function getTokens() external view returns (address[] memory tokens);

    function sovereignVault() external view returns (address);

    function protocolFactory() external view returns (address);

    function gauge() external view returns (address);

    function poolManager() external view returns (address);

    function sovereignOracleModule() external view returns (address);

    function swapFeeModule() external view returns (address);

    function verifierModule() external view returns (address);

    function isLocked() external view returns (bool);

    function isRebaseTokenPool() external view returns (bool);

    function poolManagerFeeBips() external view returns (uint256);

    function defaultSwapFeeBips() external view returns (uint256);

    function swapFeeModuleUpdateTimestamp() external view returns (uint256);

    function alm() external view returns (address);

    function getPoolManagerFees() external view returns (uint256 poolManagerFee0, uint256 poolManagerFee1);

    function getReserves() external view returns (uint256 reserve0, uint256 reserve1);

    function setPoolManager(address _manager) external;

    function setGauge(address _gauge) external;

    function setPoolManagerFeeBips(uint256 _poolManagerFeeBips) external;

    function setSovereignOracle(address sovereignOracle) external;

    function setSwapFeeModule(address _swapFeeModule) external;

    function setALM(address _alm) external;

    function swap(SovereignPoolSwapParams calldata _swapParams) external returns (uint256, uint256);

    function depositLiquidity(
        uint256 _amount0,
        uint256 _amount1,
        address _sender,
        bytes calldata _verificationContext,
        bytes calldata _depositData
    ) external returns (uint256 amount0Deposited, uint256 amount1Deposited);

    function withdrawLiquidity(
        uint256 _amount0,
        uint256 _amount1,
        address _sender,
        address _recipient,
        bytes calldata _verificationContext
    ) external;
}

File 9 of 20 : ISovereignPoolSwapCallback.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface ISovereignPoolSwapCallback {
    /**
        @notice Function called by Sovereign Pool during a swap, to transfer the funds.
        @dev This function is only called if isSwapCallback is set to true in swapParams.
        @param _tokenIn The address of the token that the user wants to swap.
        @param _amountInUsed The amount of the tokenIn used for the swap.
        @param _swapCallbackContext Arbitrary bytes data which can be sent to the swap callback.
     */
    function sovereignPoolSwapCallback(
        address _tokenIn,
        uint256 _amountInUsed,
        bytes calldata _swapCallbackContext
    ) external;
}

File 10 of 20 : IVerifierModule.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface IVerifierModule {
    /**
        @notice Used to verify user access to important pool functions.
        @param _user The address of the user.
        @param _verificationContext Arbitrary bytes data which can be sent to the verifier module.
        @param accessType The type of function being called, can be - SWAP(0), DEPOSIT(1), or WITHDRAW(2).
        @return success True if the user is verified, false otherwise.
        @return returnData Additional data which can be passed along to the LM in case of a swap.
     */
    function verify(
        address _user,
        bytes calldata _verificationContext,
        uint8 accessType
    ) external returns (bool success, bytes memory returnData);
}

File 11 of 20 : SovereignALMStructs.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

struct ALMLiquidityQuoteInput {
    bool isZeroToOne;
    uint256 amountInMinusFee;
    uint256 feeInBips;
    address sender;
    address recipient;
    address tokenOutSwap;
}

struct ALMLiquidityQuote {
    bool isCallbackOnSwap;
    uint256 amountOut;
    uint256 amountInFilled;
}

File 12 of 20 : ISovereignVaultMinimal.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

/**
    @title Minimal interface for Sovereign Pool's custom vault.
    @dev Sovereign Pools can choose to store their token0 and token1
         reserves in this contract.
         Sovereign Vault allows LPs to define where funds should be stored
         on deposits, withdrawals and swaps. Examples:
         - A custom LP vault which can provide liquidity to multiple pools on request.
         - A singleton contract.
         - Any external protocol that provides liquidity to incoming swaps on request.
         Moreover, it supports any number of tokens.
    @dev This is meant to be a minimal interface, containing only the functions
         required for Sovereign Pools to interact with.
 */
interface ISovereignVaultMinimal {
    /**
        @notice Returns array of tokens which can be swapped against for a given Sovereign Pool.
        @param _pool Sovereign Pool to query tokens for.
     */
    function getTokensForPool(address _pool) external view returns (address[] memory);

    /**
        @notice Returns reserve amounts available for a given Sovereign Pool.
        @param _pool Sovereign Pool to query token reserves for.
        @param _tokens Token addresses to query reserves for.
        @dev The full array of available tokens can be retrieved by calling `getTokensForPool` beforehand.
     */
    function getReservesForPool(address _pool, address[] calldata _tokens) external view returns (uint256[] memory);

    /**
        @notice Allows pool to attempt to claim due amount of `poolManager` fees.
        @dev Only callable by a Sovereign Pool. 
        @dev This is required, since on every swap, input token amounts are transferred
             from user into `sovereignVault`, to save on gas. Hence manager fees
             can only be claimed via this separate call.
        @param _feePoolManager0 Amount of token0 due to `poolManager`.
        @param _feePoolManager1 Amount of token1 due to `poolManager`.
     */
    function claimPoolManagerFees(uint256 _feePoolManager0, uint256 _feePoolManager1) external;
}

File 13 of 20 : ISovereignALM.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import { ALMLiquidityQuoteInput, ALMLiquidityQuote } from '../structs/SovereignALMStructs.sol';

/**
    @title Sovereign ALM interface
    @notice All ALMs bound to a Sovereign Pool must implement it.
 */
interface ISovereignALM {
    /** 
        @notice Called by the Sovereign pool to request a liquidity quote from the ALM.
        @param _almLiquidityQuoteInput Contains fundamental data about the swap.
        @param _externalContext Data received by the pool from the user.
        @param _verifierData Verification data received by the pool from the verifier module
        @return almLiquidityQuote Liquidity quote containing tokenIn and tokenOut amounts filled.
    */
    function getLiquidityQuote(
        ALMLiquidityQuoteInput memory _almLiquidityQuoteInput,
        bytes calldata _externalContext,
        bytes calldata _verifierData
    ) external returns (ALMLiquidityQuote memory);

    /**
        @notice Callback function for `depositLiquidity` .
        @param _amount0 Amount of token0 being deposited.
        @param _amount1 Amount of token1 being deposited.
        @param _data Context data passed by the ALM, while calling `depositLiquidity`.
    */
    function onDepositLiquidityCallback(uint256 _amount0, uint256 _amount1, bytes memory _data) external;

    /**
        @notice Callback to ALM after swap into liquidity pool.
        @dev Only callable by pool.
        @param _isZeroToOne Direction of swap.
        @param _amountIn Amount of tokenIn in swap.
        @param _amountOut Amount of tokenOut in swap. 
     */
    function onSwapCallback(bool _isZeroToOne, uint256 _amountIn, uint256 _amountOut) external;
}

File 14 of 20 : ISovereignOracle.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

interface ISovereignOracle {
    /**
        @notice Returns the address of the pool associated with the oracle.
        @return pool The address of the pool.
     */
    function pool() external view returns (address);

    /**
        @notice Writes an update to the oracle after a swap in the Sovereign Pool.
        @param isZeroToOne True if the swap is from token0 to token1, false otherwise.
        @param amountInMinusFee The amount of the tokenIn used minus fees.
        @param fee The fee amount.
        @param amountOut The amount of the tokenOut transferred to the user.
     */
    function writeOracleUpdate(bool isZeroToOne, uint256 amountInMinusFee, uint256 fee, uint256 amountOut) external;
}

File 15 of 20 : SovereignPoolStructs.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import { IERC20 } from '../../../lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol';

import { ISwapFeeModule } from '../../swap-fee-modules/interfaces/ISwapFeeModule.sol';

struct SovereignPoolConstructorArgs {
    address token0;
    address token1;
    address protocolFactory;
    address poolManager;
    address sovereignVault;
    address verifierModule;
    bool isToken0Rebase;
    bool isToken1Rebase;
    uint256 token0AbsErrorTolerance;
    uint256 token1AbsErrorTolerance;
    uint256 defaultSwapFeeBips;
}

struct SovereignPoolSwapContextData {
    bytes externalContext;
    bytes verifierContext;
    bytes swapCallbackContext;
    bytes swapFeeModuleContext;
}

struct SwapCache {
    ISwapFeeModule swapFeeModule;
    IERC20 tokenInPool;
    IERC20 tokenOutPool;
    uint256 amountInWithoutFee;
}

struct SovereignPoolSwapParams {
    bool isSwapCallback;
    bool isZeroToOne;
    uint256 amountIn;
    uint256 amountOutMin;
    uint256 deadline;
    address recipient;
    address swapTokenOut;
    SovereignPoolSwapContextData swapContext;
}

File 16 of 20 : IFlashBorrower.sol
// SPDX-License-Identifier: MIT

pragma solidity 0.8.19;

interface IFlashBorrower {
    /**
        @dev Receive a flash loan.
        @param initiator The initiator of the loan.
        @param token The loan currency.
        @param amount The amount of tokens lent.
        @param data Arbitrary data structure, intended to contain user-defined parameters.
        @return The keccak256 hash of "ERC3156FlashBorrower.onFlashLoan"
     */
    function onFlashLoan(
        address initiator,
        address token,
        uint256 amount,
        bytes calldata data
    ) external returns (bytes32);
}

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

pragma solidity ^0.8.0;

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

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

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

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

pragma solidity ^0.8.1;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev Returns true if `account` is a contract.
     *
     * [IMPORTANT]
     * ====
     * It is unsafe to assume that an address for which this function returns
     * false is an externally-owned account (EOA) and not a contract.
     *
     * Among others, `isContract` will return false for the following
     * types of addresses:
     *
     *  - an externally-owned account
     *  - a contract in construction
     *  - an address where a contract will be created
     *  - an address where a contract lived, but was destroyed
     *
     * Furthermore, `isContract` will also return true if the target contract within
     * the same transaction is already scheduled for destruction by `SELFDESTRUCT`,
     * which only has an effect at the end of a transaction.
     * ====
     *
     * [IMPORTANT]
     * ====
     * You shouldn't rely on `isContract` to protect against flash loan attacks!
     *
     * Preventing calls from contracts is highly discouraged. It breaks composability, breaks support for smart wallets
     * like Gnosis Safe, and does not provide security since it can be circumvented by calling from a contract
     * constructor.
     * ====
     */
    function isContract(address account) internal view returns (bool) {
        // This method relies on extcodesize/address.code.length, which returns 0
        // for contracts in construction, since the code is only stored at the end
        // of the constructor execution.

        return account.code.length > 0;
    }

    /**
     * @dev Replacement for Solidity's `transfer`: sends `amount` wei to
     * `recipient`, forwarding all available gas and reverting on errors.
     *
     * https://eips.ethereum.org/EIPS/eip-1884[EIP1884] increases the gas cost
     * of certain opcodes, possibly making contracts go over the 2300 gas limit
     * imposed by `transfer`, making them unable to receive funds via
     * `transfer`. {sendValue} removes this limitation.
     *
     * https://consensys.net/diligence/blog/2019/09/stop-using-soliditys-transfer-now/[Learn more].
     *
     * IMPORTANT: because control is transferred to `recipient`, care must be
     * taken to not create reentrancy vulnerabilities. Consider using
     * {ReentrancyGuard} or the
     * https://solidity.readthedocs.io/en/v0.8.0/security-considerations.html#use-the-checks-effects-interactions-pattern[checks-effects-interactions pattern].
     */
    function sendValue(address payable recipient, uint256 amount) internal {
        require(address(this).balance >= amount, "Address: insufficient balance");

        (bool success, ) = recipient.call{value: amount}("");
        require(success, "Address: unable to send value, recipient may have reverted");
    }

    /**
     * @dev Performs a Solidity function call using a low level `call`. A
     * plain `call` is an unsafe replacement for a function call: use this
     * function instead.
     *
     * If `target` reverts with a revert reason, it is bubbled up by this
     * function (like regular Solidity function calls).
     *
     * Returns the raw returned data. To convert to the expected return value,
     * use https://solidity.readthedocs.io/en/latest/units-and-global-variables.html?highlight=abi.decode#abi-encoding-and-decoding-functions[`abi.decode`].
     *
     * Requirements:
     *
     * - `target` must be a contract.
     * - calling `target` with `data` must not revert.
     *
     * _Available since v3.1._
     */
    function functionCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, "Address: low-level call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`], but with
     * `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        return functionCallWithValue(target, data, 0, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        return functionCallWithValue(target, data, value, "Address: low-level call with value failed");
    }

    /**
     * @dev Same as {xref-Address-functionCallWithValue-address-bytes-uint256-}[`functionCallWithValue`], but
     * with `errorMessage` as a fallback revert reason when `target` reverts.
     *
     * _Available since v3.1._
     */
    function functionCallWithValue(
        address target,
        bytes memory data,
        uint256 value,
        string memory errorMessage
    ) internal returns (bytes memory) {
        require(address(this).balance >= value, "Address: insufficient balance for call");
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(address target, bytes memory data) internal view returns (bytes memory) {
        return functionStaticCall(target, data, "Address: low-level static call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a static call.
     *
     * _Available since v3.3._
     */
    function functionStaticCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        (bool success, bytes memory returndata) = target.staticcall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(address target, bytes memory data) internal returns (bytes memory) {
        return functionDelegateCall(target, data, "Address: low-level delegate call failed");
    }

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-string-}[`functionCall`],
     * but performing a delegate call.
     *
     * _Available since v3.4._
     */
    function functionDelegateCall(
        address target,
        bytes memory data,
        string memory errorMessage
    ) internal returns (bytes memory) {
        (bool success, bytes memory returndata) = target.delegatecall(data);
        return verifyCallResultFromTarget(target, success, returndata, errorMessage);
    }

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and revert (either by bubbling
     * the revert reason or using the provided one) in case of unsuccessful call or if target was not a contract.
     *
     * _Available since v4.8._
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata,
        string memory errorMessage
    ) internal view returns (bytes memory) {
        if (success) {
            if (returndata.length == 0) {
                // only check isContract if the call was successful and the return data is empty
                // otherwise we already know that it was a contract
                require(isContract(target), "Address: call to non-contract");
            }
            return returndata;
        } else {
            _revert(returndata, errorMessage);
        }
    }

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

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

File 19 of 20 : IValantisPool.sol
// SPDX-License-Identifier: MIT
pragma solidity 0.8.19;

import { IFlashBorrower } from './IFlashBorrower.sol';

interface IValantisPool {
    /************************************************
     *  EVENTS
     ***********************************************/

    event Flashloan(address indexed initiator, address indexed receiver, uint256 amount, address token);

    /************************************************
     *  ERRORS
     ***********************************************/

    error ValantisPool__flashloan_callbackFailed();
    error ValantisPool__flashLoan_flashLoanDisabled();
    error ValantisPool__flashLoan_flashLoanNotRepaid();
    error ValantisPool__flashLoan_rebaseTokenNotAllowed();

    /************************************************
     *  VIEW FUNCTIONS
     ***********************************************/

    /**
        @notice Address of ERC20 token0 of the pool.
     */
    function token0() external view returns (address);

    /**
        @notice Address of ERC20 token1 of the pool.
     */
    function token1() external view returns (address);

    /************************************************
     *  EXTERNAL FUNCTIONS
     ***********************************************/

    /**
        @notice Claim share of protocol fees accrued by this pool.
        @dev Can only be claimed by `gauge` of the pool. 
     */
    function claimProtocolFees() external returns (uint256, uint256);

    /**
        @notice Claim share of fees accrued by this pool
                And optionally share some with the protocol.
        @dev Only callable by `poolManager`.
        @param _feeProtocol0Bips Percent of `token0` fees to be shared with protocol.
        @param _feeProtocol1Bips Percent of `token1` fees to be shared with protocol.
     */
    function claimPoolManagerFees(
        uint256 _feeProtocol0Bips,
        uint256 _feeProtocol1Bips
    ) external returns (uint256 feePoolManager0Received, uint256 feePoolManager1Received);

    /**
        @notice Sets the gauge contract address for the pool.
        @dev Only callable by `protocolFactory`.
        @dev Once a gauge is set it cannot be changed again.
        @param _gauge address of the gauge.
     */
    function setGauge(address _gauge) external;

    /**
        @notice Allows anyone to flash loan any amount of tokens from the pool.
        @param _isTokenZero True if token0 is being flash loaned, False otherwise.
        @param _receiver Address of the flash loan receiver.
        @param _amount Amount of tokens to be flash loaned.
        @param _data Bytes encoded data for flash loan callback.
     */
    function flashLoan(bool _isTokenZero, IFlashBorrower _receiver, uint256 _amount, bytes calldata _data) external;
}

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

enum Lock {
    WITHDRAWAL,
    DEPOSIT,
    SWAP,
    SPOT_PRICE_TICK
}

struct PoolLocks {
    /**
        @notice Locks all functions that require any withdrawal of funds from the pool
                This involves the following functions -
                * withdrawLiquidity
                * claimProtocolFees
                * claimPoolManagerFees
     */
    uint8 withdrawals;
    /**
        @notice Only locks the deposit function
    */
    uint8 deposit;
    /**
        @notice Only locks the swap function
    */
    uint8 swap;
    /**
        @notice Only locks the spotPriceTick function
    */
    uint8 spotPriceTick;
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "valantis-core/=lib/valantis-core/",
    "@uniswap/v3-periphery/=lib/v3-periphery/",
    "@uniswap/v3-core/=lib/v3-core/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/valantis-core/lib/openzeppelin-contracts/lib/erc4626-tests/",
    "openzeppelin-contracts/=lib/valantis-core/lib/openzeppelin-contracts/",
    "openzeppelin/=lib/valantis-core/lib/openzeppelin-contracts/contracts/",
    "v3-core/=lib/v3-core/contracts/",
    "v3-periphery/=lib/v3-periphery/contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 20000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": false,
  "libraries": {}
}

Contract Security Audit

Contract ABI

[{"inputs":[{"components":[{"internalType":"address","name":"token0","type":"address"},{"internalType":"address","name":"token1","type":"address"},{"internalType":"address","name":"protocolFactory","type":"address"},{"internalType":"address","name":"poolManager","type":"address"},{"internalType":"address","name":"sovereignVault","type":"address"},{"internalType":"address","name":"verifierModule","type":"address"},{"internalType":"bool","name":"isToken0Rebase","type":"bool"},{"internalType":"bool","name":"isToken1Rebase","type":"bool"},{"internalType":"uint256","name":"token0AbsErrorTolerance","type":"uint256"},{"internalType":"uint256","name":"token1AbsErrorTolerance","type":"uint256"},{"internalType":"uint256","name":"defaultSwapFeeBips","type":"uint256"}],"internalType":"struct SovereignPoolConstructorArgs","name":"args","type":"tuple"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"SovereignPool__ALMAlreadySet","type":"error"},{"inputs":[],"name":"SovereignPool__ZeroAddress","type":"error"},{"inputs":[],"name":"SovereignPool___claimPoolManagerFees_invalidFeeReceived","type":"error"},{"inputs":[],"name":"SovereignPool___claimPoolManagerFees_invalidProtocolFee","type":"error"},{"inputs":[],"name":"SovereignPool___handleTokenInOnSwap_excessiveTokenInErrorOnTransfer","type":"error"},{"inputs":[],"name":"SovereignPool___handleTokenInOnSwap_invalidTokenInAmount","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"},{"internalType":"uint8","name":"accessType","type":"uint8"}],"name":"SovereignPool___verifyPermission_onlyPermissionedAccess","type":"error"},{"inputs":[],"name":"SovereignPool__depositLiquidity_depositDisabled","type":"error"},{"inputs":[],"name":"SovereignPool__depositLiquidity_excessiveToken0ErrorOnTransfer","type":"error"},{"inputs":[],"name":"SovereignPool__depositLiquidity_excessiveToken1ErrorOnTransfer","type":"error"},{"inputs":[],"name":"SovereignPool__depositLiquidity_incorrectTokenAmount","type":"error"},{"inputs":[],"name":"SovereignPool__depositLiquidity_insufficientToken0Amount","type":"error"},{"inputs":[],"name":"SovereignPool__depositLiquidity_insufficientToken1Amount","type":"error"},{"inputs":[],"name":"SovereignPool__depositLiquidity_zeroTotalDepositAmount","type":"error"},{"inputs":[],"name":"SovereignPool__excessiveToken0AbsErrorTolerance","type":"error"},{"inputs":[],"name":"SovereignPool__excessiveToken1AbsErrorTolerance","type":"error"},{"inputs":[],"name":"SovereignPool__getReserves_invalidReservesLength","type":"error"},{"inputs":[],"name":"SovereignPool__onlyALM","type":"error"},{"inputs":[],"name":"SovereignPool__onlyGauge","type":"error"},{"inputs":[],"name":"SovereignPool__onlyPoolManager","type":"error"},{"inputs":[],"name":"SovereignPool__onlyProtocolFactory","type":"error"},{"inputs":[],"name":"SovereignPool__sameTokenNotAllowed","type":"error"},{"inputs":[],"name":"SovereignPool__setGauge_gaugeAlreadySet","type":"error"},{"inputs":[],"name":"SovereignPool__setGauge_invalidAddress","type":"error"},{"inputs":[],"name":"SovereignPool__setPoolManagerFeeBips_excessivePoolManagerFee","type":"error"},{"inputs":[],"name":"SovereignPool__setSovereignOracle_oracleDisabled","type":"error"},{"inputs":[],"name":"SovereignPool__setSovereignOracle_sovereignOracleAlreadySet","type":"error"},{"inputs":[],"name":"SovereignPool__setSwapFeeModule_timelock","type":"error"},{"inputs":[],"name":"SovereignPool__swap_excessiveSwapFee","type":"error"},{"inputs":[],"name":"SovereignPool__swap_expired","type":"error"},{"inputs":[],"name":"SovereignPool__swap_insufficientAmountIn","type":"error"},{"inputs":[],"name":"SovereignPool__swap_invalidLiquidityQuote","type":"error"},{"inputs":[],"name":"SovereignPool__swap_invalidPoolTokenOut","type":"error"},{"inputs":[],"name":"SovereignPool__swap_invalidRecipient","type":"error"},{"inputs":[],"name":"SovereignPool__swap_invalidSwapTokenOut","type":"error"},{"inputs":[],"name":"SovereignPool__swap_zeroAmountInOrOut","type":"error"},{"inputs":[],"name":"SovereignPool__withdrawLiquidity_insufficientReserve0","type":"error"},{"inputs":[],"name":"SovereignPool__withdrawLiquidity_insufficientReserve1","type":"error"},{"inputs":[],"name":"SovereignPool__withdrawLiquidity_invalidRecipient","type":"error"},{"inputs":[],"name":"SovereignPool__withdrawLiquidity_withdrawDisabled","type":"error"},{"inputs":[],"name":"ValantisPool__flashLoan_flashLoanDisabled","type":"error"},{"inputs":[],"name":"ValantisPool__flashLoan_flashLoanNotRepaid","type":"error"},{"inputs":[],"name":"ValantisPool__flashLoan_rebaseTokenNotAllowed","type":"error"},{"inputs":[],"name":"ValantisPool__flashloan_callbackFailed","type":"error"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"alm","type":"address"}],"name":"ALMSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"DepositLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"initiator","type":"address"},{"indexed":true,"internalType":"address","name":"receiver","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"},{"indexed":false,"internalType":"address","name":"token","type":"address"}],"name":"Flashloan","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"gauge","type":"address"}],"name":"GaugeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"poolManagerFeeBips","type":"uint256"}],"name":"PoolManagerFeeSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"PoolManagerFeesClaimed","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"poolManager","type":"address"}],"name":"PoolManagerSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"sovereignOracle","type":"address"}],"name":"SovereignOracleSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"sender","type":"address"},{"indexed":false,"internalType":"bool","name":"isZeroToOne","type":"bool"},{"indexed":false,"internalType":"uint256","name":"amountIn","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amountOut","type":"uint256"}],"name":"Swap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"swapFeeModule","type":"address"}],"name":"SwapFeeModuleSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"recipient","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount0","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"amount1","type":"uint256"}],"name":"WithdrawLiquidity","type":"event"},{"inputs":[],"name":"alm","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_feeProtocol0Bips","type":"uint256"},{"internalType":"uint256","name":"_feeProtocol1Bips","type":"uint256"}],"name":"claimPoolManagerFees","outputs":[{"internalType":"uint256","name":"feePoolManager0Received","type":"uint256"},{"internalType":"uint256","name":"feePoolManager1Received","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"claimProtocolFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"defaultSwapFeeBips","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount0","type":"uint256"},{"internalType":"uint256","name":"_amount1","type":"uint256"},{"internalType":"address","name":"_sender","type":"address"},{"internalType":"bytes","name":"_verificationContext","type":"bytes"},{"internalType":"bytes","name":"_depositData","type":"bytes"}],"name":"depositLiquidity","outputs":[{"internalType":"uint256","name":"amount0Deposited","type":"uint256"},{"internalType":"uint256","name":"amount1Deposited","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"feePoolManager0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feePoolManager1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeProtocol0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"feeProtocol1","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bool","name":"_isTokenZero","type":"bool"},{"internalType":"contract IFlashBorrower","name":"_receiver","type":"address"},{"internalType":"uint256","name":"_amount","type":"uint256"},{"internalType":"bytes","name":"_data","type":"bytes"}],"name":"flashLoan","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gauge","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getPoolManagerFees","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getReserves","outputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokens","outputs":[{"internalType":"address[]","name":"tokens","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isLocked","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isRebaseTokenPool","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isToken0Rebase","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"isToken1Rebase","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"poolManagerFeeBips","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"protocolFactory","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_alm","type":"address"}],"name":"setALM","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_gauge","type":"address"}],"name":"setGauge","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"_manager","type":"address"}],"name":"setPoolManager","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"_poolManagerFeeBips","type":"uint256"}],"name":"setPoolManagerFeeBips","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"sovereignOracle","type":"address"}],"name":"setSovereignOracle","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"swapFeeModule_","type":"address"}],"name":"setSwapFeeModule","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"sovereignOracleModule","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"sovereignVault","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"bool","name":"isSwapCallback","type":"bool"},{"internalType":"bool","name":"isZeroToOne","type":"bool"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOutMin","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"address","name":"swapTokenOut","type":"address"},{"components":[{"internalType":"bytes","name":"externalContext","type":"bytes"},{"internalType":"bytes","name":"verifierContext","type":"bytes"},{"internalType":"bytes","name":"swapCallbackContext","type":"bytes"},{"internalType":"bytes","name":"swapFeeModuleContext","type":"bytes"}],"internalType":"struct SovereignPoolSwapContextData","name":"swapContext","type":"tuple"}],"internalType":"struct SovereignPoolSwapParams","name":"_swapParams","type":"tuple"}],"name":"swap","outputs":[{"internalType":"uint256","name":"amountInUsed","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"swapFeeModule","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"swapFeeModuleUpdateTimestamp","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token0","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token0AbsErrorTolerance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token1","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"token1AbsErrorTolerance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"verifierModule","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"_amount0","type":"uint256"},{"internalType":"uint256","name":"_amount1","type":"uint256"},{"internalType":"address","name":"_sender","type":"address"},{"internalType":"address","name":"_recipient","type":"address"},{"internalType":"bytes","name":"_verificationContext","type":"bytes"}],"name":"withdrawLiquidity","outputs":[],"stateMutability":"nonpayable","type":"function"}]

6101c06040523480156200001257600080fd5b5060405162004e9d38038062004e9d83398101604081905262000035916200022f565b6001600055602081015181516001600160a01b039182169116036200006d57604051633666f5ad60e11b815260040160405180910390fd5b80516001600160a01b0316158062000090575060208101516001600160a01b0316155b15620000af576040516383c6c53360e01b815260040160405180910390fd5b60808101516001600160a01b031615620000ce578060800151620000d0565b305b6001600160a01b0390811660805260a082810151821660e0908152835183166101009081526020850151841661012052604085015184169092526060840151600380546001600160a01b031916919094161790925560c083015115156101405290820151151561016052810151600a10156200015f57604051630f33766d60e21b815260040160405180910390fd5b600a8161012001511115620001875760405163483569e560e11b815260040160405180910390fd5b610100810151610180526101208101516101a0526101408101516127101015620001b457612710620001bb565b8061014001515b60c052504260095562000309565b60405161016081016001600160401b0381118282101715620001fb57634e487b7160e01b600052604160045260246000fd5b60405290565b80516001600160a01b03811681146200021957600080fd5b919050565b805180151581146200021957600080fd5b600061016082840312156200024357600080fd5b6200024d620001c9565b620002588362000201565b8152620002686020840162000201565b60208201526200027b6040840162000201565b60408201526200028e6060840162000201565b6060820152620002a16080840162000201565b6080820152620002b460a0840162000201565b60a0820152620002c760c084016200021e565b60c0820152620002da60e084016200021e565b60e082015261010083810151908201526101208084015190820152610140928301519281019290925250919050565b60805160a05160c05160e05161010051610120516101405161016051610180516101a0516149126200058b6000396000818161034601528181611840015261300101526000818161039a015281816117020152613027015260008181610474015281816117f601528181611d9c01528181612233015281816126840152818161294901528181612f860152818161323801526132f001526000818161030f015281816116b801528181611d6d015281816122590152818161265d0152818161287301528181612fac0152818161315c01526132b00152600081816105a6015281816106d30152818161097001528181610a030152818161146f01528181611639015281816119ed01528181611e16015281816121e2015281816127510152818161299b015281816137740152818161396a0152613aee0152600081816102d20152818161067f01528181610996015281816109dd015281816113c001528181611580015281816119ae01528181611ddc01528181612208015281816126fd015281816128c5015281816136c5015281816138b70152613ab40152600081816103be01528181610b530152818161135301528181611ca90152612a580152600081816105820152610ce80152600081816105e2015261357601526000818161055b015281816106130152818161074601528181610abd015281816112b501528181611c470152818161206601528181612179015281816126ae015281816127d401528181612c9a01528181612d9c01528181612ea401528181612ef9015281816130cd01528181613195015281816132050152818161327d015281816133290152818161339f015281816133f40152818161366f015261381801526149126000f3fe608060405234801561001057600080fd5b50600436106102925760003560e01c80638a7dbaa211610160578063a8f0aa42116100d8578063c22c20841161008c578063dc4c90d311610071578063dc4c90d3146105ca578063f489048a146105dd578063fa9f1cf41461060457600080fd5b8063c22c20841461057d578063d21220a7146105a457600080fd5b8063b0de1fe2116100bd578063b0de1fe21461053a578063b8f6eb8a14610543578063baad44eb1461055657600080fd5b8063a8f0aa421461051c578063aa6ca8081461052557600080fd5b80639e25bc7d1161012f578063a040d22311610114578063a040d223146104f5578063a4e2d634146104fe578063a6f19c841461050957600080fd5b80639e25bc7d146104da5780639f3a3a67146104ed57600080fd5b80638a7dbaa2146104965780638d6d9599146104a95780638fa03de7146104bc57806395c4d51e146104cf57600080fd5b806341506fc11161020e578063712290c0116101c2578063780ef175116101a7578063780ef175146104495780637aef67151461045c5780637b4f8c651461046f57600080fd5b8063712290c01461042557806373f4ea121461043857600080fd5b80634a7d0369116101f35780634a7d0369146103f557806355a68ed3146103fd57806361b9c3ec1461041257600080fd5b806341506fc1146103bc57806341a41e9e146103e257600080fd5b80631d163adf116102655780632ddf0fa11161024a5780632ddf0fa114610379578063373290091461038257806339ba11f61461039557600080fd5b80631d163adf1461034157806323c43a511461036857600080fd5b806302a1b32c146102975780630902f1ac146102b35780630dfe1681146102d05780631645118b1461030a575b600080fd5b6102a060085481565b6040519081526020015b60405180910390f35b6102bb61060d565b604080519283526020830191909152016102aa565b7f00000000000000000000000000000000000000000000000000000000000000005b6040516001600160a01b0390911681526020016102aa565b6103317f000000000000000000000000000000000000000000000000000000000000000081565b60405190151581526020016102aa565b6102a07f000000000000000000000000000000000000000000000000000000000000000081565b600d546001600160a01b03166102f2565b6102a060045481565b6102bb610390366004613e02565b610861565b6102a07f000000000000000000000000000000000000000000000000000000000000000081565b7f00000000000000000000000000000000000000000000000000000000000000006102f2565b6102bb6103f0366004613e95565b6112a0565b6102bb61197d565b61041061040b366004613f2b565b611a23565b005b610410610420366004613f2b565b611b2d565b610410610433366004613f48565b611bf5565b600c546001600160a01b03166102f2565b6102bb610457366004613fc4565b611e93565b61041061046a366004613f2b565b611ec7565b6103317f000000000000000000000000000000000000000000000000000000000000000081565b6104106104a4366004613fe6565b611f93565b6104106104b7366004613f2b565b612014565b6104106104ca36600461400d565b61216f565b6005546006546102bb565b6104106104e8366004613f2b565b612560565b610331612659565b6102a060095481565b600054600214610331565b6002546102f2906001600160a01b031681565b6102a060065481565b61052d6126a9565b6040516102aa91906140c4565b6102a060075481565b6001546102f2906001600160a01b031681565b6102f27f000000000000000000000000000000000000000000000000000000000000000081565b6102a07f000000000000000000000000000000000000000000000000000000000000000081565b7f00000000000000000000000000000000000000000000000000000000000000006102f2565b6003546102f2906001600160a01b031681565b6102f27f000000000000000000000000000000000000000000000000000000000000000081565b6102a060055481565b600080307f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03160361065c5761064a6001612869565b6106546000612869565b915091509091565b6040805160028082526060820183526000926020830190803683370190505090507f0000000000000000000000000000000000000000000000000000000000000000816000815181106106b1576106b1614106565b60200260200101906001600160a01b031690816001600160a01b0316815250507f00000000000000000000000000000000000000000000000000000000000000008160018151811061070557610705614106565b6001600160a01b0392831660209182029290920101526040517f9ffdefad0000000000000000000000000000000000000000000000000000000081526000917f00000000000000000000000000000000000000000000000000000000000000001690639ffdefad9061077d9030908690600401614135565b600060405180830381865afa15801561079a573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526107e091908101906141ca565b9050805160021461081d576040517f77452a8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060008151811061083057610830614106565b60200260200101518160018151811061084b5761084b614106565b60200260200101519350935050509091565b9091565b60008061086c6129da565b82608001354211156108aa576040517f4c78bac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82604001356000036108e8576040517fccc5071100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006108fa60c0850160a08601613f2b565b6001600160a01b03160361093a576040517f24fb866e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051608081018252600d546001600160a01b0316815260009160208083019161096a91908801908801614255565b610994577f00000000000000000000000000000000000000000000000000000000000000006109b6565b7f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b031681526020018560200160208101906109d79190614255565b610a01577f0000000000000000000000000000000000000000000000000000000000000000610a23565b7f00000000000000000000000000000000000000000000000000000000000000005b6001600160a01b0316815260006020909101819052909150610a4b60e0860160c08701613f2b565b6001600160a01b03161480610a84575060208101516001600160a01b0316610a7960e0860160c08701613f2b565b6001600160a01b0316145b15610abb576040517f5746343e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031630148015610b18575060408101516001600160a01b0316610b0c60e0860160c08701613f2b565b6001600160a01b031614155b15610b4f576040517f6a3d76f400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60607f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031615610ba957610ba633610b9160e0880188614272565b610b9f9060208101906142b0565b6000612a52565b90505b60408051808201909152600081526060602082015282516001600160a01b031615610cdc5782600001516001600160a01b0316634c7b5106846020015185604001518960400135338b8060e00190610c019190614272565b610c0f9060608101906142b0565b6040518763ffffffff1660e01b8152600401610c309695949392919061438d565b6000604051808303816000875af1158015610c4f573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610c95919081019061447a565b905061271081600001511115610cd7576040517f3e79e9d600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d1f565b506040805180820182527f000000000000000000000000000000000000000000000000000000000000000081528151600081526020808201909352918101919091525b610d4186604001356127108360000151612710610d3c9190614536565b612b65565b60608401526001546040805160c0810182526000926001600160a01b03169163ede5e58491908190610d78908c0160208d01614255565b151581526060808901516020830152865160408301523390820152608001610da660c08c0160a08d01613f2b565b6001600160a01b03168152602001610dc460e08c0160c08d01613f2b565b6001600160a01b03169052610ddc60e08b018b614272565b610de690806142b0565b876040518563ffffffff1660e01b8152600401610e069493929190614593565b6060604051808303816000875af1158015610e25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e499190614608565b905080602001519450610e7d876020016020810190610e689190614255565b85606001518360400151888b60600135612c95565b610eb3576040517f457868f600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b841580610ec257506040810151155b15610ef9576040517f55d450f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008460600151826040015114610f3a57610f21826040015184600001516127106001612d0d565b9050808260400151610f339190614536565b9650610f57565b6060850151610f4d9060408a013561466a565b9050876040013596505b610f9e610f6a60408a0160208b01614255565b610f7760208b018b614255565b60208801518a85610f8b60e08f018f614272565b610f999060408101906142b0565b612d6c565b610fb9610fb160408a0160208b01614255565b888884613154565b600c546001600160a01b031615801590610ff7575060408501516001600160a01b0316610fec60e08a0160c08b01613f2b565b6001600160a01b0316145b80156110035750600087115b156110a857600c546001600160a01b031663819c3f4261102960408b0160208c01614255565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681529015156004820152602481018a90526044810184905260648101899052608401600060405180830381600087803b15801561108f57600080fd5b505af11580156110a3573d6000803e3d6000fd5b505050505b6110d16110bb60e08a0160c08b01613f2b565b6110cb60c08b0160a08c01613f2b565b8861339c565b84516001600160a01b03161580159061110d57506040805160008082526020820190925290508051906020012083602001518051906020012014155b156111935784516040517f280f19cf0000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063280f19cf906111609084908b908b90899060040161467d565b600060405180830381600087803b15801561117a57600080fd5b505af115801561118e573d6000803e3d6000fd5b505050505b815115611233576001546001600160a01b031663a3f3d7226111bb60408b0160208c01614255565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681529015156004820152602481018a905260448101899052606401600060405180830381600087803b15801561121a57600080fd5b505af115801561122e573d6000803e3d6000fd5b505050505b337f176648f1f11cda284c124490086be42a926ddf0ae887ebe7b1d6b337d894275661126560408b0160208c01614255565b604080519115158252602082018b905281018490526060810189905260800160405180910390a2505050505061129b6001600055565b915091565b6000806112ab61341a565b6112b36129da565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163014611315576040517f3e009a4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b878917600003611351576040517ffbfc52fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161561138f5761138d8787876001612a52565b505b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa15801561140f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061143391906146b5565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156114b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114da91906146b5565b6040517f2d4b23bd0000000000000000000000000000000000000000000000000000000081529091503390632d4b23bd9061151f908e908e908b908b906004016146ce565b600060405180830381600087803b15801561153957600080fd5b505af115801561154d573d6000803e3d6000fd5b50506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201528492507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031691506370a0823190602401602060405180830381865afa1580156115d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f491906146b5565b6115fe919061466a565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290945081906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa158015611680573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116a491906146b5565b6116ae919061466a565b92508a156117b6577f0000000000000000000000000000000000000000000000000000000000000000156117605760008b85106116f4576116ef8c8661466a565b6116fe565b6116fe858d61466a565b90507f000000000000000000000000000000000000000000000000000000000000000081111561175a576040517fc794db5300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506117ee565b8a8414611799576040517f245c1efb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83600a60008282546117ab9190614536565b909155506117ee9050565b83156117ee576040517f57330e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b89156118f4577f00000000000000000000000000000000000000000000000000000000000000001561189e5760008a84106118325761182d8b8561466a565b61183c565b61183c848c61466a565b90507f0000000000000000000000000000000000000000000000000000000000000000811115611898576040517fbdd9a55300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5061192c565b8983146118d7576040517ffb702a7900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82600b60008282546118e99190614536565b9091555061192c9050565b821561192c576040517f57330e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051858152602081018590527f5b479c5a76f3eddfff87849b354aee2a3fbdafd3c2c1a95561143bd2ec1f1e64910160405180910390a150506119716001600055565b97509795505050505050565b6000806119886129da565b611990613460565b60075460085481156119d55760006007556119d56001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633846134a4565b8015611a14576000600855611a146001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001633836134a4565b909250905061085d6001600055565b611a2b61356b565b611a336129da565b6002546001600160a01b031615611a76576040517f4937cf5100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116611ab6576040517f68dbb6bb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fb947d28129d3b40272a411eeee3a2842c6335a0f0269abf492e8c20218bf02b0906020015b60405180910390a1611b2a6001600055565b50565b611b356135cd565b611b3d6129da565b600954421015611b79576040517f39000a0500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038316179055611bb9426203f480614536565b6009556040516001600160a01b03821681527fe0d3edb906e9f17a6c8342bada5bdd7051f42bbed87eec9af9e69cd75ad98bd290602001611b18565b611bfd6129da565b611c0561341a565b6001600160a01b038316611c45576040517f8cfd9a7700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163014611ca7576040517fb0bb65d700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031615611ce557611ce38483836002612a52565b505b611cef6001612869565b861115611d28576040517f4d8313ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d326000612869565b851115611d6b576040517f8738d6cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000000611d9a57600a805487900390555b7f0000000000000000000000000000000000000000000000000000000000000000611dc957600b805486900390555b8515611e0357611e036001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001684886134a4565b8415611e3d57611e3d6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001684876134a4565b60408051878152602081018790526001600160a01b038516917fac927268ea9ae2e55027e6ab727fc2db8e3ea48c56c658223a1074567e4298c0910160405180910390a2611e8b6001600055565b505050505050565b600080611e9e6129da565b611ea66135cd565b611eb1848433613611565b9092509050611ec06001600055565b9250929050565b611ecf6135cd565b611ed76129da565b600380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038316908117909155611f5a5760006004819055611f23908033613611565b5050604051600081527f67c138aed690b53f8472c70911848132b03f2e8c321a03e5db379ad5e08502059060200160405180910390a15b6040516001600160a01b03821681527f181b126158932b45e642aeb48f81b946563bf584a35e78d6a25b013c8d41ff8f90602001611b18565b611f9b6135cd565b611fa36129da565b611388811115611fdf576040517f39f0f92c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60048190556040518181527f67c138aed690b53f8472c70911848132b03f2e8c321a03e5db379ad5e085020590602001611b18565b61201c6135cd565b6120246129da565b6001600160a01b038116612064576040517f83c6c53300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031630146120c6576040517fe9c9ef7800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c546001600160a01b031615612109576040517f5d906c2900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f307a86fd6204dd9b6a270c57d048c5f259625d260f8c03d1915a9fe6bc2708f090602001611b18565b6121776129da565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031630146121d9576040517fabf0792900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085612206577f0000000000000000000000000000000000000000000000000000000000000000612228565b7f00000000000000000000000000000000000000000000000000000000000000005b9050600086612257577f0000000000000000000000000000000000000000000000000000000000000000612279565b7f00000000000000000000000000000000000000000000000000000000000000005b905080156122b3576040517f785aa4db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa158015612313573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061233791906146b5565b905061234d6001600160a01b03841688886134a4565b6040517fc00c8b6c0000000000000000000000000000000000000000000000000000000081527f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd9906001600160a01b0389169063c00c8b6c906123bc90339088908c908c908c906004016146f8565b6020604051808303816000875af11580156123db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123ff91906146b5565b14612436576040517ff1b70e9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61244b6001600160a01b038416883089613b58565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015281906001600160a01b038516906370a0823190602401602060405180830381865afa1580156124aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124ce91906146b5565b14612505576040517ff633e4db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518781526001600160a01b03858116602083015289169133917fe875cf27c62067457025fb9b52111f2679e19907e5a1dcf2d8092afcd67cdc3a910160405180910390a35050506125596001600055565b5050505050565b6125686135cd565b6125706129da565b6001600160a01b0381166125b0576040517f83c6c53300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001546001600160a01b0316156125f3576040517f58c30fc000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f1d56850f49455a17df2bb41dd638797cdd462d3b4034b169d7979c2df141355490602001611b18565b60007f0000000000000000000000000000000000000000000000000000000000000000806126a457507f00000000000000000000000000000000000000000000000000000000000000005b905090565b6060307f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316036127a65760408051600280825260608201835290916020830190803683370190505090507f00000000000000000000000000000000000000000000000000000000000000008160008151811061272f5761272f614106565b60200260200101906001600160a01b031690816001600160a01b0316815250507f00000000000000000000000000000000000000000000000000000000000000008160018151811061278357612783614106565b60200260200101906001600160a01b031690816001600160a01b03168152505090565b6040517f0b6f5afe0000000000000000000000000000000000000000000000000000000081523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031690630b6f5afe90602401600060405180830381865afa158015612823573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526126a4919081019061472b565b60008115612947577f00000000000000000000000000000000000000000000000000000000000000001561293f576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a08231906024015b602060405180830381865afa158015612915573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061293991906146b5565b92915050565b5050600a5490565b7f0000000000000000000000000000000000000000000000000000000000000000156129d2576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a08231906024016128f8565b5050600b5490565b600260005403612a4b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b6002600055565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c0aa3cf8878787876040518563ffffffff1660e01b8152600401612aa894939291906147ba565b6000604051808303816000875af1158015612ac7573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052612b0d91908101906147f1565b9250905080612b5c576040517f3f16fa200000000000000000000000000000000000000000000000000000000081526001600160a01b038716600482015260ff84166024820152604401612a42565b50949350505050565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85870985870292508281108382030391505080600003612bbd57838281612bb357612bb3614842565b0492505050612c8e565b808411612c26576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4d6174683a206d756c446976206f766572666c6f7700000000000000000000006044820152606401612a42565b600084868809851960019081018716968790049682860381900495909211909303600082900391909104909201919091029190911760038402600290811880860282030280860282030280860282030280860282030280860282030280860290910302029150505b9392505050565b6000307f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031603612ce057612cd18615612869565b831115612ce057506000612d04565b81831015612cf057506000612d04565b84841115612d0057506000612d04565b5060015b95945050505050565b600080612d1b868686612b65565b90506001836002811115612d3157612d31614315565b148015612d4e575060008480612d4957612d49614842565b868809115b15612d6157612d5e600182614536565b90505b90505b949350505050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b037f000000000000000000000000000000000000000000000000000000000000000081166004830152600091908716906370a0823190602401602060405180830381865afa158015612def573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e1391906146b5565b90508615612e94576040517f9fbd86ad0000000000000000000000000000000000000000000000000000000081523390639fbd86ad90612e5d908990899088908890600401614871565b600060405180830381600087803b158015612e7757600080fd5b505af1158015612e8b573d6000803e3d6000fd5b50505050612ec9565b612ec96001600160a01b038716337f000000000000000000000000000000000000000000000000000000000000000088613b58565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b037f00000000000000000000000000000000000000000000000000000000000000008116600483015260009183918916906370a0823190602401602060405180830381865afa158015612f4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f7191906146b5565b612f7b919061466a565b9050600089612faa577f0000000000000000000000000000000000000000000000000000000000000000612fcc565b7f00000000000000000000000000000000000000000000000000000000000000005b9050801561308a576000828811612fec57612fe7888461466a565b612ff6565b612ff6838961466a565b905060008b613025577f0000000000000000000000000000000000000000000000000000000000000000613047565b7f00000000000000000000000000000000000000000000000000000000000000005b905080821115613083576040517f07ba54e500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50506130c3565b8682146130c3576040517f45df38c100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8080156130f857507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031630145b801561310e57506003546001600160a01b031615155b1561314857600061312487600454612710612b65565b9050801561314657600354613146906001600160a01b038b81169116836134a4565b505b50505050505050505050565b831561327b577f000000000000000000000000000000000000000000000000000000000000000061320357600061319082600454612710612b65565b9050307f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316036131e3576131cc818561466a565b600a60008282546131dd9190614536565b90915550505b80156132015780600560008282546131fb9190614536565b90915550505b505b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03163014801561325957507f0000000000000000000000000000000000000000000000000000000000000000155b156132765781600b6000828254613270919061466a565b90915550505b613396565b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316301480156132d157507f0000000000000000000000000000000000000000000000000000000000000000155b156132ee5781600a60008282546132e8919061466a565b90915550505b7f000000000000000000000000000000000000000000000000000000000000000061339657600061332482600454612710612b65565b9050307f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03160361337757613360818561466a565b600b60008282546133719190614536565b90915550505b801561255957806006600082825461338f9190614536565b9091555050505b50505050565b307f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316036133e5576133e06001600160a01b03841683836134a4565b505050565b6133e06001600160a01b0384167f00000000000000000000000000000000000000000000000000000000000000008484613b58565b6001546001600160a01b0316331461345e576040517fef410f2f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6002546001600160a01b0316331461345e576040517f6dfbb74500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516001600160a01b0383166024820152604481018290526133e09084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613ba9565b336001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000161461345e576040517fcadb26b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546001600160a01b0316331461345e576040517f6c54e96000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080612710851180613625575061271084115b1561365c576040517f50d83c0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050600554600654306001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001614613a2d576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316906370a0823190602401602060405180830381865afa158015613714573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061373891906146b5565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091506000906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156137bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137df91906146b5565b6040517f780ef17500000000000000000000000000000000000000000000000000000000815260048101869052602481018590529091507f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03169063780ef17590604401600060405180830381600087803b15801561386457600080fd5b505af1158015613878573d6000803e3d6000fd5b50506040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600092508491506001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156138fe573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061392291906146b5565b61392c919061466a565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290915060009083906001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016906370a0823190602401602060405180830381865afa1580156139b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139d591906146b5565b6139df919061466a565b9050858211806139ee57508481115b15613a25576040517fcae2d53800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b909450925050505b6000613a3c8684612710612b65565b90506000613a4d8684612710612b65565b90508160076000828254613a619190614536565b925050819055508060086000828254613a7a9190614536565b909155505060006005819055600655613a93828561466a565b9350613a9f818461466a565b92508315613adb57613adb6001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001686866134a4565b8215613b1557613b156001600160a01b037f00000000000000000000000000000000000000000000000000000000000000001686856134a4565b60408051858152602081018590527f9354b101c687c179e9516ece0f8b0cebbfdc205da033d49eb2b9598548ed75c2910160405180910390a15050935093915050565b6040516001600160a01b03808516602483015283166044820152606481018290526133969085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016134e9565b6000613bfe826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613cab9092919063ffffffff16565b9050805160001480613c1f575080806020019051810190613c1f919061489a565b6133e0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401612a42565b6060612d64848460008585600080866001600160a01b03168587604051613cd291906148b7565b60006040518083038185875af1925050503d8060008114613d0f576040519150601f19603f3d011682016040523d82523d6000602084013e613d14565b606091505b5091509150613d2587838387613d30565b979650505050505050565b60608315613db9578251600003613db2576001600160a01b0385163b613db2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401612a42565b5081612d64565b612d648383815115613dce5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612a4291906148c9565b600060208284031215613e1457600080fd5b813567ffffffffffffffff811115613e2b57600080fd5b82016101008185031215612c8e57600080fd5b6001600160a01b0381168114611b2a57600080fd5b60008083601f840112613e6557600080fd5b50813567ffffffffffffffff811115613e7d57600080fd5b602083019150836020828501011115611ec057600080fd5b600080600080600080600060a0888a031215613eb057600080fd5b87359650602088013595506040880135613ec981613e3e565b9450606088013567ffffffffffffffff80821115613ee657600080fd5b613ef28b838c01613e53565b909650945060808a0135915080821115613f0b57600080fd5b50613f188a828b01613e53565b989b979a50959850939692959293505050565b600060208284031215613f3d57600080fd5b8135612c8e81613e3e565b60008060008060008060a08789031215613f6157600080fd5b86359550602087013594506040870135613f7a81613e3e565b93506060870135613f8a81613e3e565b9250608087013567ffffffffffffffff811115613fa657600080fd5b613fb289828a01613e53565b979a9699509497509295939492505050565b60008060408385031215613fd757600080fd5b50508035926020909101359150565b600060208284031215613ff857600080fd5b5035919050565b8015158114611b2a57600080fd5b60008060008060006080868803121561402557600080fd5b853561403081613fff565b9450602086013561404081613e3e565b935060408601359250606086013567ffffffffffffffff81111561406357600080fd5b61406f88828901613e53565b969995985093965092949392505050565b600081518084526020808501945080840160005b838110156140b95781516001600160a01b031687529582019590820190600101614094565b509495945050505050565b602081526000612c8e6020830184614080565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6001600160a01b0383168152604060208201526000612d646040830184614080565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561419e5761419e6140d7565b604052919050565b600067ffffffffffffffff8211156141c0576141c06140d7565b5060051b60200190565b600060208083850312156141dd57600080fd5b825167ffffffffffffffff8111156141f457600080fd5b8301601f8101851361420557600080fd5b8051614218614213826141a6565b614157565b81815260059190911b8201830190838101908783111561423757600080fd5b928401925b82841015613d255783518252928401929084019061423c565b60006020828403121561426757600080fd5b8135612c8e81613fff565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818336030181126142a657600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126142e557600080fd5b83018035915067ffffffffffffffff82111561430057600080fd5b602001915036819003821315611ec057600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60006001600160a01b038089168352808816602084015286604084015280861660608401525060a060808301526143c860a083018486614344565b98975050505050505050565b60005b838110156143ef5781810151838201526020016143d7565b50506000910152565b600082601f83011261440957600080fd5b815167ffffffffffffffff811115614423576144236140d7565b61445460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614157565b81815284602083860101111561446957600080fd5b612d648260208301602087016143d4565b60006020828403121561448c57600080fd5b815167ffffffffffffffff808211156144a457600080fd5b90830190604082860312156144b857600080fd5b6040516040810181811083821117156144d3576144d36140d7565b604052825181526020830151828111156144ec57600080fd5b6144f8878286016143f8565b60208301525095945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561293957612939614507565b600081518084526145618160208601602086016143d4565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000610100865115158352602087015160208401526040870151604084015260608701516001600160a01b0380821660608601528060808a01511660808601528060a08a01511660a086015250508060c08401526145f48184018688614344565b905082810360e0840152613d258185614549565b60006060828403121561461a57600080fd5b6040516060810181811067ffffffffffffffff8211171561463d5761463d6140d7565b604052825161464b81613fff565b8152602083810151908201526040928301519281019290925250919050565b8181038181111561293957612939614507565b848152836020820152826040820152608060608201528151608082015260006020830151604060a0840152613d2560c0840182614549565b6000602082840312156146c757600080fd5b5051919050565b8481528360208201526060604082015260006146ee606083018486614344565b9695505050505050565b60006001600160a01b03808816835280871660208401525084604083015260806060830152613d25608083018486614344565b6000602080838503121561473e57600080fd5b825167ffffffffffffffff81111561475557600080fd5b8301601f8101851361476657600080fd5b8051614774614213826141a6565b81815260059190911b8201830190838101908783111561479357600080fd5b928401925b82841015613d255783516147ab81613e3e565b82529284019290840190614798565b6001600160a01b03851681526060602082015260006147dd606083018587614344565b905060ff8316604083015295945050505050565b6000806040838503121561480457600080fd5b825161480f81613fff565b602084015190925067ffffffffffffffff81111561482c57600080fd5b614838858286016143f8565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6001600160a01b03851681528360208201526060604082015260006146ee606083018486614344565b6000602082840312156148ac57600080fd5b8151612c8e81613fff565b600082516142a68184602087016143d4565b602081526000612c8e602083018461454956fea2646970667358221220c47f9fb4295775660d96e6e07dc64d779800a11096317fcef57fb8ad1f4f5a5164736f6c63430008130033000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000502c7c62cef3d7314597532510573b87663ef85b000000000000000000000000502c7c62cef3d7314597532510573b87663ef85b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

Deployed Bytecode

0x608060405234801561001057600080fd5b50600436106102925760003560e01c80638a7dbaa211610160578063a8f0aa42116100d8578063c22c20841161008c578063dc4c90d311610071578063dc4c90d3146105ca578063f489048a146105dd578063fa9f1cf41461060457600080fd5b8063c22c20841461057d578063d21220a7146105a457600080fd5b8063b0de1fe2116100bd578063b0de1fe21461053a578063b8f6eb8a14610543578063baad44eb1461055657600080fd5b8063a8f0aa421461051c578063aa6ca8081461052557600080fd5b80639e25bc7d1161012f578063a040d22311610114578063a040d223146104f5578063a4e2d634146104fe578063a6f19c841461050957600080fd5b80639e25bc7d146104da5780639f3a3a67146104ed57600080fd5b80638a7dbaa2146104965780638d6d9599146104a95780638fa03de7146104bc57806395c4d51e146104cf57600080fd5b806341506fc11161020e578063712290c0116101c2578063780ef175116101a7578063780ef175146104495780637aef67151461045c5780637b4f8c651461046f57600080fd5b8063712290c01461042557806373f4ea121461043857600080fd5b80634a7d0369116101f35780634a7d0369146103f557806355a68ed3146103fd57806361b9c3ec1461041257600080fd5b806341506fc1146103bc57806341a41e9e146103e257600080fd5b80631d163adf116102655780632ddf0fa11161024a5780632ddf0fa114610379578063373290091461038257806339ba11f61461039557600080fd5b80631d163adf1461034157806323c43a511461036857600080fd5b806302a1b32c146102975780630902f1ac146102b35780630dfe1681146102d05780631645118b1461030a575b600080fd5b6102a060085481565b6040519081526020015b60405180910390f35b6102bb61060d565b604080519283526020830191909152016102aa565b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485b6040516001600160a01b0390911681526020016102aa565b6103317f000000000000000000000000000000000000000000000000000000000000000081565b60405190151581526020016102aa565b6102a07f000000000000000000000000000000000000000000000000000000000000000081565b600d546001600160a01b03166102f2565b6102a060045481565b6102bb610390366004613e02565b610861565b6102a07f000000000000000000000000000000000000000000000000000000000000000081565b7f00000000000000000000000000000000000000000000000000000000000000006102f2565b6102bb6103f0366004613e95565b6112a0565b6102bb61197d565b61041061040b366004613f2b565b611a23565b005b610410610420366004613f2b565b611b2d565b610410610433366004613f48565b611bf5565b600c546001600160a01b03166102f2565b6102bb610457366004613fc4565b611e93565b61041061046a366004613f2b565b611ec7565b6103317f000000000000000000000000000000000000000000000000000000000000000081565b6104106104a4366004613fe6565b611f93565b6104106104b7366004613f2b565b612014565b6104106104ca36600461400d565b61216f565b6005546006546102bb565b6104106104e8366004613f2b565b612560565b610331612659565b6102a060095481565b600054600214610331565b6002546102f2906001600160a01b031681565b6102a060065481565b61052d6126a9565b6040516102aa91906140c4565b6102a060075481565b6001546102f2906001600160a01b031681565b6102f27f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c81565b6102a07f000000000000000000000000000000000000000000000000000000000000000081565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26102f2565b6003546102f2906001600160a01b031681565b6102f27f000000000000000000000000502c7c62cef3d7314597532510573b87663ef85b81565b6102a060055481565b600080307f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b03160361065c5761064a6001612869565b6106546000612869565b915091509091565b6040805160028082526060820183526000926020830190803683370190505090507f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48816000815181106106b1576106b1614106565b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28160018151811061070557610705614106565b6001600160a01b0392831660209182029290920101526040517f9ffdefad0000000000000000000000000000000000000000000000000000000081526000917f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c1690639ffdefad9061077d9030908690600401614135565b600060405180830381865afa15801561079a573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526107e091908101906141ca565b9050805160021461081d576040517f77452a8200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8060008151811061083057610830614106565b60200260200101518160018151811061084b5761084b614106565b60200260200101519350935050509091565b9091565b60008061086c6129da565b82608001354211156108aa576040517f4c78bac800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82604001356000036108e8576040517fccc5071100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60006108fa60c0850160a08601613f2b565b6001600160a01b03160361093a576040517f24fb866e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051608081018252600d546001600160a01b0316815260009160208083019161096a91908801908801614255565b610994577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26109b6565b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485b6001600160a01b031681526020018560200160208101906109d79190614255565b610a01577f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48610a23565b7f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc25b6001600160a01b0316815260006020909101819052909150610a4b60e0860160c08701613f2b565b6001600160a01b03161480610a84575060208101516001600160a01b0316610a7960e0860160c08701613f2b565b6001600160a01b0316145b15610abb576040517f5746343e00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b031630148015610b18575060408101516001600160a01b0316610b0c60e0860160c08701613f2b565b6001600160a01b031614155b15610b4f576040517f6a3d76f400000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60607f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031615610ba957610ba633610b9160e0880188614272565b610b9f9060208101906142b0565b6000612a52565b90505b60408051808201909152600081526060602082015282516001600160a01b031615610cdc5782600001516001600160a01b0316634c7b5106846020015185604001518960400135338b8060e00190610c019190614272565b610c0f9060608101906142b0565b6040518763ffffffff1660e01b8152600401610c309695949392919061438d565b6000604051808303816000875af1158015610c4f573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052610c95919081019061447a565b905061271081600001511115610cd7576040517f3e79e9d600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b610d1f565b506040805180820182527f000000000000000000000000000000000000000000000000000000000000000081528151600081526020808201909352918101919091525b610d4186604001356127108360000151612710610d3c9190614536565b612b65565b60608401526001546040805160c0810182526000926001600160a01b03169163ede5e58491908190610d78908c0160208d01614255565b151581526060808901516020830152865160408301523390820152608001610da660c08c0160a08d01613f2b565b6001600160a01b03168152602001610dc460e08c0160c08d01613f2b565b6001600160a01b03169052610ddc60e08b018b614272565b610de690806142b0565b876040518563ffffffff1660e01b8152600401610e069493929190614593565b6060604051808303816000875af1158015610e25573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190610e499190614608565b905080602001519450610e7d876020016020810190610e689190614255565b85606001518360400151888b60600135612c95565b610eb3576040517f457868f600000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b841580610ec257506040810151155b15610ef9576040517f55d450f000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60008460600151826040015114610f3a57610f21826040015184600001516127106001612d0d565b9050808260400151610f339190614536565b9650610f57565b6060850151610f4d9060408a013561466a565b9050876040013596505b610f9e610f6a60408a0160208b01614255565b610f7760208b018b614255565b60208801518a85610f8b60e08f018f614272565b610f999060408101906142b0565b612d6c565b610fb9610fb160408a0160208b01614255565b888884613154565b600c546001600160a01b031615801590610ff7575060408501516001600160a01b0316610fec60e08a0160c08b01613f2b565b6001600160a01b0316145b80156110035750600087115b156110a857600c546001600160a01b031663819c3f4261102960408b0160208c01614255565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681529015156004820152602481018a90526044810184905260648101899052608401600060405180830381600087803b15801561108f57600080fd5b505af11580156110a3573d6000803e3d6000fd5b505050505b6110d16110bb60e08a0160c08b01613f2b565b6110cb60c08b0160a08c01613f2b565b8861339c565b84516001600160a01b03161580159061110d57506040805160008082526020820190925290508051906020012083602001518051906020012014155b156111935784516040517f280f19cf0000000000000000000000000000000000000000000000000000000081526001600160a01b039091169063280f19cf906111609084908b908b90899060040161467d565b600060405180830381600087803b15801561117a57600080fd5b505af115801561118e573d6000803e3d6000fd5b505050505b815115611233576001546001600160a01b031663a3f3d7226111bb60408b0160208c01614255565b6040517fffffffff0000000000000000000000000000000000000000000000000000000060e084901b1681529015156004820152602481018a905260448101899052606401600060405180830381600087803b15801561121a57600080fd5b505af115801561122e573d6000803e3d6000fd5b505050505b337f176648f1f11cda284c124490086be42a926ddf0ae887ebe7b1d6b337d894275661126560408b0160208c01614255565b604080519115158252602082018b905281018490526060810189905260800160405180910390a2505050505061129b6001600055565b915091565b6000806112ab61341a565b6112b36129da565b7f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b03163014611315576040517f3e009a4200000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b878917600003611351576040517ffbfc52fd00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b03161561138f5761138d8787876001612a52565b505b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b0316906370a0823190602401602060405180830381865afa15801561140f573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061143391906146b5565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091506000906001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823190602401602060405180830381865afa1580156114b6573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906114da91906146b5565b6040517f2d4b23bd0000000000000000000000000000000000000000000000000000000081529091503390632d4b23bd9061151f908e908e908b908b906004016146ce565b600060405180830381600087803b15801561153957600080fd5b505af115801561154d573d6000803e3d6000fd5b50506040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201528492507f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b031691506370a0823190602401602060405180830381865afa1580156115d0573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906115f491906146b5565b6115fe919061466a565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290945081906001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823190602401602060405180830381865afa158015611680573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906116a491906146b5565b6116ae919061466a565b92508a156117b6577f0000000000000000000000000000000000000000000000000000000000000000156117605760008b85106116f4576116ef8c8661466a565b6116fe565b6116fe858d61466a565b90507f000000000000000000000000000000000000000000000000000000000000000081111561175a576040517fc794db5300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b506117ee565b8a8414611799576040517f245c1efb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b83600a60008282546117ab9190614536565b909155506117ee9050565b83156117ee576040517f57330e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b89156118f4577f00000000000000000000000000000000000000000000000000000000000000001561189e5760008a84106118325761182d8b8561466a565b61183c565b61183c848c61466a565b90507f0000000000000000000000000000000000000000000000000000000000000000811115611898576040517fbdd9a55300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5061192c565b8983146118d7576040517ffb702a7900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b82600b60008282546118e99190614536565b9091555061192c9050565b821561192c576040517f57330e8900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60408051858152602081018590527f5b479c5a76f3eddfff87849b354aee2a3fbdafd3c2c1a95561143bd2ec1f1e64910160405180910390a150506119716001600055565b97509795505050505050565b6000806119886129da565b611990613460565b60075460085481156119d55760006007556119d56001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb481633846134a4565b8015611a14576000600855611a146001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21633836134a4565b909250905061085d6001600055565b611a2b61356b565b611a336129da565b6002546001600160a01b031615611a76576040517f4937cf5100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001600160a01b038116611ab6576040517f68dbb6bb00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600280547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527fb947d28129d3b40272a411eeee3a2842c6335a0f0269abf492e8c20218bf02b0906020015b60405180910390a1611b2a6001600055565b50565b611b356135cd565b611b3d6129da565b600954421015611b79576040517f39000a0500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600d80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038316179055611bb9426203f480614536565b6009556040516001600160a01b03821681527fe0d3edb906e9f17a6c8342bada5bdd7051f42bbed87eec9af9e69cd75ad98bd290602001611b18565b611bfd6129da565b611c0561341a565b6001600160a01b038316611c45576040517f8cfd9a7700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b03163014611ca7576040517fb0bb65d700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031615611ce557611ce38483836002612a52565b505b611cef6001612869565b861115611d28576040517f4d8313ce00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b611d326000612869565b851115611d6b576040517f8738d6cc00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000000000000000000000000000000000000000000000611d9a57600a805487900390555b7f0000000000000000000000000000000000000000000000000000000000000000611dc957600b805486900390555b8515611e0357611e036001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb481684886134a4565b8415611e3d57611e3d6001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21684876134a4565b60408051878152602081018790526001600160a01b038516917fac927268ea9ae2e55027e6ab727fc2db8e3ea48c56c658223a1074567e4298c0910160405180910390a2611e8b6001600055565b505050505050565b600080611e9e6129da565b611ea66135cd565b611eb1848433613611565b9092509050611ec06001600055565b9250929050565b611ecf6135cd565b611ed76129da565b600380547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b038316908117909155611f5a5760006004819055611f23908033613611565b5050604051600081527f67c138aed690b53f8472c70911848132b03f2e8c321a03e5db379ad5e08502059060200160405180910390a15b6040516001600160a01b03821681527f181b126158932b45e642aeb48f81b946563bf584a35e78d6a25b013c8d41ff8f90602001611b18565b611f9b6135cd565b611fa36129da565b611388811115611fdf576040517f39f0f92c00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b60048190556040518181527f67c138aed690b53f8472c70911848132b03f2e8c321a03e5db379ad5e085020590602001611b18565b61201c6135cd565b6120246129da565b6001600160a01b038116612064576040517f83c6c53300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b7f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b031630146120c6576040517fe9c9ef7800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c546001600160a01b031615612109576040517f5d906c2900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600c80547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f307a86fd6204dd9b6a270c57d048c5f259625d260f8c03d1915a9fe6bc2708f090602001611b18565b6121776129da565b7f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b031630146121d9576040517fabf0792900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600085612206577f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2612228565b7f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb485b9050600086612257577f0000000000000000000000000000000000000000000000000000000000000000612279565b7f00000000000000000000000000000000000000000000000000000000000000005b905080156122b3576040517f785aa4db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000906001600160a01b038416906370a0823190602401602060405180830381865afa158015612313573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061233791906146b5565b905061234d6001600160a01b03841688886134a4565b6040517fc00c8b6c0000000000000000000000000000000000000000000000000000000081527f439148f0bbc682ca079e46d6e2c2f0c1e3b820f1a291b069d8882abf8cf18dd9906001600160a01b0389169063c00c8b6c906123bc90339088908c908c908c906004016146f8565b6020604051808303816000875af11580156123db573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906123ff91906146b5565b14612436576040517ff1b70e9a00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b61244b6001600160a01b038416883089613b58565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015281906001600160a01b038516906370a0823190602401602060405180830381865afa1580156124aa573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906124ce91906146b5565b14612505576040517ff633e4db00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b604080518781526001600160a01b03858116602083015289169133917fe875cf27c62067457025fb9b52111f2679e19907e5a1dcf2d8092afcd67cdc3a910160405180910390a35050506125596001600055565b5050505050565b6125686135cd565b6125706129da565b6001600160a01b0381166125b0576040517f83c6c53300000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6001546001600160a01b0316156125f3576040517f58c30fc000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600180547fffffffffffffffffffffffff0000000000000000000000000000000000000000166001600160a01b0383169081179091556040519081527f1d56850f49455a17df2bb41dd638797cdd462d3b4034b169d7979c2df141355490602001611b18565b60007f0000000000000000000000000000000000000000000000000000000000000000806126a457507f00000000000000000000000000000000000000000000000000000000000000005b905090565b6060307f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b0316036127a65760408051600280825260608201835290916020830190803683370190505090507f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb488160008151811061272f5761272f614106565b60200260200101906001600160a01b031690816001600160a01b0316815250507f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc28160018151811061278357612783614106565b60200260200101906001600160a01b031690816001600160a01b03168152505090565b6040517f0b6f5afe0000000000000000000000000000000000000000000000000000000081523060048201527f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b031690630b6f5afe90602401600060405180830381865afa158015612823573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe01682016040526126a4919081019061472b565b60008115612947577f00000000000000000000000000000000000000000000000000000000000000001561293f576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b0316906370a08231906024015b602060405180830381865afa158015612915573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061293991906146b5565b92915050565b5050600a5490565b7f0000000000000000000000000000000000000000000000000000000000000000156129d2576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201527f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc26001600160a01b0316906370a08231906024016128f8565b5050600b5490565b600260005403612a4b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601f60248201527f5265656e7472616e637947756172643a207265656e7472616e742063616c6c0060448201526064015b60405180910390fd5b6002600055565b606060007f00000000000000000000000000000000000000000000000000000000000000006001600160a01b031663c0aa3cf8878787876040518563ffffffff1660e01b8152600401612aa894939291906147ba565b6000604051808303816000875af1158015612ac7573d6000803e3d6000fd5b505050506040513d6000823e601f3d9081017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0168201604052612b0d91908101906147f1565b9250905080612b5c576040517f3f16fa200000000000000000000000000000000000000000000000000000000081526001600160a01b038716600482015260ff84166024820152604401612a42565b50949350505050565b600080807fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff85870985870292508281108382030391505080600003612bbd57838281612bb357612bb3614842565b0492505050612c8e565b808411612c26576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601560248201527f4d6174683a206d756c446976206f766572666c6f7700000000000000000000006044820152606401612a42565b600084868809851960019081018716968790049682860381900495909211909303600082900391909104909201919091029190911760038402600290811880860282030280860282030280860282030280860282030280860282030280860290910302029150505b9392505050565b6000307f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b031603612ce057612cd18615612869565b831115612ce057506000612d04565b81831015612cf057506000612d04565b84841115612d0057506000612d04565b5060015b95945050505050565b600080612d1b868686612b65565b90506001836002811115612d3157612d31614315565b148015612d4e575060008480612d4957612d49614842565b868809115b15612d6157612d5e600182614536565b90505b90505b949350505050565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c81166004830152600091908716906370a0823190602401602060405180830381865afa158015612def573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612e1391906146b5565b90508615612e94576040517f9fbd86ad0000000000000000000000000000000000000000000000000000000081523390639fbd86ad90612e5d908990899088908890600401614871565b600060405180830381600087803b158015612e7757600080fd5b505af1158015612e8b573d6000803e3d6000fd5b50505050612ec9565b612ec96001600160a01b038716337f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c88613b58565b6040517f70a082310000000000000000000000000000000000000000000000000000000081526001600160a01b037f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c8116600483015260009183918916906370a0823190602401602060405180830381865afa158015612f4d573d6000803e3d6000fd5b505050506040513d601f19601f82011682018060405250810190612f7191906146b5565b612f7b919061466a565b9050600089612faa577f0000000000000000000000000000000000000000000000000000000000000000612fcc565b7f00000000000000000000000000000000000000000000000000000000000000005b9050801561308a576000828811612fec57612fe7888461466a565b612ff6565b612ff6838961466a565b905060008b613025577f0000000000000000000000000000000000000000000000000000000000000000613047565b7f00000000000000000000000000000000000000000000000000000000000000005b905080821115613083576040517f07ba54e500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b50506130c3565b8682146130c3576040517f45df38c100000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b8080156130f857507f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b031630145b801561310e57506003546001600160a01b031615155b1561314857600061312487600454612710612b65565b9050801561314657600354613146906001600160a01b038b81169116836134a4565b505b50505050505050505050565b831561327b577f000000000000000000000000000000000000000000000000000000000000000061320357600061319082600454612710612b65565b9050307f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b0316036131e3576131cc818561466a565b600a60008282546131dd9190614536565b90915550505b80156132015780600560008282546131fb9190614536565b90915550505b505b7f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b03163014801561325957507f0000000000000000000000000000000000000000000000000000000000000000155b156132765781600b6000828254613270919061466a565b90915550505b613396565b7f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b0316301480156132d157507f0000000000000000000000000000000000000000000000000000000000000000155b156132ee5781600a60008282546132e8919061466a565b90915550505b7f000000000000000000000000000000000000000000000000000000000000000061339657600061332482600454612710612b65565b9050307f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b03160361337757613360818561466a565b600b60008282546133719190614536565b90915550505b801561255957806006600082825461338f9190614536565b9091555050505b50505050565b307f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b0316036133e5576133e06001600160a01b03841683836134a4565b505050565b6133e06001600160a01b0384167f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c8484613b58565b6001546001600160a01b0316331461345e576040517fef410f2f00000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b565b6002546001600160a01b0316331461345e576040517f6dfbb74500000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6040516001600160a01b0383166024820152604481018290526133e09084907fa9059cbb00000000000000000000000000000000000000000000000000000000906064015b604080517fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe08184030181529190526020810180517bffffffffffffffffffffffffffffffffffffffffffffffffffffffff167fffffffff0000000000000000000000000000000000000000000000000000000090931692909217909152613ba9565b336001600160a01b037f000000000000000000000000502c7c62cef3d7314597532510573b87663ef85b161461345e576040517fcadb26b700000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b6003546001600160a01b0316331461345e576040517f6c54e96000000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b600080612710851180613625575061271084115b1561365c576040517f50d83c0900000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b5050600554600654306001600160a01b037f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c1614613a2d576040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526000907f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb486001600160a01b0316906370a0823190602401602060405180830381865afa158015613714573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061373891906146b5565b6040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201529091506000906001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823190602401602060405180830381865afa1580156137bb573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906137df91906146b5565b6040517f780ef17500000000000000000000000000000000000000000000000000000000815260048101869052602481018590529091507f0000000000000000000000009c05bdcc909c2b190837e8fe71619cf389598c2c6001600160a01b03169063780ef17590604401600060405180830381600087803b15801561386457600080fd5b505af1158015613878573d6000803e3d6000fd5b50506040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152600092508491506001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb4816906370a0823190602401602060405180830381865afa1580156138fe573d6000803e3d6000fd5b505050506040513d601f19601f8201168201806040525081019061392291906146b5565b61392c919061466a565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015290915060009083906001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc216906370a0823190602401602060405180830381865afa1580156139b1573d6000803e3d6000fd5b505050506040513d601f19601f820116820180604052508101906139d591906146b5565b6139df919061466a565b9050858211806139ee57508481115b15613a25576040517fcae2d53800000000000000000000000000000000000000000000000000000000815260040160405180910390fd5b909450925050505b6000613a3c8684612710612b65565b90506000613a4d8684612710612b65565b90508160076000828254613a619190614536565b925050819055508060086000828254613a7a9190614536565b909155505060006005819055600655613a93828561466a565b9350613a9f818461466a565b92508315613adb57613adb6001600160a01b037f000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb481686866134a4565b8215613b1557613b156001600160a01b037f000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc21686856134a4565b60408051858152602081018590527f9354b101c687c179e9516ece0f8b0cebbfdc205da033d49eb2b9598548ed75c2910160405180910390a15050935093915050565b6040516001600160a01b03808516602483015283166044820152606481018290526133969085907f23b872dd00000000000000000000000000000000000000000000000000000000906084016134e9565b6000613bfe826040518060400160405280602081526020017f5361666545524332303a206c6f772d6c6576656c2063616c6c206661696c6564815250856001600160a01b0316613cab9092919063ffffffff16565b9050805160001480613c1f575080806020019051810190613c1f919061489a565b6133e0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152602a60248201527f5361666545524332303a204552433230206f7065726174696f6e20646964206e60448201527f6f742073756363656564000000000000000000000000000000000000000000006064820152608401612a42565b6060612d64848460008585600080866001600160a01b03168587604051613cd291906148b7565b60006040518083038185875af1925050503d8060008114613d0f576040519150601f19603f3d011682016040523d82523d6000602084013e613d14565b606091505b5091509150613d2587838387613d30565b979650505050505050565b60608315613db9578251600003613db2576001600160a01b0385163b613db2576040517f08c379a000000000000000000000000000000000000000000000000000000000815260206004820152601d60248201527f416464726573733a2063616c6c20746f206e6f6e2d636f6e74726163740000006044820152606401612a42565b5081612d64565b612d648383815115613dce5781518083602001fd5b806040517f08c379a0000000000000000000000000000000000000000000000000000000008152600401612a4291906148c9565b600060208284031215613e1457600080fd5b813567ffffffffffffffff811115613e2b57600080fd5b82016101008185031215612c8e57600080fd5b6001600160a01b0381168114611b2a57600080fd5b60008083601f840112613e6557600080fd5b50813567ffffffffffffffff811115613e7d57600080fd5b602083019150836020828501011115611ec057600080fd5b600080600080600080600060a0888a031215613eb057600080fd5b87359650602088013595506040880135613ec981613e3e565b9450606088013567ffffffffffffffff80821115613ee657600080fd5b613ef28b838c01613e53565b909650945060808a0135915080821115613f0b57600080fd5b50613f188a828b01613e53565b989b979a50959850939692959293505050565b600060208284031215613f3d57600080fd5b8135612c8e81613e3e565b60008060008060008060a08789031215613f6157600080fd5b86359550602087013594506040870135613f7a81613e3e565b93506060870135613f8a81613e3e565b9250608087013567ffffffffffffffff811115613fa657600080fd5b613fb289828a01613e53565b979a9699509497509295939492505050565b60008060408385031215613fd757600080fd5b50508035926020909101359150565b600060208284031215613ff857600080fd5b5035919050565b8015158114611b2a57600080fd5b60008060008060006080868803121561402557600080fd5b853561403081613fff565b9450602086013561404081613e3e565b935060408601359250606086013567ffffffffffffffff81111561406357600080fd5b61406f88828901613e53565b969995985093965092949392505050565b600081518084526020808501945080840160005b838110156140b95781516001600160a01b031687529582019590820190600101614094565b509495945050505050565b602081526000612c8e6020830184614080565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b6001600160a01b0383168152604060208201526000612d646040830184614080565b604051601f82017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe016810167ffffffffffffffff8111828210171561419e5761419e6140d7565b604052919050565b600067ffffffffffffffff8211156141c0576141c06140d7565b5060051b60200190565b600060208083850312156141dd57600080fd5b825167ffffffffffffffff8111156141f457600080fd5b8301601f8101851361420557600080fd5b8051614218614213826141a6565b614157565b81815260059190911b8201830190838101908783111561423757600080fd5b928401925b82841015613d255783518252928401929084019061423c565b60006020828403121561426757600080fd5b8135612c8e81613fff565b600082357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff818336030181126142a657600080fd5b9190910192915050565b60008083357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18436030181126142e557600080fd5b83018035915067ffffffffffffffff82111561430057600080fd5b602001915036819003821315611ec057600080fd5b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602160045260246000fd5b8183528181602085013750600060208284010152600060207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f840116840101905092915050565b60006001600160a01b038089168352808816602084015286604084015280861660608401525060a060808301526143c860a083018486614344565b98975050505050505050565b60005b838110156143ef5781810151838201526020016143d7565b50506000910152565b600082601f83011261440957600080fd5b815167ffffffffffffffff811115614423576144236140d7565b61445460207fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0601f84011601614157565b81815284602083860101111561446957600080fd5b612d648260208301602087016143d4565b60006020828403121561448c57600080fd5b815167ffffffffffffffff808211156144a457600080fd5b90830190604082860312156144b857600080fd5b6040516040810181811083821117156144d3576144d36140d7565b604052825181526020830151828111156144ec57600080fd5b6144f8878286016143f8565b60208301525095945050505050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8082018082111561293957612939614507565b600081518084526145618160208601602086016143d4565b601f017fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0169290920160200192915050565b6000610100865115158352602087015160208401526040870151604084015260608701516001600160a01b0380821660608601528060808a01511660808601528060a08a01511660a086015250508060c08401526145f48184018688614344565b905082810360e0840152613d258185614549565b60006060828403121561461a57600080fd5b6040516060810181811067ffffffffffffffff8211171561463d5761463d6140d7565b604052825161464b81613fff565b8152602083810151908201526040928301519281019290925250919050565b8181038181111561293957612939614507565b848152836020820152826040820152608060608201528151608082015260006020830151604060a0840152613d2560c0840182614549565b6000602082840312156146c757600080fd5b5051919050565b8481528360208201526060604082015260006146ee606083018486614344565b9695505050505050565b60006001600160a01b03808816835280871660208401525084604083015260806060830152613d25608083018486614344565b6000602080838503121561473e57600080fd5b825167ffffffffffffffff81111561475557600080fd5b8301601f8101851361476657600080fd5b8051614774614213826141a6565b81815260059190911b8201830190838101908783111561479357600080fd5b928401925b82841015613d255783516147ab81613e3e565b82529284019290840190614798565b6001600160a01b03851681526060602082015260006147dd606083018587614344565b905060ff8316604083015295945050505050565b6000806040838503121561480457600080fd5b825161480f81613fff565b602084015190925067ffffffffffffffff81111561482c57600080fd5b614838858286016143f8565b9150509250929050565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b6001600160a01b03851681528360208201526060604082015260006146ee606083018486614344565b6000602082840312156148ac57600080fd5b8151612c8e81613fff565b600082516142a68184602087016143d4565b602081526000612c8e602083018461454956fea2646970667358221220c47f9fb4295775660d96e6e07dc64d779800a11096317fcef57fb8ad1f4f5a5164736f6c63430008130033

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

000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2000000000000000000000000502c7c62cef3d7314597532510573b87663ef85b000000000000000000000000502c7c62cef3d7314597532510573b87663ef85b0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : args (tuple): System.Collections.Generic.List`1[Nethereum.ABI.FunctionEncoding.ParameterOutput]

-----Encoded View---------------
11 Constructor Arguments found :
Arg [0] : 000000000000000000000000a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48
Arg [1] : 000000000000000000000000c02aaa39b223fe8d0a0e5c4f27ead9083c756cc2
Arg [2] : 000000000000000000000000502c7c62cef3d7314597532510573b87663ef85b
Arg [3] : 000000000000000000000000502c7c62cef3d7314597532510573b87663ef85b
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [6] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [8] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000000
Arg [10] : 0000000000000000000000000000000000000000000000000000000000000000


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
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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