ETH Price: $2,862.83 (-2.67%)

Contract

0x000000004aBe0D620b25b8B06B0712BDcff21899
 

Overview

ETH Balance

0 ETH

ETH Value

$0.00

More Info

Private Name Tags

Multichain Info

No addresses found
Transaction Hash
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Block From To
36965222024-05-20 11:00:59615 days ago1716202859  Contract Creation0 ETH

Cross-Chain Transactions
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
CatalystVaultAmplified

Compiler Version
v0.8.22+commit.4fc1097e

Optimization Enabled:
Yes with 10000 runs

Other Settings:
paris EvmVersion, MIT license
//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.19;

import { ERC20 } from 'solady/tokens/ERC20.sol';
import { SafeTransferLib } from 'solady/utils/SafeTransferLib.sol';
import { FixedPointMathLib } from "solady/utils/FixedPointMathLib.sol";

import { ICatalystChainInterface } from "./interfaces/ICatalystChainInterface.sol";
import { WADWAD } from "./utils/MathConstants.sol";
import { CatalystVaultCommon } from "./CatalystVaultCommon.sol";
import { IntegralsAmplified } from "./IntegralsAmplified.sol";
import { ICatalystReceiver} from "./interfaces/IOnCatalyst.sol";
import "./ICatalystV1Vault.sol";

/**
 * @title Catalyst: Fixed rate cross-chain swap vault.
 * @author Cata Labs Inc.
 * @notice Catalyst multi-chain vault using the asset specific
 * pricing curve: 1/w^\theta (1 - \theta) where \theta is 
 * the vault amplification and w is the vault asset balance.
 *
 * The following contract supports between 1 and 3 assets for
 * atomic swaps. To increase the number of tokens supported,
 * change MAX_ASSETS to the desired maximum token amount.
 * This constant is set in "CatalystVaultCommon.sol"
 *
 * This vault implements the ERC20 specification, such that the
 * contract will be its own vault token.
 * @dev This contract is deployed inactive: It cannot be used as a
 * vault as is. To use it, a proxy contract duplicating the
 * logic of this contract needs to be deployed. In Vyper, this
 * can be done through (vy >= 0.3.4) create_minimal_proxy_to.
 * In Solidity, this can be done through OZ clones: Clones.clone(...)
 * After deployment of the proxy, call setup(...) AND initializeSwapCurves(...).
 * This will initialize the vault and prepare it for cross-chain transactions.
 * However, only the Catalyst factory is allowed to perform these functions.
 * It is important that both setup(...) AND initializeSwapCurves(...) are called
 * in the same transaciton, as otherwise the vault is not fully finalised and
 * may be configured incorrectly by a third-party.
 *
 * If connected to a supported cross-chain interface, call
 * setConnection to connect the vault with vaults on other chains.
 *
 * Finally, call finishSetup to give up the creator's control over the vault. 
 * !If finishSetup is not called, the vault can be drained by the creator!
 */
contract CatalystVaultAmplified is CatalystVaultCommon, IntegralsAmplified {
    //--- Storage ---//
    
    /** 
     * @notice Adjustment used to remove a certain escrow amount from the balance * computation
     */
    mapping(address => uint256) public _underwriteEscrowMatchBalance0;

    //--- ERRORS ---//
    // Errors are defined in interfaces/ICatalystV1VaultErrors.sol

    //--- Config ---//

    /** @notice Minimum time parameter adjustments can be made over. */
    uint256 constant MIN_ADJUSTMENT_TIME = 7 days;

    /**
     * @dev  When the swap is a very small size of the vault, the
     * swaps returns slightly more. To counteract this, an additional 
     * fee slightly larger than the error is added. The below 
     * constants determines when this fee is added and the size.
     */
    uint256 constant SMALL_SWAP_RATIO = 1e12;
    uint256 constant SMALL_SWAP_RETURN = 95e16;

    // For other config options, see CatalystVaultCommon.sol

    //-- Variables --//
    int64 public _oneMinusAmp;
    int64 public _targetAmplification;
 
    /**
     * @dev To keep track of pool ownership, the vault needs to keep track of
     * the local unit balance. That is, do other vaults own or owe assets to this vault?
     */
    int256 public _unitTracker;

    constructor(address factory_, address mathlib_) payable CatalystVaultCommon(factory_, mathlib_) {}

    /**
     * @notice Configures an empty vault.
     * @dev The initial token amounts should have been sent to the vault before setup is called.
     * Since someone can call setup can claim the initial tokens, this needs to be
     * done atomically!
     *
     * If 0 of a token in assets is provided, the setup reverts.
     * @param assets A list of token addresses to be associated with the vault.
     * @param weights Weights brings the price into a true 1:1 swap. That is:
     * i_t \cdot W_i = j_t \cdot W_j \forall i, j when P_i(i_t) = P_j(j_t).
     * in other words, weights are used to compensate for the difference in decimals. (or non 1:1.)
     * @param amp Amplification factor. Should be < 10**18.
     * @param depositor The address to mint tokens to.
     */
    function initializeSwapCurves(
        address[] calldata assets,
        uint256[] calldata weights,
        uint64 amp,
        address depositor
    ) external override {
        // May only be invoked by the FACTORY. The factory only invokes this function for proxy contracts.
        require(msg.sender == FACTORY); // dev: swap curves may only be initialized once by the factory
        require(_tokenIndexing[0] == address(0)); // dev: swap curves may only be initialized once by the factory
        // Check that the amplification is correct.
        require(amp < FixedPointMathLib.WAD);  // dev: amplification not set correctly.
        // Note there is no need to check whether assets.length/weights.length are valid, as invalid arguments
        // will either cause the function to fail (e.g. if assets.length > MAX_ASSETS the assignment
        // to initialBalances[it] will fail) or will cause the vault to get initialized with an undesired state
        // (and the vault shouldn't be used by anyone until its configuration has been finalised). 
        // In any case, the factory does check for valid assets/weights arguments to prevent erroneous configurations.
        // Note Since assets.len != 0 is not checked, the initial depositor may invoke this function many times, resulting
        // in vault tokens being minted for the 'depositor' every time. This is not an issue, since 'INITIAL_MINT_AMOUNT' is
        // an arbitrary number; the value of the vault tokens is determined by the ratio of the vault asset balances and vault
        // tokens supply once setup has finalized. Furthermore, the vault should not be used until setup has finished and the
        // vault configuration has been verified.
        
        unchecked {
            // Amplification is stored as 1 - amp since most equations uses amp this way.
            _oneMinusAmp = int64(uint64(FixedPointMathLib.WAD - amp));
            _targetAmplification = int64(uint64(FixedPointMathLib.WAD - amp));
        }   

        // Compute the security limit.
        uint256[] memory initialBalances = new uint256[](MAX_ASSETS);
        uint256 maxUnitCapacity = 0;
        uint assetLength = assets.length;
        for (uint256 it; it < assetLength;) {

            address tokenAddress = assets[it];
            _tokenIndexing[it] = tokenAddress;

            uint256 weight = weights[it];
            require(weight != 0);  // dev: invalid 0-valued weight provided
            _weight[tokenAddress] = weight;

            // The contract expects the tokens to have been sent to it before setup is
            // called. Make sure the vault has more than 0 tokens.
            // Reverts if tokenAddress is address(0).
            // This contract uses safeTransferLib from Solady. When "safeTransfering", there is no
            // check for smart contract code. This could be an issue if non-tokens are allowed to enter
            // the pool, as then the pool could be expoited by later deploying an address to the addres.
            // The below check ensure that there is a token deployed to the contract.
            uint256 balanceOfSelf = ERC20(tokenAddress).balanceOf(address(this));
            require(balanceOfSelf != 0); // dev: 0 tokens provided in setup.
            initialBalances[it] = balanceOfSelf;

            maxUnitCapacity += weight * balanceOfSelf;

            unchecked {
                ++it;
            }
        }

        // The security limit is implemented as being 50% of the current balance. 
        // Since the security limit is evaluated after balance changes, the limit in
        // storage should be the current balance.
        _maxUnitCapacity = maxUnitCapacity;

        // Mint vault tokens to the vault creator.
        _mint(depositor, INITIAL_MINT_AMOUNT);

        emit VaultDeposit(depositor, INITIAL_MINT_AMOUNT, initialBalances);
    }

    /** 
     * @notice Returns the current cross-chain swap capacity. 
     * @dev Overwrites the common implementation because of the
     * differences as to how it is used. As a result, this always returns
     * half of the common implementation (or of _maxUnitCapacity)
     */
    function getUnitCapacity() public view override returns (uint256) {
        return super.getUnitCapacity() >> 1;  // Equal to divide by 2.
    }

    /**
     * @notice Re-computes the security limit incase funds have been sent to the vault.
     */
    function updateMaxUnitCapacity() external {
        uint256 maxUnitCapacity;
        for (uint256 it; it < MAX_ASSETS;) {
            address asset = _tokenIndexing[it];
            if (asset == address(0)) break;

            maxUnitCapacity += (ERC20(asset).balanceOf(address(this)) - _escrowedTokens[asset]) * _weight[asset];

            unchecked {
                ++it;
            }
        }
        _maxUnitCapacity = maxUnitCapacity;
    }

    //--- Swap integrals ---//

    // Inherited from the Integrals file.

    /**
     * @notice Computes the return of SendAsset excluding fees.
     * @dev Reverts if 'fromAsset' is not a token in the vault or if 
     * 'amount' and the vault asset balance are both 0.
     * Does not contain the swap fee.
     * @param fromAsset Address of the token to sell.
     * @param amount Amount of from token to sell.
     * @return uint256 Units.
     */
    function calcSendAsset(
        address fromAsset,
        uint256 amount
    ) public view override returns (uint256) {
        // A high => fewer units returned. Do not subtract the escrow amount
        uint256 A = ERC20(fromAsset).balanceOf(address(this));
        uint256 W = _weight[fromAsset];

        // If 'fromAsset' is not part of the vault (i.e. W is 0) or if 'amount' and 
        // the vault asset balance (i.e. 'A') are both 0 this will revert, since 0**p is 
        // implemented as exp(ln(0) * p) and ln(0) is undefined.
        uint256 U = _calcPriceCurveArea(amount, A, W, _oneMinusAmp);

        // If the swap is a very small portion of the vault add an additional fee. This covers mathematical errors.
        unchecked { //SMALL_SWAP_RATIO is not zero, and if U * SMALL_SWAP_RETURN overflows, less is returned to the user.
            // Also U * SMALL_SWAP_RETURN cannot overflow, since U depends heavily on amount/A. If this number is small (which it is in this case) then U is also "small".
            if (A/SMALL_SWAP_RATIO >= amount) return U * SMALL_SWAP_RETURN / FixedPointMathLib.WAD;
        }
        
        return U;
    }

    /**
     * @notice Computes the output of ReceiveAsset excluding fees.
     * @dev Reverts if 'toAsset' is not a token in the vault.
     * Does not contain the swap fee.
     * @param toAsset Address of the token to buy.
     * @param U Number of units to convert.
     * @return uint256 Ppurchased tokens.
     */
    function calcReceiveAsset(
        address toAsset,
        uint256 U
    ) public view override returns (uint256) {
        // B low => fewer tokens returned. Subtract the escrow amount to decrease the balance.
        uint256 B = ERC20(toAsset).balanceOf(address(this)) - _escrowedTokens[toAsset];
        uint256 W = _weight[toAsset];

        // If someone were to purchase a token that is not part of the vault on setup
        // they would just add value to the vault. We don't care about it.
        // However, it will revert since the solved integral contains U/W and when
        // W = 0 then U/W returns division by 0 error.
        return _calcPriceCurveLimit(U, B, W, _oneMinusAmp);
    }

    /**
     * @notice Computes the output of localSwap excluding fees.
     * @dev Implemented through _calcCombinedPriceCurves.
     * Reverts if either 'fromAsset' or 'toAsset' is not in the vault, or if the vault 'fromAsset'
     * balance and 'amount' are both 0.
     * Does not contain the swap fee.
     * @param fromAsset Address of the token to sell.
     * @param toAsset Address of the token to buy.
     * @param amount Amount of from token to sell for to token.
     * @return output Output denominated in toAsset.
     */
    function calcLocalSwap(
        address fromAsset,
        address toAsset,
        uint256 amount
    ) public view override returns (uint256 output) {
        uint256 A = ERC20(fromAsset).balanceOf(address(this));
        uint256 B = ERC20(toAsset).balanceOf(address(this)) - _escrowedTokens[toAsset];
        uint256 W_A = _weight[fromAsset];
        uint256 W_B = _weight[toAsset];
        int256 oneMinusAmp = _oneMinusAmp;

        output = _calcPriceCurveLimit(_calcPriceCurveArea(amount, A, W_A, oneMinusAmp), B, W_B, oneMinusAmp);

        // If the swap is a very small portion of the vault add an additional fee. This covers mathematical errors.
        unchecked { //SMALL_SWAP_RATIO is not zero, and if output * SMALL_SWAP_RETURN overflows, less is returned to the user
            if (A/SMALL_SWAP_RATIO >= amount) return output * SMALL_SWAP_RETURN / FixedPointMathLib.WAD;
        }
    }

    /**
     * @notice Deposits a user-configurable amount of tokens.
     * @dev The swap fee is imposed on deposits.
     * Requires approvals for all tokens within the vault.
     * It is advised that the deposit matches the vault's %token distribution.
     * Deposit is done by converting tokenAmounts into units and then using
     * the macro for units to vault tokens. (_calcPriceCurveLimitShare).
     * The elements of tokenAmounts correspond to _tokenIndexing[0...N].
     * @param tokenAmounts Array of the tokens amounts to be deposited.
     * @param minOut Minimum number of vault tokens to be minted.
     * @return vaultTokens Number of minted vault tokens.
     */
    function depositMixed(
        uint256[] memory tokenAmounts,
        uint256 minOut
    ) nonReentrant external override returns(uint256 vaultTokens) {
        // _updateAmplification();
        int256 oneMinusAmp = _oneMinusAmp;

        uint256 it_times_walpha_amped;

        // There is a Stack too deep issue in a later branch. To counteract this,
        // wab is stored short-lived. This requires letting U get negative.
        // As such, we define an additional variable called intU which is signed
        int256 intU;
        
        // Compute walpha_0 to find the reference balances. This lets us evaluate the
        // number of tokens the vault should have If the price in the pool is 1:1.
        // walpha_0 is computed several times in this contract:
        // - DepositMixed
        // - WithdrawMixed
        // - WithdrawAll
        // - sendLiquidity
        // - receiveLiquidity
        // Since the implementation is very similar, it could be computed seperatly.
        // However, some of the implementations differ notably:
        // - DepositMixed: The for loop is reused for computing the value of incoming assets.
        // - WithdrawMixed: The for loop is used to cache tokenIndexed, effAssetBalances, and assetWeight.
        // - WithdrawAll: The for loop is used to cache tokenIndexed, effWeightAssetBalances.
        // - Both sendLiquidity and receiveLiquidity implements the reference computation: computeBalance0().
        // Before each implementation, there will be a short comment to describe how the implementation is different.
        {
            int256 weightedAssetBalanceSum = 0;
            uint256 assetDepositSum = 0;
            for (uint256 it; it < MAX_ASSETS;) {
                address token = _tokenIndexing[it];
                if (token == address(0)) break;
                uint256 weight = _weight[token];
                uint256 tokenAmount = tokenAmounts[it];
                {
                // Whenever balance0 is computed, the true balance should be used.
                uint256 weightAssetBalance = weight * ERC20(token).balanceOf(address(this));

                {
                    // wa^(1-k) is required twice. It is F(A) in the
                    // sendAsset equation and part of the wa_0^(1-k) calculation.
                    // If weightAssetBalance == 0, then this computation would fail. However since 0^(1-k) = 0, we can set it to 0.
                    int256 wab = 0;
                    if (weightAssetBalance != 0) {
                        // calculate balance 0 with the underwritten amount subtracted.
                        wab = FixedPointMathLib.powWad(
                            int256((weightAssetBalance - _underwriteEscrowMatchBalance0[token] * weight) * FixedPointMathLib.WAD),  // If casting overflows to a negative number, powWad fails
                            oneMinusAmp
                        );

                        // if wab == 0, there is no need to add it. So only add if != 0.
                        weightedAssetBalanceSum += wab;

                        wab = FixedPointMathLib.powWad(
                            int256(weightAssetBalance * FixedPointMathLib.WAD),  // If casting overflows to a negative number, powWad fails
                            oneMinusAmp
                        );
                    }
                    
                    // This line is the origin of the stack too deep issue.
                    // Moving intU += before this section would solve the issue but it is not possible since it would evaluate incorrectly.
                    // Save gas if the user provides no tokens, as the rest of the loop has no effect in that case
                    if (tokenAmount == 0) {
                        unchecked {
                            ++it;
                        }
                        continue;
                    }
                    
                    // int_A^{A+x} f(w) dw = F(A+x) - F(A).
                    unchecked {
                        // This is -F(A). Since we are subtracting first, U (i.e. intU) must be able to go negative.
                        // |intU| < weightedAssetBalanceSum since F(A+x) is added to intU in the lines after this.
                        intU -= wab;
                    }
                }
                
                // Add F(A+x).
                // This computation will not revert, since we know tokenAmount != 0.
                intU += FixedPointMathLib.powWad(
                    int256((weightAssetBalance + weight * tokenAmount) * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails
                    oneMinusAmp
                );
                
                }
                assetDepositSum += tokenAmount * weight;
                
                SafeTransferLib.safeTransferFrom(
                    token,
                    msg.sender,
                    address(this),
                    tokenAmount
                );  // dev: Token withdrawal from user failed.

                unchecked {
                    ++it;
                }
            }
            // Increase the security limit by the amount deposited.
            _maxUnitCapacity += assetDepositSum;
            // Short term decrease the security limit by the amount deposited.
            // While one may assume _usedUnitCapacity < _maxUnitCapacity, this is not always the case. As such, this remains checked.
            _usedUnitCapacity += assetDepositSum;
            
            // Compute the reference liquidity.
            // weightedAssetBalanceSum > _unitTracker always, since _unitTracker correlates to exactly
            // the difference between weightedAssetBalanceSum and weightedAssetBalance0Sum and thus
            // _unitTracker < weightedAssetBalance0Sum
            unchecked {
                // weightedAssetBalanceSum - _unitTracker can overflow for negative _unitTracker.
                // The result will be correct once it is casted to uint256.
                it_times_walpha_amped = uint256(weightedAssetBalanceSum - _unitTracker); // By design, weightedAssetBalanceSum > _unitTracker
                // Notice that we are not dividing by it. That is because we would then later have to multiply by it.
            }
        }

        // Subtract fee from U (intU). This prevents people from using deposit and withdrawal as a method of swapping.
        // To reduce costs, the governance fee is not taken. As a result, swapping through deposit+withdrawal circumvents
        // the governance fee. No incentives align for traders to abuse this and is nativly disincentivised by the higher gas cost.
        // intU should not be negative. But in the case it is, the result is very bad. For safety, check it is above 0.
        require(intU >= 0); // dev: U needs to be positive, otherwise, the uint256 casting becomes too larger.
        unchecked {
            // U (intU) is generally small, so the below equation should not overflow.
            // If it does, it has to be (uint256(intU) * (FixedPointMathLib.WAD - _vaultFee)) that overflows.
            // In which case, something close to 0 will be returned. When divided by FixedPointMathLib.WAD
            // it will return 0. The casting to int256 is then 0.
            intU = int256(
                // intU shouldn't be negative but the above check ensures it is ALWAYS positive.
                (uint256(intU) * (FixedPointMathLib.WAD - _vaultFee))/FixedPointMathLib.WAD
            );
        }

        int256 oneMinusAmpInverse = WADWAD / oneMinusAmp;

        // On totalSupply(). Do not add escrow amount, as higher amount results in a larger return.
        vaultTokens = _calcPriceCurveLimitShare(uint256(intU), totalSupply(), it_times_walpha_amped, oneMinusAmpInverse); // uint256: intU is positive by design.

        // Check that the minimum output is honoured.
        if (minOut > vaultTokens) revert ReturnInsufficient(vaultTokens, minOut);

        // Mint the desired number of vault tokens to the user.
        _mint(msg.sender, vaultTokens);

        // Emit the deposit event
        emit VaultDeposit(msg.sender, vaultTokens, tokenAmounts);
    }

    /**
     * @notice Burns vault tokens and releases the symmetrical share of tokens to the burner.
     * This can impact the vault prices.
     * @dev This is the cheapest way to withdraw and only way to withdraw 100% of the liquidity.
     * @param vaultTokens Number of vault tokens to burn.
     * @param minOut Minimum token output. If less is returned, the transaction reverts.
     * @return amounts Array containing the amounts withdrawn.
     */
    function withdrawAll(
        uint256 vaultTokens,
        uint256[] memory minOut
    ) nonReentrant external override returns(uint256[] memory amounts) {
        // _updateAmplification();
        // Burn the desired number of vault tokens to the user.
        // If they don't have it, it saves gas.
        // * Remember to add vaultTokens when accessing totalSupply()
        _burn(msg.sender, vaultTokens);
        // (For everyone else, it is probably cheaper to burn last. However, burning here makes
        // the implementation more similar to the volatile one)

        int256 oneMinusAmp = _oneMinusAmp;

        // Cache weights and balances.
        address[MAX_ASSETS] memory tokenIndexed;
        uint256[MAX_ASSETS] memory effWeightAssetBalances;  // The 'effective' balances (compensated with the escrowed balances)

        uint256 walpha_0_ampped;
        // Compute walpha_0 to find the reference balances. This lets us evaluate the
        // number of tokens the vault should have If the price in the pool is 1:1.

        // This is a balance0 implementation. The for loop is used to cache tokenIndexed and effWeightAssetBalances.
        {
            int256 weightedAssetBalanceSum = 0;
            // The number of iterations, "it", is needed briefly outside the loop.
            uint256 it;
            for (it = 0; it < MAX_ASSETS;) {
                address token = _tokenIndexing[it];
                if (token == address(0)) break;
                tokenIndexed[it] = token;
                uint256 weight = _weight[token];

                // Whenever balance0 is computed, the true balance should be used.
                uint256 weightAssetBalance = weight * ERC20(token).balanceOf(address(this));

                // Since this is used for a withdrawal, the escrow amount needs to be subtracted to return less.
                effWeightAssetBalances[it] = weightAssetBalance - _escrowedTokens[token] * weight; // Store 

                // If weightAssetBalance == 0, then this computation would fail. However since 0^(1-k) = 0, we can set it to 0.
                if (weightAssetBalance != 0) {
                    int256 wab = FixedPointMathLib.powWad(
                        int256((weightAssetBalance - _underwriteEscrowMatchBalance0[token] * weight) * FixedPointMathLib.WAD),  // If casting overflows to a negative number, powWad fails
                        oneMinusAmp
                    );

                    // if wab == 0, there is no need to add it. So only add if != 0.
                    weightedAssetBalanceSum += wab;
                }

                unchecked {
                    ++it;
                }
            }

            // Compute the reference liquidity.
            // weightedAssetBalanceSum > _unitTracker always, since _unitTracker correlates to exactly
            // the difference between weightedAssetBalanceSum and weightedAssetBalance0Sum and thus
            // _unitTracker < weightedAssetBalance0Sum
            unchecked {
                // weightedAssetBalanceSum - _unitTracker can overflow for negative _unitTracker. The result will
                // be correct once it is casted to uint256.
                walpha_0_ampped = uint256(weightedAssetBalanceSum - _unitTracker) / it; // By design, weightedAssetBalanceSum > _unitTracker
            }
        }

        // For later event logging, the amounts transferred from the vault are stored.
        amounts = new uint256[](MAX_ASSETS);
        
        // The vault token to assets equation is:
        // wtk = wa ·(1 - ((wa^(1-k) - wa_0^(1-k) · (1 - (PT-pt)/PT)^(1-k))/wa^(1-k))^(1/(1-k))
        // The inner diff is wa_0^(1-k) · (1 - (PT-pt)/PT)^(1-k).
        // since it doesn't depend on the token, it should only be computed once.
        uint256 innerdiff;
        {
            // Remember to add the number of vault tokens burned to totalSupply()
            // _escrowedVaultTokens is added, since it makes pt_fraction smaller
            uint256 ts = (totalSupply() + _escrowedVaultTokens + vaultTokens);
            uint256 pt_fraction = ((ts - vaultTokens) * FixedPointMathLib.WAD) / ts;

            // If pt_fraction == 0 => 0^oneMinusAmp = powWad(0, oneMinusAmp) => exp(ln(0) * oneMinusAmp) which is undefined.
            // However, we know what 0^oneMinusAmp is: 0!. So we just set it to 0.
            innerdiff = pt_fraction == 0 ? walpha_0_ampped : FixedPointMathLib.mulWad(
                walpha_0_ampped, 
                    FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad(  // Always casts a positive value
                    int256(pt_fraction),  // Casting always safe, as pt_fraction < 1
                    oneMinusAmp
                ))
            );
        }

        uint256 totalWithdrawn;
        int256 oneMinusAmpInverse = WADWAD / oneMinusAmp;
        for (uint256 it; it < MAX_ASSETS;) {
            address token = tokenIndexed[it];
            if (token == address(0)) break;

            // ampWeightAssetBalance cannot be cached because the balance0 computation does it without the escrow.
            // This computation needs to do it with the escrow.
            uint256 ampWeightAssetBalance = uint256(FixedPointMathLib.powWad(  // Powwad is always positive.
                int256(effWeightAssetBalances[it] * FixedPointMathLib.WAD),  // If casting overflows to a negative number, powWad fails
                oneMinusAmp
            ));
            //! If the vault doesn't have enough assets for a withdrawal, then
            //! withdraw all of the vaults assets. This should be protected against by setting minOut != 0.
            //! This happens because the vault expects assets to come back. (it is owed assets)
            //! We don't want to keep track of debt so we simply return less
            uint256 weightedTokenAmount = effWeightAssetBalances[it];
            //! The above happens if innerdiff >= ampWeightAssetBalance. So if that isn't
            //! the case, we should compute the true value.
            if (innerdiff < ampWeightAssetBalance) {
                // wtk = wa ·(1 - ((wa^(1-k) - wa_0^(1-k) · (1 - (PT-pt)/PT)^(1-k))/wa^(1-k))^(1/(1-k))
                // wtk = wa ·(1 - ((wa^(1-k) - innerdiff)/wa^(1-k))^(1/(1-k))
                // Since ampWeightAssetBalance ** (1/(1-amp)) == effWeightAssetBalances but the
                // mathematical lib returns ampWeightAssetBalance ** (1/(1-amp)) < effWeightAssetBalances.
                // the result is that if innerdiff isn't big enough to make up for the difference
                // the transaction reverts. If that is the case, use withdrawAll.
                // This quirk is "okay", since it means fewer tokens are always returned.

                // Since tokens are withdrawn, the change is negative. As such, multiply the equation by -1.
                weightedTokenAmount = FixedPointMathLib.mulWad(
                    weightedTokenAmount,
                    FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad(  // The inner is between 0 and 1. Power of < 1 is always between 0 and 1.
                        int256(FixedPointMathLib.divWadUp( // 0 < innerdiff < ampWeightAssetBalance => < 1 thus casting never overflows. 
                            ampWeightAssetBalance - innerdiff,
                            ampWeightAssetBalance
                        )),
                        oneMinusAmpInverse // 1/(1-amp)
                    ))
                );
            }

            // Store the amount withdrawn to subtract from the security limit later.
            totalWithdrawn += weightedTokenAmount;

            unchecked {
                // remove the weight from weightedTokenAmount.
                weightedTokenAmount /= _weight[token];
            }
            
            // Check if the user is satisfied with the output.
            uint256 tokenMinOut = minOut[it];   // GAS SAVING
            if (tokenMinOut > weightedTokenAmount) revert ReturnInsufficient(weightedTokenAmount, tokenMinOut);

            // Store the token amount.
            amounts[it] = weightedTokenAmount;

            // Transfer the released tokens to the user.
            SafeTransferLib.safeTransfer(token, msg.sender, weightedTokenAmount);

            unchecked {
                ++it;
            }
        }

        // Decrease the security limit by the amount withdrawn.
        _maxUnitCapacity -= totalWithdrawn;
        if (_usedUnitCapacity <= totalWithdrawn) {
            _usedUnitCapacity = 0;
        } else {
            unchecked {
                // We know: _usedUnitCapacity > totalWithdrawn.
                _usedUnitCapacity -= totalWithdrawn;
            }
        }

        // Emit the event
        emit VaultWithdraw(msg.sender, vaultTokens, amounts);
    }

    /**
     * @notice Burns vaultTokens and release a token distribution set by the user.
     * @dev It is advised that the withdrawal matches the vault's %token distribution.
     * Notice the special scheme for the ratios used. This is done to optimise gas since it doesn't require a sum or ratios.
     * Cannot be used to withdraw all liquidity. For that, withdrawAll should be used.
     * @param vaultTokens Number of vault tokens to withdraw.
     * @param withdrawRatio Percentage of units used to withdraw. In the following special scheme: U_0 = U · withdrawRatio[0], U_1 = (U - U_0) · withdrawRatio[1], U_2 = (U - U_0 - U_1) · withdrawRatio[2], .... Is WAD.
     * @param minOut Minimum number of tokens withdrawn.
     * @return amounts Array containing the amounts withdrawn.
     */
    function withdrawMixed(
        uint256 vaultTokens,
        uint256[] calldata withdrawRatio,
        uint256[] calldata minOut
    ) nonReentrant external override returns(uint256[] memory amounts) {
        // _updateAmplification();
        // Burn the desired number of vault tokens to the user. If they don't have it, it saves gas.
        // * Remember to add vaultTokens when accessing totalSupply()
        _burn(msg.sender, vaultTokens);
        // (For everyone else, it is probably cheaper to burn last. However, burning here makes
        // the implementation more similar to the volatile one).

        int256 oneMinusAmp = _oneMinusAmp;

        // Cache weights and balances.
        address[MAX_ASSETS] memory tokenIndexed;
        uint256[MAX_ASSETS] memory effAssetBalances; // The 'effective' balances (compensated with the escrowed balances)

        uint256 U = 0;
        // Compute walpha_0 to find the reference balances. This lets us evaluate the
        // number of tokens the vault should have if the price in the pool is 1:1.
        // unlike in withdrawAll, this value is needed to compute U.
        {
            // As such, we don't need to remember the value beyond this section.
            uint256 walpha_0_ampped;

            // This is a balance0 implementation. The for loop is used to cache tokenIndexed, effAssetBalances and assetWeight.
            {
                int256 weightedAssetBalanceSum = 0;
                // A very careful stack optimisation is made here.
                // The number of iterations, "it", is needed briefly outside the loop.
                // To reduce the number of items in the stack, U = it.
                for (U = 0; U < MAX_ASSETS;) {
                    address token = _tokenIndexing[U];
                    if (token == address(0)) break;
                    tokenIndexed[U] = token;
                    uint256 weight = _weight[token];

                    // Whenever balance0 is computed, the true balance should be used.
                    uint256 ab = ERC20(token).balanceOf(address(this));

                    // Later we need to use the asset balances. Since it is for a withdrawal, we should subtract the escrowed tokens
                    // such that less is returned.
                    effAssetBalances[U] = ab - _escrowedTokens[token];

                    // subtract _underwriteEscrowMatchBalance0 since this is used for balance0.
                    uint256 weightAssetBalance = weight * (ab - _underwriteEscrowMatchBalance0[token]);

                    // If weightAssetBalance == 0, then this computation would fail. However since 0^(1-k) = 0, we can set it to 0.
                    int256 wab = 0;
                    if (weightAssetBalance != 0) {
                        wab = FixedPointMathLib.powWad(
                            int256(weightAssetBalance * FixedPointMathLib.WAD),  // If casting overflows to a negative number, powWad fails
                            oneMinusAmp
                        );

                        // if wab == 0, there is no need to add it. So only add if != 0.
                        weightedAssetBalanceSum += wab;
                    }

                    unchecked {
                        ++U;
                    }
                }

                // weightedAssetBalanceSum > _unitTracker always, since _unitTracker correlates to exactly
                // the difference between weightedAssetBalanceSum and weightedAssetBalance0Sum and thus
                // _unitTracker < weightedAssetBalance0Sum
                unchecked {
                    // weightedAssetBalanceSum - _unitTracker can overflow for negative _unitTracker. The result will
                    // be correct once it is casted to uint256.
                    walpha_0_ampped = uint256(weightedAssetBalanceSum - _unitTracker) / U; // By design, weightedAssetBalanceSum > _unitTracker
                }

                // set U = number of tokens in the vault. But that is exactly what it is.
            }
            // Remember to add the number of vault tokens burned to totalSupply()
            uint256 ts = totalSupply() + _escrowedVaultTokens + vaultTokens;
            // Since vault tokens are getting subtracted from the total supply, remember
            // to add a negative sign to vault tokens.
            uint256 pt_fraction = FixedPointMathLib.divWad(ts - vaultTokens, ts);

            // Compute the unit worth of the vault tokens.
            // Recall that U is equal to N already. So we only need to multiply by the right side.
            // Since pt_fraction < 1, the units are negative. This is expected for swap to tokens. As such
            // FixedPointMathLib.WAD is moved in front to make U positive.
            U *= FixedPointMathLib.mulWad(
                walpha_0_ampped, 
                FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad( // Always casts a positive value
                    int256(pt_fraction), // If casting overflows to a negative number, powWad fails
                    oneMinusAmp
                )) 
            );
        }

        // For later event logging, the amounts transferred to the vault are stored.
        amounts = new uint256[](MAX_ASSETS);
        uint256 totalWithdrawn;
        for (uint256 it; it < MAX_ASSETS;) {
            // Ideally we would collect the token into memory to save gas but there isn't space in the stack.
            if (tokenIndexed[it] == address(0)) break;

            // Units allocated for the specific token.
            uint256 U_i = FixedPointMathLib.mulWad(U, withdrawRatio[it]);
            if (U_i == 0) {
                // After a withdrawRatio of 1, all other withdrawRatios should be 0. Otherwise, there was an input error.
                if (withdrawRatio[it] != 0) revert WithdrawRatioNotZero();
                // Check the minimum output. This is important, since the normal check is skipped.
                if (minOut[it] != 0) revert ReturnInsufficient(0, minOut[it]);

                unchecked {
                    ++it;
                }
                continue;
            }
            U -= U_i;  // Subtract the number of units used. This will underflow for malicious withdrawRatios > 1.
            
            uint256 assetWeight = _weight[tokenIndexed[it]];
            // Units are shared between "liquidity units" and "token units". As such, we just need to convert the units to tokens.
            uint256 tokenAmount = _calcPriceCurveLimit(U_i, effAssetBalances[it], assetWeight, oneMinusAmp);

            // Ensure the output satisfies the user.
            if (minOut[it] > tokenAmount) revert ReturnInsufficient(tokenAmount, minOut[it]);

            // Store amount for withdraw event.
            amounts[it] = tokenAmount;

            // Transfer the released tokens to the user.
            SafeTransferLib.safeTransfer(tokenIndexed[it], msg.sender, tokenAmount);

            // Decrease the security limit by the amount withdrawn.
            totalWithdrawn += tokenAmount * assetWeight;

            unchecked {
                ++it;
            }
        }
        // Ensure all units are used. This should be done by setting at least one withdrawRatio to 1 (WAD).
        if (U != 0) revert UnusedUnitsAfterWithdrawal(U);
        
        // Decrease the security limit by the amount withdrawn.
        _maxUnitCapacity -= totalWithdrawn;
        if (_usedUnitCapacity <= totalWithdrawn) {
            _usedUnitCapacity = 0;
        } else {
            unchecked {
                // We know: _usedUnitCapacity > totalWithdrawn >= 0.
                _usedUnitCapacity -= totalWithdrawn;
            }
        }

        // Emit the event
        emit VaultWithdraw(msg.sender, vaultTokens, amounts);
    }

    /**
     * @notice A swap between 2 assets within the vault. Is atomic.
     * @param fromAsset Asset the user wants to sell.
     * @param toAsset Asset the user wants to buy.
     * @param amount Amount of fromAsset the user wants to sell.
     * @param minOut Minimum output the user wants. Otherwise, the transaction reverts.
     * @return out The number of tokens purchased.
     */
    function localSwap(
        address fromAsset,
        address toAsset,
        uint256 amount,
        uint256 minOut
    ) nonReentrant external override returns (uint256 out) {
        // _updateAmplification();
        uint256 fee = FixedPointMathLib.mulWad(amount, _vaultFee);

        // Calculate the return value.
        out = calcLocalSwap(fromAsset, toAsset, amount - fee);

        // Ensure the return value is more than the minimum output.
        if (minOut > out) revert ReturnInsufficient(out, minOut);

        // Transfer tokens to the user and collect tokens from the user.
        // The order doesn't matter, since the function is reentrant protected.
        // The transaction that is most likly to revert is first.
        SafeTransferLib.safeTransferFrom(fromAsset, msg.sender, address(this), amount);
        SafeTransferLib.safeTransfer(toAsset, msg.sender, out);

        // Collect potential governance fee
        _collectGovernanceFee(fromAsset, fee);

        // For amplified vaults, the security limit is based on the sum of the tokens in the vault.
        uint256 weightedAmount = amount * _weight[fromAsset];
        uint256 weightedOut = out * _weight[toAsset];
        // The if statement ensures the independent calculations never under or overflow.
        if (weightedOut > weightedAmount) {
            _maxUnitCapacity -= weightedOut - weightedAmount;
        } else {
            _maxUnitCapacity += weightedAmount - weightedOut;
        }

        emit LocalSwap(msg.sender, fromAsset, toAsset, amount, out);
    }

    /** @notice Common logic between sendAsset implementations */
    function _sendAsset(
        RouteDescription calldata routeDescription,
        address fromAsset,
        uint8 toAssetIndex,
        uint256 U,
        uint256 amount,
        uint256 fee,
        uint256 minOut,
        address fallbackUser,
        uint16 underwriteIncentiveX16,
        bytes calldata calldata_
    ) internal {
        // Fallback user cannot be address(0) since this is used as a check for the existance of an escrow.
        // It would also be a silly fallback address.
        require(fallbackUser != address(0));
        // onSendAssetSuccess requires casting U to int256 to update the _unitTracker and must never revert. Check for overflow here.
        require(U < uint256(type(int256).max));  // int256 max fits in uint256
        _unitTracker += int256(U);

        // Send the purchased units to the target vault on the target chain.
        ICatalystChainInterface(_chainInterface).sendCrossChainAsset{value: msg.value}(
            routeDescription,
            toAssetIndex,
            U,
            minOut,
            amount - fee,
            fromAsset,
            underwriteIncentiveX16,
            calldata_
        );

        // Store the escrow information. For that, an index is required. Since we need this index twice, we store it.
        // Only information that is relevant for the escrow has to be hashed. (+ some extra for randomisation)
        // No need to hash context (as token/liquidity escrow data is different), fromVault, toVault, targetAssetIndex, minOut, CallData
        bytes32 sendAssetHash = _computeSendAssetHash(
            routeDescription.toAccount,              // Ensures no collisions between different users
            U,                      // Used to randomise the hash
            amount - fee,           // Required! to validate release escrow data
            fromAsset,              // Required! to validate release escrow data
            uint32(block.number)    // May overflow, but this is desired (% 2**32)
        );

        // Escrow the tokens used to purchase units. These will be sent back if transaction doesn't arrive / timeout.
        _setTokenEscrow(
            sendAssetHash,
            fallbackUser,
            fromAsset,
            amount - fee
        );
        // Notice that the fee is subtracted from the escrow. If this is not done, the escrow can be used as a cheap denial of service vector.
        // This is unfortunate.

        // Collect the tokens from the user.
        SafeTransferLib.safeTransferFrom(fromAsset, msg.sender, address(this), amount);

        // Governance Fee
        _collectGovernanceFee(fromAsset, fee);

        // Adjustment of the security limit is delayed until ack to avoid a router abusing timeout to circumvent the security limit.

        emit SendAsset(
            routeDescription.chainIdentifier,
            routeDescription.toVault,
            routeDescription.toAccount,
            fromAsset,
            toAssetIndex,
            amount,
            minOut,
            U,
            fee,
            underwriteIncentiveX16
        );
    }

    /**
     * @notice Initiate a cross-chain swap by purchasing units and transfering the units to the target vault.
     * @param routeDescription Cross-chain route description that contains the chainIdentifier, toAccount, toVault and relaying incentive.
     * @param fromAsset Asset the user wants to sell.
     * @param toAssetIndex Index of the asset the user wants to buy in the target vault.
     * @param amount Number of fromAsset to sell to the vault.
     * @param minOut Minimum number output of tokens on the target chain.
     * @param fallbackUser If the transaction fails, send the escrowed funds to this address.
     * @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16).max).
     * @param calldata_ Data field if a call should be made on the target chain.
     * Encoding depends on the target chain, with EVM: bytes.concat(bytes20(uint160(<address>)), <data>). At maximum 65535 bytes can be passed.
     * @return U Number of units bought.
     */
    function sendAsset(
        RouteDescription calldata routeDescription,
        address fromAsset,
        uint8 toAssetIndex,
        uint256 amount,
        uint256 minOut,
        address fallbackUser,
        uint16 underwriteIncentiveX16,
        bytes calldata calldata_
    ) nonReentrant onlyConnectedPool(routeDescription.chainIdentifier, routeDescription.toVault) external payable override returns (uint256 U) {
        // _updateAmplification();

        uint256 fee = FixedPointMathLib.mulWad(amount, _vaultFee);

        // Calculate the units bought.
        U = calcSendAsset(fromAsset, amount - fee);

        // Execute the common sendAsset logic.
        _sendAsset(
            routeDescription,
            fromAsset,
            toAssetIndex,
            U,
            amount,
            fee,
            minOut,
            fallbackUser,
            underwriteIncentiveX16,
            calldata_
        );
    }

    /**
     * @notice Initiate a cross-chain swap by purchasing units and transfer them to another vault using a fixed number of units.
     * @dev This function is intended to match an existing underwrite. Since normal sendAssets aren't "exact" in regards to U,
     * this functions makes it easier to hit a specific underwrite.
     * @param routeDescription Cross-chain route description that contains the chainIdentifier, toAccount, toVault and relaying incentive.
     * @param fromAsset Asset the user wants to sell.
     * @param toAssetIndex Index of the asset the user wants to buy in the target vault.
     * @param amount Number of fromAsset to sell to the vault.
     * @param minOut Minimum number output of tokens on the target chain.
     * @param minU Minimum and exact number of units sent.
     * @param fallbackUser If the transaction fails, send the escrowed funds to this address.
     * @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16).max).
     * @param calldata_ Data field if a call should be made on the target chain.
     * Encoding depends on the target chain, with EVM: bytes.concat(bytes20(uint160(<address>)), <data>). At maximum 65535 bytes can be passed..
     * @return U Always equal to minOut, as that is the number of units to be used on the destination chain.
     */
     function sendAssetFixedUnit(
        ICatalystV1Structs.RouteDescription calldata routeDescription,
        address fromAsset,
        uint8 toAssetIndex,
        uint256 amount,
        uint256 minOut,
        uint256 minU,
        address fallbackUser,
        uint16 underwriteIncentiveX16,
        bytes calldata calldata_
    ) nonReentrant onlyConnectedPool(routeDescription.chainIdentifier, routeDescription.toVault) external payable override returns (uint256 U) {
        // _updateAmplification();

        uint256 fee = FixedPointMathLib.mulWad(amount, _vaultFee);

        // Calculate the units bought.
        U = calcSendAsset(fromAsset, amount - fee);

        if (U < minU) revert ReturnInsufficient(U, minU);
        // The set number of units bought to minU.
        U = minU;

        // Execute the common sendAsset logic.
        _sendAsset(
            routeDescription,
            fromAsset,
            toAssetIndex,
            U,
            amount,
            fee,
            minOut,
            fallbackUser,
            underwriteIncentiveX16,
            calldata_
        );
    }

    /**
     * @notice Handles common logic associated with the completion of a cross-chain swap.
     * This function convert incoming Units (expected to be from an incoming cross-chain swap) into a specific token.
     * @dev This function is intended to finalise a receiveAsset call.
     * @param toAsset Asset to buy with the units.
     * @param U Incoming units to be turned into vaults tokens.
     * @param minOut Minimum number of tokens to purchase. Will revert if less.
     * @return purchasedTokens Number of toAsset bought.
     */
    function _receiveAsset(
        address toAsset,
        uint256 U,
        uint256 minOut
    ) internal override returns (uint256 purchasedTokens) {
        // _updateAmplification();

        // Calculate the swap return value. Fee is always taken on the sending token.
        purchasedTokens = calcReceiveAsset(toAsset, U);

        // Check if the swap is according to the swap limits.
        uint256 deltaSecurityLimit = purchasedTokens * _weight[toAsset];
        if (_maxUnitCapacity <= deltaSecurityLimit) revert ExceedsSecurityLimit();
        unchecked {
            // We know that _maxUnitCapacity > deltaSecurityLimit so it cannot underflow.
            _maxUnitCapacity -= deltaSecurityLimit;
        }
        _updateUnitCapacity(deltaSecurityLimit);

        // Ensure the user is satisfied with the number of tokens.
        if (minOut > purchasedTokens) revert ReturnInsufficient(purchasedTokens, minOut);

        // Track units for balance0 computation.
        _unitTracker -= int256(U);
    }

    /**
     * @notice Completes a cross-chain swap by converting units to the desired token.
     * @dev Security checks are performed by _receiveAsset.
     * @param channelId Source chain identifier.
     * @param fromVault Source vault.
     * @param toAssetIndex Index of the asset to be purchased.
     * @param toAccount Recipient of assets on destination chain.
     * @param U Incoming units.
     * @param minOut Minimum number of token to buy. Reverts back to the sending side.
     * @param fromAmount Used to match cross-chain swap events. The input amount minus fees on the sending chain.
     * @param fromAsset Used to match cross-chain swap events. The input asset on the source chain.
     * @param blockNumberMod Used to match cross-chain swap events. The block number from the source chain.
     * @param purchasedTokens Number of toAsset bought.
     */
    function receiveAsset(
        bytes32 channelId,
        bytes calldata fromVault,
        uint256 toAssetIndex,
        address toAccount,
        uint256 U,
        uint256 minOut,
        uint256 fromAmount,
        bytes calldata fromAsset,
        uint32 blockNumberMod
    ) nonReentrant onlyChainInterface onlyConnectedPool(channelId, fromVault) external override returns(uint256 purchasedTokens) {
        // Convert the asset index (toAsset) into the asset to be purchased.
        address toAsset = _tokenIndexing[toAssetIndex];
        purchasedTokens = _receiveAsset(
            toAsset,
            U,
            minOut
        );

        // Send the assets to the user.
        SafeTransferLib.safeTransfer(toAsset, toAccount, purchasedTokens);

        emit ReceiveAsset(
            channelId, 
            fromVault, 
            toAccount, 
            toAsset, 
            U, 
            purchasedTokens, 
            fromAmount,
            fromAsset,
            blockNumberMod
        );
    }

    //--- Liquidity swapping ---//
    // Because of the way vault tokens work in a pool, there needs to be a way for users to easily get
    // a distributed stake. Liquidity swaps is a macro implemented at the smart contract level equivalent to:
    // 1. Withdraw tokens.
    // 2. Convert tokens to units & transfer to target vault.
    // 3. Convert units to an even mix of tokens.
    // 4. Deposit the even mix of tokens.
    // In 1 user invocation.

    /** 
     * @notice Computes balance0**(1-amp) without any special caching.
     * @dev Whenever balance0 is computed, the true balance should be used instead of the one
     * modifed by the escrow. This is because balance0 is constant during swaps. Thus, if the
     * balance was modified, it would not be constant during swaps.
     * The function also returns the vault asset count as it is always used in conjunction with walpha_0_ampped.
     * The external function does not.
     * @return walpha_0_ampped Balance0**(1-amp)
     * @return it the vault asset count
     */
    function _computeBalance0(int256 oneMinusAmp) internal view returns(uint256 walpha_0_ampped, uint256 it) {
        // Compute walpha_0 to find the reference balances. This lets us evaluate the
        // number of tokens the vault should have IF the price in the pool is 1:1.

        // This is a balance0 implementation. The balance 0 implementation here is reference.
        int256 weightedAssetBalanceSum = 0;
        for (it; it < MAX_ASSETS;) {
            address token = _tokenIndexing[it];
            if (token == address(0)) break;
            uint256 weight = _weight[token];

            uint256 weightAssetBalance = weight * (ERC20(token).balanceOf(address(this)) - _underwriteEscrowMatchBalance0[token]);

            // If weightAssetBalance == 0, then this computation would fail. However since 0^(1-k) = 0, we can set it to 0.
            int256 wab = 0;
            if (weightAssetBalance != 0){
                wab = FixedPointMathLib.powWad(
                    int256(weightAssetBalance * FixedPointMathLib.WAD),     // If casting overflows to a negative number, powWad fails
                    oneMinusAmp
                );

                // if wab == 0, there is no need to add it. So only add if != 0.
                weightedAssetBalanceSum += wab;
            }

            unchecked {
                ++it;
            }
        }

        // weightedAssetBalanceSum > _unitTracker always, since _unitTracker correlates to exactly
        // the difference between weightedAssetBalanceSum and weightedAssetBalance0Sum and thus
        // _unitTracker < weightedAssetBalance0Sum
        unchecked {
            // weightedAssetBalanceSum - _unitTracker can overflow for negative _unitTracker. The result will
            // be correct once it is casted to uint256.
            walpha_0_ampped = uint256(weightedAssetBalanceSum - _unitTracker) / it;   // By design, weightedAssetBalanceSum > _unitTracker
        }   
    }

    /** 
     * @notice Computes balance0 for the pool.
     * @dev This can be used as a local invariant. Is constant (or slowly increasing) for swaps.
     * Deposits and withdrawals change balance0 and as such, it cannot be used to examine if a vault is secure
     * by it self.
     * This function does not return balance0 as it, is returns a weighted amplified form.
     * For pretty much any real world usage of the function, this is the relevant form.
     * @return walpha_0 Balance0**(1-amp)
     */
    function computeBalance0() external view returns(uint256 walpha_0) {
        int256 oneMinusAmp = _oneMinusAmp;
       (uint256 walpha_0_ampped, ) = _computeBalance0(oneMinusAmp);

        walpha_0 = uint256( // casting: powWad is not negative.
            FixedPointMathLib.powWad(
                int256(walpha_0_ampped),  // Casting: If overflow, then powWad fails as the overflow is into negative.
                WADWAD / oneMinusAmp
            )
        );
    }

    /**
     * @notice Initiate a cross-chain liquidity swap by withdrawing tokens and converting them to units.
     * @dev While the description says tokens are withdrawn and then converted to units, vault tokens are converted
     * directly into units through the following equation: U = N · wa^(1-k) · (((PT + pt)/PT)^(1-k) - 1)
     * @param routeDescription Cross-chain route description that contains the chainIdentifier, toAccount, toVault, and relaying incentive.
     * @param vaultTokens Number of vault tokens to exchange.
     * @param minOut Array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets].
     * @param fallbackUser If the transaction fails, send the escrowed funds to this address.
     * @param calldata_ Data field if a call should be made on the target chain.
     * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(<address>), <data>). At maximum 65535 bytes can be passed.
     * @return U Number of units bought.
     */
    function sendLiquidity(
        RouteDescription calldata routeDescription,
        uint256 vaultTokens,
        uint256[2] calldata minOut,
        address fallbackUser,
        bytes calldata calldata_
    ) nonReentrant onlyConnectedPool(routeDescription.chainIdentifier, routeDescription.toVault) external payable override returns (uint256 U) {
        // Fallback user cannot be address(0) since this is used as a check for the existance of an escrow.
        // It would also be a silly fallback address.
        require(fallbackUser != address(0));
        // Correct address format is checked on the cross-chain interface.

        // _updateAmplification();

        // When accesssing totalSupply, remember that we already burnt the incoming vaultTokens.
        _burn(msg.sender, vaultTokens);

        int256 oneMinusAmp = _oneMinusAmp;

        // Compute walpha_0 to find the reference balances. This lets us evaluate the
        // number of tokens the vault should have If the price in the pool is 1:1.
        (uint256 walpha_0_ampped, uint256 it) = _computeBalance0(oneMinusAmp);

        {
            // Plus _escrowedVaultTokens since we want the withdrawal to return less. Adding vaultTokens as these have already been burnt.
            uint256 ts = totalSupply() + _escrowedVaultTokens + vaultTokens;
            uint256 pt_fraction = FixedPointMathLib.divWad(ts + vaultTokens, ts);

            U = it * FixedPointMathLib.mulWad(
                walpha_0_ampped, 
                uint256(FixedPointMathLib.powWad( // Always casts a positive value
                    int256(pt_fraction), // If casting overflows to a negative number, powWad fails
                    oneMinusAmp
                )) - FixedPointMathLib.WAD
            );
            // onSendLiquiditySuccess requires casting U to int256 to update the _unitTracker and must never revert. Check for overflow here.
            require(U < uint256(type(int256).max)); // int256 max fits in uint256
            _unitTracker += int256(U);
        }

        // Transfer the units to the target vault.
        ICatalystChainInterface(_chainInterface).sendCrossChainLiquidity{value: msg.value}(
            routeDescription,
            U,
            minOut,
            vaultTokens,
            calldata_
        );

        // Store the escrow information. For that, an index is required. Since we need this index twice, we store it.
        // Only information that is relevant for the escrow has to be hashed. (+ some extra for randomisation)
        // No need to hash context (as token/liquidity escrow data is different), fromVault, toVault, targetAssetIndex, minOut, CallData
        bytes32 sendLiquidityHash = _computeSendLiquidityHash(
            routeDescription.toAccount,              // Ensures no collisions between different users
            U,                      // Used to randomise the hash
            vaultTokens,            // Required! to validate release escrow data
            uint32(block.number)    // May overflow, but this is desired (% 2**32)
        );

        // Emit event before setting escrow to clear up variables from stack.
        emit SendLiquidity(
            routeDescription.chainIdentifier,
            routeDescription.toVault,
            routeDescription.toAccount,
            vaultTokens,
            minOut,
            U
        );

        // Escrow the vault token used to purchase units. These will be sent back if transaction doesn't arrive / timeout.
        _setLiquidityEscrow(
            sendLiquidityHash,
            fallbackUser,
            vaultTokens
        );

        // Adjustment of the security limit is delayed until ack to avoid
        // a router abusing timeout to circumvent the security limit at a low cost.
    }

    /**
     * @notice Handles common logic assocaited with the completion of a cross-chain liquidity swap.
     * This function convert incoming units directly to vault tokens.
     * @dev This function is meant to finalise a receiveLiquidity call.
     * @param U Incoming units to be turned into vault tokens.
     * @param minVaultTokens Minimum number of vault tokens to mint (revert if less).
     * @param minReferenceAsset Minimum number of reference tokens the vaults tokens are worth (revert if less).
     * @return vaultTokens Minted vault tokens.
     */
    function _receiveLiquidity(
        uint256 U,
        uint256 minVaultTokens,
        uint256 minReferenceAsset
    ) internal returns (uint256 vaultTokens) {
        // _updateAmplification();

        int256 oneMinusAmp = _oneMinusAmp;

        // Compute walpha_0 to find the reference balances. This lets us evaluate the
        // number of tokens the vault should have If the price in the pool is 1:1.
        (uint256 walpha_0_ampped, uint256 it) = _computeBalance0(oneMinusAmp);

        int256 oneMinusAmpInverse = WADWAD / oneMinusAmp;

        uint256 it_times_walpha_amped = it * walpha_0_ampped;

        // On totalSupply(). Do not add escrow amount, as higher amount results in a larger return.
        vaultTokens = _calcPriceCurveLimitShare(U, totalSupply(), it_times_walpha_amped, oneMinusAmpInverse);

        // Check if more vault tokens than the minimum can be minted.
        if (minVaultTokens > vaultTokens) revert ReturnInsufficient(vaultTokens, minVaultTokens);
        // Then check if the minimum number of reference assets is honoured.
        if (minReferenceAsset != 0) {
            uint256 walpha_0 = uint256(FixedPointMathLib.powWad(  // uint256 casting: Is always positive.
                int256(walpha_0_ampped), // int256 casting: If casts to a negative number, powWad fails because it uses ln which can't take negative numbers.
                oneMinusAmpInverse
            )); 
            // Add escrow to ensure that even if all ongoing transaction revert, the user gets their expected amount.
            // Add vault tokens because they are going to be minted (We want the reference value after mint).
            uint256 walpha_0_owned = ((walpha_0 * vaultTokens) / (totalSupply() + _escrowedVaultTokens + vaultTokens)) / FixedPointMathLib.WAD;
            if (minReferenceAsset > walpha_0_owned) revert ReturnInsufficient(walpha_0_owned, minReferenceAsset);
        }

        // Update the unit tracker:
        _unitTracker -= int256(U);

        // Security limit
        {
            // To calculate the vaultTokenEquiv, we set \alpha_t = \alpha_0.
            // This should be a close enough approximation.
            // If U > it_times_walpha_amped, then U can purchase more than 50% of the vault.
            // And the below calculation doesn't work.
            if (it_times_walpha_amped <= U) revert ExceedsSecurityLimit();
            uint256 vaultTokenEquiv = FixedPointMathLib.mulWadUp(
                uint256(FixedPointMathLib.powWad( // Always casts a positive value
                    int256(it_times_walpha_amped), // If casting overflows to a negative number, powWad fails
                    oneMinusAmpInverse
                )),
                FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad( // powWad is always <= 1, as 'base' is always <= 1
                    int256(FixedPointMathLib.divWad( // Casting never overflows, as division result is always <= 1
                        it_times_walpha_amped - U,
                        it_times_walpha_amped
                    )),
                    oneMinusAmpInverse
                ))
            );
            // Check if the swap is according to the swap limits
            _updateUnitCapacity(FixedPointMathLib.mulWad(2, vaultTokenEquiv));
        }
    }

    /**
     * @notice Completes a cross-chain liquidity swap by converting units to tokens and depositing.
     * @dev Security checks are performed by _receiveLiquidity.
     * While the description says units are converted to tokens and then deposited, units are converted
     * directly to vault tokens through the following equation: pt = PT · (((N · wa_0^(1-k) + U)/(N · wa_0^(1-k))^(1/(1-k)) - 1)
     * @param channelId Source chain identifier.
     * @param fromVault Source vault.
     * @param toAccount Recipient of vault tokens.
     * @param U Incoming units to be turned into vault tokens.
     * @param minVaultTokens Minimum number of vault tokens to mint (revert if less).
     * @param minReferenceAsset Minimum number of reference tokens the vaults tokens are worth (revert if less).
     * @param fromAmount Used to match cross-chain swap events. The input amount on the source chain.
     * @param blockNumberMod Used to match cross-chain swap events. The block number from the source chain.
     * @return purchasedVaultTokens Minted vault tokens.
     */
    function receiveLiquidity(
        bytes32 channelId,
        bytes calldata fromVault,
        address toAccount,
        uint256 U,
        uint256 minVaultTokens,
        uint256 minReferenceAsset,
        uint256 fromAmount,
        uint32 blockNumberMod
    ) nonReentrant onlyChainInterface onlyConnectedPool(channelId, fromVault) external override returns(uint256 purchasedVaultTokens) {
        purchasedVaultTokens = _receiveLiquidity(
            U,
            minVaultTokens,
            minReferenceAsset
        );

        emit ReceiveLiquidity(channelId, fromVault, toAccount, U, purchasedVaultTokens, fromAmount, blockNumberMod);

        // Mint vault tokens for the user.
        _mint(toAccount, purchasedVaultTokens);
    }

    //-- Escrow Functions --//

    /** 
     * @notice Deletes and releases escrowed tokens to the vault and updates the security limit.
     * @dev Should never revert!
     * The base implementation exists in CatalystVaultCommon. The function adds security limit
     * adjustment to the implementation to swap volume supported.
     * @param toAccount Recipient of the transaction on the target chain.
     * @param U Number of units purchased.
     * @param escrowAmount Number of tokens escrowed.
     * @param escrowToken Token escrowed.
     * @param blockNumberMod Block number at which the swap transaction was commited (mod 32)
     */
    function onSendAssetSuccess(
        bytes32 channelId,
        bytes calldata toAccount,
        uint256 U,
        uint256 escrowAmount,
        address escrowToken,
        uint32 blockNumberMod
    ) public override {
        // Execute common escrow logic.
        super.onSendAssetSuccess(channelId, toAccount, U, escrowAmount, escrowToken, blockNumberMod);

        // Received assets should be subtracted from the used unit capacity.
        // It is assumed if the router was fraudulent no-one would execute a trade.
        // As a result, if people swap into the vault, we should expect that there is exactly
        // the inswapped amount of trust in the vault. If this wasn't implemented, there would be
        // a maximum daily cross chain volume, which is bad for liquidity providers.
        unchecked {
            // escrowAmount * _weight[escrowToken] has been calculated before.
            uint256 escrowAmountTimesWeight = escrowAmount * _weight[escrowToken];

            uint256 UC = _usedUnitCapacity;
            // If UC < escrowAmount and we do UC - escrowAmount < 0 underflow => bad.
            if (UC > escrowAmountTimesWeight) {
                _usedUnitCapacity = UC - escrowAmountTimesWeight; // Does not underflow since _usedUnitCapacity > escrowAmount.
            } else if (UC != 0) {
                // If UC == 0, then we shouldn't do anything. Skip that case.
                // when UC <= escrowAmount => UC - escrowAmount <= 0 => max(UC - escrowAmount, 0) = 0
                _usedUnitCapacity = 0;
            }

            // There is a chance that _maxUnitCapacity + escrowAmount * _weight[escrowToken] will overflow.
            // since the number has never been calculated before. This function should never revert so the computation
            // has to be done unchecked.
            uint256 muc = _maxUnitCapacity;
            uint256 new_muc = muc + escrowAmountTimesWeight; // Might overflow. Can be checked by comparing it against MUC.

            // If new_muc < muc, then new_muc has overflown. As a result, we should set muc = uint256::MAX
            if (new_muc < muc) {
                _maxUnitCapacity = type(uint256).max;
            } else {
                _maxUnitCapacity = new_muc;
            }
        }
    }

    /** 
     * @notice Deletes and releases escrowed tokens to the vault and updates the security limit.
     * @dev Should never revert!
     * The base implementation exists in CatalystVaultCommon. The function adds security limit
     * adjustment to the implementation to swap volume supported.
     * @param toAccount Recipient of the transaction on the target chain.
     * @param U Number of units acquired.
     * @param escrowAmount Number of tokens escrowed.
     * @param escrowToken Token escrowed.
     * @param blockNumberMod Block number at which the swap transaction was commited (mod 32)
     */
    function onSendAssetFailure(
        bytes32 channelId,
        bytes calldata toAccount,
        uint256 U,
        uint256 escrowAmount,
        address escrowToken,
        uint32 blockNumberMod
    ) public override {
        // Execute common escrow logic.
        super.onSendAssetFailure(channelId, toAccount, U, escrowAmount, escrowToken, blockNumberMod);

        // Removed timed-out units from the unit tracker. This will keep the
        // balance0 in balance, since tokens also leave the vault
        _unitTracker -= int256(U);  // It has already been checked on sendAsset that casting to int256 will not overflow.
                                    // Cannot be manipulated by the router as, otherwise, the swapHash check will fail
    }

    // onSendLiquiditySuccess is not overwritten since we are unable to increase
    // the security limit. This is because it is very expensive to compute the update
    // to the security limit. If someone liquidity swapped a significant amount of assets
    // it is assumed the vault has low liquidity. In these cases, liquidity swaps shouldn't be used.

    /** 
     * @notice Deletes and releases liquidity escrowed tokens to the vault and updates the security limit.
     * @dev Should never revert!  
     * The base implementation exists in CatalystVaultCommon.
     * @param toAccount Recipient of the transaction on the target chain. Encoded in bytes32.
     * @param U Number of units initially acquired.
     * @param escrowAmount Number of vault tokens escrowed.
     * @param blockNumberMod Block number at which the swap transaction was commited (mod 32)
     */
    function onSendLiquidityFailure(
        bytes32 channelId,
        bytes calldata toAccount,
        uint256 U,
        uint256 escrowAmount,
        uint32 blockNumberMod
    ) public override {
        super.onSendLiquidityFailure(channelId, toAccount, U, escrowAmount, blockNumberMod);

        // Removed timed-out units from the unit tracker. This will keep the
        // balance0 in balance, since tokens also leave the vault
        _unitTracker -= int256(U);  // It has already been checked on sendAsset that casting to int256 will not overflow.
                                    // Cannot be manipulated by the router as, otherwise, the swapHash check will fail
    }

    function underwriteAsset(
        bytes32 identifier,
        address toAsset,
        uint256 U,
        uint256 minOut
    ) override public returns (uint256 purchasedTokens) {
        // We need to ensure that the conversion in deleteUnderwrite (and receiveAsset) doesn't overflow.
        require(U < uint256(type(int256).max));  // int256 max fits in uint256
        
        purchasedTokens = super.underwriteAsset(identifier, toAsset, U, minOut);

        // unit tracking is handled by _receiveAsset.

        // since _unitTrakcer -= int256(U) has been set but no tokens have
        // let the pool, we need to remove the corresponding amount from
        // the balance 0 computation to ensure it is still correct.
        unchecked {
            // Must be less than the vault balance.
            _underwriteEscrowMatchBalance0[toAsset] += purchasedTokens;
        }
    }

    function releaseUnderwriteAsset(
        address refundTo,
        bytes32 identifier,
        uint256 escrowAmount,
        address escrowToken,
        bytes32 sourceIdentifier,
        bytes calldata fromVault
    )  override public {
         super.releaseUnderwriteAsset(refundTo, identifier, escrowAmount, escrowToken, sourceIdentifier, fromVault);

        unchecked {
            _underwriteEscrowMatchBalance0[escrowToken] -= escrowAmount;
        }
    }

    function deleteUnderwriteAsset(
        bytes32 identifier,
        uint256 U,
        uint256 escrowAmount,
        address escrowToken
    ) override public {
        super.deleteUnderwriteAsset(identifier, U, escrowAmount, escrowToken);
        
        // update the unit tracker. When underwriteAsset was called, the _unitTracker was updated with
        // _unitTrakcer -= int256(U) so we need to cancel that.
        _unitTracker += int256(U);  // It has already been checked on sendAsset that casting to int256 will not overflow.
                                    // Cannot be manipulated by the router as, otherwise, the swapHash check will fail

        unchecked {
            _underwriteEscrowMatchBalance0[escrowToken] -= escrowAmount;
        }
    }
}

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

/// @notice Simple ERC20 + EIP-2612 implementation.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/tokens/ERC20.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/tokens/ERC20.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)
///
/// @dev Note:
/// - The ERC20 standard allows minting and transferring to and from the zero address,
///   minting and transferring zero tokens, as well as self-approvals.
///   For performance, this implementation WILL NOT revert for such actions.
///   Please add any checks with overrides if desired.
/// - The `permit` function uses the ecrecover precompile (0x1).
///
/// If you are overriding:
/// - NEVER violate the ERC20 invariant:
///   the total sum of all balances must be equal to `totalSupply()`.
/// - Check that the overridden function is actually used in the function you want to
///   change the behavior of. Much of the code has been manually inlined for performance.
abstract contract ERC20 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The total supply has overflowed.
    error TotalSupplyOverflow();

    /// @dev The allowance has overflowed.
    error AllowanceOverflow();

    /// @dev The allowance has underflowed.
    error AllowanceUnderflow();

    /// @dev Insufficient balance.
    error InsufficientBalance();

    /// @dev Insufficient allowance.
    error InsufficientAllowance();

    /// @dev The permit is invalid.
    error InvalidPermit();

    /// @dev The permit has expired.
    error PermitExpired();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Emitted when `amount` tokens is transferred from `from` to `to`.
    event Transfer(address indexed from, address indexed to, uint256 amount);

    /// @dev Emitted when `amount` tokens is approved by `owner` to be used by `spender`.
    event Approval(address indexed owner, address indexed spender, uint256 amount);

    /// @dev `keccak256(bytes("Transfer(address,address,uint256)"))`.
    uint256 private constant _TRANSFER_EVENT_SIGNATURE =
        0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef;

    /// @dev `keccak256(bytes("Approval(address,address,uint256)"))`.
    uint256 private constant _APPROVAL_EVENT_SIGNATURE =
        0x8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The storage slot for the total supply.
    uint256 private constant _TOTAL_SUPPLY_SLOT = 0x05345cdf77eb68f44c;

    /// @dev The balance slot of `owner` is given by:
    /// ```
    ///     mstore(0x0c, _BALANCE_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let balanceSlot := keccak256(0x0c, 0x20)
    /// ```
    uint256 private constant _BALANCE_SLOT_SEED = 0x87a211a2;

    /// @dev The allowance slot of (`owner`, `spender`) is given by:
    /// ```
    ///     mstore(0x20, spender)
    ///     mstore(0x0c, _ALLOWANCE_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let allowanceSlot := keccak256(0x0c, 0x34)
    /// ```
    uint256 private constant _ALLOWANCE_SLOT_SEED = 0x7f5e9f20;

    /// @dev The nonce slot of `owner` is given by:
    /// ```
    ///     mstore(0x0c, _NONCES_SLOT_SEED)
    ///     mstore(0x00, owner)
    ///     let nonceSlot := keccak256(0x0c, 0x20)
    /// ```
    uint256 private constant _NONCES_SLOT_SEED = 0x38377508;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev `(_NONCES_SLOT_SEED << 16) | 0x1901`.
    uint256 private constant _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX = 0x383775081901;

    /// @dev `keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)")`.
    bytes32 private constant _DOMAIN_TYPEHASH =
        0x8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f;

    /// @dev `keccak256("1")`.
    bytes32 private constant _VERSION_HASH =
        0xc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6;

    /// @dev `keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)")`.
    bytes32 private constant _PERMIT_TYPEHASH =
        0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ERC20 METADATA                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the name of the token.
    function name() public view virtual returns (string memory);

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

    /// @dev Returns the decimals places of the token.
    function decimals() public view virtual returns (uint8) {
        return 18;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           ERC20                            */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the amount of tokens in existence.
    function totalSupply() public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_TOTAL_SUPPLY_SLOT)
        }
    }

    /// @dev Returns the amount of tokens owned by `owner`.
    function balanceOf(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Returns the amount of tokens that `spender` can spend on behalf of `owner`.
    function allowance(address owner, address spender)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x34))
        }
    }

    /// @dev Sets `amount` as the allowance of `spender` over the caller's tokens.
    ///
    /// Emits a {Approval} event.
    function approve(address spender, uint256 amount) public virtual returns (bool) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the allowance slot and store the amount.
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x34), amount)
            // Emit the {Approval} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, caller(), shr(96, mload(0x2c)))
        }
        return true;
    }

    /// @dev Transfer `amount` tokens from the caller to `to`.
    ///
    /// Requirements:
    /// - `from` must at least have `amount`.
    ///
    /// Emits a {Transfer} event.
    function transfer(address to, uint256 amount) public virtual returns (bool) {
        _beforeTokenTransfer(msg.sender, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, caller())
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, caller(), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(msg.sender, to, amount);
        return true;
    }

    /// @dev Transfers `amount` tokens from `from` to `to`.
    ///
    /// Note: Does not update the allowance if it is the maximum uint256 value.
    ///
    /// Requirements:
    /// - `from` must at least have `amount`.
    /// - The caller must have at least `amount` of allowance to transfer the tokens of `from`.
    ///
    /// Emits a {Transfer} event.
    function transferFrom(address from, address to, uint256 amount) public virtual returns (bool) {
        _beforeTokenTransfer(from, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let from_ := shl(96, from)
            // Compute the allowance slot and load its value.
            mstore(0x20, caller())
            mstore(0x0c, or(from_, _ALLOWANCE_SLOT_SEED))
            let allowanceSlot := keccak256(0x0c, 0x34)
            let allowance_ := sload(allowanceSlot)
            // If the allowance is not the maximum uint256 value.
            if add(allowance_, 1) {
                // Revert if the amount to be transferred exceeds the allowance.
                if gt(amount, allowance_) {
                    mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated allowance.
                sstore(allowanceSlot, sub(allowance_, amount))
            }
            // Compute the balance slot and load its value.
            mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(from, to, amount);
        return true;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          EIP-2612                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev For more performance, override to return the constant value
    /// of `keccak256(bytes(name()))` if `name()` will never change.
    function _constantNameHash() internal view virtual returns (bytes32 result) {}

    /// @dev Returns the current nonce for `owner`.
    /// This value is used to compute the signature for EIP-2612 permit.
    function nonces(address owner) public view virtual returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the nonce slot and load its value.
            mstore(0x0c, _NONCES_SLOT_SEED)
            mstore(0x00, owner)
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /// @dev Sets `value` as the allowance of `spender` over the tokens of `owner`,
    /// authorized by a signed approval by `owner`.
    ///
    /// Emits a {Approval} event.
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        bytes32 nameHash = _constantNameHash();
        //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
        if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
        /// @solidity memory-safe-assembly
        assembly {
            // Revert if the block timestamp is greater than `deadline`.
            if gt(timestamp(), deadline) {
                mstore(0x00, 0x1a15a3cc) // `PermitExpired()`.
                revert(0x1c, 0x04)
            }
            let m := mload(0x40) // Grab the free memory pointer.
            // Clean the upper 96 bits.
            owner := shr(96, shl(96, owner))
            spender := shr(96, shl(96, spender))
            // Compute the nonce slot and load its value.
            mstore(0x0e, _NONCES_SLOT_SEED_WITH_SIGNATURE_PREFIX)
            mstore(0x00, owner)
            let nonceSlot := keccak256(0x0c, 0x20)
            let nonceValue := sload(nonceSlot)
            // Prepare the domain separator.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), nameHash)
            mstore(add(m, 0x40), _VERSION_HASH)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            mstore(0x2e, keccak256(m, 0xa0))
            // Prepare the struct hash.
            mstore(m, _PERMIT_TYPEHASH)
            mstore(add(m, 0x20), owner)
            mstore(add(m, 0x40), spender)
            mstore(add(m, 0x60), value)
            mstore(add(m, 0x80), nonceValue)
            mstore(add(m, 0xa0), deadline)
            mstore(0x4e, keccak256(m, 0xc0))
            // Prepare the ecrecover calldata.
            mstore(0x00, keccak256(0x2c, 0x42))
            mstore(0x20, and(0xff, v))
            mstore(0x40, r)
            mstore(0x60, s)
            let t := staticcall(gas(), 1, 0, 0x80, 0x20, 0x20)
            // If the ecrecover fails, the returndatasize will be 0x00,
            // `owner` will be checked if it equals the hash at 0x00,
            // which evaluates to false (i.e. 0), and we will revert.
            // If the ecrecover succeeds, the returndatasize will be 0x20,
            // `owner` will be compared against the returned address at 0x20.
            if iszero(eq(mload(returndatasize()), owner)) {
                mstore(0x00, 0xddafbaef) // `InvalidPermit()`.
                revert(0x1c, 0x04)
            }
            // Increment and store the updated nonce.
            sstore(nonceSlot, add(nonceValue, t)) // `t` is 1 if ecrecover succeeds.
            // Compute the allowance slot and store the value.
            // The `owner` is already at slot 0x20.
            mstore(0x40, or(shl(160, _ALLOWANCE_SLOT_SEED), spender))
            sstore(keccak256(0x2c, 0x34), value)
            // Emit the {Approval} event.
            log3(add(m, 0x60), 0x20, _APPROVAL_EVENT_SIGNATURE, owner, spender)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero pointer.
        }
    }

    /// @dev Returns the EIP-712 domain separator for the EIP-2612 permit.
    function DOMAIN_SEPARATOR() public view virtual returns (bytes32 result) {
        bytes32 nameHash = _constantNameHash();
        //  We simply calculate it on-the-fly to allow for cases where the `name` may change.
        if (nameHash == bytes32(0)) nameHash = keccak256(bytes(name()));
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Grab the free memory pointer.
            mstore(m, _DOMAIN_TYPEHASH)
            mstore(add(m, 0x20), nameHash)
            mstore(add(m, 0x40), _VERSION_HASH)
            mstore(add(m, 0x60), chainid())
            mstore(add(m, 0x80), address())
            result := keccak256(m, 0xa0)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL MINT FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Mints `amount` tokens to `to`, increasing the total supply.
    ///
    /// Emits a {Transfer} event.
    function _mint(address to, uint256 amount) internal virtual {
        _beforeTokenTransfer(address(0), to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let totalSupplyBefore := sload(_TOTAL_SUPPLY_SLOT)
            let totalSupplyAfter := add(totalSupplyBefore, amount)
            // Revert if the total supply overflows.
            if lt(totalSupplyAfter, totalSupplyBefore) {
                mstore(0x00, 0xe5cfe957) // `TotalSupplyOverflow()`.
                revert(0x1c, 0x04)
            }
            // Store the updated total supply.
            sstore(_TOTAL_SUPPLY_SLOT, totalSupplyAfter)
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, 0, shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(address(0), to, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  INTERNAL BURN FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Burns `amount` tokens from `from`, reducing the total supply.
    ///
    /// Emits a {Transfer} event.
    function _burn(address from, uint256 amount) internal virtual {
        _beforeTokenTransfer(from, address(0), amount);
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the balance slot and load its value.
            mstore(0x0c, _BALANCE_SLOT_SEED)
            mstore(0x00, from)
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Subtract and store the updated total supply.
            sstore(_TOTAL_SUPPLY_SLOT, sub(sload(_TOTAL_SUPPLY_SLOT), amount))
            // Emit the {Transfer} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, shl(96, from)), 0)
        }
        _afterTokenTransfer(from, address(0), amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL TRANSFER FUNCTIONS                 */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Moves `amount` of tokens from `from` to `to`.
    function _transfer(address from, address to, uint256 amount) internal virtual {
        _beforeTokenTransfer(from, to, amount);
        /// @solidity memory-safe-assembly
        assembly {
            let from_ := shl(96, from)
            // Compute the balance slot and load its value.
            mstore(0x0c, or(from_, _BALANCE_SLOT_SEED))
            let fromBalanceSlot := keccak256(0x0c, 0x20)
            let fromBalance := sload(fromBalanceSlot)
            // Revert if insufficient balance.
            if gt(amount, fromBalance) {
                mstore(0x00, 0xf4d678b8) // `InsufficientBalance()`.
                revert(0x1c, 0x04)
            }
            // Subtract and store the updated balance.
            sstore(fromBalanceSlot, sub(fromBalance, amount))
            // Compute the balance slot of `to`.
            mstore(0x00, to)
            let toBalanceSlot := keccak256(0x0c, 0x20)
            // Add and store the updated balance of `to`.
            // Will not overflow because the sum of all user balances
            // cannot exceed the maximum uint256 value.
            sstore(toBalanceSlot, add(sload(toBalanceSlot), amount))
            // Emit the {Transfer} event.
            mstore(0x20, amount)
            log3(0x20, 0x20, _TRANSFER_EVENT_SIGNATURE, shr(96, from_), shr(96, mload(0x0c)))
        }
        _afterTokenTransfer(from, to, amount);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                INTERNAL ALLOWANCE FUNCTIONS                */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Updates the allowance of `owner` for `spender` based on spent `amount`.
    function _spendAllowance(address owner, address spender, uint256 amount) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the allowance slot and load its value.
            mstore(0x20, spender)
            mstore(0x0c, _ALLOWANCE_SLOT_SEED)
            mstore(0x00, owner)
            let allowanceSlot := keccak256(0x0c, 0x34)
            let allowance_ := sload(allowanceSlot)
            // If the allowance is not the maximum uint256 value.
            if add(allowance_, 1) {
                // Revert if the amount to be transferred exceeds the allowance.
                if gt(amount, allowance_) {
                    mstore(0x00, 0x13be252b) // `InsufficientAllowance()`.
                    revert(0x1c, 0x04)
                }
                // Subtract and store the updated allowance.
                sstore(allowanceSlot, sub(allowance_, amount))
            }
        }
    }

    /// @dev Sets `amount` as the allowance of `spender` over the tokens of `owner`.
    ///
    /// Emits a {Approval} event.
    function _approve(address owner, address spender, uint256 amount) internal virtual {
        /// @solidity memory-safe-assembly
        assembly {
            let owner_ := shl(96, owner)
            // Compute the allowance slot and store the amount.
            mstore(0x20, spender)
            mstore(0x0c, or(owner_, _ALLOWANCE_SLOT_SEED))
            sstore(keccak256(0x0c, 0x34), amount)
            // Emit the {Approval} event.
            mstore(0x00, amount)
            log3(0x00, 0x20, _APPROVAL_EVENT_SIGNATURE, shr(96, owner_), shr(96, mload(0x2c)))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     HOOKS TO OVERRIDE                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Hook that is called before any transfer of tokens.
    /// This includes minting and burning.
    function _beforeTokenTransfer(address from, address to, uint256 amount) internal virtual {}

    /// @dev Hook that is called after any transfer of tokens.
    /// This includes minting and burning.
    function _afterTokenTransfer(address from, address to, uint256 amount) internal virtual {}
}

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

/// @notice Safe ETH and ERC20 transfer library that gracefully handles missing return values.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/SafeTransferLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/SafeTransferLib.sol)
///
/// @dev Note:
/// - For ETH transfers, please use `forceSafeTransferETH` for DoS protection.
/// - For ERC20s, this implementation won't check that a token has code,
///   responsibility is delegated to the caller.
library SafeTransferLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /// @dev The ERC20 `transferFrom` has failed.
    error TransferFromFailed();

    /// @dev The ERC20 `transfer` has failed.
    error TransferFailed();

    /// @dev The ERC20 `approve` has failed.
    error ApproveFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Suggested gas stipend for contract receiving ETH that disallows any storage writes.
    uint256 internal constant GAS_STIPEND_NO_STORAGE_WRITES = 2300;

    /// @dev Suggested gas stipend for contract receiving ETH to perform a few
    /// storage reads and writes, but low enough to prevent griefing.
    uint256 internal constant GAS_STIPEND_NO_GRIEF = 100000;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       ETH OPERATIONS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // If the ETH transfer MUST succeed with a reasonable gas budget, use the force variants.
    //
    // The regular variants:
    // - Forwards all remaining gas to the target.
    // - Reverts if the target reverts.
    // - Reverts if the current contract has insufficient balance.
    //
    // The force variants:
    // - Forwards with an optional gas stipend
    //   (defaults to `GAS_STIPEND_NO_GRIEF`, which is sufficient for most cases).
    // - If the target reverts, or if the gas stipend is exhausted,
    //   creates a temporary contract to force send the ETH via `SELFDESTRUCT`.
    //   Future compatible with `SENDALL`: https://eips.ethereum.org/EIPS/eip-4758.
    // - Reverts if the current contract has insufficient balance.
    //
    // The try variants:
    // - Forwards with a mandatory gas stipend.
    // - Instead of reverting, returns whether the transfer succeeded.

    /// @dev Sends `amount` (in wei) ETH to `to`.
    function safeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gas(), to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`.
    function safeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // Transfer all the ETH and check if it succeeded or not.
            if iszero(call(gas(), to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function forceSafeTransferETH(address to, uint256 amount, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function forceSafeTransferAllETH(address to, uint256 gasStipend) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends `amount` (in wei) ETH to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferETH(address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            if lt(selfbalance(), amount) {
                mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                revert(0x1c, 0x04)
            }
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, amount, codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(amount, 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Force sends all the ETH in the current contract to `to`, with `GAS_STIPEND_NO_GRIEF`.
    function forceSafeTransferAllETH(address to) internal {
        /// @solidity memory-safe-assembly
        assembly {
            // forgefmt: disable-next-item
            if iszero(call(GAS_STIPEND_NO_GRIEF, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)) {
                mstore(0x00, to) // Store the address in scratch space.
                mstore8(0x0b, 0x73) // Opcode `PUSH20`.
                mstore8(0x20, 0xff) // Opcode `SELFDESTRUCT`.
                if iszero(create(selfbalance(), 0x0b, 0x16)) { revert(codesize(), codesize()) } // For gas estimation.
            }
        }
    }

    /// @dev Sends `amount` (in wei) ETH to `to`, with a `gasStipend`.
    function trySafeTransferETH(address to, uint256 amount, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, amount, codesize(), 0x00, codesize(), 0x00)
        }
    }

    /// @dev Sends all the ETH in the current contract to `to`, with a `gasStipend`.
    function trySafeTransferAllETH(address to, uint256 gasStipend)
        internal
        returns (bool success)
    {
        /// @solidity memory-safe-assembly
        assembly {
            success := call(gasStipend, to, selfbalance(), codesize(), 0x00, codesize(), 0x00)
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      ERC20 OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Sends `amount` of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have at least `amount` approved for
    /// the current contract to manage.
    function safeTransferFrom(address token, address from, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, amount) // Store the `amount` argument.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x23b872dd000000000000000000000000) // `transferFrom(address,address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends all of ERC20 `token` from `from` to `to`.
    /// Reverts upon failure.
    ///
    /// The `from` account must have their entire balance approved for
    /// the current contract to manage.
    function safeTransferAllFrom(address token, address from, address to)
        internal
        returns (uint256 amount)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x40, to) // Store the `to` argument.
            mstore(0x2c, shl(96, from)) // Store the `from` argument.
            mstore(0x0c, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x60, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x00, 0x23b872dd) // `transferFrom(address,address,uint256)`.
            amount := mload(0x60) // The `amount` is already at 0x60. We'll need to return it.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x1c, 0x64, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x7939f424) // `TransferFromFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x60, 0) // Restore the zero slot to zero.
            mstore(0x40, m) // Restore the free memory pointer.
        }
    }

    /// @dev Sends `amount` of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransfer(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sends all of ERC20 `token` from the current contract to `to`.
    /// Reverts upon failure.
    function safeTransferAll(address token, address to) internal returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, 0x70a08231) // Store the function selector of `balanceOf(address)`.
            mstore(0x20, address()) // Store the address of the current contract.
            // Read the balance, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                    staticcall(gas(), token, 0x1c, 0x24, 0x34, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x14, to) // Store the `to` argument.
            amount := mload(0x34) // The `amount` is already at 0x34. We'll need to return it.
            mstore(0x00, 0xa9059cbb000000000000000000000000) // `transfer(address,uint256)`.
            // Perform the transfer, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x90b8ec18) // `TransferFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// Reverts upon failure.
    function safeApprove(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, reverting upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Sets `amount` of ERC20 `token` for `to` to manage on behalf of the current contract.
    /// If the initial attempt to approve fails, attempts to reset the approved amount to zero,
    /// then retries the approval again (some tokens, e.g. USDT, requires this).
    /// Reverts upon failure.
    function safeApproveWithRetry(address token, address to, uint256 amount) internal {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, to) // Store the `to` argument.
            mstore(0x34, amount) // Store the `amount` argument.
            mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
            // Perform the approval, retrying upon failure.
            if iszero(
                and( // The arguments of `and` are evaluated from right to left.
                    or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                    call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                )
            ) {
                mstore(0x34, 0) // Store 0 for the `amount`.
                mstore(0x00, 0x095ea7b3000000000000000000000000) // `approve(address,uint256)`.
                pop(call(gas(), token, 0, 0x10, 0x44, codesize(), 0x00)) // Reset the approval.
                mstore(0x34, amount) // Store back the original `amount`.
                // Retry the approval, reverting upon failure.
                if iszero(
                    and(
                        or(eq(mload(0x00), 1), iszero(returndatasize())), // Returned 1 or nothing.
                        call(gas(), token, 0, 0x10, 0x44, 0x00, 0x20)
                    )
                ) {
                    mstore(0x00, 0x3e3f8f73) // `ApproveFailed()`.
                    revert(0x1c, 0x04)
                }
            }
            mstore(0x34, 0) // Restore the part of the free memory pointer that was overwritten.
        }
    }

    /// @dev Returns the amount of ERC20 `token` owned by `account`.
    /// Returns zero if the `token` does not exist.
    function balanceOf(address token, address account) internal view returns (uint256 amount) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x14, account) // Store the `account` argument.
            mstore(0x00, 0x70a08231000000000000000000000000) // `balanceOf(address)`.
            amount :=
                mul(
                    mload(0x20),
                    and( // The arguments of `and` are evaluated from right to left.
                        gt(returndatasize(), 0x1f), // At least 32 bytes returned.
                        staticcall(gas(), token, 0x10, 0x24, 0x20, 0x20)
                    )
                )
        }
    }
}

File 4 of 29 : FixedPointMathLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Arithmetic library with operations for fixed-point numbers.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/FixedPointMathLib.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/FixedPointMathLib.sol)
library FixedPointMathLib {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error ExpOverflow();

    /// @dev The operation failed, as the output exceeds the maximum value of uint256.
    error FactorialOverflow();

    /// @dev The operation failed, due to an overflow.
    error RPowOverflow();

    /// @dev The mantissa is too big to fit.
    error MantissaOverflow();

    /// @dev The operation failed, due to an multiplication overflow.
    error MulWadFailed();

    /// @dev The operation failed, due to an multiplication overflow.
    error SMulWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error DivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error SDivWadFailed();

    /// @dev The operation failed, either due to a multiplication overflow, or a division by a zero.
    error MulDivFailed();

    /// @dev The division failed, as the denominator is zero.
    error DivFailed();

    /// @dev The full precision multiply-divide operation failed, either due
    /// to the result being larger than 256 bits, or a division by a zero.
    error FullMulDivFailed();

    /// @dev The output is undefined, as the input is less-than-or-equal to zero.
    error LnWadUndefined();

    /// @dev The input outside the acceptable domain.
    error OutOfDomain();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The scalar of ETH and most ERC20s.
    uint256 internal constant WAD = 1e18;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*              SIMPLIFIED FIXED POINT OPERATIONS             */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function mulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if mul(y, gt(x, div(not(0), y))) {
                mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down.
    function sMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, y)
            // Equivalent to `require((x == 0 || z / x == y) && !(x == -1 && y == type(int256).min))`.
            if iszero(gt(or(iszero(x), eq(sdiv(z, x), y)), lt(not(x), eq(y, shl(255, 1))))) {
                mstore(0x00, 0xedcd4dd4) // `SMulWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(z, WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawMulWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded down, but without overflow checks.
    function rawSMulWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, y), WAD)
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up.
    function mulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y == 0 || x <= type(uint256).max / y)`.
            if mul(y, gt(x, div(not(0), y))) {
                mstore(0x00, 0xbac65e5b) // `MulWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
        }
    }

    /// @dev Equivalent to `(x * y) / WAD` rounded up, but without overflow checks.
    function rawMulWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, y), WAD))), div(mul(x, y), WAD))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function divWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`.
            if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down.
    function sDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(x, WAD)
            // Equivalent to `require(y != 0 && ((x * WAD) / WAD == x))`.
            if iszero(and(iszero(iszero(y)), eq(sdiv(z, WAD), x))) {
                mstore(0x00, 0x5c43740d) // `SDivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := sdiv(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawDivWad(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded down, but without overflow and divide by zero checks.
    function rawSDivWad(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(mul(x, WAD), y)
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up.
    function divWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to `require(y != 0 && (WAD == 0 || x <= type(uint256).max / WAD))`.
            if iszero(mul(y, iszero(mul(WAD, gt(x, div(not(0), WAD)))))) {
                mstore(0x00, 0x7c5f487d) // `DivWadFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

    /// @dev Equivalent to `(x * WAD) / y` rounded up, but without overflow and divide by zero checks.
    function rawDivWadUp(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := add(iszero(iszero(mod(mul(x, WAD), y))), div(mul(x, WAD), y))
        }
    }

    /// @dev Equivalent to `x` to the power of `y`.
    /// because `x ** y = (e ** ln(x)) ** y = e ** (ln(x) * y)`.
    function powWad(int256 x, int256 y) internal pure returns (int256) {
        // Using `ln(x)` means `x` must be greater than 0.
        return expWad((lnWad(x) * y) / int256(WAD));
    }

    /// @dev Returns `exp(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    function expWad(int256 x) internal pure returns (int256 r) {
        unchecked {
            // When the result is less than 0.5 we return zero.
            // This happens when `x <= (log(1e-18) * 1e18) ~ -4.15e19`.
            if (x <= -41446531673892822313) return r;

            /// @solidity memory-safe-assembly
            assembly {
                // When the result is greater than `(2**255 - 1) / 1e18` we can not represent it as
                // an int. This happens when `x >= floor(log((2**255 - 1) / 1e18) * 1e18) ≈ 135`.
                if iszero(slt(x, 135305999368893231589)) {
                    mstore(0x00, 0xa37bfec9) // `ExpOverflow()`.
                    revert(0x1c, 0x04)
                }
            }

            // `x` is now in the range `(-42, 136) * 1e18`. Convert to `(-42, 136) * 2**96`
            // for more intermediate precision and a binary basis. This base conversion
            // is a multiplication by 1e18 / 2**96 = 5**18 / 2**78.
            x = (x << 78) / 5 ** 18;

            // Reduce range of x to (-½ ln 2, ½ ln 2) * 2**96 by factoring out powers
            // of two such that exp(x) = exp(x') * 2**k, where k is an integer.
            // Solving this gives k = round(x / log(2)) and x' = x - k * log(2).
            int256 k = ((x << 96) / 54916777467707473351141471128 + 2 ** 95) >> 96;
            x = x - k * 54916777467707473351141471128;

            // `k` is in the range `[-61, 195]`.

            // Evaluate using a (6, 7)-term rational approximation.
            // `p` is made monic, we'll multiply by a scale factor later.
            int256 y = x + 1346386616545796478920950773328;
            y = ((y * x) >> 96) + 57155421227552351082224309758442;
            int256 p = y + x - 94201549194550492254356042504812;
            p = ((p * y) >> 96) + 28719021644029726153956944680412240;
            p = p * x + (4385272521454847904659076985693276 << 96);

            // We leave `p` in `2**192` basis so we don't need to scale it back up for the division.
            int256 q = x - 2855989394907223263936484059900;
            q = ((q * x) >> 96) + 50020603652535783019961831881945;
            q = ((q * x) >> 96) - 533845033583426703283633433725380;
            q = ((q * x) >> 96) + 3604857256930695427073651918091429;
            q = ((q * x) >> 96) - 14423608567350463180887372962807573;
            q = ((q * x) >> 96) + 26449188498355588339934803723976023;

            /// @solidity memory-safe-assembly
            assembly {
                // Div in assembly because solidity adds a zero check despite the unchecked.
                // The q polynomial won't have zeros in the domain as all its roots are complex.
                // No scaling is necessary because p is already `2**96` too large.
                r := sdiv(p, q)
            }

            // r should be in the range `(0.09, 0.25) * 2**96`.

            // We now need to multiply r by:
            // - The scale factor `s ≈ 6.031367120`.
            // - The `2**k` factor from the range reduction.
            // - The `1e18 / 2**96` factor for base conversion.
            // We do this all at once, with an intermediate result in `2**213`
            // basis, so the final right shift is always by a positive amount.
            r = int256(
                (uint256(r) * 3822833074963236453042738258902158003155416615667) >> uint256(195 - k)
            );
        }
    }

    /// @dev Returns `ln(x)`, denominated in `WAD`.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/22/exp-ln
    function lnWad(int256 x) internal pure returns (int256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            // We want to convert `x` from `10**18` fixed point to `2**96` fixed point.
            // We do this by multiplying by `2**96 / 10**18`. But since
            // `ln(x * C) = ln(x) + ln(C)`, we can simply do nothing here
            // and add `ln(2**96 / 10**18)` at the end.

            // Compute `k = log2(x) - 96`, `r = 159 - k = 255 - log2(x) = 255 ^ log2(x)`.
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // We place the check here for more optimal stack operations.
            if iszero(sgt(x, 0)) {
                mstore(0x00, 0x1615e638) // `LnWadUndefined()`.
                revert(0x1c, 0x04)
            }
            // forgefmt: disable-next-item
            r := xor(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0xf8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff))

            // Reduce range of x to (1, 2) * 2**96
            // ln(2^k * x) = k * ln(2) + ln(x)
            x := shr(159, shl(r, x))

            // Evaluate using a (8, 8)-term rational approximation.
            // `p` is made monic, we will multiply by a scale factor later.
            // forgefmt: disable-next-item
            let p := sub( // This heavily nested expression is to avoid stack-too-deep for via-ir.
                sar(96, mul(add(43456485725739037958740375743393,
                sar(96, mul(add(24828157081833163892658089445524,
                sar(96, mul(add(3273285459638523848632254066296,
                    x), x))), x))), x)), 11111509109440967052023855526967)
            p := sub(sar(96, mul(p, x)), 45023709667254063763336534515857)
            p := sub(sar(96, mul(p, x)), 14706773417378608786704636184526)
            p := sub(mul(p, x), shl(96, 795164235651350426258249787498))
            // We leave `p` in `2**192` basis so we don't need to scale it back up for the division.

            // `q` is monic by convention.
            let q := add(5573035233440673466300451813936, x)
            q := add(71694874799317883764090561454958, sar(96, mul(x, q)))
            q := add(283447036172924575727196451306956, sar(96, mul(x, q)))
            q := add(401686690394027663651624208769553, sar(96, mul(x, q)))
            q := add(204048457590392012362485061816622, sar(96, mul(x, q)))
            q := add(31853899698501571402653359427138, sar(96, mul(x, q)))
            q := add(909429971244387300277376558375, sar(96, mul(x, q)))

            // `p / q` is in the range `(0, 0.125) * 2**96`.

            // Finalization, we need to:
            // - Multiply by the scale factor `s = 5.549…`.
            // - Add `ln(2**96 / 10**18)`.
            // - Add `k * ln(2)`.
            // - Multiply by `10**18 / 2**96 = 5**18 >> 78`.

            // The q polynomial is known not to have zeros in the domain.
            // No scaling required because p is already `2**96` too large.
            p := sdiv(p, q)
            // Multiply by the scaling factor: `s * 5**18 * 2**96`, base is now `5**18 * 2**192`.
            p := mul(1677202110996718588342820967067443963516166, p)
            // Add `ln(2) * k * 5**18 * 2**192`.
            // forgefmt: disable-next-item
            p := add(mul(16597577552685614221487285958193947469193820559219878177908093499208371, sub(159, r)), p)
            // Add `ln(2**96 / 10**18) * 5**18 * 2**192`.
            p := add(600920179829731861736702779321621459595472258049074101567377883020018308, p)
            // Base conversion: mul `2**18 / 2**192`.
            r := sar(174, p)
        }
    }

    /// @dev Returns `W_0(x)`, denominated in `WAD`.
    /// See: https://en.wikipedia.org/wiki/Lambert_W_function
    /// a.k.a. Product log function. This is an approximation of the principal branch.
    function lambertW0Wad(int256 x) internal pure returns (int256 w) {
        // forgefmt: disable-next-item
        unchecked {
            if ((w = x) <= -367879441171442322) revert OutOfDomain(); // `x` less than `-1/e`.
            int256 wad = int256(WAD);
            int256 p = x;
            uint256 c; // Whether we need to avoid catastrophic cancellation.
            uint256 i = 4; // Number of iterations.
            if (w <= 0x1ffffffffffff) {
                if (-0x4000000000000 <= w) {
                    i = 1; // Inputs near zero only take one step to converge.
                } else if (w <= -0x3ffffffffffffff) {
                    i = 32; // Inputs near `-1/e` take very long to converge.
                }
            } else if (w >> 63 == 0) {
                /// @solidity memory-safe-assembly
                assembly {
                    // Inline log2 for more performance, since the range is small.
                    let v := shr(49, w)
                    let l := shl(3, lt(0xff, v))
                    l := add(or(l, byte(and(0x1f, shr(shr(l, v), 0x8421084210842108cc6318c6db6d54be)),
                        0x0706060506020504060203020504030106050205030304010505030400000000)), 49)
                    w := sdiv(shl(l, 7), byte(sub(l, 31), 0x0303030303030303040506080c13))
                    c := gt(l, 60)
                    i := add(2, add(gt(l, 53), c))
                }
            } else {
                int256 ll = lnWad(w = lnWad(w));
                /// @solidity memory-safe-assembly
                assembly {
                    // `w = ln(x) - ln(ln(x)) + b * ln(ln(x)) / ln(x)`.
                    w := add(sdiv(mul(ll, 1023715080943847266), w), sub(w, ll))
                    i := add(3, iszero(shr(68, x)))
                    c := iszero(shr(143, x))
                }
                if (c == 0) {
                    do { // If `x` is big, use Newton's so that intermediate values won't overflow.
                        int256 e = expWad(w);
                        /// @solidity memory-safe-assembly
                        assembly {
                            let t := mul(w, div(e, wad))
                            w := sub(w, sdiv(sub(t, x), div(add(e, t), wad)))
                        }
                        if (p <= w) break;
                        p = w;
                    } while (--i != 0);
                    /// @solidity memory-safe-assembly
                    assembly {
                        w := sub(w, sgt(w, 2))
                    }
                    return w;
                }
            }
            do { // Otherwise, use Halley's for faster convergence.
                int256 e = expWad(w);
                /// @solidity memory-safe-assembly
                assembly {
                    let t := add(w, wad)
                    let s := sub(mul(w, e), mul(x, wad))
                    w := sub(w, sdiv(mul(s, wad), sub(mul(e, t), sdiv(mul(add(t, wad), s), add(t, t)))))
                }
                if (p <= w) break;
                p = w;
            } while (--i != c);
            /// @solidity memory-safe-assembly
            assembly {
                w := sub(w, sgt(w, 2))
            }
            // For certain ranges of `x`, we'll use the quadratic-rate recursive formula of
            // R. Iacono and J.P. Boyd for the last iteration, to avoid catastrophic cancellation.
            if (c != 0) {
                int256 t = w | 1;
                /// @solidity memory-safe-assembly
                assembly {
                    x := sdiv(mul(x, wad), t)
                }
                x = (t * (wad + lnWad(x)));
                /// @solidity memory-safe-assembly
                assembly {
                    w := sdiv(x, add(wad, t))
                }
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  GENERAL NUMBER UTILITIES                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Calculates `floor(x * y / d)` with full precision.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Remco Bloemen under MIT license: https://2π.com/21/muldiv
    function fullMulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            for {} 1 {} {
                // 512-bit multiply `[p1 p0] = x * y`.
                // Compute the product mod `2**256` and mod `2**256 - 1`
                // then use the Chinese Remainder Theorem to reconstruct
                // the 512 bit result. The result is stored in two 256
                // variables such that `product = p1 * 2**256 + p0`.

                // Least significant 256 bits of the product.
                result := mul(x, y) // Temporarily use `result` as `p0` to save gas.
                let mm := mulmod(x, y, not(0))
                // Most significant 256 bits of the product.
                let p1 := sub(mm, add(result, lt(mm, result)))

                // Handle non-overflow cases, 256 by 256 division.
                if iszero(p1) {
                    if iszero(d) {
                        mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                        revert(0x1c, 0x04)
                    }
                    result := div(result, d)
                    break
                }

                // Make sure the result is less than `2**256`. Also prevents `d == 0`.
                if iszero(gt(d, p1)) {
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }

                /*------------------- 512 by 256 division --------------------*/

                // Make division exact by subtracting the remainder from `[p1 p0]`.
                // Compute remainder using mulmod.
                let r := mulmod(x, y, d)
                // `t` is the least significant bit of `d`.
                // Always greater or equal to 1.
                let t := and(d, sub(0, d))
                // Divide `d` by `t`, which is a power of two.
                d := div(d, t)
                // Invert `d mod 2**256`
                // Now that `d` is an odd number, it has an inverse
                // modulo `2**256` such that `d * inv = 1 mod 2**256`.
                // Compute the inverse by starting with a seed that is correct
                // correct for four bits. That is, `d * inv = 1 mod 2**4`.
                let inv := xor(2, mul(3, d))
                // Now use 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.
                inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**8
                inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**16
                inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**32
                inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**64
                inv := mul(inv, sub(2, mul(d, inv))) // inverse mod 2**128
                result :=
                    mul(
                        // Divide [p1 p0] by the factors of two.
                        // Shift in bits from `p1` into `p0`. For this we need
                        // to flip `t` such that it is `2**256 / t`.
                        or(
                            mul(sub(p1, gt(r, result)), add(div(sub(0, t), t), 1)),
                            div(sub(result, r), t)
                        ),
                        // inverse mod 2**256
                        mul(inv, sub(2, mul(d, inv)))
                    )
                break
            }
        }
    }

    /// @dev Calculates `floor(x * y / d)` with full precision, rounded up.
    /// Throws if result overflows a uint256 or when `d` is zero.
    /// Credit to Uniswap-v3-core under MIT license:
    /// https://github.com/Uniswap/v3-core/blob/main/contracts/libraries/FullMath.sol
    function fullMulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 result) {
        result = fullMulDiv(x, y, d);
        /// @solidity memory-safe-assembly
        assembly {
            if mulmod(x, y, d) {
                result := add(result, 1)
                if iszero(result) {
                    mstore(0x00, 0xae47f702) // `FullMulDivFailed()`.
                    revert(0x1c, 0x04)
                }
            }
        }
    }

    /// @dev Returns `floor(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDiv(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := div(mul(x, y), d)
        }
    }

    /// @dev Returns `ceil(x * y / d)`.
    /// Reverts if `x * y` overflows, or `d` is zero.
    function mulDivUp(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // Equivalent to require(d != 0 && (y == 0 || x <= type(uint256).max / y))
            if iszero(mul(d, iszero(mul(y, gt(x, div(not(0), y)))))) {
                mstore(0x00, 0xad251c27) // `MulDivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(mul(x, y), d))), div(mul(x, y), d))
        }
    }

    /// @dev Returns `ceil(x / d)`.
    /// Reverts if `d` is zero.
    function divUp(uint256 x, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(d) {
                mstore(0x00, 0x65244e4e) // `DivFailed()`.
                revert(0x1c, 0x04)
            }
            z := add(iszero(iszero(mod(x, d))), div(x, d))
        }
    }

    /// @dev Returns `max(0, x - y)`.
    function zeroFloorSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(gt(x, y), sub(x, y))
        }
    }

    /// @dev Exponentiate `x` to `y` by squaring, denominated in base `b`.
    /// Reverts if the computation overflows.
    function rpow(uint256 x, uint256 y, uint256 b) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mul(b, iszero(y)) // `0 ** 0 = 1`. Otherwise, `0 ** n = 0`.
            if x {
                z := xor(b, mul(xor(b, x), and(y, 1))) // `z = isEven(y) ? scale : x`
                let half := shr(1, b) // Divide `b` by 2.
                // Divide `y` by 2 every iteration.
                for { y := shr(1, y) } y { y := shr(1, y) } {
                    let xx := mul(x, x) // Store x squared.
                    let xxRound := add(xx, half) // Round to the nearest number.
                    // Revert if `xx + half` overflowed, or if `x ** 2` overflows.
                    if or(lt(xxRound, xx), shr(128, x)) {
                        mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                        revert(0x1c, 0x04)
                    }
                    x := div(xxRound, b) // Set `x` to scaled `xxRound`.
                    // If `y` is odd:
                    if and(y, 1) {
                        let zx := mul(z, x) // Compute `z * x`.
                        let zxRound := add(zx, half) // Round to the nearest number.
                        // If `z * x` overflowed or `zx + half` overflowed:
                        if or(xor(div(zx, x), z), lt(zxRound, zx)) {
                            // Revert if `x` is non-zero.
                            if iszero(iszero(x)) {
                                mstore(0x00, 0x49f7642b) // `RPowOverflow()`.
                                revert(0x1c, 0x04)
                            }
                        }
                        z := div(zxRound, b) // Return properly scaled `zxRound`.
                    }
                }
            }
        }
    }

    /// @dev Returns the square root of `x`.
    function sqrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            // `floor(sqrt(2**15)) = 181`. `sqrt(2**15) - 181 = 2.84`.
            z := 181 // The "correct" value is 1, but this saves a multiplication later.

            // This segment is to get a reasonable initial estimate for the Babylonian method. With a bad
            // start, the correct # of bits increases ~linearly each iteration instead of ~quadratically.

            // Let `y = x / 2**r`. We check `y >= 2**(k + 8)`
            // but shift right by `k` bits to ensure that if `x >= 256`, then `y >= 256`.
            let r := shl(7, lt(0xffffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffffff, shr(r, x))))
            z := shl(shr(1, r), z)

            // Goal was to get `z*z*y` within a small factor of `x`. More iterations could
            // get y in a tighter range. Currently, we will have y in `[256, 256*(2**16))`.
            // We ensured `y >= 256` so that the relative difference between `y` and `y+1` is small.
            // That's not possible if `x < 256` but we can just verify those cases exhaustively.

            // Now, `z*z*y <= x < z*z*(y+1)`, and `y <= 2**(16+8)`, and either `y >= 256`, or `x < 256`.
            // Correctness can be checked exhaustively for `x < 256`, so we assume `y >= 256`.
            // Then `z*sqrt(y)` is within `sqrt(257)/sqrt(256)` of `sqrt(x)`, or about 20bps.

            // For `s` in the range `[1/256, 256]`, the estimate `f(s) = (181/1024) * (s+1)`
            // is in the range `(1/2.84 * sqrt(s), 2.84 * sqrt(s))`,
            // with largest error when `s = 1` and when `s = 256` or `1/256`.

            // Since `y` is in `[256, 256*(2**16))`, let `a = y/65536`, so that `a` is in `[1/256, 256)`.
            // Then we can estimate `sqrt(y)` using
            // `sqrt(65536) * 181/1024 * (a + 1) = 181/4 * (y + 65536)/65536 = 181 * (y + 65536)/2**18`.

            // There is no overflow risk here since `y < 2**136` after the first branch above.
            z := shr(18, mul(z, add(shr(r, x), 65536))) // A `mul()` is saved from starting `z` at 181.

            // Given the worst case multiplicative error of 2.84 above, 7 iterations should be enough.
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))
            z := shr(1, add(z, div(x, z)))

            // If `x+1` is a perfect square, the Babylonian method cycles between
            // `floor(sqrt(x))` and `ceil(sqrt(x))`. This statement ensures we return floor.
            // See: https://en.wikipedia.org/wiki/Integer_square_root#Using_only_integer_division
            z := sub(z, lt(div(x, z), z))
        }
    }

    /// @dev Returns the cube root of `x`.
    /// Credit to bout3fiddy and pcaversaccio under AGPLv3 license:
    /// https://github.com/pcaversaccio/snekmate/blob/main/src/utils/Math.vy
    function cbrt(uint256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            let r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))

            z := div(shl(div(r, 3), shl(lt(0xf, shr(r, x)), 0xf)), xor(7, mod(r, 3)))

            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)
            z := div(add(add(div(x, mul(z, z)), z), z), 3)

            z := sub(z, lt(div(x, mul(z, z)), z))
        }
    }

    /// @dev Returns the square root of `x`, denominated in `WAD`.
    function sqrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            z = 10 ** 9;
            if (x <= type(uint256).max / 10 ** 36 - 1) {
                x *= 10 ** 18;
                z = 1;
            }
            z *= sqrt(x);
        }
    }

    /// @dev Returns the cube root of `x`, denominated in `WAD`.
    function cbrtWad(uint256 x) internal pure returns (uint256 z) {
        unchecked {
            z = 10 ** 12;
            if (x <= (type(uint256).max / 10 ** 36) * 10 ** 18 - 1) {
                if (x >= type(uint256).max / 10 ** 36) {
                    x *= 10 ** 18;
                    z = 10 ** 6;
                } else {
                    x *= 10 ** 36;
                    z = 1;
                }
            }
            z *= cbrt(x);
        }
    }

    /// @dev Returns the factorial of `x`.
    function factorial(uint256 x) internal pure returns (uint256 result) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(lt(x, 58)) {
                mstore(0x00, 0xaba0f2a2) // `FactorialOverflow()`.
                revert(0x1c, 0x04)
            }
            for { result := 1 } x { x := sub(x, 1) } { result := mul(result, x) }
        }
    }

    /// @dev Returns the log2 of `x`.
    /// Equivalent to computing the index of the most significant bit (MSB) of `x`.
    /// Returns 0 if `x` is zero.
    function log2(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(r, shl(3, lt(0xff, shr(r, x))))
            // forgefmt: disable-next-item
            r := or(r, byte(and(0x1f, shr(shr(r, x), 0x8421084210842108cc6318c6db6d54be)),
                0x0706060506020504060203020504030106050205030304010505030400000000))
        }
    }

    /// @dev Returns the log2 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log2Up(uint256 x) internal pure returns (uint256 r) {
        r = log2(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(r, 1), x))
        }
    }

    /// @dev Returns the log10 of `x`.
    /// Returns 0 if `x` is zero.
    function log10(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(lt(x, 100000000000000000000000000000000000000)) {
                x := div(x, 100000000000000000000000000000000000000)
                r := 38
            }
            if iszero(lt(x, 100000000000000000000)) {
                x := div(x, 100000000000000000000)
                r := add(r, 20)
            }
            if iszero(lt(x, 10000000000)) {
                x := div(x, 10000000000)
                r := add(r, 10)
            }
            if iszero(lt(x, 100000)) {
                x := div(x, 100000)
                r := add(r, 5)
            }
            r := add(r, add(gt(x, 9), add(gt(x, 99), add(gt(x, 999), gt(x, 9999)))))
        }
    }

    /// @dev Returns the log10 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log10Up(uint256 x) internal pure returns (uint256 r) {
        r = log10(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(exp(10, r), x))
        }
    }

    /// @dev Returns the log256 of `x`.
    /// Returns 0 if `x` is zero.
    function log256(uint256 x) internal pure returns (uint256 r) {
        /// @solidity memory-safe-assembly
        assembly {
            r := shl(7, lt(0xffffffffffffffffffffffffffffffff, x))
            r := or(r, shl(6, lt(0xffffffffffffffff, shr(r, x))))
            r := or(r, shl(5, lt(0xffffffff, shr(r, x))))
            r := or(r, shl(4, lt(0xffff, shr(r, x))))
            r := or(shr(3, r), lt(0xff, shr(r, x)))
        }
    }

    /// @dev Returns the log256 of `x`, rounded up.
    /// Returns 0 if `x` is zero.
    function log256Up(uint256 x) internal pure returns (uint256 r) {
        r = log256(x);
        /// @solidity memory-safe-assembly
        assembly {
            r := add(r, lt(shl(shl(3, r), 1), x))
        }
    }

    /// @dev Returns the scientific notation format `mantissa * 10 ** exponent` of `x`.
    /// Useful for compressing prices (e.g. using 25 bit mantissa and 7 bit exponent).
    function sci(uint256 x) internal pure returns (uint256 mantissa, uint256 exponent) {
        /// @solidity memory-safe-assembly
        assembly {
            mantissa := x
            if mantissa {
                if iszero(mod(mantissa, 1000000000000000000000000000000000)) {
                    mantissa := div(mantissa, 1000000000000000000000000000000000)
                    exponent := 33
                }
                if iszero(mod(mantissa, 10000000000000000000)) {
                    mantissa := div(mantissa, 10000000000000000000)
                    exponent := add(exponent, 19)
                }
                if iszero(mod(mantissa, 1000000000000)) {
                    mantissa := div(mantissa, 1000000000000)
                    exponent := add(exponent, 12)
                }
                if iszero(mod(mantissa, 1000000)) {
                    mantissa := div(mantissa, 1000000)
                    exponent := add(exponent, 6)
                }
                if iszero(mod(mantissa, 10000)) {
                    mantissa := div(mantissa, 10000)
                    exponent := add(exponent, 4)
                }
                if iszero(mod(mantissa, 100)) {
                    mantissa := div(mantissa, 100)
                    exponent := add(exponent, 2)
                }
                if iszero(mod(mantissa, 10)) {
                    mantissa := div(mantissa, 10)
                    exponent := add(exponent, 1)
                }
            }
        }
    }

    /// @dev Convenience function for packing `x` into a smaller number using `sci`.
    /// The `mantissa` will be in bits [7..255] (the upper 249 bits).
    /// The `exponent` will be in bits [0..6] (the lower 7 bits).
    /// Use `SafeCastLib` to safely ensure that the `packed` number is small
    /// enough to fit in the desired unsigned integer type:
    /// ```
    ///     uint32 packed = SafeCastLib.toUint32(FixedPointMathLib.packSci(777 ether));
    /// ```
    function packSci(uint256 x) internal pure returns (uint256 packed) {
        (x, packed) = sci(x); // Reuse for `mantissa` and `exponent`.
        /// @solidity memory-safe-assembly
        assembly {
            if shr(249, x) {
                mstore(0x00, 0xce30380c) // `MantissaOverflow()`.
                revert(0x1c, 0x04)
            }
            packed := or(shl(7, x), packed)
        }
    }

    /// @dev Convenience function for unpacking a packed number from `packSci`.
    function unpackSci(uint256 packed) internal pure returns (uint256 unpacked) {
        unchecked {
            unpacked = (packed >> 7) * 10 ** (packed & 0x7f);
        }
    }

    /// @dev Returns the average of `x` and `y`.
    function avg(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = (x & y) + ((x ^ y) >> 1);
        }
    }

    /// @dev Returns the average of `x` and `y`.
    function avg(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = (x >> 1) + (y >> 1) + (((x & 1) + (y & 1)) >> 1);
        }
    }

    /// @dev Returns the absolute value of `x`.
    function abs(int256 x) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(sub(0, shr(255, x)), add(sub(0, shr(255, x)), x))
        }
    }

    /// @dev Returns the absolute distance between `x` and `y`.
    function dist(int256 x, int256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(mul(xor(sub(y, x), sub(x, y)), sgt(x, y)), sub(y, x))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), lt(y, x)))
        }
    }

    /// @dev Returns the minimum of `x` and `y`.
    function min(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), slt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), gt(y, x)))
        }
    }

    /// @dev Returns the maximum of `x` and `y`.
    function max(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, y), sgt(y, x)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(uint256 x, uint256 minValue, uint256 maxValue)
        internal
        pure
        returns (uint256 z)
    {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), gt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), lt(maxValue, z)))
        }
    }

    /// @dev Returns `x`, bounded to `minValue` and `maxValue`.
    function clamp(int256 x, int256 minValue, int256 maxValue) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := xor(x, mul(xor(x, minValue), sgt(minValue, x)))
            z := xor(z, mul(xor(z, maxValue), slt(maxValue, z)))
        }
    }

    /// @dev Returns greatest common divisor of `x` and `y`.
    function gcd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            for { z := x } y {} {
                let t := y
                y := mod(z, y)
                z := t
            }
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   RAW NUMBER OPERATIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x + y`, without checking for overflow.
    function rawAdd(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x + y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x - y`, without checking for underflow.
    function rawSub(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x - y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(uint256 x, uint256 y) internal pure returns (uint256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x * y`, without checking for overflow.
    function rawMul(int256 x, int256 y) internal pure returns (int256 z) {
        unchecked {
            z = x * y;
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawDiv(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := div(x, y)
        }
    }

    /// @dev Returns `x / y`, returning 0 if `y` is zero.
    function rawSDiv(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := sdiv(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawMod(uint256 x, uint256 y) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mod(x, y)
        }
    }

    /// @dev Returns `x % y`, returning 0 if `y` is zero.
    function rawSMod(int256 x, int256 y) internal pure returns (int256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := smod(x, y)
        }
    }

    /// @dev Returns `(x + y) % d`, return 0 if `d` if zero.
    function rawAddMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := addmod(x, y, d)
        }
    }

    /// @dev Returns `(x * y) % d`, return 0 if `d` if zero.
    function rawMulMod(uint256 x, uint256 y, uint256 d) internal pure returns (uint256 z) {
        /// @solidity memory-safe-assembly
        assembly {
            z := mulmod(x, y, d)
        }
    }
}

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

import { ICatalystV1Structs } from "./ICatalystV1VaultState.sol";
import { ICrossChainReceiver } from "GeneralisedIncentives/src/interfaces/ICrossChainReceiver.sol";

// Autogenerated
interface ICatalystChainInterface is ICatalystV1Structs, ICrossChainReceiver {

    function EXPIRE_CALLER_REWARD() external view returns (uint256);
    function EXPIRE_CALLER_REWARD_DENOMINATOR() external view returns (uint256);
    function UNDERWRITING_COLLATERAL() external view returns (uint256);
    function UNDERWRITING_COLLATERAL_DENOMINATOR() external view returns (uint256);
    function chainIdentifierToDestinationAddress(bytes32) external view returns (bytes memory);
    function connectNewChain(bytes32 chainIdentifier, bytes memory remoteCCI, bytes memory remoteGARP) external;
    function estimateAdditionalCost() external view returns (address asset, uint256 amount);
    function expireUnderwrite(
        address targetVault,
        address toAsset,
        uint256 U,
        uint256 minOut,
        address toAccount,
        uint16 underwriteIncentiveX16,
        bytes memory cdata
    ) external;
    function getUnderwriteIdentifier(
        address targetVault,
        address toAsset,
        uint256 U,
        uint256 minOut,
        address toAccount,
        uint16 underwriteIncentiveX16,
        bytes memory cdata
    ) external pure returns (bytes32 identifier);
    function maxUnderwritingDuration() external view returns (uint256);
    function minGasFor(bytes32) external view returns (uint48);
    function sendCrossChainAsset(
        RouteDescription memory routeDescription,
        uint8 toAssetIndex,
        uint256 U,
        uint256 minOut,
        uint256 fromAmount,
        address fromAsset,
        uint16 underwriteIncentiveX16,
        bytes memory calldata_
    ) external payable;
    function sendCrossChainLiquidity(
        RouteDescription memory routeDescription,
        uint256 U,
        uint256[2] memory minOut,
        uint256 fromAmount,
        bytes memory calldata_
    ) external payable;
    function setMaxUnderwritingDuration(uint256 newMaxUnderwriteDuration) external;
    function setMinGasFor(bytes32 chainIdentifier, uint48 minGas) external;
    function underwrite(
        address targetVault,
        address toAsset,
        uint256 U,
        uint256 minOut,
        address toAccount,
        uint16 underwriteIncentiveX16,
        bytes memory cdata
    ) external returns (bytes32 identifier);
    function underwriteAndCheckConnection(
        bytes32 sourceIdentifier,
        bytes memory fromVault,
        address targetVault,
        address toAsset,
        uint256 U,
        uint256 minOut,
        address toAccount,
        uint16 underwriteIncentiveX16,
        bytes memory cdata
    ) external;
    function underwritingStorage(bytes32) external view returns (uint256 tokens, address refundTo, uint96 expiry);
}

File 6 of 29 : MathConstants.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;


int256 constant WADWAD = 1e36;
uint256 constant LN2 = 693147180559945344; // from numpy import np; int(np.log(2)*10**18).

// library MathConstants {
//     int256 internal constant WADWAD = 1e36;
//     uint256 internal constant LN2 = 693147180559945344; // from numpy import np; int(np.log(2)*10**18).
// }

//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.19;

import { Ownable } from "solady/auth/Ownable.sol";
import {ERC20} from 'solady/tokens/ERC20.sol';
import {SafeTransferLib} from 'solady/utils/SafeTransferLib.sol';
import { ReentrancyGuard} from "solady/utils/ReentrancyGuard.sol";
import { Initializable } from "solady/utils/Initializable.sol";
import { Multicallable } from "solady/utils/Multicallable.sol";
import { FixedPointMathLib} from "solady/utils/FixedPointMathLib.sol";

import { IMessageEscrowStructs } from "GeneralisedIncentives/src/interfaces/IMessageEscrowStructs.sol";

import { ICatalystReceiver} from "./interfaces/IOnCatalyst.sol";
import { ICatalystV1Factory } from "./interfaces/ICatalystV1Factory.sol";
import "./interfaces/ICatalystV1VaultErrors.sol";
import { MAX_GOVERNANCE_FEE_SHARE } from"./CatalystFactory.sol";
import { ICatalystV1Vault } from "./ICatalystV1Vault.sol";

/**
 * @title Catalyst: Common Vault Logic
 * @author Cata Labs Inc.
 * @notice This abstract contract defines general logic of a Catalyst vault like:
 * - Vault Token through Solmate's ERC20 implementation.
 * - Connection management
 * - Security limit
 * - Swap Escrow
 *
 * By inheriting this abstract contract, a Vault automatically implements common vault logic.
 * @dev This contract uses the following special notation:
 * CAPITAL_LETTER_VARIABLES are constants or immutable.
 * _ prefixed varaibles are storage.
 * _ prefixed functions are internal.
 * Unless otherwise required, variables are exposed directly. Such that storage functions are
 * prefixed with _.
 *
 * Upon deleting the escrow, this contract special logic in case of refunds. We want to ensure that
 * any acks does not revert to clear up the escrows. However, some tokens can revert on demand (blacklist tokens)
 * For these tokens, we make an optimistic call to the token to send the assets to the user. We don't
 * care if it actually goes through. Cases where this call would fail the user does not get anything.
 * A caveat of the implementation is that tokens that revert by PANIC'ing or spending all gas, are not supported
 * since there is a catch of OOO gas that reverts. It is then dependent on replaying the ack to release the escrow.
 */
abstract contract CatalystVaultCommon is
    Initializable,
    Multicallable,
    ReentrancyGuard,
    ERC20,
    ICatalystV1Vault
{
    /** @notice The fallback user used as a FALLBACK placeholder when underwrite escrows are set. */
    address constant UNDERWRITE_FALLBACK_USER = address(uint160(1));

    //--- Config ---//
    // The following section contains the configurable variables.

    /**
     * @notice Determines how fast the security limit decreases.
     * @dev Needs to be long enough for vault token providers to be notified of a breach but short enough for volatility to not soft-freeze the vault.
     */
    uint256 constant DECAY_RATE = 1 days;

    /** @notice Number of decimals used by the vault's vault tokens */
    uint8 constant DECIMALS = 18;

    /**
     * @notice The vault tokens initially minted to the user who set up the vault.
     * @dev The initial deposit along with this value determines the base value of a vault token.
     */
    uint256 constant INITIAL_MINT_AMOUNT = 1e18; // 10**decimals

    /**
     * @notice Maximum number of assets supported
     * @dev Impacts the cost of some for loops. Can be changed without breaking compatibility.
     */
    uint8 constant MAX_ASSETS = 3;

    //-- ERC20 --//

    string _name;
    string _symbol;

    function name() public override view returns(string memory) {
        return _name;
    }

    function symbol() public override view returns(string memory) {
        return _symbol;
    }

    //-- Variables --//

    // immutable variables can be read by proxies, thus it is safe to set this on the constructor.
    address public immutable FACTORY;
    address public _chainInterface;

    /**
     * @notice The approved connections for this vault, stored as _vaultConnection[connectionId][toVault]
     * @dev to vault is encoded as 64 + 1 bytes.
     */
    mapping(bytes32 => mapping(bytes => bool)) public _vaultConnection;

    /**
     * @notice To indicate which token is desired on the target vault,
     * the desired tokens are provided as an integer which maps to the
     * asset address. This variable is the map.
     */
    mapping(uint256 => address) public _tokenIndexing;

    /** @notice The token weights. Used for maintaining a non-symmetric vault asset balance. */
    mapping(address => uint256) public _weight;

    //-- Parameter Flow & Change variables --//

    // Use getUnitCapacity to indirectly access these variables
    uint256 _usedUnitCapacity;
    uint48 public _adjustmentTarget;

    uint48 public _lastModificationTime;
    uint48 _usedUnitCapacityTimestamp;

    //-- Vault fee variables --//

    /**
     * @notice The total vault fee. Multiplied by 10**18. 
     * @dev To compute the respective fee, use mulWad: FixedPointMathLib.mulWad(amount, _vaultFee);
     */
    uint64 public _vaultFee;

    /**
     * @notice The governance's cut of _vaultFee. 
     * @dev Usage: FixedPointMathLib.mulWad(FixedPointMathLib.mulWad(amount, _vaultFee), _governanceFeeShare);
     */
    uint64 public _governanceFeeShare;

    /** @notice The vault fee can be changed. _feeAdministrator is the address allowed to change it */
    address public _feeAdministrator; 

    /**
     * @notice The setupMaster is the short-term owner of the vault.
     * They can connect the vault to vaults on other chains.
     * @dev !Can extract all of the vault value! Should be set to address(0) once setup is complete via 'finishSetup()'.
     */
    address public _setupMaster;

    //--- Messaging router limit ---//
    // The router is not completely trusted. Some limits are
    // imposed on the DECAY_RATE-ly unidirectional liquidity flow. That is:
    // if the vault observes more than _maxUnitCapacity of incoming
    // units, then it will not accept further incoming units. This means the router
    // can only drain a prefigured percentage of the vault every DECAY_RATE.
    // For amplified vaults, the security limit is denominated in assets rather than Units.

    // Outgoing flow is subtracted from incoming flow until 0.

    /** @notice The max incoming liquidity flow from the router. */
    uint256 public _maxUnitCapacity;

    // Escrow reference
    /** @notice Total current escrowed tokens. */
    mapping(address => uint256) public _escrowedTokens;
    /** @notice Total current escrowed vault tokens. */
    uint256 public _escrowedVaultTokens;

    /** @notice Find escrow information. Used for both normal swaps and liquidity swaps. */
    mapping(bytes32 => address) public _escrowLookup;

    /**
     * @notice A mathematical lib that describes various properties of this contract.
     * These helper functions are not contained in the swap template, since they notisably inflate
     * the contract side which reduceses the number of optimizer runs => increase the gas cost.
     */
    address immutable public MATHLIB;

    constructor(address factory_, address mathlib) payable {
        FACTORY = factory_;
        MATHLIB = mathlib;

        _name = "Catalyst Vault Template";
        _symbol = "";

        // Disable the contract from being initialized. This ensures the factory is
        // used to deploy vaults.
        _disableInitializers();
    }

    /** @notice Get the factory owner. That is the owner that can configure this vault post finishSetup(). */
    function factoryOwner() public view override returns (address) {
        return Ownable(FACTORY).owner();
    }

    /** @notice Governance fee destination. This is the address the governance fee is sent to. */
    function governanceFeeDestination() public view override returns (address) {
        return ICatalystV1Factory(FACTORY)._governanceFeeDestination();
    }

    /**
     * @notice Only allow Governance to change vault parameters
     * @dev Because of dangerous permissions (setConnection, weight changes):
     * !CatalystFactory(_factory).owner() must be set to a timelock! 
     */ 
    modifier onlyFactoryOwner() {
        require(msg.sender == factoryOwner()); // dev: Only factory owner
        _;
    }

    /**
     * @notice Require the sender of the transaction to be the chain interface. 
     */
     modifier onlyChainInterface() {
        require(msg.sender == _chainInterface); // dev: Only chain interface
        _;
    }

    /**
     * @notice Verify a connected pool.
     */ 
    modifier onlyConnectedPool(bytes32 channelId, bytes memory vault) {
        // Only allow connected vaults
        if (!_vaultConnection[channelId][vault]) revert VaultNotConnected();
        _;
    }

    // -- Receive Abstract Functions -- //

    function _receiveAsset(
        address toAsset,
        uint256 U,
        uint256 minOut
    ) virtual internal returns (uint256);

    // -- Setup Functions -- //

    /**
     * @notice Setup a vault.
     * @dev Is initializer.
     * @param name_ Name of the vault token.
     * @param symbol_ Symbol of the vault token.
     * @param chainInterface The chain interface to use. Set address(0) to disable cross-chain swaps.
     * @param vaultFee Initial vault fee, that is the fee charged on swaps.
     * @param governanceFee Initial governance fee, that is the percentage of the vault fee that is sent to designated address.
     * @param feeAdministrator Special address that can modify the pool fees.
     * @param setupMaster User that is configuring the vault.
     */
    function setup(
        string calldata name_,
        string calldata symbol_,
        address chainInterface,
        uint64 vaultFee,
        uint64 governanceFee,
        address feeAdministrator,
        address setupMaster
    ) initializer external override {
        // The vault is designed to be used by a proxy and not as a standalone vault.
        // initializer ensures this function is only called once.

        _chainInterface = chainInterface;
        _setupMaster = setupMaster;

        _setVaultFee(vaultFee);
        _setGovernanceFee(governanceFee);
        _setFeeAdministrator(feeAdministrator);

        // Name the ERC20 vault token
        _name = name_;
        _symbol = symbol_;
    }


    /**
     * @notice Creates a connection to toVault on the channelId.
     * @dev Encoding addresses in 64 + 1 bytes for EVM.
     * For Solidity, this can be done as bytes.concat(bytes1(0x14), bytes32(0), abi.encode(toAddress))
     * @param channelId Target chain identifier. Varies from AMB to AMB.
     * @param toVault 64 + 1 bytes representation of the target vault.
     * @param state Boolean indicating if the connection should be open or closed.
     */
    function setConnection(
        bytes32 channelId,
        bytes calldata toVault,
        bool state
    ) external override {
        require(msg.sender == _setupMaster); // dev: No auth
        require(toVault.length == 65); // dev: Vault addresses are uint8 + 64 bytes.

        _vaultConnection[channelId][toVault] = state;

        emit SetConnection(channelId, toVault, state);
    }

    /**
     * @notice Gives up short-term ownership of the vault. This makes the vault unstoppable.
     * @dev This function should ALWAYS be called before other liquidity providers deposit liquidity.
     * While it is not recommended, swapping should be relativly safe since because of the escrow (assuming a minimum output is set).
     */
    function finishSetup() external override {
        require(msg.sender == _setupMaster); // dev: No auth

        _setupMaster = address(0);

        emit FinishSetup();
    }

    /**
     * @notice View function to signal if a vault is safe to use.
     * @dev Checks if setupMaster has been set to ZERO_ADDRESS.  In other words, has finishSetup been called?
     * This function is not "safe()". To properly verify if a pool is "ready", verify:
     *  - The vault template is trusted.
     *  - The cross-chain interfce is trusted.
     *  - All connections are correctly set. (Valid chains, valid vaults)
     *
     * If you are providing liquidity to a vault, furthermore check:
     *  - All assets in the pool are trusted.
     *
     * The above checks have to be done on every vault in the pool.
     */
    function ready() external view override returns (bool) {
        // _setupMaster == address(0) ensures a safe pool cannot be made unsafe. The setup master can drain the pool!
        // _tokenIndexing[0] != address(0) check if the pool has been initialized correctly.
        // The additional check is there to ensure that the initial deployment returns false. 
        return _setupMaster == address(0) && _tokenIndexing[0] != address(0);
    }


    /**
     * @notice Returns the current cross-chain swap capacity.
     * @dev can be overridden to implement other limit curves.
     */
    function getUnitCapacity() public view virtual override returns (uint256) {
        uint256 MUC = _maxUnitCapacity;

        // The delta change to the limit is: timePassed · slope = timePassed · Max/decayrate
        uint256 unitCapacityReleased;
        unchecked {
            // block.timestamp >= _usedUnitCapacityTimestamp, always.
            // MUC is generally low.
            unitCapacityReleased = block.timestamp - _usedUnitCapacityTimestamp;

            // This line can overflow. While unlikely to, if it does it would semi-freeze the pool until
            // some assets are withdrawn. As a fix, let if overflow. If it overflows, then 
            // unitCapacityReleased becomes smaller so there are no security implications.
            unitCapacityReleased *= MUC;

            // DECAY_RATE != 0.
            unitCapacityReleased /= DECAY_RATE;
        }

        uint256 UC = _usedUnitCapacity;
        // If the change is greater than the units than the spent security limit return maximum.
        //If we computed it as (MUC - UC + unitCapacityReleased > MUC) it would obviously be wrong.
        if (UC <= unitCapacityReleased) return MUC;

        // Amplified vaults can have MUC <= UC since MUC is modified when swapping.
        unchecked {
            // We know UC > unitCapacityReleased.
            if (MUC <= UC - unitCapacityReleased) return 0; 

            // We know UC > unitCapacityReleased and with the above statement we know
            // MUC > (UC - unitCapacityReleased). Thus we can compute the difference unchecked.
            return MUC - (UC - unitCapacityReleased);
        }

    }

    // -- Utils -- // 

    /**
     * @notice Check if the vault supports an inflow of units and decrease
     * unit capacity by the inflow.
     * @dev Implement a lot of similar logic to getUnitCapacity. 
     * @param Units The number of units to check and set.
     */
    function _updateUnitCapacity(uint256 Units) internal {
        uint256 MUC = _maxUnitCapacity;

        // The delta change to the limit is: timePassed · slope = timePassed · Max/decayrate
        uint256 unitCapacityReleased;
        unchecked {
            // block.timestamp > _usedUnitCapacityTimestamp, always.
            // MUC is generally low.
            unitCapacityReleased = (block.timestamp - _usedUnitCapacityTimestamp);

            // This line can overflow. While unlikely to, if it does it would semi-freeze the pool until
            // some assets are withdrawn. As a fix, let if overflow. If it overflows, then 
            // unitCapacityReleased becomes smaller so there are no security implications.
            unitCapacityReleased *= MUC;

            // DECAY_RATE != 0.
            unitCapacityReleased /= DECAY_RATE;
        }

        uint256 UC = _usedUnitCapacity; 
        // If the change is greater than the units than the spent security limit it is at max.
        if (UC <= unitCapacityReleased) {
            // The new limit is MUC, check if more units than MUC are getting spent.
            if (Units > MUC) revert ExceedsSecurityLimit();
            // Set last change and the new spent security limit.
            _usedUnitCapacityTimestamp = uint48(block.timestamp);
            _usedUnitCapacity = Units;
            return;
        }
        
        // Compute the new spent security limit. Start by adding used unit capacity
        // and to be spent units
        uint256 newUnitFlow = UC + Units;
        unchecked {
            // Then subtract the units that have been decayed. We know UC + Units >= UC > unitCapacityReleased
            newUnitFlow -= unitCapacityReleased;
        }
        // Then check if the new spent security limit is larger than maximum. 
        if (newUnitFlow > MUC) revert ExceedsSecurityLimit();

        // Set last change and the new spent security limit.
        _usedUnitCapacityTimestamp = uint48(block.timestamp);
        _usedUnitCapacity = newUnitFlow;
    }


    // -- Governance Functions -- //

    /** @notice Sets a new fee administrator that can configure vault fees. */ 
    function _setFeeAdministrator(address administrator) internal {
        _feeAdministrator = administrator;
        emit SetFeeAdministrator(administrator);
    }

    /**
     * @notice Sets a new vault fee, taken from input amount.
     * @param fee Fee in WAD terms. 12e16 is 12%.
     */
    function _setVaultFee(uint64 fee) internal {
        require(fee <= FixedPointMathLib.WAD); // dev: VaultFee is maximum 100%.
        _vaultFee = fee;
        emit SetVaultFee(fee);
    }

    /**
     * @notice Sets a new governance fee. Taken of the vault fee.
     * @param fee Fee in WAD terms. 12e16 is 12%.
     */
    function _setGovernanceFee(uint64 fee) internal {
        require(fee <= MAX_GOVERNANCE_FEE_SHARE);  // dev: Maximum GovernanceFeeSare exceeded.
        _governanceFeeShare = fee;
        emit SetGovernanceFee(fee);
    }

    /** @notice Allows the factory owner to set a new fee administrator. */
    function setFeeAdministrator(address administrator) public override onlyFactoryOwner {
        _setFeeAdministrator(administrator);
    }

    /** 
     * @notice Allows the factory owner to set the governance fee.
     * @dev Can only be called by factory owner.
     * @param fee Fee in WAD terms. 12e16 is 12%.
     */
    function setGovernanceFee(uint64 fee) public override onlyFactoryOwner {
        _setGovernanceFee(fee);
    }

    /**
     * @notice Allows the feeAdministrator to modify the vault fee.
     * @dev Can only be called by feeAdministrator
     * @param fee Fee in WAD terms. 12e16 is 12%.
     */
    function setVaultFee(uint64 fee) public override {
        require(msg.sender == _feeAdministrator); // dev: Only feeAdministrator can set new fee
        _setVaultFee(fee);
    }

    /**
     * @notice Collect the governance fee share of the specified vault fee.
     * @dev The governance fee share is transfered to governanceFeeDestination.
     */
    function _collectGovernanceFee(address asset, uint256 vaultFeeAmount) internal {
        uint256 governanceFeeShare = _governanceFeeShare;

        // If governanceFeeShare == 0, then skip the rest of the logic.
        if (governanceFeeShare != 0) {
            uint256 governanceFeeAmount = FixedPointMathLib.mulWad(vaultFeeAmount, governanceFeeShare);
            SafeTransferLib.safeTransfer(asset, governanceFeeDestination(), governanceFeeAmount);
        }
    }

    //-- Escrow Functions --//

    /**
     * @notice Create a token escrow for a swap.
     * @dev It is not checked if fallbackUser is set to address(0) but if it is, the escrow is lost.
     * @param sendAssetHash The escrow context hash. Will be used to recover the escrow.
     * From a implementation / usage perspective, if this hash contains fromAsset and amount
     * it will improves by allowing on to verify these values independently.
     * @param fallbackUser The user who the escrow belongs to. Do not set to address(0).
     * @param fromAsset Asset to escrow.
     * @param amount Amount to escrow.
     */
    function _setTokenEscrow(
        bytes32 sendAssetHash,
        address fallbackUser,
        address fromAsset,
        uint256 amount
    ) internal {
        if (_escrowLookup[sendAssetHash] != address(0))  revert EscrowAlreadyExists();
        _escrowLookup[sendAssetHash] = fallbackUser;
        unchecked {
            // Must be less than the vault balance.
            _escrowedTokens[fromAsset] += amount;
        }
    }

    /**
     * @notice Create a liquidity escrow for a swap.
     * @dev It is not checked if fallbackUser is set to address(0) but if it is, the escrow is lost.
     * @param sendLiquidityHash The escrow context hash. Will be used to recover the escrow.
     * From a implementation / usage perspective, if this hash contains vaultTokens
     * it will improves by allowing on to verify these values independently.
     * @param fallbackUser The user who the escrow belongs to. Do not set to address(0).
     * @param vaultTokens Number of vault tokens to escrow.
     */
    function _setLiquidityEscrow(
        bytes32 sendLiquidityHash,
        address fallbackUser,
        uint256 vaultTokens
    ) internal {
        if (_escrowLookup[sendLiquidityHash] != address(0)) revert EscrowAlreadyExists();
        _escrowLookup[sendLiquidityHash] = fallbackUser;

        // Escrow vault tokens are first burned and then escrowed. As a result, this may overflow unlike _escrowedTokens.
        _escrowedVaultTokens += vaultTokens;
    }

    /**
     * @notice Returns the fallbackUser for the escrow and cleans up the escrow information.
     * @dev 'delete _escrowLookup[sendAssetHash]' ensures this function can only be called once.
     */
    function _releaseAssetEscrow(
        bytes32 sendAssetHash,
        uint256 escrowAmount,
        address escrowToken
    ) internal returns(address) {
        address fallbackUser = _escrowLookup[sendAssetHash]; // Passing in an invalid swapHash returns address(0).
        require(fallbackUser != address(0)); // dev: Invalid swapHash. Alt: Escrow doesn't exist.
        delete _escrowLookup[sendAssetHash]; // Stops timeout and further acks from being called.

        unchecked {
            // escrowAmount \subseteq _escrowedTokens => escrowAmount <= _escrowedTokens. 
            // Cannot be called twice since the 3 lines before ensure this can only be reached once.
            _escrowedTokens[escrowToken] -= escrowAmount;
        }
        
        return fallbackUser;
    }

    /**
     * @notice Returns the fallbackUser for the escrow and cleans up the escrow information.
     * @dev 'delete _escrowLookup[sendAssetHash]' ensures this function can only be called once.
     */
    function _releaseLiquidityEscrow(
        bytes32 sendLiquidityHash,
        uint256 escrowAmount
    ) internal returns(address) {
        address fallbackUser = _escrowLookup[sendLiquidityHash]; // Passing in an invalid swapHash returns address(0).
        require(fallbackUser != address(0)); // dev: Invalid swapHash. Alt: Escrow doesn't exist.
        delete _escrowLookup[sendLiquidityHash]; // Stops timeout and further acks from being called.

        unchecked {
            // escrowAmount \subseteq _escrowedVaultTokens => escrowAmount <= _escrowedVaultTokens.
            // Cannot be called twice since the 3 lines before ensure this can only be reached once.
            _escrowedVaultTokens -= escrowAmount;
        }

        return fallbackUser;
    }

    /** 
     * @notice Implements basic ack logic: Deletes and releases tokens to the vault
     * @dev Should never revert! For security limit adjustments, the implementation may have to be overwritten.
     * @param toAccount Recipient of the transaction on the target chain.
     * @param U Number of units initially purchased.
     * @param escrowAmount Number of tokens escrowed.
     * @param escrowToken Address of the escrowed token.
     * @param blockNumberMod Block number when the transaction was commited (mod 32)
     */
    function onSendAssetSuccess(
        bytes32 channelId,
        bytes calldata toAccount,
        uint256 U,
        uint256 escrowAmount,
        address escrowToken,
        uint32 blockNumberMod
    ) onlyChainInterface public override virtual {
        // We need to find the location of the escrow using the information below.
        // We need to do this twice: 1. Get the address. 2. Delete the escrow.
        // To save a bit of gas, this hash is computed, cached, and then used.
        bytes32 sendAssetHash = _computeSendAssetHash( // Computing the hash doesn't revert.
            toAccount,      // Ensures no collisions between different users
            U,              // Used to randomise the hash
            escrowAmount,   // Required! to validate release escrow data
            escrowToken,    // Required! to validate release escrow data
            blockNumberMod  // Used to randomize the hash.
        );

        _releaseAssetEscrow(sendAssetHash, escrowAmount, escrowToken); // Only reverts for missing escrow

        emit SendAssetSuccess(
            channelId,
            toAccount,
            U,
            escrowAmount,
            escrowToken,
            blockNumberMod
        );
    }

    /** 
     * @notice Implements basic timeout logic: Deletes and sends tokens to the user.
     * @dev Should never revert!
     * For blacklist tokens, this function contains custom logic to support failing ERC20 transfer.
     * If an ERC20 transfer fails (say because of a blocklist), we only revert if it was because of OOO.
     * This implies that if an ERC20 transfer fails, then the escrow is lost. This shouldn't be the cast
     * except if a token is paused, blacklisted, or the vault is exploited such that it has less assets
     * than the escrow.
     * @param toAccount Recipient of the transaction on the target chain. Encoded in bytes32.
     * @param U Number of units initially purchased.
     * @param escrowAmount Number of tokens escrowed.
     * @param escrowToken Token escrowed.
     * @param blockNumberMod Block number of the transaction that commited the swap (mod 32)
     */
    function onSendAssetFailure(
        bytes32 channelId,
        bytes calldata toAccount,
        uint256 U,
        uint256 escrowAmount,
        address escrowToken,
        uint32 blockNumberMod
    ) onlyChainInterface public override virtual {
        // We need to find the location of the escrow using the information below.
        // We need to do this twice: 1. Get the address. 2. Delete the escrow.
        // To save a bit of gas, this hash is computed and saved and then used.
        bytes32 sendAssetHash = _computeSendAssetHash( // Computing the hash doesn't revert.
            toAccount,      // Ensures no collisions between different users
            U,              // Used to randomise the hash
            escrowAmount,   // Required! to validate release escrow data
            escrowToken,    // Required! to validate release escrow data
            blockNumberMod  // Used to randomize the hash.
        );

        // This call provides re-entry protection against re-entering this call. Otherwise, this call can always be called.
        address fallbackAddress = _releaseAssetEscrow(sendAssetHash, escrowAmount, escrowToken); // Only reverts for missing escrow,

        // We are going to make a low-level call. It may revert (see comment below) but it should not revert if it runs out of gas (that should be raised).
        // As such, get the current gas in the contract.
        uint256 gasLeftBeforeCall = gasleft();

        bool success;
        // Make a low level call such that the transfer never fails. This is important for tokens using block lists.
        // This also implies that if you get blacklisted between when you initiated the swap and the swap failed, you would lose the tokens.
        bytes memory payload = abi.encodeWithSignature("transfer(address,uint256)", fallbackAddress, escrowAmount);
        assembly ("memory-safe") {
            // The gas limit is set to 0x8000000000000000000000000000000000000000000000000000000000000000.
            // This is essentially all gas since the actual gas forwarded is min(gasForwarded, gasleft * 63/64).
            success := call(0x8000000000000000000000000000000000000000000000000000000000000000, escrowToken, 0,  add(payload, 0x20), mload(payload), 0, 0)
            // SafeTransferLib.safeTransferFrom(escrowToken, fallbackAddress, escrowAmount);
        }

        // If the call failed, check if it failed with OOO (If the call used all of the gas it has available).
        if (!success) if (gasleft() < gasLeftBeforeCall * 1 / 63) revert NotEnoughGas();

        emit SendAssetFailure(
            channelId,
            toAccount,
            U,
            escrowAmount,
            escrowToken,
            blockNumberMod
        );
    }

    /** 
     * @notice Implements basic liquidity ack logic: Deletes and releases vault tokens to the vault.
     * @dev Should never revert! For security limit adjustments, the implementation should be overwritten.
     * @param toAccount Recipient of the transaction on the target chain.
     * @param U Number of units initially acquired.
     * @param escrowAmount Number of vault tokens escrowed.
     * @param blockNumberMod Block number at which the swap transaction was commited (mod 32)
     */
    function onSendLiquiditySuccess(
        bytes32 channelId,
        bytes calldata toAccount,
        uint256 U,
        uint256 escrowAmount,
        uint32 blockNumberMod
    ) onlyChainInterface public override virtual {

        // We need to find the location of the escrow using the information below.
        // We need to do this twice: 1. Get the address. 2. Delete the escrow.
        // To save a bit of gas, this hash is computed and saved and then used.
        bytes32 sendLiquidityHash = _computeSendLiquidityHash(
            toAccount,      // Ensures no collisions between different users
            U,              // Used to randomise the hash
            escrowAmount,   // Required! to validate release escrow data
            blockNumberMod  // Used to randomize the hash.
        );

        _releaseLiquidityEscrow(sendLiquidityHash, escrowAmount); // Reverts for missing escrow

        emit SendLiquiditySuccess(
            channelId,
            toAccount,
            U,
            escrowAmount,
            blockNumberMod
        );
    }

    /** 
     * @notice Implements basic liquidity timeout logic: Deletes and sends vault tokens to the user.
     * @dev Should never revert!
     * @param toAccount Recipient of the transaction on the target chain. Encoded in bytes32.
     * @param U Number of units initially acquired.
     * @param escrowAmount Number of vault tokens escrowed.
     * @param blockNumberMod Block number at which the swap transaction was commited (mod 32)
     */
    function onSendLiquidityFailure(
        bytes32 channelId,
        bytes calldata toAccount,
        uint256 U,
        uint256 escrowAmount,
        uint32 blockNumberMod
    ) onlyChainInterface public override virtual {

        bytes32 sendLiquidityHash = _computeSendLiquidityHash(
            toAccount,      // Ensures no collisions between different users
            U,              // Used to randomise the hash
            escrowAmount,   // Required! to validate release escrow data
            blockNumberMod  // Used to randomize the hash.
        );

        // This function only allows entering this function once. Once called, it can never be called without reverting again.
        address fallbackAddress = _releaseLiquidityEscrow(sendLiquidityHash, escrowAmount); // Reverts for missing escrow

        _mint(fallbackAddress, escrowAmount); // Never reverts.

        emit SendLiquidityFailure(
            channelId,
            toAccount,
            U,
            escrowAmount,
            blockNumberMod
        );
    }

    /** 
     * @notice Computes a unique identifier for a swap. This unique identifier can be used to identify swaps cross-chain.
     * However, it is never exposed. This is done to let the hashing algorithm be flexible between implementations.
     */
    function _computeSendAssetHash(
        bytes calldata toAccount,
        uint256 U,
        uint256 amount,
        address fromAsset,
        uint32 blockNumberMod
    ) internal pure returns(bytes32) {
        return keccak256(
            bytes.concat(
                toAccount,               // Ensures no collisions between different users.
                bytes32(U),              // Used to randomise the hash.
                bytes32(amount),         // Required! to validate release escrow data.
                bytes20(fromAsset),      // Required! to validate release escrow data.
                bytes4(blockNumberMod)   // Used to randomize the hash.
            )
        );
    }

    /**
     * @notice Computes a unique identifier for a swap. This unique identifier can be used to identify swaps cross-chain.
     * However, it is never exposed. This is done to let the hashing algorithm be flexible between implementations.
     */
    function _computeSendLiquidityHash(
        bytes calldata toAccount,
        uint256 U,
        uint256 amount,
        uint32 blockNumberMod
    ) internal pure returns(bytes32) {
        return keccak256(
            bytes.concat(
                toAccount,               // Ensures no collisions between different users.
                bytes32(U),              // Used to randomise the hash.
                bytes32(amount),         // Required! to validate release escrow data.
                bytes4(blockNumberMod)   // Used to randomize the hash.
            )
        );
    }

    // -- Underwrite Asset Swaps -- //

    function underwriteAsset(
        bytes32 identifier,
        address toAsset,
        uint256 U,
        uint256 minOut
    ) onlyChainInterface virtual public returns (uint256 purchasedTokens) {
        // Simulate a receiveAsset call. This gets us the purchased tokens.
        purchasedTokens = _receiveAsset(toAsset, U, minOut);

        // Set the escrow.
        _setTokenEscrow(
            identifier,
            UNDERWRITE_FALLBACK_USER,
            toAsset,
            purchasedTokens
        );

        emit SwapUnderwritten(
            identifier,
            toAsset,
            U,
            purchasedTokens
        );
    }

    /**
     * @notice Release assets associated with an underwrite escrow.
     * @param refundTo Released assets are sent to this address.
     * @param identifier Underwriting identifier. Is used to index the storage for valid escrows.
     * @param escrowAmount Number of tokens escrowed.
     * @param escrowToken Escrowed token address.
     * @param sourceIdentifier The source chain identifier.
     * @param fromVault The originating vault.
     */
    function releaseUnderwriteAsset(
        address refundTo,
        bytes32 identifier,
        uint256 escrowAmount,
        address escrowToken,
        bytes32 sourceIdentifier,
        bytes calldata fromVault
    ) onlyChainInterface onlyConnectedPool(sourceIdentifier, fromVault) virtual public {
        _releaseAssetEscrow(identifier, escrowAmount, escrowToken); // Reverts for missing escrow.

        // Send the assets to the user.
        SafeTransferLib.safeTransfer(escrowToken, refundTo, escrowAmount);
    }

    /**
     * @notice Delete an underwrite escrow without releasing any tokens.
     * @dev The unsued parameter U is used for overwrites. (see CataulystVaultAmplified.sol)
     * @param identifier Underwriting identifier. Is used to index the storage for valid escrows.
     * param U Number of underwritten units. Is used for Amplified vaults to modify the unit tracker.
     * @param escrowAmount Number of tokens escrowed.
     * @param escrowToken Escrowed token address.
     */
    function deleteUnderwriteAsset(
        bytes32 identifier,
        uint256 /* U */,
        uint256 escrowAmount,
        address escrowToken
    ) onlyChainInterface virtual public {
        _releaseAssetEscrow(identifier, escrowAmount, escrowToken); // Reverts for missing escrow.
    }

}

//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.19;

import { FixedPointMathLib } from "solady/utils/FixedPointMathLib.sol";

import { WADWAD } from "./utils/MathConstants.sol";

/**
 * @title Catalyst: Amplified Integrals
 * @author Catalyst Labs Inc.
 */
contract IntegralsAmplified {
    /**
     * @notice Computes the integral \int_{wA}^{wA+wx} 1/w^k · (1-k) dw
     *     = (wA + wx)^(1-k) - wA^(1-k)
     * The value is returned as units, which is always WAD.
     * @dev All input amounts should be the raw numbers and not WAD.
     * Since units are always denominated in WAD, the function should be treated as mathematically *native*.
     * @param input Input amount.
     * @param A Current vault balance of the x token.
     * @param W Weight of the x token.
     * @param oneMinusAmp Amplification. Provided as (1-k).
     * @return uint256 Units (units are **always** WAD).
     */
    function _calcPriceCurveArea(
        uint256 input,
        uint256 A,
        uint256 W,
        int256 oneMinusAmp
    ) internal pure returns (uint256) {
        // Will revert if W = 0. 
        // Or if A + input == 0.
        int256 calc = FixedPointMathLib.powWad(
            int256(W * (A + input) * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails.
            oneMinusAmp
        );

        // If the vault contains 0 assets, the below computation will fail. This is bad.
        // Instead, check if A is 0. If it is then skip because: (W · A)^(1-k) = (W · 0)^(1-k) = 0
        if (A != 0) {
            unchecked {
                // W * A * FixedPointMathLib.WAD < W * (A + input) * FixedPointMathLib.WAD 
                calc -= FixedPointMathLib.powWad(
                    int256(W * A * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails
                    oneMinusAmp
                );
            }
        }
        
        return uint256(calc); // Casting always safe, as calc always > =0
    }

    /**
     * @notice Solves the equation U = \int_{wA-_wy}^{wA} W/w^k · (1-k) dw for y
     *     = B · (1 - (
     *             (wB^(1-k) - U) / (wB^(1-k))
     *         )^(1/(1-k))
     *     )
     * The value is returned as output token. (not WAD)
     * @dev All input amounts should be the raw numbers and not WAD.
     * Since units are always multiplied by WAD, the function should be treated as mathematically *native*.
     * @param U Incoming vault specific units.
     * @param B Current vault balance of the y token.
     * @param W Weight of the y token.
     * @return uint25 Output denominated in output token. (not WAD)
     */
    function _calcPriceCurveLimit(
        uint256 U,
        uint256 B,
        uint256 W,
        int256 oneMinusAmp
    ) internal pure returns (uint256) {
        // W_B · B^(1-k) is repeated twice and requires 1 power. As a result, we compute it and cache it.
        uint256 W_BxBtoOMA = uint256( // Always casts a positive value.
            FixedPointMathLib.powWad(
                int256(W * B * FixedPointMathLib.WAD), // If casting overflows to a negative number, powWad fails.
                oneMinusAmp
            )
        );

        return FixedPointMathLib.mulWad(
            B,
            FixedPointMathLib.WAD - uint256( // Always casts a positive value
                FixedPointMathLib.powWad(
                    int256(FixedPointMathLib.divWadUp(W_BxBtoOMA - U, W_BxBtoOMA)), // Casting never overflows, as division result is always < 1
                    WADWAD / oneMinusAmp 
                )
            )
        );
    }

    /**
     * @notice Solves the equation
     *     \int_{wA}^{wA + wx} 1/w^k · (1-k) dw = \int_{wB-wy}^{wB} 1/w^k · (1-k) dw for y
     *         => out = B · (1 - (
     *                 (wB^(1-k) - (wA+wx)^(1-k) - wA^(1-k)) / (wB^(1-k))
     *             )^(1/(1-k))
     *         )
     * Alternatively, the integral can be computed through:
     * _calcPriceCurveLimit(_calcPriceCurveArea(input, A, W_A, amp), B, W_B, amp).
     * @dev All input amounts should be the raw numbers and not WAD.
     * @param input Input amount.
     * @param A Current vault balance of the x token.
     * @param B Current vault balance of the y token.
     * @param W_A Weight of the x token.
     * @param W_B Weight of the y token.
     * @param oneMinusAmp Amplification.
     * @return uint256 Output denominated in output token.
     */
    function _calcCombinedPriceCurves(
        uint256 input,
        uint256 A,
        uint256 B,
        uint256 W_A,
        uint256 W_B,
        int256 oneMinusAmp
    ) internal pure returns (uint256) {
        // uint256 W_BxBtoOMA = uint256(FixedPointMathLib.powWad(
        //     int256(W_B * B * FixedPointMathLib.WAD),
        //     oneMinusAmp
        // ));

        // uint256 U = uint256(FixedPointMathLib.powWad(
        //     int256(W_A * (A + input) * FixedPointMathLib.WAD),
        //     oneMinusAmp
        // ) - FixedPointMathLib.powWad(
        //     int256(W_A * A * FixedPointMathLib.WAD),
        //     oneMinusAmp
        // )); // _calcPriceCurveArea(input, A, W_A, amp)

        // return B * (FixedPointMathLib.WAD - uint256(FixedPointMathLib.powWad(
        //             int256(FixedPointMathLib.divWadUp(W_BxBtoOMA - U, W_BxBtoOMA)),
        //             int256(FixedPointMathLib.WAD * FixedPointMathLib.WAD / uint256(oneMinusAmp)))
        //         )) / FixedPointMathLib.WAD; // _calcPriceCurveLimit
        return _calcPriceCurveLimit(_calcPriceCurveArea(input, A, W_A, oneMinusAmp), B, W_B, oneMinusAmp);
    }

    /**
     * @notice Converts units into vault tokens with the below formula
     *      pt = PT · (((N · wa_0^(1-k) + U)/(N · wa_0^(1-k))^(1/(1-k)) - 1)
     * @dev The function leaves a lot of computation to the external implementation. This is done to avoid recomputing values several times.
     * @param U Number of units to convert into vault tokens.
     * @param ts Current vault token supply. The escrowed vault tokens should not be added, since the function then returns more.
     * @param it_times_walpha_amped wa_0^(1-k)
     * @param oneMinusAmpInverse Vault amplification.
     * @return uint256 Output denominated in vault tokens.
     */
    function _calcPriceCurveLimitShare(uint256 U, uint256 ts, uint256 it_times_walpha_amped, int256 oneMinusAmpInverse) internal pure returns (uint256) {
        uint256 vaultTokens = FixedPointMathLib.mulWad(
            ts,
            uint256( // Always casts a positive value, as powWad >= 1, hence powWad - WAD >= 0
                FixedPointMathLib.powWad( // poWad always >= 1, as the 'base' is always >= 1
                    int256(FixedPointMathLib.divWad( // If casting overflows to a negative number, powWad fails
                        it_times_walpha_amped + U,
                        it_times_walpha_amped
                    )),
                    oneMinusAmpInverse
                ) - int256(FixedPointMathLib.WAD)
            )
        );

        return vaultTokens;
    }
}

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

interface ICatalystReceiver {
    /** 
     * @notice The callback from a catalyst call. To determine if the swap was an asset or liquidity swap, either the current balance should be checked or it should be encoded into data.
     * @dev If you want full finality (not just economical finality)
     * revert on underwritten == true.
     */
    function onCatalystCall(uint256 purchasedTokens, bytes calldata data, bool underwritten) external;
}

File 10 of 29 : ICatalystV1Vault.sol
//SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.19;

import "./interfaces/ICatalystV1VaultSuccessFailure.sol";
import "./interfaces/ICatalystV1VaultAdministration.sol";
import "./interfaces/ICatalystV1VaultDerived.sol";
import "./interfaces/ICatalystV1VaultErrors.sol";
import "./interfaces/ICatalystV1VaultEvents.sol";
import "./interfaces/ICatalystV1VaultImmutables.sol";
import "./interfaces/ICatalystV1VaultPermissionless.sol";
import "./interfaces/ICatalystV1VaultState.sol";
import "./interfaces/ICatalystV1Underwriting.sol";

interface ICatalystV1Vault is
    ICatalystV1VaultSuccessFailure,
    ICatalystV1VaultAdministration,
    ICatalystV1VaultDerived,
    ICatalystV1VaultEvents,
    ICatalystV1VaultImmutables,
    ICatalystV1VaultPermissionless,
    ICatalystV1VaultState,
    ICatalystV1Structs,
    ICatalystV1Underwriting
{}

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

import { IMessageEscrowStructs } from "GeneralisedIncentives/src/interfaces/IMessageEscrowStructs.sol";

interface ICatalystV1Structs is IMessageEscrowStructs {
    /**
     * @param chainIdentifier target chain identifier.
     * @param toVault Target vault on the target chain. Encoded in 64 + 1 bytes.
     * @param toAccount Recipient of the transaction on the target chain. Encoded in 64 + 1 bytes.
     * @param incentive Cross-chain relaying incentive description.
     */
    struct RouteDescription {
        bytes32 chainIdentifier;
        bytes toVault;
        bytes toAccount;
        IncentiveDescription incentive;
        uint64 deadline;
    }
}

/**
 * @title Vault state
 * @notice Contains all vault storage which depends on the vault state.
 */
interface ICatalystV1VaultState {
    /** @notice Token weights. Used for maintaining a non symmetric vault asset balance. */
    function _weight(address token) external view returns (uint256);

    function _adjustmentTarget() external view returns (uint48);

    function _lastModificationTime() external view returns (uint48);

    /** @notice The vault fee in WAD. Implementation of fee: mulWadDown(_amount, _vaultFee) */
    function _vaultFee() external view returns (uint64);

    function _governanceFeeShare() external view returns (uint64);

    /** @notice The address of the responsible for adjusting the fees. */
    function _feeAdministrator() external view returns (address);

    /** @notice The setupMaster is the short-term owner of the vault. They can connect the vault to vaults on other chains. */
    function _setupMaster() external view returns (address);

    // Security limit
    /** @notice The max incoming liquidity flow from the router. */
    function _maxUnitCapacity() external view returns (uint256);

    // Escrow reference
    /** @notice Total current escrowed tokens */
    function _escrowedTokens(address token) external view returns (uint256);

    /** @notice Find escrow information. Used for both normal swaps and liquidity swaps. */
    function _escrowLookup(bytes32 sendAssetHash) external view returns (address);

    /** @notice Total current escrowed vault tokens */
    function _escrowedVaultTokens() external view returns (uint256);

    /** @notice Checks if there is a connection to the described vault */
    function _vaultConnection(bytes32 sourceIdentifier, bytes calldata fromVault) external view returns (bool);

    function factoryOwner() external view returns (address);

    function governanceFeeDestination() external view returns (address);

    /**
     * @notice External view function purely used to signal if a vault is safe to use.
     * @dev Just checks if the setup master has been set to ZERO_ADDRESS. In other words, has finishSetup been called?
     */
    function ready() external view returns (bool);
}

// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

interface ICrossChainReceiver {
    /**
     * @notice Handles the acknowledgement from the destination
     * @dev acknowledgement is exactly the output of receiveMessage except if receiveMessage failed, then it is error code (0xff or 0xfe) + original message.
     * If an acknowledgement isn't needed, this can be implemented as {}.
     * - This function can be called by someone else again! Ensure that if this endpoint is called twice with the same message nothing bad happens.
     * - If the application expects that the maxGasAck will be provided, then it should check that it got enough and revert if it didn't.
     * Otherwise, it is assumed that you didn't need the extra gas.
     * @param destinationIdentifier An identifier for the destination chain.
     * @param messageIdentifier A unique identifier for the message. The identifier matches the identifier returned when escrowed the message.
     * This identifier can be mismanaged by the messaging protocol.
     * @param acknowledgement The acknowledgement sent back by receiveMessage. Is 0xff if receiveMessage reverted.
     */
    function receiveAck(bytes32 destinationIdentifier, bytes32 messageIdentifier, bytes calldata acknowledgement) external;

    /**
     * @notice receiveMessage from a cross-chain call.
     * @dev The application needs to check the fromApplication combined with sourceIdentifierbytes to figure out if the call is authenticated.
     * - If the application expects that the maxGasDelivery will be provided, then it should check that it got enough and revert if it didn't.
     * Otherwise, it is assumed that you didn't need the extra gas.
     * @return acknowledgement Information which is passed to receiveAck. 
     *  If you return 0xff, you cannot know the difference between Executed but "failed" and outright failed.
     */
    function receiveMessage(bytes32 sourceIdentifierbytes, bytes32 messageIdentifier, bytes calldata fromApplication, bytes calldata message) external returns(bytes memory acknowledgement);
}

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

/// @notice Simple single owner authorization mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/auth/Ownable.sol)
///
/// @dev Note:
/// This implementation does NOT auto-initialize the owner to `msg.sender`.
/// You MUST call the `_initializeOwner` in the constructor / initializer.
///
/// While the ownable portion follows
/// [EIP-173](https://eips.ethereum.org/EIPS/eip-173) for compatibility,
/// the nomenclature for the 2-step ownership handover may be unique to this codebase.
abstract contract Ownable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The caller is not authorized to call the function.
    error Unauthorized();

    /// @dev The `newOwner` cannot be the zero address.
    error NewOwnerIsZeroAddress();

    /// @dev The `pendingOwner` does not have a valid handover request.
    error NoHandoverRequest();

    /// @dev Cannot double-initialize.
    error AlreadyInitialized();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The ownership is transferred from `oldOwner` to `newOwner`.
    /// This event is intentionally kept the same as OpenZeppelin's Ownable to be
    /// compatible with indexers and [EIP-173](https://eips.ethereum.org/EIPS/eip-173),
    /// despite it not being as lightweight as a single argument event.
    event OwnershipTransferred(address indexed oldOwner, address indexed newOwner);

    /// @dev An ownership handover to `pendingOwner` has been requested.
    event OwnershipHandoverRequested(address indexed pendingOwner);

    /// @dev The ownership handover to `pendingOwner` has been canceled.
    event OwnershipHandoverCanceled(address indexed pendingOwner);

    /// @dev `keccak256(bytes("OwnershipTransferred(address,address)"))`.
    uint256 private constant _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE =
        0x8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e0;

    /// @dev `keccak256(bytes("OwnershipHandoverRequested(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE =
        0xdbf36a107da19e49527a7176a1babf963b4b0ff8cde35ee35d6cd8f1f9ac7e1d;

    /// @dev `keccak256(bytes("OwnershipHandoverCanceled(address)"))`.
    uint256 private constant _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE =
        0xfa7b8eab7da67f412cc9575ed43464468f9bfbae89d1675917346ca6d8fe3c92;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The owner slot is given by:
    /// `bytes32(~uint256(uint32(bytes4(keccak256("_OWNER_SLOT_NOT")))))`.
    /// It is intentionally chosen to be a high value
    /// to avoid collision with lower slots.
    /// The choice of manual storage layout is to enable compatibility
    /// with both regular and upgradeable contracts.
    bytes32 internal constant _OWNER_SLOT =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff74873927;

    /// The ownership handover slot of `newOwner` is given by:
    /// ```
    ///     mstore(0x00, or(shl(96, user), _HANDOVER_SLOT_SEED))
    ///     let handoverSlot := keccak256(0x00, 0x20)
    /// ```
    /// It stores the expiry timestamp of the two-step ownership handover.
    uint256 private constant _HANDOVER_SLOT_SEED = 0x389a75e1;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                     INTERNAL FUNCTIONS                     */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Override to return true to make `_initializeOwner` prevent double-initialization.
    function _guardInitializeOwner() internal pure virtual returns (bool guard) {}

    /// @dev Initializes the owner directly without authorization guard.
    /// This function must be called upon initialization,
    /// regardless of whether the contract is upgradeable or not.
    /// This is to enable generalization to both regular and upgradeable contracts,
    /// and to save gas in case the initial owner is not the caller.
    /// For performance reasons, this function will not check if there
    /// is an existing owner.
    function _initializeOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                if sload(ownerSlot) {
                    mstore(0x00, 0x0dc149f0) // `AlreadyInitialized()`.
                    revert(0x1c, 0x04)
                }
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Store the new value.
                sstore(_OWNER_SLOT, newOwner)
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, 0, newOwner)
            }
        }
    }

    /// @dev Sets the owner directly without authorization guard.
    function _setOwner(address newOwner) internal virtual {
        if (_guardInitializeOwner()) {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, or(newOwner, shl(255, iszero(newOwner))))
            }
        } else {
            /// @solidity memory-safe-assembly
            assembly {
                let ownerSlot := _OWNER_SLOT
                // Clean the upper 96 bits.
                newOwner := shr(96, shl(96, newOwner))
                // Emit the {OwnershipTransferred} event.
                log3(0, 0, _OWNERSHIP_TRANSFERRED_EVENT_SIGNATURE, sload(ownerSlot), newOwner)
                // Store the new value.
                sstore(ownerSlot, newOwner)
            }
        }
    }

    /// @dev Throws if the sender is not the owner.
    function _checkOwner() internal view virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // If the caller is not the stored owner, revert.
            if iszero(eq(caller(), sload(_OWNER_SLOT))) {
                mstore(0x00, 0x82b42900) // `Unauthorized()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Returns how long a two-step ownership handover is valid for in seconds.
    /// Override to return a different value if needed.
    /// Made internal to conserve bytecode. Wrap it in a public function if needed.
    function _ownershipHandoverValidFor() internal view virtual returns (uint64) {
        return 48 * 3600;
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  PUBLIC UPDATE FUNCTIONS                   */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Allows the owner to transfer the ownership to `newOwner`.
    function transferOwnership(address newOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(shl(96, newOwner)) {
                mstore(0x00, 0x7448fbae) // `NewOwnerIsZeroAddress()`.
                revert(0x1c, 0x04)
            }
        }
        _setOwner(newOwner);
    }

    /// @dev Allows the owner to renounce their ownership.
    function renounceOwnership() public payable virtual onlyOwner {
        _setOwner(address(0));
    }

    /// @dev Request a two-step ownership handover to the caller.
    /// The request will automatically expire in 48 hours (172800 seconds) by default.
    function requestOwnershipHandover() public payable virtual {
        unchecked {
            uint256 expires = block.timestamp + _ownershipHandoverValidFor();
            /// @solidity memory-safe-assembly
            assembly {
                // Compute and set the handover slot to `expires`.
                mstore(0x0c, _HANDOVER_SLOT_SEED)
                mstore(0x00, caller())
                sstore(keccak256(0x0c, 0x20), expires)
                // Emit the {OwnershipHandoverRequested} event.
                log2(0, 0, _OWNERSHIP_HANDOVER_REQUESTED_EVENT_SIGNATURE, caller())
            }
        }
    }

    /// @dev Cancels the two-step ownership handover to the caller, if any.
    function cancelOwnershipHandover() public payable virtual {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, caller())
            sstore(keccak256(0x0c, 0x20), 0)
            // Emit the {OwnershipHandoverCanceled} event.
            log2(0, 0, _OWNERSHIP_HANDOVER_CANCELED_EVENT_SIGNATURE, caller())
        }
    }

    /// @dev Allows the owner to complete the two-step ownership handover to `pendingOwner`.
    /// Reverts if there is no existing ownership handover requested by `pendingOwner`.
    function completeOwnershipHandover(address pendingOwner) public payable virtual onlyOwner {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and set the handover slot to 0.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            let handoverSlot := keccak256(0x0c, 0x20)
            // If the handover does not exist, or has expired.
            if gt(timestamp(), sload(handoverSlot)) {
                mstore(0x00, 0x6f5e8818) // `NoHandoverRequest()`.
                revert(0x1c, 0x04)
            }
            // Set the handover slot to 0.
            sstore(handoverSlot, 0)
        }
        _setOwner(pendingOwner);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                   PUBLIC READ FUNCTIONS                    */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the owner of the contract.
    function owner() public view virtual returns (address result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := sload(_OWNER_SLOT)
        }
    }

    /// @dev Returns the expiry timestamp for the two-step ownership handover to `pendingOwner`.
    function ownershipHandoverExpiresAt(address pendingOwner)
        public
        view
        virtual
        returns (uint256 result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute the handover slot.
            mstore(0x0c, _HANDOVER_SLOT_SEED)
            mstore(0x00, pendingOwner)
            // Load the handover slot.
            result := sload(keccak256(0x0c, 0x20))
        }
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         MODIFIERS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Marks a function as only callable by the owner.
    modifier onlyOwner() virtual {
        _checkOwner();
        _;
    }
}

File 14 of 29 : ReentrancyGuard.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Reentrancy guard mixin.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/ReentrancyGuard.sol)
abstract contract ReentrancyGuard {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unauthorized reentrant call.
    error Reentrancy();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Equivalent to: `uint72(bytes9(keccak256("_REENTRANCY_GUARD_SLOT")))`.
    /// 9 bytes is large enough to avoid collisions with lower slots,
    /// but not too large to result in excessive bytecode bloat.
    uint256 private constant _REENTRANCY_GUARD_SLOT = 0x929eee149b4bd21268;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      REENTRANCY GUARD                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Guards a function from reentrancy.
    modifier nonReentrant() virtual {
        /// @solidity memory-safe-assembly
        assembly {
            if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                mstore(0x00, 0xab143c06) // `Reentrancy()`.
                revert(0x1c, 0x04)
            }
            sstore(_REENTRANCY_GUARD_SLOT, address())
        }
        _;
        /// @solidity memory-safe-assembly
        assembly {
            sstore(_REENTRANCY_GUARD_SLOT, codesize())
        }
    }

    /// @dev Guards a view function from read-only reentrancy.
    modifier nonReadReentrant() virtual {
        /// @solidity memory-safe-assembly
        assembly {
            if eq(sload(_REENTRANCY_GUARD_SLOT), address()) {
                mstore(0x00, 0xab143c06) // `Reentrancy()`.
                revert(0x1c, 0x04)
            }
        }
        _;
    }
}

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

/// @notice Initializable mixin for the upgradeable contracts.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Initializable.sol)
/// @author Modified from OpenZeppelin (https://github.com/OpenZeppelin/openzeppelin-contracts/tree/master/contracts/proxy/utils/Initializable.sol)
abstract contract Initializable {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The contract is already initialized.
    error InvalidInitialization();

    /// @dev The contract is not initializing.
    error NotInitializing();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Triggered when the contract has been initialized.
    event Initialized(uint64 version);

    /// @dev `keccak256(bytes("Initialized(uint64)"))`.
    bytes32 private constant _INTIALIZED_EVENT_SIGNATURE =
        0xc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                          STORAGE                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The default initializable slot is given by:
    /// `bytes32(~uint256(uint32(bytes4(keccak256("_INITIALIZABLE_SLOT")))))`.
    ///
    /// Bits Layout:
    /// - [0]     `initializing`
    /// - [1..64] `initializedVersion`
    bytes32 private constant _INITIALIZABLE_SLOT =
        0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf601132;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         OPERATIONS                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Override to return a custom storage slot if required.
    function _initializableSlot() internal pure virtual returns (bytes32) {
        return _INITIALIZABLE_SLOT;
    }

    /// @dev Guards an initializer function so that it can be invoked at most once.
    ///
    /// You can guard a function with `onlyInitializing` such that it can be called
    /// through a function guarded with `initializer`.
    ///
    /// This is similar to `reinitializer(1)`, except that in the context of a constructor,
    /// an `initializer` guarded function can be invoked multiple times.
    /// This can be useful during testing and is not expected to be used in production.
    ///
    /// Emits an {Initialized} event.
    modifier initializer() virtual {
        bytes32 s = _initializableSlot();
        /// @solidity memory-safe-assembly
        assembly {
            let i := sload(s)
            // Set `initializing` to 1, `initializedVersion` to 1.
            sstore(s, 3)
            // If `!(initializing == 0 && initializedVersion == 0)`.
            if i {
                // If `!(address(this).code.length == 0 && initializedVersion == 1)`.
                if iszero(lt(extcodesize(address()), eq(shr(1, i), 1))) {
                    mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
                    revert(0x1c, 0x04)
                }
                s := shl(shl(255, i), s) // Skip initializing if `initializing == 1`.
            }
        }
        _;
        /// @solidity memory-safe-assembly
        assembly {
            if s {
                // Set `initializing` to 0, `initializedVersion` to 1.
                sstore(s, 2)
                // Emit the {Initialized} event.
                mstore(0x20, 1)
                log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
            }
        }
    }

    /// @dev Guards an reinitialzer function so that it can be invoked at most once.
    ///
    /// You can guard a function with `onlyInitializing` such that it can be called
    /// through a function guarded with `reinitializer`.
    ///
    /// Emits an {Initialized} event.
    modifier reinitializer(uint64 version) virtual {
        bytes32 s = _initializableSlot();
        /// @solidity memory-safe-assembly
        assembly {
            version := and(version, 0xffffffffffffffff) // Clean upper bits.
            let i := sload(s)
            // If `initializing == 1 || initializedVersion >= version`.
            if iszero(lt(and(i, 1), lt(shr(1, i), version))) {
                mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
                revert(0x1c, 0x04)
            }
            // Set `initializing` to 1, `initializedVersion` to `version`.
            sstore(s, or(1, shl(1, version)))
        }
        _;
        /// @solidity memory-safe-assembly
        assembly {
            // Set `initializing` to 0, `initializedVersion` to `version`.
            sstore(s, shl(1, version))
            // Emit the {Initialized} event.
            mstore(0x20, version)
            log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
        }
    }

    /// @dev Guards a function such that it can only be called in the scope
    /// of a function guarded with `initializer` or `reinitializer`.
    modifier onlyInitializing() virtual {
        _checkInitializing();
        _;
    }

    /// @dev Reverts if the contract is not initializing.
    function _checkInitializing() internal view virtual {
        bytes32 s = _initializableSlot();
        /// @solidity memory-safe-assembly
        assembly {
            if iszero(and(1, sload(s))) {
                mstore(0x00, 0xd7e6bcf8) // `NotInitializing()`.
                revert(0x1c, 0x04)
            }
        }
    }

    /// @dev Locks any future initializations by setting the initialized version to `2**64 - 1`.
    ///
    /// Calling this in the constructor will prevent the contract from being initialized
    /// or reinitialized. It is recommended to use this to lock implementation contracts
    /// that are designed to be called through proxies.
    ///
    /// Emits an {Initialized} event the first time it is successfully called.
    function _disableInitializers() internal virtual {
        bytes32 s = _initializableSlot();
        /// @solidity memory-safe-assembly
        assembly {
            let i := sload(s)
            if and(i, 1) {
                mstore(0x00, 0xf92ee8a9) // `InvalidInitialization()`.
                revert(0x1c, 0x04)
            }
            let uint64max := shr(192, s) // Computed to save bytecode.
            if iszero(eq(shr(1, i), uint64max)) {
                // Set `initializing` to 0, `initializedVersion` to `2**64 - 1`.
                sstore(s, shl(1, uint64max))
                // Emit the {Initialized} event.
                mstore(0x20, uint64max)
                log1(0x20, 0x20, _INTIALIZED_EVENT_SIGNATURE)
            }
        }
    }

    /// @dev Returns the highest version that has been initialized.
    function _getInitializedVersion() internal view virtual returns (uint64 version) {
        bytes32 s = _initializableSlot();
        /// @solidity memory-safe-assembly
        assembly {
            version := shr(1, sload(s))
        }
    }

    /// @dev Returns whether the contract is currently initializing.
    function _isInitializing() internal view virtual returns (bool result) {
        bytes32 s = _initializableSlot();
        /// @solidity memory-safe-assembly
        assembly {
            result := and(1, sload(s))
        }
    }
}

File 16 of 29 : Multicallable.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Contract that enables a single call to call multiple methods on itself.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/Multicallable.sol)
/// @author Modified from Solmate (https://github.com/transmissions11/solmate/blob/main/src/utils/Multicallable.sol)
///
/// WARNING:
/// This implementation is NOT to be used with ERC2771 out-of-the-box.
/// https://blog.openzeppelin.com/arbitrary-address-spoofing-vulnerability-erc2771context-multicall-public-disclosure
/// This also applies to potentially other ERCs / patterns appending to the back of calldata.
///
/// We do NOT have a check for ERC2771, as we do not inherit from OpenZeppelin's context.
/// Moreover, it is infeasible and inefficient for us to add checks and mitigations
/// for all possible ERC / patterns appending to the back of calldata.
///
/// We would highly recommend using an alternative pattern such as
/// https://github.com/Vectorized/multicaller
/// which is more flexible, futureproof, and safer by default.
abstract contract Multicallable {
    /// @dev Apply `DELEGATECALL` with the current contract to each calldata in `data`,
    /// and store the `abi.encode` formatted results of each `DELEGATECALL` into `results`.
    /// If any of the `DELEGATECALL`s reverts, the entire context is reverted,
    /// and the error is bubbled up.
    ///
    /// This function is deliberately made non-payable to guard against double-spending.
    /// (See: https://www.paradigm.xyz/2021/08/two-rights-might-make-a-wrong)
    ///
    /// For efficiency, this function will directly return the results, terminating the context.
    /// If called internally, it must be called at the end of a function
    /// that returns `(bytes[] memory)`.
    function multicall(bytes[] calldata data) public virtual returns (bytes[] memory) {
        assembly {
            mstore(0x00, 0x20)
            mstore(0x20, data.length) // Store `data.length` into `results`.
            // Early return if no data.
            if iszero(data.length) { return(0x00, 0x40) }

            let results := 0x40
            // `shl` 5 is equivalent to multiplying by 0x20.
            let end := shl(5, data.length)
            // Copy the offsets from calldata into memory.
            calldatacopy(0x40, data.offset, end)
            // Offset into `results`.
            let resultsOffset := end
            // Pointer to the end of `results`.
            end := add(results, end)

            for {} 1 {} {
                // The offset of the current bytes in the calldata.
                let o := add(data.offset, mload(results))
                let m := add(resultsOffset, 0x40)
                // Copy the current bytes from calldata to the memory.
                calldatacopy(
                    m,
                    add(o, 0x20), // The offset of the current bytes' bytes.
                    calldataload(o) // The length of the current bytes.
                )
                if iszero(delegatecall(gas(), address(), m, calldataload(o), codesize(), 0x00)) {
                    // Bubble up the revert if the delegatecall reverts.
                    returndatacopy(0x00, 0x00, returndatasize())
                    revert(0x00, returndatasize())
                }
                // Append the current `resultsOffset` into `results`.
                mstore(results, resultsOffset)
                results := add(results, 0x20)
                // Append the `returndatasize()`, and the return data.
                mstore(m, returndatasize())
                returndatacopy(add(m, 0x20), 0x00, returndatasize())
                // Advance the `resultsOffset` by `returndatasize() + 0x20`,
                // rounded up to the next multiple of 32.
                resultsOffset :=
                    and(add(add(resultsOffset, returndatasize()), 0x3f), 0xffffffffffffffe0)
                if iszero(lt(results, end)) { break }
            }
            return(0x00, add(resultsOffset, 0x40))
        }
    }
}

File 17 of 29 : IMessageEscrowStructs.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.0;

interface IMessageEscrowStructs {
    struct IncentiveDescription {
        uint48 maxGasDelivery;      // 0: 6/32 bytes
        uint48 maxGasAck;           // 0: 12/32 bytes
        address refundGasTo;        // 0: 32/32 bytes
        uint96 priceOfDeliveryGas;  // 1: 12/32 bytes
        uint96 priceOfAckGas;       // 1: 24/32 bytes
        uint64 targetDelta;         // 1: 32/32 bytes
    }
}

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

import { ICatalystV1FactoryEvents } from "./ICatalystV1FactoryEvents.sol";

interface ICatalystV1Factory is ICatalystV1FactoryEvents {
    function _defaultGovernanceFee() external view returns (uint64);
    function _governanceFeeDestination() external view returns (address);
    function deployVault(
        address vaultTemplate,
        address[] memory assets,
        uint256[] memory init_balances,
        uint256[] memory weights,
        uint64 amp,
        uint64 vaultFee,
        string memory name,
        string memory symbol,
        address chainInterface
    ) external returns (address);
    function isCreatedByFactory(address, address) external view returns (bool);
    function setDefaultGovernanceFee(uint64 fee) external;
    function setGovernanceFeeDestination(address feeDestination) external;
}

File 19 of 29 : ICatalystV1VaultErrors.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

error EscrowAlreadyExists(); // 0xed778779
error ExceedsSecurityLimit(); // 0x7c1e66d0
error NotEnoughGas(); // 0xdd629f86
error ReturnInsufficient(uint256,uint256); // 0x24557f05
error UnusedUnitsAfterWithdrawal(uint256); // 0x0289311f
error VaultNotConnected(); // 0x2c64c1b2
error WithdrawRatioNotZero(); // 0xb8003bfa

// SPDX-License-Identifier: BUSL-1.1
pragma solidity ^0.8.19;

import { Ownable } from "solady/auth/Ownable.sol";
import { SafeTransferLib } from 'solady/utils/SafeTransferLib.sol';
import { LibClone } from "solady/utils/LibClone.sol";

import { ICatalystV1Factory } from "./interfaces/ICatalystV1Factory.sol";
import { ICatalystV1Vault } from "./ICatalystV1Vault.sol";

uint256 constant MAX_GOVERNANCE_FEE_SHARE = 75e16;   // 75%

/**
 * @title Catalyst Swap Factory
 * @author Cata Labs Inc.
 * @notice Allows permissionless deployment Catalyst vaults and defines governance address for vaults to read.
 * Importantly, this vault allows anyone to deploy a vault using any vault template and vault cross-chain interface.
 * As a result, just because a vault was deployed using this contract does not imply that it is safe. Vaults should
 * be cross-checked for their template, cross-chain interface, and if they are setup correctly. It may even be
 * that some vault templates only work with some cross-chain interfaces.
 *
 * Using the reference Catalyst Vault Templates, the owner of the factory is also the _owner_ of the Vaults.
 * They have certain privilege that may be able to be abused depending on the vault. One of these is configurating
 * fees. As a result:
 * !The owner of the factory must be a timelock!
 */
contract CatalystFactory is Ownable, ICatalystV1Factory {

    error InvalidAssetCount();
    error InvalidWeightCount();
    error FeeDestinationAddress0();
    error MaximumGovernanceFeeShare();

    /**
     * @notice A mapping which describes if a vault has been created by this factory.
     * Indexed by chainInterface then vault address.
     */
    mapping(address => mapping(address => bool)) public isCreatedByFactory;

    /** 
     * @notice Default governance fee. When a vault is created, this is the governance fee applied to that vault.
     */
    uint64 public _defaultGovernanceFee;

    /**
     * @notice The address to send governance fees to.
     * @dev Not enforced by the factory but vaults are expected to follow it.
     */
    address public _governanceFeeDestination;

    // The 2 above storage slots are packed together.

    constructor(address defaultOwner) payable {
        _initializeOwner(defaultOwner);
        _governanceFeeDestination = defaultOwner;
    }

    /**
     * @notice Set default governance fee share. 
     * @dev The set governance fee only applies to newly created vaults. Vaults have to be individual modified post creation. 
     * Is in WAD, (1e18 terms). So 1e16 is 1%. Cannot be set larger than 75% (75e16).
     */
    function setDefaultGovernanceFee(uint64 fee) override public onlyOwner {
        if (fee > MAX_GOVERNANCE_FEE_SHARE) revert MaximumGovernanceFeeShare();

        emit SetDefaultGovernanceFee(fee);

        _defaultGovernanceFee = fee;
    }

    /**
     * @notice Set the recipient of the governance.
     * @dev It is expected that vaults read this value and send their governance fees here.
     * This contract has no way to enforce if vaults honour this value.
     * Cannot be set to address(0). If wish to burn, set to 0xdead.
     */
    function setGovernanceFeeDestination(address feeDestination) override public onlyOwner {
        if (feeDestination == address(0)) revert FeeDestinationAddress0();
        emit SetGovernanceFeeDestination(feeDestination);

        _governanceFeeDestination = feeDestination;
    }

    /**
     * @notice Deploys a Catalyst vault, funds the vault with tokens, and calls setup.
     * When deploying vaults, there are 2 stages that needs to happen:
     * 1. We need to setup the vaults with the correct configuration.
     * 2. We need to set the vault swap curve. This consists of setting assets, vaults, amplification, etc.
     * The reason it is done in 2 steps is because of the stack limit. By spreading it across 2 calls, it is
     * cheaper gas wise.
     * This is done in a safe way by expecting both of these init. calls to be done in a single transaction.
     * As a result, the vaults are never left in a vulnerable state. It is expected that the latter call
     * (initializeSwapCurves) completes initialization and blocks the setup functions from being called again.
     * @dev The deployer needs to set relevant approvals to this contract before calling deployVault.
     * @param vaultTemplate The template the transparent proxy should target.
     * @param assets The list of assets the vault should support.
     * @param init_balances The initial balances of the vault. (Should be approved)
     * @param weights The weights of the tokens.
     * @param amp Token parameter 1. (Amplification)
     * @param vaultFee The vault fee.
     * @param name Name of the Vault token.
     * @param symbol Symbol for the Vault token.
     * @param chainInterface The cross chain interface used for cross-chain swaps. (Can be address(0) to disable cross-chain swaps.)
     * @return vault The address of the created Catalyst Vault. (minimal transparent proxy)
     */
    function deployVault(
        address vaultTemplate,
        address[] calldata assets,
        uint256[] calldata init_balances,
        uint256[] calldata weights,
        uint64 amp,
        uint64 vaultFee,
        string memory name,
        string memory symbol,
        address chainInterface
    ) override external returns (address vault) {
        // Check if an invalid asset count has been provided
        if (assets.length == 0) revert InvalidAssetCount();
        // Check if an invalid weight count has been provided
        if (weights.length != assets.length) revert InvalidWeightCount();
        // init_balances length not checked: if shorter than assets, the funds transfer loop
        // will fail. If longer, values will just be ignored.

        // Create a minimal transparent proxy:
        vault = LibClone.clone(vaultTemplate);

        // The vault expects the balances to exist in the vault when setup is called.
        uint256 assetLength = assets.length;
        for (uint256 it; it < assetLength;) {
            SafeTransferLib.safeTransferFrom(
                assets[it],
                msg.sender,
                vault,
                init_balances[it]
            );

            unchecked {
                ++it;
            }
        }

        // Call setup
        ICatalystV1Vault(vault).setup(
            name,
            symbol,
            chainInterface,
            vaultFee,
            _defaultGovernanceFee,
            owner(),        // Fee administrator
            msg.sender      // setup master
        );

        // Initialize swap curves
        ICatalystV1Vault(vault).initializeSwapCurves(
            assets,
            weights,
            amp,
            msg.sender
        );

        // Emit event for vault discovery.
        emit VaultDeployed(
            vaultTemplate,
            chainInterface,
            msg.sender,
            vault,
            assets,
            amp
        );
        isCreatedByFactory[chainInterface][vault] = true;

        return vault;
    }
}

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

/**
 * @title Escrow related functions defined by Catalyst v1 Vaults
 * @notice Contains the functions used to manage escrows by the cross-chain interface.
 */
interface ICatalystV1VaultSuccessFailure {
    /** @notice Release the escrowed tokens into the vault.  */
    function onSendAssetSuccess(
        bytes32 channelId,
        bytes calldata toAccount,
        uint256 U,
        uint256 escrowAmount,
        address escrowToken,
        uint32 blockNumberMod
    ) external;

    /** @notice Returned the escrowed tokens to the user */
    function onSendAssetFailure(
        bytes32 channelId,
        bytes calldata toAccount,
        uint256 U,
        uint256 escrowAmount,
        address escrowToken,
        uint32 blockNumberMod
    ) external;

    /** @notice Release the escrowed tokens into the vault.  */
    function onSendLiquiditySuccess(
        bytes32 channelId,
        bytes calldata toAccount,
        uint256 U,
        uint256 escrowAmount,
        uint32 blockNumberMod
    ) external;

    /** @notice Returned the escrowed tokens to the user */
    function onSendLiquidityFailure(
        bytes32 channelId,
        bytes calldata toAccount,
        uint256 U,
        uint256 escrowAmount,
        uint32 blockNumberMod
    ) external;
}

//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;
/**
 * @title Administrative actions defined by Catalyst v1 Vaults
 * @notice Contains all functions which can only be called by privileged users.
 */
interface ICatalystV1VaultAdministration {
    function setFeeAdministrator(address administrator) external;

    function setVaultFee(uint64 fee) external;

    function setGovernanceFee(uint64 fee) external;

    /**
     * @notice Initializes the vault pricing parameters.
     * @param assets The list of assets the vault will support.
     * @param weights The weights of the tokens.
     * @param amp Vault amplification.
     * @param depositor The account to which the initial vault tokens are minted to.
     */
    function initializeSwapCurves(
        address[] calldata assets,
        uint256[] calldata weights,
        uint64 amp,
        address depositor
    ) external;

    /**
     * @notice Creates a connection to the vault toVault on the channel _channelId.
     * @dev if _vaultReceiving is an EVM vault, it can be computes as:
     *     Vyper: convert(_vaultAddress, bytes32)
     *     Solidity: abi.encode(_vaultAddress)
     *     Brownie: brownie.convert.to_bytes(_vaultAddress, type_str="bytes32")
     * setupMaster == ZERO_ADDRESS
     * @param channelId The _channelId of the target vault.
     * @param toVault The bytes32 representation of the target vault
     * @param state Boolean indicating if the connection should be open or closed.
     */
    function setConnection(
        bytes32 channelId,
        bytes calldata toVault,
        bool state
    ) external;

    /**
     * @notice Gives up short term ownership of the vault. This makes the vault unstoppable.
     */
    function finishSetup() external;
}

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

/**
 * @title Derived Vault state
 * @notice Contains all vault state which is derived from vault storage
 */
interface ICatalystV1VaultDerived {
    /** @notice  Returns the current cross-chain unit capacity. */
    function getUnitCapacity() external view returns (uint256);

    function calcSendAsset(address from, uint256 amount) external view returns (uint256);

    /**
     * @notice Computes the output of ReceiveAsset, without executing one.
     * @param to The address of the token to buy.
     * @param U The number of units used to buy to.
     * @return uint256 Number of purchased tokens.
     */
    function calcReceiveAsset(address to, uint256 U) external view returns (uint256);

    /**
     * @notice Computes the output of localSwap, without executing one.
     * @param from The address of the token to sell.
     * @param to The address of the token to buy.
     * @param amount The amount of from token to sell for to token.
     * @return Output denominated in to token.
     */
    function calcLocalSwap(address from, address to, uint256 amount) external view returns (uint256);
}

File 24 of 29 : ICatalystV1VaultEvents.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/**
 * @title Events emitted by Catalyst v1 Vaults
 * @notice Contains all events emitted by the vault
 * @dev When using events to match transations, the combination of: channelId, fromVault, toAccount, toAsset, units, and block number is semi-guranteed to be unique.
 *      If more than 2**32 blocks exist, then all instances are guaranteed to be non-overlapping
 */
interface ICatalystV1VaultEvents {
    /**
     * @notice  Describes an atomic swap between the 2 tokens: _fromAsset and _toAsset.
     * @param account The user / exchange who facilitated the trade (msg.sender)
     * @param fromAsset The asset which was sold in exchange for _toAsset
     * @param toAsset The asset which was purchased with _fromAsset
     * @param fromAmount The number of _fromAsset sold
     * @param toAmount The number of tokens provided to toAccount
     */
    event LocalSwap(
        address indexed account,
        address fromAsset,
        address toAsset,
        uint256 fromAmount,
        uint256 toAmount
    );

    /**
     * @notice Describes the creation of an external swap: Cross-chain swap.
     * @param channelId The target chain identifier
     * @param toVault The target vault.
     * @param toAccount The recipient of the trade. The person who bought the trade is not present.
     * @param fromAsset The asset which was sold in exchange for _toAsset.
     * @param toAssetIndex The token index of the asset to purchase on _toChain.
     * @param fromAmount The number of _fromAsset sold.
     * @param minOut The minimum output to be accepted of fromAsset.
     * @param units The calculated number of units bought. Will be sold to buy _toAsset
     * @param underwriteIncentiveX16 The incentive out of 2**16 - 1 provided to the underwriter.
     * @param fee The number of tokens paid to the vault in fees.
     */
    event SendAsset(
        bytes32 channelId,
        bytes toVault,
        bytes toAccount,
        address fromAsset,
        uint8 toAssetIndex,
        uint256 fromAmount,
        uint256 minOut,
        uint256 units,
        uint256 fee,
        uint16 underwriteIncentiveX16
    );

    /**
     * @notice Describes the arrival of an external swap: Cross-chain swap.
     * If toAccount is used to match trades, remember to convert it into 64 + 1 bytes.
     * @param channelId The target chain identifier
     * @param fromVault The source vault.
     * @param toAccount The recipient of the trade.
     * @param toAsset The asset which was purchased with _fromAsset
     * @param units The number of units sent from the other chain.
     * @param toAmount The number of tokens provided to toAccount
     * @param fromAmount The amount spent to get units on the source side.
     * @param fromAsset The provided asset on the source side.
     * @param sourceBlockNumberMod The block number of the sending transaction mod 2**32 - 1
     */
    event ReceiveAsset(
        bytes32 channelId,
        bytes fromVault,
        address toAccount,
        address toAsset,
        uint256 units,
        uint256 toAmount,
        uint256 fromAmount,
        bytes fromAsset,
        uint32 sourceBlockNumberMod

    );

    /**
     * @notice Describes the creation of a liquidity swap
     * @param channelId The target chain identifier
     * @param toVault The target vault.
     * @param toAccount The recipient of the liquidity. The person who bought the trade is not present.
     * @param fromAmount The number of _fromAsset sold
     * @param minOut An array containing a list of minimum outputs [minVaultTokens, minReferenceAssets]
     * @param units The calculated number of liquidity units bought.
     */
    event SendLiquidity(
        bytes32 channelId,
        bytes toVault,
        bytes toAccount,
        uint256 fromAmount,
        uint256[2] minOut,
        uint256 units
    );

    /**
     * @notice Describes the arrival of a liquidity swap
     * @param channelId The target chain identifier
     * @param fromVault The source vault.
     * @param toAccount The recipient of the liquidity.
     * @param units The number of liquidity units sent from the other chain.
     * @param toAmount The number of vault tokens provided to toAccount
     * @param fromAmount The amount spent to get units on the source side.
     * @param sourceBlockNumberMod The block number of the sending transaction mod 2**32 - 1
     */
    event ReceiveLiquidity(
        bytes32 channelId,
        bytes fromVault,
        address toAccount,
        uint256 units,
        uint256 toAmount,
        uint256 fromAmount,
        uint256 sourceBlockNumberMod
    );

    /**
     * @notice Emitted on liquidity deposits.
     * @param toAccount The depositor. Is credited with _mints vault tokens.
     * @param mint The number of minted vault tokens credited to toAccount
     * @param assets An array of the number of deposited assets.
     */
    event VaultDeposit(address indexed toAccount, uint256 mint, uint256[] assets);

    /**
     * @notice Emitted on liquidity withdrawal.
     * @param toAccount The withdrawer. Is debited _burns vault tokens.
     * @param burn The number of burned vault tokens.
     * @param assets An array of the token amounts returned
     */
    event VaultWithdraw(address indexed toAccount, uint256 burn, uint256[] assets);

    /** @notice Called upon successful asset swap. */
    event SendAssetSuccess(
        bytes32 channelId,
        bytes toAccount,
        uint256 units,
        uint256 escrowAmount,
        address escrowToken,
        uint32 blockNumberMod
    );

    /** @notice Called upon failed asset swap. */
    event SendAssetFailure(
        bytes32 channelId,
        bytes toAccount,
        uint256 units,
        uint256 escrowAmount,
        address escrowToken,
        uint32 blockNumberMod
    );

    /** @notice Called upon successful liquidity swap. */
    event SendLiquiditySuccess(
        bytes32 channelId,
        bytes toAccount,
        uint256 units,
        uint256 escrowAmount,
        uint32 blockNumberMod
    );

    /** @notice Called upon failed liquidity swap. */
    event SendLiquidityFailure(
        bytes32 channelId,
        bytes toAccount,
        uint256 units,
        uint256 escrowAmount,
        uint32 blockNumberMod
    );

    /** @notice Vault setup has been finalised. */
    event FinishSetup();

    /**
     * @notice Emitted on fee administrator adjustment
     * @param administrator The new vault fee administrator
     */
    event SetFeeAdministrator(
        address administrator
    );

    /**
     * @notice Emitted on vault fee adjustment
     * @param fee The new vault fee
     */
    event SetVaultFee(
        uint64 fee
    );

    /**
     * @notice Emitted on governance fee adjustment
     * @param fee The new governance fee
     */
    event SetGovernanceFee(
        uint64 fee
    );

    /**
     * @notice Emitted on weights modification
     * @param targetTime Time at which the weights adjustment must complete.
     * @param targetWeights The desired new weights.
     */
    event SetWeights(
        uint248 targetTime,
        uint256[] targetWeights
    );

    /**
     * @notice Amplification has been modification
     * @param targetTime Time at which the amplification adjustment must complete.
     * @param targetAmplification The desired new amplification.
     */
    event SetAmplification(
        uint48 targetTime,
        uint256 targetAmplification
    );

    /**
     * @notice A connection has been modified
     * @param channelId Target chain identifier.
     * @param toVault Bytes32 representation of the target vault.
     * @param newState Boolean indicating if the connection should be open or closed.
     */
    event SetConnection(
        bytes32 channelId,
        bytes toVault,
        bool newState
    );

    //-- Underwriting Events --//

    /**
     * @notice A swap has been underwritten.
     */
    event SwapUnderwritten(
        bytes32 indexed identifier,
        address toAsset,
        uint256 U,
        uint256 purchasedTokens
    );

}

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

/**
 * @title Immutable vault state
 * @notice Contains all vault state which doesn't change once set.
 */
interface ICatalystV1VaultImmutables {
    function _chainInterface() external view returns (address);

    function FACTORY() external view returns (address);

    function MATHLIB() external view returns (address);

    /** 
     * @notice To indicate which token is desired on the target vault,the _toAsset is an integer
     * from 0 to MAX_ASSETS indicating which asset the vault should purchase with units.
     */
    function _tokenIndexing(uint256 tokenIndex) external view returns (address);
}

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

import { ICatalystV1Structs } from "./ICatalystV1VaultState.sol";

interface ICatalystV1VaultPermissionless {
    /** 
     * @notice Setup a vault.
     * @param name_ Name of the Vault token.
     * @param symbol_ Symbol for the Vault token.
     * @param chainInterface Cross chain interface used for cross-chain swaps. (Can be address(0) to disable cross-chain swaps.)
     * @param vaultFee Vault fee.
     * @param governanceFee Governance fee share.
     * @param feeAdministrator Account that can modify the fees.
     * @param setupMaster Short-term owner of the vault (until finishSetup is called).
     */
    function setup(
        string calldata name_,
        string calldata symbol_,
        address chainInterface,
        uint64 vaultFee,
        uint64 governanceFee,
        address feeAdministrator,
        address setupMaster
    ) external;

    //--- Balance Changes ---//

    /**
     * @notice Deposits a user configurable amount of tokens.
     * @dev Requires approvals for all deposited tokens within the vault.
     * Volatile: It is advised that the deposit matches the vault's %token distribution.
     * Amplified: It is advised that the deposit is as close to 1,1,... as possible.
     *            Otherwise between 1,1,... and the vault's %token distribution.
     * @param tokenAmounts Array of the tokens amounts to be deposited.
     * @param minOut Minimum number of vault tokens to be minted.
     */
    function depositMixed(uint256[] calldata tokenAmounts, uint256 minOut)
        external returns(uint256);

    /**
     * @notice Burns baseAmount and releases the symmetrical share
     * of tokens to the burner. This doesn't change the vault price.
     * @param baseAmount Number of vault tokens to burn.
     */
    function withdrawAll(uint256 baseAmount, uint256[] calldata minOut)
        external returns(uint256[] memory);

    /**
     * @notice Burns vaultTokens and release a token distribution which can be set by the user.
     * @dev Requires approvals for all tokens within the vault.
     * Volatile: It is advised that the deposit matches the vault's %token distribution.
     * Amplified: It is advised that the deposit matches the vault's %token distribution.
     *            Otherwise it should be weighted towards the tokens the vault has more of.
     * @param vaultTokens Number of vault tokens to withdraw.
     * @param withdrawRatio Percentage of units used to withdraw. In the following special scheme: U_0 = U · withdrawRatio[0], U_1 = (U - U_0) · withdrawRatio[1], U_2 = (U - U_0 - U_1) · withdrawRatio[2], .... Is WAD.
     * @param minOut Minimum number of tokens minted.
     */
    function withdrawMixed(
        uint256 vaultTokens,
        uint256[] calldata withdrawRatio,
        uint256[] calldata minOut
    ) external returns(uint256[] memory);

    //--- Swaps ---//

    /**
     * @notice A swap between 2 assets which both are inside the vault. Is atomic.
     * @param fromAsset Asset the user wants to sell.
     * @param toAsset Asset the user wants to buy.
     * @param amount Amount of fromAsset the user wants to sell.
     * @param minOut Minimum output of _toAsset the user wants.
     */
    function localSwap(
        address fromAsset,
        address toAsset,
        uint256 amount,
        uint256 minOut
    ) external returns (uint256);

    /**
     * @notice Initiate a cross-chain swap by purchasing units and transfer them to another vault.
     * @dev Addresses are encoded in 64 + 1 bytes. To encode for EVM, encode as:
     * Solidity: abi.encodePacket(uint8(20), bytes32(0), abi.encode(<vaultAddress>))
     * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive.
     * @param fromAsset Asset the user wants to sell.
     * @param toAssetIndex Index of the asset the user wants to buy in the target vault.
     * @param amount Number of fromAsset to sell to the vault.
     * @param minOut Minimum number of returned tokens to the toAccount on the target chain.
     * @param fallbackUser If the transaction fails, send the escrowed funds to this address.
     * @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16.max))
     * @param calldata_ Data field if a call should be made on the target chain.
     * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(<address>), <data>). At maximum 65535 bytes can be passed.
     * @return uint256 The number of units minted.
     */
    function sendAsset(
        ICatalystV1Structs.RouteDescription calldata routeDescription,
        address fromAsset,
        uint8 toAssetIndex,
        uint256 amount,
        uint256 minOut,
        address fallbackUser,
        uint16 underwriteIncentiveX16,
        bytes calldata calldata_
    ) external payable returns (uint256);

    /**
     * @notice Initiate a cross-chain swap by purchasing units and transfer them to another vault using a fixed number of units.
     * @dev Addresses are encoded in 64 + 1 bytes. To encode for EVM, encode as:
     * Solidity: abi.encodePacket(uint8(20), bytes32(0), abi.encode(<vaultAddress>))
     * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive.
     * @param fromAsset Asset the user wants to sell.
     * @param toAssetIndex Index of the asset the user wants to buy in the target vault.
     * @param amount Number of fromAsset to sell to the vault.
     * @param minOut Minimum number of returned tokens to the toAccount on the target chain.
     * @param minU Minimum and exact number of units sent.
     * @param fallbackUser If the transaction fails, send the escrowed funds to this address.
     * @param calldata_ Data field if a call should be made on the target chain.
     * Encoding depends on the target chain, with evm being: abi.encodePacket(bytes20(<address>), <data>)
     * @param underwriteIncentiveX16 Payment for underwriting the swap (out of type(uint16.max))
     * @return uint256 Number of units minted.
     */
    function sendAssetFixedUnit(
        ICatalystV1Structs.RouteDescription calldata routeDescription,
        address fromAsset,
        uint8 toAssetIndex,
        uint256 amount,
        uint256 minOut,
        uint256 minU,
        address fallbackUser,
        uint16 underwriteIncentiveX16,
        bytes calldata calldata_
    ) external payable returns (uint256);

    /**
     * @notice Completes a cross-chain swap by converting units to the desired token (toAsset)
     * @dev Can only be called by the chainInterface.
     * @param channelId The incoming connection identifier.
     * @param fromVault The source vault.
     * @param toAssetIndex Index of the asset to be purchased with Units.
     * @param toAccount The recipient.
     * @param U Number of units to convert into toAsset.
     * @param minOut Minimum number of tokens bought. Reverts if less.
     * @param fromAmount Used to match cross-chain swap events. The input amount on the sending chain.
     * @param fromAsset Used to match cross-chain swap events. The input asset on the sending chain.
     * @param blockNumberMod Used to match cross-chain swap events. The block number from the host side.
     */
    function receiveAsset(
        bytes32 channelId,
        bytes calldata fromVault,
        uint256 toAssetIndex,
        address toAccount,
        uint256 U,
        uint256 minOut,
        uint256 fromAmount,
        bytes calldata fromAsset,
        uint32 blockNumberMod
    ) external returns(uint256 purchasedTokens);

    /**
     * @notice Initiate a cross-chain liquidity swap by withdrawing tokens and converting them to units.
     * @dev While the description says tokens are withdrawn and then converted to units, vault tokens are converted
     * directly into units through the following equation:
     *      U = ln(PT/(PT-pt)) * \sum W_i
     * @param routeDescription A cross-chain route description which contains the chainIdentifier, toAccount, toVault and relaying incentive.
     * @param vaultTokens Number of vault tokens to exchange.
     * @param minOut Array of minout describing: [the minimum number of vault tokens, the minimum number of reference assets].
     * @param fallbackUser If the transaction fails, send the escrowed funds to this address.
     * @param calldata_ Data field if a call should be made on the target chain.
     * Encoding depends on the target chain, with EVM: abi.encodePacket(bytes20(<address>), <data>). At maximum 65535 bytes can be passed.
     * @return uint256 Number of units minted.
     */
    function sendLiquidity(
        ICatalystV1Structs.RouteDescription calldata routeDescription,
        uint256 vaultTokens,
        uint256[2] calldata minOut,
        address fallbackUser,
        bytes calldata calldata_
    ) external payable returns (uint256);

    /**
     * @notice Completes a cross-chain liquidity swap by converting units to tokens and depositing.
     * @dev Called exclusively by the chainInterface.
     * @param fromVault Source vault
     * @param toAccount Recipient of the vault tokens
     * @param U Number of units to convert into vault tokens.
     * @param minVaultTokens Minimum number of vault tokens to mint on target vault. Otherwise: Reject
     * @param minReferenceAsset Minimum number of reference asset the vaults tokens are worth. Otherwise: Reject
     * @param fromAmount Used to match cross-chain swap events. The input amount on the sending chain.
     * @param blockNumberMod Used to match cross-chain swap events. The block number from the host side.
     */
    function receiveLiquidity(
        bytes32 channelId,
        bytes calldata fromVault,
        address toAccount,
        uint256 U,
        uint256 minVaultTokens,
        uint256 minReferenceAsset,
        uint256 fromAmount,
        uint32 blockNumberMod
    ) external returns(uint256 purchasedVaultTokens);
}

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

/** @title Extensions to vaults which supports underwriting. */
interface ICatalystV1Underwriting {
    function underwriteAsset(
        bytes32 identifier,
        address toAsset,
        uint256 U,
        uint256 minOut
    ) external returns (uint256);

    function releaseUnderwriteAsset(
        address refundTo,
        bytes32 identifier,
        uint256 escrowAmount,
        address escrowToken,
        bytes32 sourceIdentifier,
        bytes calldata fromVault
    ) external;

    function deleteUnderwriteAsset(
        bytes32 identifier,
        uint256 U,
        uint256 escrowAmount,
        address escrowToken
    ) external;
}

File 28 of 29 : ICatalystV1FactoryEvents.sol
//SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

/**
 * @title Events emitted by Catalyst v1 Factory
 * @notice Contains all events emitted by the Factory
*/
interface ICatalystV1FactoryEvents {
    /**
     * @notice  Describes the deployment of a new vault as a proxy of the given vault template.
     * @dev Should be used for vault discovery and pathing.
     * @param vaultTemplate The address of the template used by the transparent proxy.
     * @param chainInterface The address of the CCI used by the transparent proxy.
     * @param vaultAddress The minimal transparent proxy address for the vault.
     * @param assets List of the assets the vault supports.
     * @param k Set to 10**18 if the vault is volatile, otherwise the vault is an amplified vault.
     */
    event VaultDeployed(
        address indexed vaultTemplate,
        address indexed chainInterface, 
        address indexed deployer,
        address vaultAddress,
        address[] assets,
        uint256 k
    );

    /**
     * @notice Describes governance fee changes.
     * @dev Only applies to new vaults, has no impact on existing vaults.
     * @param fee The new governance fee.
     */
    event SetDefaultGovernanceFee(
        uint256 fee
    );

    /**
     * @notice Sets a new destination for governance fees.
     */
    event SetGovernanceFeeDestination(
        address newFeeDestination
    );
}

File 29 of 29 : LibClone.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.4;

/// @notice Minimal proxy library.
/// @author Solady (https://github.com/vectorized/solady/blob/main/src/utils/LibClone.sol)
/// @author Minimal proxy by 0age (https://github.com/0age)
/// @author Clones with immutable args by wighawag, zefram.eth, Saw-mon & Natalie
/// (https://github.com/Saw-mon-and-Natalie/clones-with-immutable-args)
/// @author Minimal ERC1967 proxy by jtriley-eth (https://github.com/jtriley-eth/minimum-viable-proxy)
///
/// @dev Minimal proxy:
/// Although the sw0nt pattern saves 5 gas over the erc-1167 pattern during runtime,
/// it is not supported out-of-the-box on Etherscan. Hence, we choose to use the 0age pattern,
/// which saves 4 gas over the erc-1167 pattern during runtime, and has the smallest bytecode.
///
/// @dev Minimal proxy (PUSH0 variant):
/// This is a new minimal proxy that uses the PUSH0 opcode introduced during Shanghai.
/// It is optimized first for minimal runtime gas, then for minimal bytecode.
/// The PUSH0 clone functions are intentionally postfixed with a jarring "_PUSH0" as
/// many EVM chains may not support the PUSH0 opcode in the early months after Shanghai.
/// Please use with caution.
///
/// @dev Clones with immutable args (CWIA):
/// The implementation of CWIA here implements a `receive()` method that emits the
/// `ReceiveETH(uint256)` event. This skips the `DELEGATECALL` when there is no calldata,
/// enabling us to accept hard gas-capped `sends` & `transfers` for maximum backwards
/// composability. The minimal proxy implementation does not offer this feature.
///
/// @dev Minimal ERC1967 proxy:
/// An minimal ERC1967 proxy, intended to be upgraded with UUPS.
/// This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic.
///
/// @dev ERC1967I proxy:
/// An variant of the minimal ERC1967 proxy, with a special code path that activates
/// if `calldatasize() == 1`. This code path skips the delegatecall and directly returns the
/// `implementation` address. The returned implementation is guaranteed to be valid if the
/// keccak256 of the proxy's code is equal to `ERC1967I_CODE_HASH`.
library LibClone {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         CONSTANTS                          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev The keccak256 of the deployed code for the ERC1967 proxy.
    bytes32 internal constant ERC1967_CODE_HASH =
        0xaaa52c8cc8a0e3fd27ce756cc6b4e70c51423e9b597b11f32d3e49f8b1fc890d;

    /// @dev The keccak256 of the deployed code for the ERC1967I proxy.
    bytes32 internal constant ERC1967I_CODE_HASH =
        0xce700223c0d4cea4583409accfc45adac4a093b3519998a9cbbe1504dadba6f7;

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Unable to deploy the clone.
    error DeploymentFailed();

    /// @dev The salt must start with either the zero address or `by`.
    error SaltDoesNotStartWith();

    /// @dev The ETH transfer has failed.
    error ETHTransferFailed();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                  MINIMAL PROXY OPERATIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Deploys a clone of `implementation`.
    function clone(address implementation) internal returns (address instance) {
        instance = clone(0, implementation);
    }

    /// @dev Deploys a clone of `implementation`.
    /// Deposits `value` ETH during deployment.
    function clone(uint256 value, address implementation) internal returns (address instance) {
        /// @solidity memory-safe-assembly
        assembly {
            /**
             * --------------------------------------------------------------------------+
             * CREATION (9 bytes)                                                        |
             * --------------------------------------------------------------------------|
             * Opcode     | Mnemonic          | Stack     | Memory                       |
             * --------------------------------------------------------------------------|
             * 60 runSize | PUSH1 runSize     | r         |                              |
             * 3d         | RETURNDATASIZE    | 0 r       |                              |
             * 81         | DUP2              | r 0 r     |                              |
             * 60 offset  | PUSH1 offset      | o r 0 r   |                              |
             * 3d         | RETURNDATASIZE    | 0 o r 0 r |                              |
             * 39         | CODECOPY          | 0 r       | [0..runSize): runtime code   |
             * f3         | RETURN            |           | [0..runSize): runtime code   |
             * --------------------------------------------------------------------------|
             * RUNTIME (44 bytes)                                                        |
             * --------------------------------------------------------------------------|
             * Opcode  | Mnemonic       | Stack                  | Memory                |
             * --------------------------------------------------------------------------|
             *                                                                           |
             * ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: |
             * 3d      | RETURNDATASIZE | 0                      |                       |
             * 3d      | RETURNDATASIZE | 0 0                    |                       |
             * 3d      | RETURNDATASIZE | 0 0 0                  |                       |
             * 3d      | RETURNDATASIZE | 0 0 0 0                |                       |
             *                                                                           |
             * ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: |
             * 36      | CALLDATASIZE   | cds 0 0 0 0            |                       |
             * 3d      | RETURNDATASIZE | 0 cds 0 0 0 0          |                       |
             * 3d      | RETURNDATASIZE | 0 0 cds 0 0 0 0        |                       |
             * 37      | CALLDATACOPY   | 0 0 0 0                | [0..cds): calldata    |
             *                                                                           |
             * ::: delegate call to the implementation contract :::::::::::::::::::::::: |
             * 36      | CALLDATASIZE   | cds 0 0 0 0            | [0..cds): calldata    |
             * 3d      | RETURNDATASIZE | 0 cds 0 0 0 0          | [0..cds): calldata    |
             * 73 addr | PUSH20 addr    | addr 0 cds 0 0 0 0     | [0..cds): calldata    |
             * 5a      | GAS            | gas addr 0 cds 0 0 0 0 | [0..cds): calldata    |
             * f4      | DELEGATECALL   | success 0 0            | [0..cds): calldata    |
             *                                                                           |
             * ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: |
             * 3d      | RETURNDATASIZE | rds success 0 0        | [0..cds): calldata    |
             * 3d      | RETURNDATASIZE | rds rds success 0 0    | [0..cds): calldata    |
             * 93      | SWAP4          | 0 rds success 0 rds    | [0..cds): calldata    |
             * 80      | DUP1           | 0 0 rds success 0 rds  | [0..cds): calldata    |
             * 3e      | RETURNDATACOPY | success 0 rds          | [0..rds): returndata  |
             *                                                                           |
             * 60 0x2a | PUSH1 0x2a     | 0x2a success 0 rds     | [0..rds): returndata  |
             * 57      | JUMPI          | 0 rds                  | [0..rds): returndata  |
             *                                                                           |
             * ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * fd      | REVERT         |                        | [0..rds): returndata  |
             *                                                                           |
             * ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 5b      | JUMPDEST       | 0 rds                  | [0..rds): returndata  |
             * f3      | RETURN         |                        | [0..rds): returndata  |
             * --------------------------------------------------------------------------+
             */
            mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
            mstore(0x14, implementation)
            mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
            instance := create(value, 0x0c, 0x35)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Deploys a deterministic clone of `implementation` with `salt`.
    function cloneDeterministic(address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = cloneDeterministic(0, implementation, salt);
    }

    /// @dev Deploys a deterministic clone of `implementation` with `salt`.
    /// Deposits `value` ETH during deployment.
    function cloneDeterministic(uint256 value, address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
            mstore(0x14, implementation)
            mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
            instance := create2(value, 0x0c, 0x35, salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the initialization code of the clone of `implementation`.
    function initCode(address implementation) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            mstore(add(result, 0x40), 0x5af43d3d93803e602a57fd5bf30000000000000000000000)
            mstore(add(result, 0x28), implementation)
            mstore(add(result, 0x14), 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
            mstore(result, 0x35) // Store the length.
            mstore(0x40, add(result, 0x60)) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the clone of `implementation`.
    /// Used for mining vanity addresses with create2crunch.
    function initCodeHash(address implementation) internal pure returns (bytes32 hash) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x21, 0x5af43d3d93803e602a57fd5bf3)
            mstore(0x14, implementation)
            mstore(0x00, 0x602c3d8160093d39f33d3d3d3d363d3d37363d73)
            hash := keccak256(0x0c, 0x35)
            mstore(0x21, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the address of the deterministic clone of `implementation`,
    /// with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddress(address implementation, bytes32 salt, address deployer)
        internal
        pure
        returns (address predicted)
    {
        bytes32 hash = initCodeHash(implementation);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*          MINIMAL PROXY OPERATIONS (PUSH0 VARIANT)          */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Deploys a PUSH0 clone of `implementation`.
    function clone_PUSH0(address implementation) internal returns (address instance) {
        instance = clone_PUSH0(0, implementation);
    }

    /// @dev Deploys a PUSH0 clone of `implementation`.
    /// Deposits `value` ETH during deployment.
    function clone_PUSH0(uint256 value, address implementation)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            /**
             * --------------------------------------------------------------------------+
             * CREATION (9 bytes)                                                        |
             * --------------------------------------------------------------------------|
             * Opcode     | Mnemonic          | Stack     | Memory                       |
             * --------------------------------------------------------------------------|
             * 60 runSize | PUSH1 runSize     | r         |                              |
             * 5f         | PUSH0             | 0 r       |                              |
             * 81         | DUP2              | r 0 r     |                              |
             * 60 offset  | PUSH1 offset      | o r 0 r   |                              |
             * 5f         | PUSH0             | 0 o r 0 r |                              |
             * 39         | CODECOPY          | 0 r       | [0..runSize): runtime code   |
             * f3         | RETURN            |           | [0..runSize): runtime code   |
             * --------------------------------------------------------------------------|
             * RUNTIME (45 bytes)                                                        |
             * --------------------------------------------------------------------------|
             * Opcode  | Mnemonic       | Stack                  | Memory                |
             * --------------------------------------------------------------------------|
             *                                                                           |
             * ::: keep some values in stack ::::::::::::::::::::::::::::::::::::::::::: |
             * 5f      | PUSH0          | 0                      |                       |
             * 5f      | PUSH0          | 0 0                    |                       |
             *                                                                           |
             * ::: copy calldata to memory ::::::::::::::::::::::::::::::::::::::::::::: |
             * 36      | CALLDATASIZE   | cds 0 0                |                       |
             * 5f      | PUSH0          | 0 cds 0 0              |                       |
             * 5f      | PUSH0          | 0 0 cds 0 0            |                       |
             * 37      | CALLDATACOPY   | 0 0                    | [0..cds): calldata    |
             *                                                                           |
             * ::: delegate call to the implementation contract :::::::::::::::::::::::: |
             * 36      | CALLDATASIZE   | cds 0 0                | [0..cds): calldata    |
             * 5f      | PUSH0          | 0 cds 0 0              | [0..cds): calldata    |
             * 73 addr | PUSH20 addr    | addr 0 cds 0 0         | [0..cds): calldata    |
             * 5a      | GAS            | gas addr 0 cds 0 0     | [0..cds): calldata    |
             * f4      | DELEGATECALL   | success                | [0..cds): calldata    |
             *                                                                           |
             * ::: copy return data to memory :::::::::::::::::::::::::::::::::::::::::: |
             * 3d      | RETURNDATASIZE | rds success            | [0..cds): calldata    |
             * 5f      | PUSH0          | 0 rds success          | [0..cds): calldata    |
             * 5f      | PUSH0          | 0 0 rds success        | [0..cds): calldata    |
             * 3e      | RETURNDATACOPY | success                | [0..rds): returndata  |
             *                                                                           |
             * 60 0x29 | PUSH1 0x29     | 0x29 success           | [0..rds): returndata  |
             * 57      | JUMPI          |                        | [0..rds): returndata  |
             *                                                                           |
             * ::: revert :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d      | RETURNDATASIZE | rds                    | [0..rds): returndata  |
             * 5f      | PUSH0          | 0 rds                  | [0..rds): returndata  |
             * fd      | REVERT         |                        | [0..rds): returndata  |
             *                                                                           |
             * ::: return :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 5b      | JUMPDEST       |                        | [0..rds): returndata  |
             * 3d      | RETURNDATASIZE | rds                    | [0..rds): returndata  |
             * 5f      | PUSH0          | 0 rds                  | [0..rds): returndata  |
             * f3      | RETURN         |                        | [0..rds): returndata  |
             * --------------------------------------------------------------------------+
             */
            mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16
            mstore(0x14, implementation) // 20
            mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
            instance := create(value, 0x0e, 0x36)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x24, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`.
    function cloneDeterministic_PUSH0(address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = cloneDeterministic_PUSH0(0, implementation, salt);
    }

    /// @dev Deploys a deterministic PUSH0 clone of `implementation` with `salt`.
    /// Deposits `value` ETH during deployment.
    function cloneDeterministic_PUSH0(uint256 value, address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16
            mstore(0x14, implementation) // 20
            mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
            instance := create2(value, 0x0e, 0x36, salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x24, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the initialization code of the PUSH0 clone of `implementation`.
    function initCode_PUSH0(address implementation) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            mstore(add(result, 0x40), 0x5af43d5f5f3e6029573d5ffd5b3d5ff300000000000000000000) // 16
            mstore(add(result, 0x26), implementation) // 20
            mstore(add(result, 0x12), 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
            mstore(result, 0x36) // Store the length.
            mstore(0x40, add(result, 0x60)) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the PUSH0 clone of `implementation`.
    /// Used for mining vanity addresses with create2crunch.
    function initCodeHash_PUSH0(address implementation) internal pure returns (bytes32 hash) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x24, 0x5af43d5f5f3e6029573d5ffd5b3d5ff3) // 16
            mstore(0x14, implementation) // 20
            mstore(0x00, 0x602d5f8160095f39f35f5f365f5f37365f73) // 9 + 9
            hash := keccak256(0x0e, 0x36)
            mstore(0x24, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Returns the address of the deterministic PUSH0 clone of `implementation`,
    /// with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddress_PUSH0(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes32 hash = initCodeHash_PUSH0(implementation);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*           CLONES WITH IMMUTABLE ARGS OPERATIONS            */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // Note: This implementation of CWIA differs from the original implementation.
    // If the calldata is empty, it will emit a `ReceiveETH(uint256)` event and skip the `DELEGATECALL`.

    /// @dev Deploys a clone of `implementation` with immutable arguments encoded in `data`.
    function clone(address implementation, bytes memory data) internal returns (address instance) {
        instance = clone(0, implementation, data);
    }

    /// @dev Deploys a clone of `implementation` with immutable arguments encoded in `data`.
    /// Deposits `value` ETH during deployment.
    function clone(uint256 value, address implementation, bytes memory data)
        internal
        returns (address instance)
    {
        assembly {
            // Compute the boundaries of the data and cache the memory slots around it.
            let mBefore3 := mload(sub(data, 0x60))
            let mBefore2 := mload(sub(data, 0x40))
            let mBefore1 := mload(sub(data, 0x20))
            let dataLength := mload(data)
            let dataEnd := add(add(data, 0x20), dataLength)
            let mAfter1 := mload(dataEnd)

            // +2 bytes for telling how much data there is appended to the call.
            let extraLength := add(dataLength, 2)
            // The `creationSize` is `extraLength + 108`
            // The `runSize` is `creationSize - 10`.

            /**
             * ---------------------------------------------------------------------------------------------------+
             * CREATION (10 bytes)                                                                                |
             * ---------------------------------------------------------------------------------------------------|
             * Opcode     | Mnemonic          | Stack     | Memory                                                |
             * ---------------------------------------------------------------------------------------------------|
             * 61 runSize | PUSH2 runSize     | r         |                                                       |
             * 3d         | RETURNDATASIZE    | 0 r       |                                                       |
             * 81         | DUP2              | r 0 r     |                                                       |
             * 60 offset  | PUSH1 offset      | o r 0 r   |                                                       |
             * 3d         | RETURNDATASIZE    | 0 o r 0 r |                                                       |
             * 39         | CODECOPY          | 0 r       | [0..runSize): runtime code                            |
             * f3         | RETURN            |           | [0..runSize): runtime code                            |
             * ---------------------------------------------------------------------------------------------------|
             * RUNTIME (98 bytes + extraLength)                                                                   |
             * ---------------------------------------------------------------------------------------------------|
             * Opcode   | Mnemonic       | Stack                    | Memory                                      |
             * ---------------------------------------------------------------------------------------------------|
             *                                                                                                    |
             * ::: if no calldata, emit event & return w/o `DELEGATECALL` ::::::::::::::::::::::::::::::::::::::: |
             * 36       | CALLDATASIZE   | cds                      |                                             |
             * 60 0x2c  | PUSH1 0x2c     | 0x2c cds                 |                                             |
             * 57       | JUMPI          |                          |                                             |
             * 34       | CALLVALUE      | cv                       |                                             |
             * 3d       | RETURNDATASIZE | 0 cv                     |                                             |
             * 52       | MSTORE         |                          | [0..0x20): callvalue                        |
             * 7f sig   | PUSH32 0x9e..  | sig                      | [0..0x20): callvalue                        |
             * 59       | MSIZE          | 0x20 sig                 | [0..0x20): callvalue                        |
             * 3d       | RETURNDATASIZE | 0 0x20 sig               | [0..0x20): callvalue                        |
             * a1       | LOG1           |                          | [0..0x20): callvalue                        |
             * 00       | STOP           |                          | [0..0x20): callvalue                        |
             * 5b       | JUMPDEST       |                          |                                             |
             *                                                                                                    |
             * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 36       | CALLDATASIZE   | cds                      |                                             |
             * 3d       | RETURNDATASIZE | 0 cds                    |                                             |
             * 3d       | RETURNDATASIZE | 0 0 cds                  |                                             |
             * 37       | CALLDATACOPY   |                          | [0..cds): calldata                          |
             *                                                                                                    |
             * ::: keep some values in stack :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d       | RETURNDATASIZE | 0                        | [0..cds): calldata                          |
             * 3d       | RETURNDATASIZE | 0 0                      | [0..cds): calldata                          |
             * 3d       | RETURNDATASIZE | 0 0 0                    | [0..cds): calldata                          |
             * 3d       | RETURNDATASIZE | 0 0 0 0                  | [0..cds): calldata                          |
             * 61 extra | PUSH2 extra    | e 0 0 0 0                | [0..cds): calldata                          |
             *                                                                                                    |
             * ::: copy extra data to memory :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 80       | DUP1           | e e 0 0 0 0              | [0..cds): calldata                          |
             * 60 0x62  | PUSH1 0x62     | 0x62 e e 0 0 0 0         | [0..cds): calldata                          |
             * 36       | CALLDATASIZE   | cds 0x62 e e 0 0 0 0     | [0..cds): calldata                          |
             * 39       | CODECOPY       | e 0 0 0 0                | [0..cds): calldata, [cds..cds+e): extraData |
             *                                                                                                    |
             * ::: delegate call to the implementation contract ::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 36       | CALLDATASIZE   | cds e 0 0 0 0            | [0..cds): calldata, [cds..cds+e): extraData |
             * 01       | ADD            | cds+e 0 0 0 0            | [0..cds): calldata, [cds..cds+e): extraData |
             * 3d       | RETURNDATASIZE | 0 cds+e 0 0 0 0          | [0..cds): calldata, [cds..cds+e): extraData |
             * 73 addr  | PUSH20 addr    | addr 0 cds+e 0 0 0 0     | [0..cds): calldata, [cds..cds+e): extraData |
             * 5a       | GAS            | gas addr 0 cds+e 0 0 0 0 | [0..cds): calldata, [cds..cds+e): extraData |
             * f4       | DELEGATECALL   | success 0 0              | [0..cds): calldata, [cds..cds+e): extraData |
             *                                                                                                    |
             * ::: copy return data to memory ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d       | RETURNDATASIZE | rds success 0 0          | [0..cds): calldata, [cds..cds+e): extraData |
             * 3d       | RETURNDATASIZE | rds rds success 0 0      | [0..cds): calldata, [cds..cds+e): extraData |
             * 93       | SWAP4          | 0 rds success 0 rds      | [0..cds): calldata, [cds..cds+e): extraData |
             * 80       | DUP1           | 0 0 rds success 0 rds    | [0..cds): calldata, [cds..cds+e): extraData |
             * 3e       | RETURNDATACOPY | success 0 rds            | [0..rds): returndata                        |
             *                                                                                                    |
             * 60 0x60  | PUSH1 0x60     | 0x60 success 0 rds       | [0..rds): returndata                        |
             * 57       | JUMPI          | 0 rds                    | [0..rds): returndata                        |
             *                                                                                                    |
             * ::: revert ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * fd       | REVERT         |                          | [0..rds): returndata                        |
             *                                                                                                    |
             * ::: return ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 5b       | JUMPDEST       | 0 rds                    | [0..rds): returndata                        |
             * f3       | RETURN         |                          | [0..rds): returndata                        |
             * ---------------------------------------------------------------------------------------------------+
             */
            mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the bytecode before the data.
            mstore(sub(data, 0x0d), implementation) // Write the address of the implementation.
            // Write the rest of the bytecode.
            mstore(
                sub(data, 0x21),
                or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73)
            )
            // `keccak256("ReceiveETH(uint256)")`
            mstore(
                sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff
            )
            mstore(
                // Do a out-of-gas revert if `extraLength` is too big. 0xffff - 0x62 + 0x01 = 0xff9e.
                // The actual EVM limit may be smaller and may change over time.
                sub(data, add(0x59, lt(extraLength, 0xff9e))),
                or(shl(0x78, add(extraLength, 0x62)), 0xfd6100003d81600a3d39f336602c57343d527f)
            )
            mstore(dataEnd, shl(0xf0, extraLength))

            instance := create(value, sub(data, 0x4c), add(extraLength, 0x6c))
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }

            // Restore the overwritten memory surrounding `data`.
            mstore(dataEnd, mAfter1)
            mstore(data, dataLength)
            mstore(sub(data, 0x20), mBefore1)
            mstore(sub(data, 0x40), mBefore2)
            mstore(sub(data, 0x60), mBefore3)
        }
    }

    /// @dev Deploys a deterministic clone of `implementation`
    /// with immutable arguments encoded in `data` and `salt`.
    function cloneDeterministic(address implementation, bytes memory data, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = cloneDeterministic(0, implementation, data, salt);
    }

    /// @dev Deploys a deterministic clone of `implementation`
    /// with immutable arguments encoded in `data` and `salt`.
    function cloneDeterministic(
        uint256 value,
        address implementation,
        bytes memory data,
        bytes32 salt
    ) internal returns (address instance) {
        assembly {
            // Compute the boundaries of the data and cache the memory slots around it.
            let mBefore3 := mload(sub(data, 0x60))
            let mBefore2 := mload(sub(data, 0x40))
            let mBefore1 := mload(sub(data, 0x20))
            let dataLength := mload(data)
            let dataEnd := add(add(data, 0x20), dataLength)
            let mAfter1 := mload(dataEnd)

            // +2 bytes for telling how much data there is appended to the call.
            let extraLength := add(dataLength, 2)

            mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the bytecode before the data.
            mstore(sub(data, 0x0d), implementation) // Write the address of the implementation.
            // Write the rest of the bytecode.
            mstore(
                sub(data, 0x21),
                or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73)
            )
            // `keccak256("ReceiveETH(uint256)")`
            mstore(
                sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff
            )
            mstore(
                // Do a out-of-gas revert if `extraLength` is too big. 0xffff - 0x62 + 0x01 = 0xff9e.
                // The actual EVM limit may be smaller and may change over time.
                sub(data, add(0x59, lt(extraLength, 0xff9e))),
                or(shl(0x78, add(extraLength, 0x62)), 0xfd6100003d81600a3d39f336602c57343d527f)
            )
            mstore(dataEnd, shl(0xf0, extraLength))

            instance := create2(value, sub(data, 0x4c), add(extraLength, 0x6c), salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }

            // Restore the overwritten memory surrounding `data`.
            mstore(dataEnd, mAfter1)
            mstore(data, dataLength)
            mstore(sub(data, 0x20), mBefore1)
            mstore(sub(data, 0x40), mBefore2)
            mstore(sub(data, 0x60), mBefore3)
        }
    }

    /// @dev Returns the initialization code hash of the clone of `implementation`
    /// using immutable arguments encoded in `data`.
    function initCode(address implementation, bytes memory data)
        internal
        pure
        returns (bytes memory result)
    {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            let dataLength := mload(data)

            // Do a out-of-gas revert if `dataLength` is too big. 0xffff - 0x02 - 0x62 = 0xff9b.
            // The actual EVM limit may be smaller and may change over time.
            returndatacopy(returndatasize(), returndatasize(), gt(dataLength, 0xff9b))

            let o := add(result, 0x8c)
            let end := add(o, dataLength)

            // Copy the `data` into `result`.
            for { let d := sub(add(data, 0x20), o) } 1 {} {
                mstore(o, mload(add(o, d)))
                o := add(o, 0x20)
                if iszero(lt(o, end)) { break }
            }

            // +2 bytes for telling how much data there is appended to the call.
            let extraLength := add(dataLength, 2)

            mstore(add(result, 0x6c), 0x5af43d3d93803e606057fd5bf3) // Write the bytecode before the data.
            mstore(add(result, 0x5f), implementation) // Write the address of the implementation.
            // Write the rest of the bytecode.
            mstore(
                add(result, 0x4b),
                or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73)
            )
            // `keccak256("ReceiveETH(uint256)")`
            mstore(
                add(result, 0x32),
                0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff
            )
            mstore(
                add(result, 0x12),
                or(shl(0x78, add(extraLength, 0x62)), 0x6100003d81600a3d39f336602c57343d527f)
            )
            mstore(end, shl(0xf0, extraLength))
            mstore(add(end, 0x02), 0) // Zeroize the slot after the result.
            mstore(result, add(extraLength, 0x6c)) // Store the length.
            mstore(0x40, add(0x22, end)) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the clone of `implementation`
    /// using immutable arguments encoded in `data`.
    /// Used for mining vanity addresses with create2crunch.
    function initCodeHash(address implementation, bytes memory data)
        internal
        pure
        returns (bytes32 hash)
    {
        assembly {
            // Compute the boundaries of the data and cache the memory slots around it.
            let mBefore3 := mload(sub(data, 0x60))
            let mBefore2 := mload(sub(data, 0x40))
            let mBefore1 := mload(sub(data, 0x20))
            let dataLength := mload(data)
            let dataEnd := add(add(data, 0x20), dataLength)
            let mAfter1 := mload(dataEnd)

            // Do a out-of-gas revert if `dataLength` is too big. 0xffff - 0x02 - 0x62 = 0xff9b.
            // The actual EVM limit may be smaller and may change over time.
            returndatacopy(returndatasize(), returndatasize(), gt(dataLength, 0xff9b))

            // +2 bytes for telling how much data there is appended to the call.
            let extraLength := add(dataLength, 2)

            mstore(data, 0x5af43d3d93803e606057fd5bf3) // Write the bytecode before the data.
            mstore(sub(data, 0x0d), implementation) // Write the address of the implementation.
            // Write the rest of the bytecode.
            mstore(
                sub(data, 0x21),
                or(shl(0x48, extraLength), 0x593da1005b363d3d373d3d3d3d610000806062363936013d73)
            )
            // `keccak256("ReceiveETH(uint256)")`
            mstore(
                sub(data, 0x3a), 0x9e4ac34f21c619cefc926c8bd93b54bf5a39c7ab2127a895af1cc0691d7e3dff
            )
            mstore(
                sub(data, 0x5a),
                or(shl(0x78, add(extraLength, 0x62)), 0x6100003d81600a3d39f336602c57343d527f)
            )
            mstore(dataEnd, shl(0xf0, extraLength))

            hash := keccak256(sub(data, 0x4c), add(extraLength, 0x6c))

            // Restore the overwritten memory surrounding `data`.
            mstore(dataEnd, mAfter1)
            mstore(data, dataLength)
            mstore(sub(data, 0x20), mBefore1)
            mstore(sub(data, 0x40), mBefore2)
            mstore(sub(data, 0x60), mBefore3)
        }
    }

    /// @dev Returns the address of the deterministic clone of
    /// `implementation` using immutable arguments encoded in `data`, with `salt`, by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddress(
        address implementation,
        bytes memory data,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes32 hash = initCodeHash(implementation, data);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*              MINIMAL ERC1967 PROXY OPERATIONS              */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // Note: The ERC1967 proxy here is intended to be upgraded with UUPS.
    // This is NOT the same as ERC1967Factory's transparent proxy, which includes admin logic.

    /// @dev Deploys a minimal ERC1967 proxy with `implementation`.
    function deployERC1967(address implementation) internal returns (address instance) {
        instance = deployERC1967(0, implementation);
    }

    /// @dev Deploys a minimal ERC1967 proxy with `implementation`.
    /// Deposits `value` ETH during deployment.
    function deployERC1967(uint256 value, address implementation)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            /**
             * ---------------------------------------------------------------------------------+
             * CREATION (34 bytes)                                                              |
             * ---------------------------------------------------------------------------------|
             * Opcode     | Mnemonic       | Stack            | Memory                          |
             * ---------------------------------------------------------------------------------|
             * 60 runSize | PUSH1 runSize  | r                |                                 |
             * 3d         | RETURNDATASIZE | 0 r              |                                 |
             * 81         | DUP2           | r 0 r            |                                 |
             * 60 offset  | PUSH1 offset   | o r 0 r          |                                 |
             * 3d         | RETURNDATASIZE | 0 o r 0 r        |                                 |
             * 39         | CODECOPY       | 0 r              | [0..runSize): runtime code      |
             * 73 impl    | PUSH20 impl    | impl 0 r         | [0..runSize): runtime code      |
             * 60 slotPos | PUSH1 slotPos  | slotPos impl 0 r | [0..runSize): runtime code      |
             * 51         | MLOAD          | slot impl 0 r    | [0..runSize): runtime code      |
             * 55         | SSTORE         | 0 r              | [0..runSize): runtime code      |
             * f3         | RETURN         |                  | [0..runSize): runtime code      |
             * ---------------------------------------------------------------------------------|
             * RUNTIME (61 bytes)                                                               |
             * ---------------------------------------------------------------------------------|
             * Opcode     | Mnemonic       | Stack            | Memory                          |
             * ---------------------------------------------------------------------------------|
             *                                                                                  |
             * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 36         | CALLDATASIZE   | cds              |                                 |
             * 3d         | RETURNDATASIZE | 0 cds            |                                 |
             * 3d         | RETURNDATASIZE | 0 0 cds          |                                 |
             * 37         | CALLDATACOPY   |                  | [0..calldatasize): calldata     |
             *                                                                                  |
             * ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | 0                |                                 |
             * 3d         | RETURNDATASIZE | 0 0              |                                 |
             * 36         | CALLDATASIZE   | cds 0 0          | [0..calldatasize): calldata     |
             * 3d         | RETURNDATASIZE | 0 cds 0 0        | [0..calldatasize): calldata     |
             * 7f slot    | PUSH32 slot    | s 0 cds 0 0      | [0..calldatasize): calldata     |
             * 54         | SLOAD          | i 0 cds 0 0      | [0..calldatasize): calldata     |
             * 5a         | GAS            | g i 0 cds 0 0    | [0..calldatasize): calldata     |
             * f4         | DELEGATECALL   | succ             | [0..calldatasize): calldata     |
             *                                                                                  |
             * ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | rds succ         | [0..calldatasize): calldata     |
             * 60 0x00    | PUSH1 0x00     | 0 rds succ       | [0..calldatasize): calldata     |
             * 80         | DUP1           | 0 0 rds succ     | [0..calldatasize): calldata     |
             * 3e         | RETURNDATACOPY | succ             | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
             * 60 0x38    | PUSH1 0x38     | dest succ        | [0..returndatasize): returndata |
             * 57         | JUMPI          |                  | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | rds              | [0..returndatasize): returndata |
             * 60 0x00    | PUSH1 0x00     | 0 rds            | [0..returndatasize): returndata |
             * fd         | REVERT         |                  | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
             * 5b         | JUMPDEST       |                  | [0..returndatasize): returndata |
             * 3d         | RETURNDATASIZE | rds              | [0..returndatasize): returndata |
             * 60 0x00    | PUSH1 0x00     | 0 rds            | [0..returndatasize): returndata |
             * f3         | RETURN         |                  | [0..returndatasize): returndata |
             * ---------------------------------------------------------------------------------+
             */
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
            mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
            mstore(0x20, 0x6009)
            mstore(0x1e, implementation)
            mstore(0x0a, 0x603d3d8160223d3973)
            instance := create(value, 0x21, 0x5f)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
    function deployDeterministicERC1967(address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = deployDeterministicERC1967(0, implementation, salt);
    }

    /// @dev Deploys a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
    /// Deposits `value` ETH during deployment.
    function deployDeterministicERC1967(uint256 value, address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
            mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
            mstore(0x20, 0x6009)
            mstore(0x1e, implementation)
            mstore(0x0a, 0x603d3d8160223d3973)
            instance := create2(value, 0x21, 0x5f, salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967(address implementation, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        return createDeterministicERC1967(0, implementation, salt);
    }

    /// @dev Creates a deterministic minimal ERC1967 proxy with `implementation` and `salt`.
    /// Deposits `value` ETH during deployment.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967(uint256 value, address implementation, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
            mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
            mstore(0x20, 0x6009)
            mstore(0x1e, implementation)
            mstore(0x0a, 0x603d3d8160223d3973)
            // Compute and store the bytecode hash.
            mstore(add(m, 0x35), keccak256(0x21, 0x5f))
            mstore(m, shl(88, address()))
            mstore8(m, 0xff) // Write the prefix.
            mstore(add(m, 0x15), salt)
            instance := keccak256(m, 0x55)
            for {} 1 {} {
                if iszero(extcodesize(instance)) {
                    instance := create2(value, 0x21, 0x5f, salt)
                    if iszero(instance) {
                        mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                        revert(0x1c, 0x04)
                    }
                    break
                }
                alreadyDeployed := 1
                if iszero(value) { break }
                if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
                    mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                    revert(0x1c, 0x04)
                }
                break
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Returns the initialization code of the minimal ERC1967 proxy of `implementation`.
    function initCodeERC1967(address implementation) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            mstore(
                add(result, 0x60),
                0x3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f300
            )
            mstore(
                add(result, 0x40),
                0x55f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076cc
            )
            mstore(add(result, 0x20), or(shl(24, implementation), 0x600951))
            mstore(add(result, 0x09), 0x603d3d8160223d3973)
            mstore(result, 0x5f) // Store the length.
            mstore(0x40, add(result, 0x80)) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the minimal ERC1967 proxy of `implementation`.
    /// Used for mining vanity addresses with create2crunch.
    function initCodeHashERC1967(address implementation) internal pure returns (bytes32 hash) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0xcc3735a920a3ca505d382bbc545af43d6000803e6038573d6000fd5b3d6000f3)
            mstore(0x40, 0x5155f3363d3d373d3d363d7f360894a13ba1a3210667c828492db98dca3e2076)
            mstore(0x20, 0x6009)
            mstore(0x1e, implementation)
            mstore(0x0a, 0x603d3d8160223d3973)
            hash := keccak256(0x21, 0x5f)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Returns the address of the deterministic ERC1967 proxy of `implementation`,
    /// with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddressERC1967(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes32 hash = initCodeHashERC1967(implementation);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                 ERC1967I PROXY OPERATIONS                  */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    // Note: This proxy has a special code path that activates if `calldatasize() == 1`.
    // This code path skips the delegatecall and directly returns the `implementation` address.
    // The returned implementation is guaranteed to be valid if the keccak256 of the
    // proxy's code is equal to `ERC1967I_CODE_HASH`.

    /// @dev Deploys a minimal ERC1967I proxy with `implementation`.
    function deployERC1967I(address implementation) internal returns (address instance) {
        instance = deployERC1967I(0, implementation);
    }

    /// @dev Deploys a ERC1967I proxy with `implementation`.
    /// Deposits `value` ETH during deployment.
    function deployERC1967I(uint256 value, address implementation)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            /**
             * ---------------------------------------------------------------------------------+
             * CREATION (34 bytes)                                                              |
             * ---------------------------------------------------------------------------------|
             * Opcode     | Mnemonic       | Stack            | Memory                          |
             * ---------------------------------------------------------------------------------|
             * 60 runSize | PUSH1 runSize  | r                |                                 |
             * 3d         | RETURNDATASIZE | 0 r              |                                 |
             * 81         | DUP2           | r 0 r            |                                 |
             * 60 offset  | PUSH1 offset   | o r 0 r          |                                 |
             * 3d         | RETURNDATASIZE | 0 o r 0 r        |                                 |
             * 39         | CODECOPY       | 0 r              | [0..runSize): runtime code      |
             * 73 impl    | PUSH20 impl    | impl 0 r         | [0..runSize): runtime code      |
             * 60 slotPos | PUSH1 slotPos  | slotPos impl 0 r | [0..runSize): runtime code      |
             * 51         | MLOAD          | slot impl 0 r    | [0..runSize): runtime code      |
             * 55         | SSTORE         | 0 r              | [0..runSize): runtime code      |
             * f3         | RETURN         |                  | [0..runSize): runtime code      |
             * ---------------------------------------------------------------------------------|
             * RUNTIME (82 bytes)                                                               |
             * ---------------------------------------------------------------------------------|
             * Opcode     | Mnemonic       | Stack            | Memory                          |
             * ---------------------------------------------------------------------------------|
             *                                                                                  |
             * ::: check calldatasize ::::::::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 36         | CALLDATASIZE   | cds              |                                 |
             * 58         | PC             | 1 cds            |                                 |
             * 14         | EQ             | eqs              |                                 |
             * 60 0x43    | PUSH1 0x43     | dest eqs         |                                 |
             * 57         | JUMPI          |                  |                                 |
             *                                                                                  |
             * ::: copy calldata to memory :::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 36         | CALLDATASIZE   | cds              |                                 |
             * 3d         | RETURNDATASIZE | 0 cds            |                                 |
             * 3d         | RETURNDATASIZE | 0 0 cds          |                                 |
             * 37         | CALLDATACOPY   |                  | [0..calldatasize): calldata     |
             *                                                                                  |
             * ::: delegatecall to implementation ::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | 0                |                                 |
             * 3d         | RETURNDATASIZE | 0 0              |                                 |
             * 36         | CALLDATASIZE   | cds 0 0          | [0..calldatasize): calldata     |
             * 3d         | RETURNDATASIZE | 0 cds 0 0        | [0..calldatasize): calldata     |
             * 7f slot    | PUSH32 slot    | s 0 cds 0 0      | [0..calldatasize): calldata     |
             * 54         | SLOAD          | i 0 cds 0 0      | [0..calldatasize): calldata     |
             * 5a         | GAS            | g i 0 cds 0 0    | [0..calldatasize): calldata     |
             * f4         | DELEGATECALL   | succ             | [0..calldatasize): calldata     |
             *                                                                                  |
             * ::: copy returndata to memory :::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | rds succ         | [0..calldatasize): calldata     |
             * 60 0x00    | PUSH1 0x00     | 0 rds succ       | [0..calldatasize): calldata     |
             * 80         | DUP1           | 0 0 rds succ     | [0..calldatasize): calldata     |
             * 3e         | RETURNDATACOPY | succ             | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: branch on delegatecall status :::::::::::::::::::::::::::::::::::::::::::::: |
             * 60 0x3E    | PUSH1 0x3E     | dest succ        | [0..returndatasize): returndata |
             * 57         | JUMPI          |                  | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: delegatecall failed, revert :::::::::::::::::::::::::::::::::::::::::::::::: |
             * 3d         | RETURNDATASIZE | rds              | [0..returndatasize): returndata |
             * 60 0x00    | PUSH1 0x00     | 0 rds            | [0..returndatasize): returndata |
             * fd         | REVERT         |                  | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: delegatecall succeeded, return ::::::::::::::::::::::::::::::::::::::::::::: |
             * 5b         | JUMPDEST       |                  | [0..returndatasize): returndata |
             * 3d         | RETURNDATASIZE | rds              | [0..returndatasize): returndata |
             * 60 0x00    | PUSH1 0x00     | 0 rds            | [0..returndatasize): returndata |
             * f3         | RETURN         |                  | [0..returndatasize): returndata |
             *                                                                                  |
             * ::: implementation , return :::::::::::::::::::::::::::::::::::::::::::::::::::: |
             * 5b         | JUMPDEST       |                  |                                 |
             * 60 0x20    | PUSH1 0x20     | 32               |                                 |
             * 60 0x0F    | PUSH1 0x0F     | o 32             |                                 |
             * 3d         | RETURNDATASIZE | 0 o 32           |                                 |
             * 39         | CODECOPY       |                  | [0..32): implementation slot    |
             * 3d         | RETURNDATASIZE | 0                | [0..32): implementation slot    |
             * 51         | MLOAD          | slot             | [0..32): implementation slot    |
             * 54         | SLOAD          | impl             | [0..32): implementation slot    |
             * 3d         | RETURNDATASIZE | 0 impl           | [0..32): implementation slot    |
             * 52         | MSTORE         |                  | [0..32): implementation address |
             * 59         | MSIZE          | 32               | [0..32): implementation address |
             * 3d         | RETURNDATASIZE | 0 32             | [0..32): implementation address |
             * f3         | RETURN         |                  | [0..32): implementation address |
             *                                                                                  |
             * ---------------------------------------------------------------------------------+
             */
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
            mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
            mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
            mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
            instance := create(value, 0x0c, 0x74)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Deploys a deterministic ERC1967I proxy with `implementation` and `salt`.
    function deployDeterministicERC1967I(address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        instance = deployDeterministicERC1967I(0, implementation, salt);
    }

    /// @dev Deploys a deterministic ERC1967I proxy with `implementation` and `salt`.
    /// Deposits `value` ETH during deployment.
    function deployDeterministicERC1967I(uint256 value, address implementation, bytes32 salt)
        internal
        returns (address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
            mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
            mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
            mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
            instance := create2(value, 0x0c, 0x74, salt)
            if iszero(instance) {
                mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                revert(0x1c, 0x04)
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Creates a deterministic ERC1967I proxy with `implementation` and `salt`.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967I(address implementation, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        return createDeterministicERC1967I(0, implementation, salt);
    }

    /// @dev Creates a deterministic ERC1967I proxy with `implementation` and `salt`.
    /// Deposits `value` ETH during deployment.
    /// Note: This method is intended for use in ERC4337 factories,
    /// which are expected to NOT revert if the proxy is already deployed.
    function createDeterministicERC1967I(uint256 value, address implementation, bytes32 salt)
        internal
        returns (bool alreadyDeployed, address instance)
    {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
            mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
            mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
            mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
            // Compute and store the bytecode hash.
            mstore(add(m, 0x35), keccak256(0x0c, 0x74))
            mstore(m, shl(88, address()))
            mstore8(m, 0xff) // Write the prefix.
            mstore(add(m, 0x15), salt)
            instance := keccak256(m, 0x55)
            for {} 1 {} {
                if iszero(extcodesize(instance)) {
                    instance := create2(value, 0x0c, 0x74, salt)
                    if iszero(instance) {
                        mstore(0x00, 0x30116425) // `DeploymentFailed()`.
                        revert(0x1c, 0x04)
                    }
                    break
                }
                alreadyDeployed := 1
                if iszero(value) { break }
                if iszero(call(gas(), instance, value, codesize(), 0x00, codesize(), 0x00)) {
                    mstore(0x00, 0xb12d13eb) // `ETHTransferFailed()`.
                    revert(0x1c, 0x04)
                }
                break
            }
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Returns the initialization code of the minimal ERC1967 proxy of `implementation`.
    function initCodeERC1967I(address implementation) internal pure returns (bytes memory result) {
        /// @solidity memory-safe-assembly
        assembly {
            result := mload(0x40)
            mstore(
                add(result, 0x74),
                0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3
            )
            mstore(
                add(result, 0x54),
                0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4
            )
            mstore(add(result, 0x34), 0x600f5155f3365814604357363d3d373d3d363d7f360894)
            mstore(add(result, 0x1d), implementation)
            mstore(add(result, 0x09), 0x60523d8160223d3973)
            mstore(add(result, 0x94), 0)
            mstore(result, 0x74) // Store the length.
            mstore(0x40, add(result, 0xa0)) // Allocate memory.
        }
    }

    /// @dev Returns the initialization code hash of the minimal ERC1967 proxy of `implementation`.
    /// Used for mining vanity addresses with create2crunch.
    function initCodeHashERC1967I(address implementation) internal pure returns (bytes32 hash) {
        /// @solidity memory-safe-assembly
        assembly {
            let m := mload(0x40) // Cache the free memory pointer.
            mstore(0x60, 0x3d6000803e603e573d6000fd5b3d6000f35b6020600f3d393d51543d52593df3)
            mstore(0x40, 0xa13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc545af4)
            mstore(0x20, 0x600f5155f3365814604357363d3d373d3d363d7f360894)
            mstore(0x09, or(shl(160, 0x60523d8160223d3973), shr(96, shl(96, implementation))))
            hash := keccak256(0x0c, 0x74)
            mstore(0x40, m) // Restore the free memory pointer.
            mstore(0x60, 0) // Restore the zero slot.
        }
    }

    /// @dev Returns the address of the deterministic ERC1967I proxy of `implementation`,
    /// with `salt` by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddressERC1967I(
        address implementation,
        bytes32 salt,
        address deployer
    ) internal pure returns (address predicted) {
        bytes32 hash = initCodeHashERC1967I(implementation);
        predicted = predictDeterministicAddress(hash, salt, deployer);
    }

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                      OTHER OPERATIONS                      */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @dev Returns the address when a contract with initialization code hash,
    /// `hash`, is deployed with `salt`, by `deployer`.
    /// Note: The returned result has dirty upper 96 bits. Please clean if used in assembly.
    function predictDeterministicAddress(bytes32 hash, bytes32 salt, address deployer)
        internal
        pure
        returns (address predicted)
    {
        /// @solidity memory-safe-assembly
        assembly {
            // Compute and store the bytecode hash.
            mstore8(0x00, 0xff) // Write the prefix.
            mstore(0x35, hash)
            mstore(0x01, shl(96, deployer))
            mstore(0x15, salt)
            predicted := keccak256(0x00, 0x55)
            mstore(0x35, 0) // Restore the overwritten part of the free memory pointer.
        }
    }

    /// @dev Requires that `salt` starts with either the zero address or `by`.
    function checkStartsWith(bytes32 salt, address by) internal pure {
        /// @solidity memory-safe-assembly
        assembly {
            // If the salt does not start with the zero address or `by`.
            if iszero(or(iszero(shr(96, salt)), eq(shr(96, shl(96, by)), shr(96, salt)))) {
                mstore(0x00, 0x0c4549ef) // `SaltDoesNotStartWith()`.
                revert(0x1c, 0x04)
            }
        }
    }
}

Settings
{
  "remappings": [
    "forge-std/=lib/forge-std/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "solmate/=lib/solmate/src/",
    "GeneralisedIncentives/=lib/GeneralisedIncentives/",
    "@lazyledger/protobuf3-solidity-lib/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/protobuf3-solidity-lib/",
    "@openzeppelin-upgradeable/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/openzeppelin-contracts-upgradeable/",
    "@openzeppelin/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/openzeppelin-contracts/",
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "base64/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/base64/",
    "clones-with-immutable-args/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/optimism/packages/contracts-bedrock/lib/clones-with-immutable-args/src/",
    "erc4626-tests/=lib/openzeppelin-contracts/lib/erc4626-tests/",
    "openzeppelin-contracts-upgradeable/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/",
    "openzeppelin/=lib/GeneralisedIncentives/lib/openzeppelin-contracts/contracts/",
    "optimism/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/",
    "proto/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/proto/",
    "protobuf3-solidity-lib/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/protobuf3-solidity-lib/contracts/",
    "safe-contracts/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/optimism/packages/contracts-bedrock/lib/safe-contracts/contracts/",
    "solady/=lib/solady/src/",
    "solmate/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/lib/optimism/packages/contracts-bedrock/lib/solmate/src/",
    "vibc-core-smart-contracts/=lib/GeneralisedIncentives/lib/vibc-core-smart-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 10000
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "abi"
      ]
    }
  },
  "evmVersion": "paris",
  "viaIR": true,
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"internalType":"address","name":"factory_","type":"address"},{"internalType":"address","name":"mathlib_","type":"address"}],"stateMutability":"payable","type":"constructor"},{"inputs":[],"name":"AllowanceOverflow","type":"error"},{"inputs":[],"name":"AllowanceUnderflow","type":"error"},{"inputs":[],"name":"EscrowAlreadyExists","type":"error"},{"inputs":[],"name":"ExceedsSecurityLimit","type":"error"},{"inputs":[],"name":"InsufficientAllowance","type":"error"},{"inputs":[],"name":"InsufficientBalance","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidPermit","type":"error"},{"inputs":[],"name":"NotEnoughGas","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"PermitExpired","type":"error"},{"inputs":[],"name":"Reentrancy","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"ReturnInsufficient","type":"error"},{"inputs":[],"name":"TotalSupplyOverflow","type":"error"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"UnusedUnitsAfterWithdrawal","type":"error"},{"inputs":[],"name":"VaultNotConnected","type":"error"},{"inputs":[],"name":"WithdrawRatioNotZero","type":"error"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"owner","type":"address"},{"indexed":true,"internalType":"address","name":"spender","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[],"name":"FinishSetup","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"account","type":"address"},{"indexed":false,"internalType":"address","name":"fromAsset","type":"address"},{"indexed":false,"internalType":"address","name":"toAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"}],"name":"LocalSwap","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"fromVault","type":"bytes"},{"indexed":false,"internalType":"address","name":"toAccount","type":"address"},{"indexed":false,"internalType":"address","name":"toAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"bytes","name":"fromAsset","type":"bytes"},{"indexed":false,"internalType":"uint32","name":"sourceBlockNumberMod","type":"uint32"}],"name":"ReceiveAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"fromVault","type":"bytes"},{"indexed":false,"internalType":"address","name":"toAccount","type":"address"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"toAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"sourceBlockNumberMod","type":"uint256"}],"name":"ReceiveLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"toVault","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"toAccount","type":"bytes"},{"indexed":false,"internalType":"address","name":"fromAsset","type":"address"},{"indexed":false,"internalType":"uint8","name":"toAssetIndex","type":"uint8"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"minOut","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"fee","type":"uint256"},{"indexed":false,"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"}],"name":"SendAsset","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"toAccount","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"escrowToken","type":"address"},{"indexed":false,"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"SendAssetFailure","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"toAccount","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"indexed":false,"internalType":"address","name":"escrowToken","type":"address"},{"indexed":false,"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"SendAssetSuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"toVault","type":"bytes"},{"indexed":false,"internalType":"bytes","name":"toAccount","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"fromAmount","type":"uint256"},{"indexed":false,"internalType":"uint256[2]","name":"minOut","type":"uint256[2]"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"}],"name":"SendLiquidity","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"toAccount","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"SendLiquidityFailure","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"toAccount","type":"bytes"},{"indexed":false,"internalType":"uint256","name":"units","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"indexed":false,"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"SendLiquiditySuccess","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint48","name":"targetTime","type":"uint48"},{"indexed":false,"internalType":"uint256","name":"targetAmplification","type":"uint256"}],"name":"SetAmplification","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"bytes32","name":"channelId","type":"bytes32"},{"indexed":false,"internalType":"bytes","name":"toVault","type":"bytes"},{"indexed":false,"internalType":"bool","name":"newState","type":"bool"}],"name":"SetConnection","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"administrator","type":"address"}],"name":"SetFeeAdministrator","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"fee","type":"uint64"}],"name":"SetGovernanceFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"fee","type":"uint64"}],"name":"SetVaultFee","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint248","name":"targetTime","type":"uint248"},{"indexed":false,"internalType":"uint256[]","name":"targetWeights","type":"uint256[]"}],"name":"SetWeights","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"bytes32","name":"identifier","type":"bytes32"},{"indexed":false,"internalType":"address","name":"toAsset","type":"address"},{"indexed":false,"internalType":"uint256","name":"U","type":"uint256"},{"indexed":false,"internalType":"uint256","name":"purchasedTokens","type":"uint256"}],"name":"SwapUnderwritten","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"amount","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"toAccount","type":"address"},{"indexed":false,"internalType":"uint256","name":"mint","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"assets","type":"uint256[]"}],"name":"VaultDeposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"toAccount","type":"address"},{"indexed":false,"internalType":"uint256","name":"burn","type":"uint256"},{"indexed":false,"internalType":"uint256[]","name":"assets","type":"uint256[]"}],"name":"VaultWithdraw","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"result","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"FACTORY","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"MATHLIB","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_adjustmentTarget","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_chainInterface","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"name":"_escrowLookup","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_escrowedTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_escrowedVaultTokens","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_feeAdministrator","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_governanceFeeShare","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_lastModificationTime","outputs":[{"internalType":"uint48","name":"","type":"uint48"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_maxUnitCapacity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_oneMinusAmp","outputs":[{"internalType":"int64","name":"","type":"int64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_setupMaster","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_targetAmplification","outputs":[{"internalType":"int64","name":"","type":"int64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"_tokenIndexing","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_underwriteEscrowMatchBalance0","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_unitTracker","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"","type":"bytes32"},{"internalType":"bytes","name":"","type":"bytes"}],"name":"_vaultConnection","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"_vaultFee","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"","type":"address"}],"name":"_weight","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fromAsset","type":"address"},{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calcLocalSwap","outputs":[{"internalType":"uint256","name":"output","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"}],"name":"calcReceiveAsset","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"fromAsset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"calcSendAsset","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"computeBalance0","outputs":[{"internalType":"uint256","name":"walpha_0","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"internalType":"address","name":"escrowToken","type":"address"}],"name":"deleteUnderwriteAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"tokenAmounts","type":"uint256[]"},{"internalType":"uint256","name":"minOut","type":"uint256"}],"name":"depositMixed","outputs":[{"internalType":"uint256","name":"vaultTokens","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"factoryOwner","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"finishSetup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getUnitCapacity","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"governanceFeeDestination","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address[]","name":"assets","type":"address[]"},{"internalType":"uint256[]","name":"weights","type":"uint256[]"},{"internalType":"uint64","name":"amp","type":"uint64"},{"internalType":"address","name":"depositor","type":"address"}],"name":"initializeSwapCurves","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"fromAsset","type":"address"},{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"}],"name":"localSwap","outputs":[{"internalType":"uint256","name":"out","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes[]","name":"data","type":"bytes[]"}],"name":"multicall","outputs":[{"internalType":"bytes[]","name":"","type":"bytes[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"name","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"}],"name":"nonces","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"internalType":"address","name":"escrowToken","type":"address"},{"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"onSendAssetFailure","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"internalType":"address","name":"escrowToken","type":"address"},{"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"onSendAssetSuccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"onSendLiquidityFailure","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"onSendLiquiditySuccess","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"value","type":"uint256"},{"internalType":"uint256","name":"deadline","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"permit","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"ready","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"bytes","name":"fromVault","type":"bytes"},{"internalType":"uint256","name":"toAssetIndex","type":"uint256"},{"internalType":"address","name":"toAccount","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"bytes","name":"fromAsset","type":"bytes"},{"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"receiveAsset","outputs":[{"internalType":"uint256","name":"purchasedTokens","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"bytes","name":"fromVault","type":"bytes"},{"internalType":"address","name":"toAccount","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minVaultTokens","type":"uint256"},{"internalType":"uint256","name":"minReferenceAsset","type":"uint256"},{"internalType":"uint256","name":"fromAmount","type":"uint256"},{"internalType":"uint32","name":"blockNumberMod","type":"uint32"}],"name":"receiveLiquidity","outputs":[{"internalType":"uint256","name":"purchasedVaultTokens","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"refundTo","type":"address"},{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"uint256","name":"escrowAmount","type":"uint256"},{"internalType":"address","name":"escrowToken","type":"address"},{"internalType":"bytes32","name":"sourceIdentifier","type":"bytes32"},{"internalType":"bytes","name":"fromVault","type":"bytes"}],"name":"releaseUnderwriteAsset","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"internalType":"bytes","name":"toVault","type":"bytes"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"components":[{"internalType":"uint48","name":"maxGasDelivery","type":"uint48"},{"internalType":"uint48","name":"maxGasAck","type":"uint48"},{"internalType":"address","name":"refundGasTo","type":"address"},{"internalType":"uint96","name":"priceOfDeliveryGas","type":"uint96"},{"internalType":"uint96","name":"priceOfAckGas","type":"uint96"},{"internalType":"uint64","name":"targetDelta","type":"uint64"}],"internalType":"struct IMessageEscrowStructs.IncentiveDescription","name":"incentive","type":"tuple"},{"internalType":"uint64","name":"deadline","type":"uint64"}],"internalType":"struct ICatalystV1Structs.RouteDescription","name":"routeDescription","type":"tuple"},{"internalType":"address","name":"fromAsset","type":"address"},{"internalType":"uint8","name":"toAssetIndex","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"address","name":"fallbackUser","type":"address"},{"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"},{"internalType":"bytes","name":"calldata_","type":"bytes"}],"name":"sendAsset","outputs":[{"internalType":"uint256","name":"U","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"internalType":"bytes","name":"toVault","type":"bytes"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"components":[{"internalType":"uint48","name":"maxGasDelivery","type":"uint48"},{"internalType":"uint48","name":"maxGasAck","type":"uint48"},{"internalType":"address","name":"refundGasTo","type":"address"},{"internalType":"uint96","name":"priceOfDeliveryGas","type":"uint96"},{"internalType":"uint96","name":"priceOfAckGas","type":"uint96"},{"internalType":"uint64","name":"targetDelta","type":"uint64"}],"internalType":"struct IMessageEscrowStructs.IncentiveDescription","name":"incentive","type":"tuple"},{"internalType":"uint64","name":"deadline","type":"uint64"}],"internalType":"struct ICatalystV1Structs.RouteDescription","name":"routeDescription","type":"tuple"},{"internalType":"address","name":"fromAsset","type":"address"},{"internalType":"uint8","name":"toAssetIndex","type":"uint8"},{"internalType":"uint256","name":"amount","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"},{"internalType":"uint256","name":"minU","type":"uint256"},{"internalType":"address","name":"fallbackUser","type":"address"},{"internalType":"uint16","name":"underwriteIncentiveX16","type":"uint16"},{"internalType":"bytes","name":"calldata_","type":"bytes"}],"name":"sendAssetFixedUnit","outputs":[{"internalType":"uint256","name":"U","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"components":[{"internalType":"bytes32","name":"chainIdentifier","type":"bytes32"},{"internalType":"bytes","name":"toVault","type":"bytes"},{"internalType":"bytes","name":"toAccount","type":"bytes"},{"components":[{"internalType":"uint48","name":"maxGasDelivery","type":"uint48"},{"internalType":"uint48","name":"maxGasAck","type":"uint48"},{"internalType":"address","name":"refundGasTo","type":"address"},{"internalType":"uint96","name":"priceOfDeliveryGas","type":"uint96"},{"internalType":"uint96","name":"priceOfAckGas","type":"uint96"},{"internalType":"uint64","name":"targetDelta","type":"uint64"}],"internalType":"struct IMessageEscrowStructs.IncentiveDescription","name":"incentive","type":"tuple"},{"internalType":"uint64","name":"deadline","type":"uint64"}],"internalType":"struct ICatalystV1Structs.RouteDescription","name":"routeDescription","type":"tuple"},{"internalType":"uint256","name":"vaultTokens","type":"uint256"},{"internalType":"uint256[2]","name":"minOut","type":"uint256[2]"},{"internalType":"address","name":"fallbackUser","type":"address"},{"internalType":"bytes","name":"calldata_","type":"bytes"}],"name":"sendLiquidity","outputs":[{"internalType":"uint256","name":"U","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"channelId","type":"bytes32"},{"internalType":"bytes","name":"toVault","type":"bytes"},{"internalType":"bool","name":"state","type":"bool"}],"name":"setConnection","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"administrator","type":"address"}],"name":"setFeeAdministrator","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"fee","type":"uint64"}],"name":"setGovernanceFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint64","name":"fee","type":"uint64"}],"name":"setVaultFee","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"string","name":"name_","type":"string"},{"internalType":"string","name":"symbol_","type":"string"},{"internalType":"address","name":"chainInterface","type":"address"},{"internalType":"uint64","name":"vaultFee","type":"uint64"},{"internalType":"uint64","name":"governanceFee","type":"uint64"},{"internalType":"address","name":"feeAdministrator","type":"address"},{"internalType":"address","name":"setupMaster","type":"address"}],"name":"setup","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"result","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes32","name":"identifier","type":"bytes32"},{"internalType":"address","name":"toAsset","type":"address"},{"internalType":"uint256","name":"U","type":"uint256"},{"internalType":"uint256","name":"minOut","type":"uint256"}],"name":"underwriteAsset","outputs":[{"internalType":"uint256","name":"purchasedTokens","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateMaxUnitCapacity","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultTokens","type":"uint256"},{"internalType":"uint256[]","name":"minOut","type":"uint256[]"}],"name":"withdrawAll","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"vaultTokens","type":"uint256"},{"internalType":"uint256[]","name":"withdrawRatio","type":"uint256[]"},{"internalType":"uint256[]","name":"minOut","type":"uint256[]"}],"name":"withdrawMixed","outputs":[{"internalType":"uint256[]","name":"amounts","type":"uint256[]"}],"stateMutability":"nonpayable","type":"function"}]

6001600160401b0360c0601f62005d9138819003918201601f191683019184831184841017620001b6578084926040948552833981010312620001b1576200004781620001cc565b620000566020809301620001cc565b9060805260a0526000916200006c8354620001e1565b601f811162000185575b50602e7f436174616c797374205661756c742054656d706c617465000000000000000000018355600192620000ac8454620001e1565b601f811162000157575b506000845563409feecd19938454918183166200014a57501c8190036200010f575b604051615b7290816200021f8239608051818181610c330152818161182a0152818161378d01526143ca015260a051816123990152f35b6002600160411b039092559081527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d29080a1388080620000d8565b63f92ee8a990526004601cfd5b84825284601f858420920160051c8201915b82811062000179575050620000b6565b83815501859062000169565b838052601f838520910160051c8101905b818110620001a5575062000076565b84815560010162000196565b600080fd5b634e487b7160e01b600052604160045260246000fd5b51906001600160a01b0382168203620001b157565b90600182811c9216801562000213575b6020831014620001fd57565b634e487b7160e01b600052602260045260246000fd5b91607f1691620001f156fe6080604052600436101561001257600080fd5b60003560e01c8063033597b21461042757806306fdde0314610422578063095ea7b31461041d57806309de4fea146104185780630bc350d714610413578063119f8e681461040e578063128581001461040957806313193e6a1461040457806316604f4f146103ff57806318160ddd146103fa5780631fd1602b146103f5578063202ad2c5146103f057806323b872dd146103eb57806324351d57146103e65780632dd31000146103e15780632f48ed57146103dc578063313ce567146103d757806332a3385e146103d257806334420dbd146103cd5780633644e515146103c857806336ae087e146103c357806337fd5135146103be57806339a391de146103b95780633b49580d146103b45780633cf183fa146103af5780634273601c146103aa57806346c3a2fd146103a5578063505b0c12146103a0578063586f98001461039b5780635c459a5b1461039657806364d1e1c01461039157806366ae79e61461038c5780636caea8a3146103875780636d0f691b146103825780636defbf801461037d57806370a08231146103785780637265563114610373578063739fc9bc1461036e5780637ecebe0014610369578063818431be1461036457806386f25e4d1461035f578063904e0a011461035a5780639584c8881461035557806395d89b4114610350578063994e1ada1461034b5780639cf80f20146103465780639d29db4514610341578063a57ecf4f1461033c578063a9059cbb14610337578063aa14786214610332578063ac9650d81461032d578063b22889e414610328578063b9ff629b14610323578063bf5086421461031e578063c86380d914610319578063c8d710d514610314578063c90da0131461030f578063d505accf1461030a578063dd62ed3e14610305578063de6bbd9d14610300578063e6b91aba146102fb578063e75552fb146102f6578063eb80cd11146102f1578063f71f4026146102ec5763f754fc35146102e757600080fd5b613365565b613341565b6132d8565b612fc9565b612fab565b612f1d565b612ed6565b612d25565b612cfe565b612cc9565b612bbb565b612b94565b612b28565b612a3e565b612985565b612964565b6128db565b612848565b6127f3565b6127c9565b6126b4565b6125c1565b61259b565b612566565b6124f1565b6124d8565b6124a1565b612463565b612445565b61240e565b6123bd565b612379565b61234e565b6122bd565b61224e565b611e2e565b611d07565b611b16565b611aeb565b611ad0565b6117ad565b611226565b61111e565b6110eb565b610df6565b610d5e565b610cee565b610cb0565b610c94565b610c57565b610c13565b610bd5565b610b01565b610aa9565b610951565b6108d1565b610845565b610763565b6106f8565b610611565b6105eb565b6105b2565b6104e6565b6104a9565b61043c565b600091031261043757565b600080fd5b34610437576000600319360112610437576020600a54604051908152f35b60005b83811061046d5750506000910152565b818101518382015260200161045d565b601f19601f604093602084526104a2815180928160208801526020888801910161045a565b0116010190565b34610437576000600319360112610437576104d16104c5613494565b6040519182918261047d565b0390f35b6001600160a01b0381160361043757565b3461043757604060031936011261043757600435610503816104d5565b60243590602052637f5e9f20600c5233600052806034600c2055600052602c5160601c337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560206000a3602060405160018152f35b6004359067ffffffffffffffff8216820361043757565b6084359067ffffffffffffffff8216820361043757565b6044359067ffffffffffffffff8216820361043757565b359067ffffffffffffffff8216820361043757565b34610437576020600319360112610437576105cb610558565b6001600160a01b0360085460401c163303610437576105e99061482b565b005b346104375760006003193601126104375760206106066148be565b60011c604051908152f35b3461043757608060031936011261043757600435602435610631816104d5565b604435907f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821015610437576001600160a01b039081600254163303610437576104d1937fd4228165b1b0dc50bd8ac0a74d2b5766000d5ee9f7488b9e74e183deb55c8f1d60606106e2946106a960643588876148fd565b966106b5888787615689565b60405191861682526020820152866040820152a26001600160a01b0316600052600e602052604060002090565b8054820190556040519081529081906020820190565b3461043757600060031936011261043757602065ffffffffffff60075460301c16604051908152f35b9181601f840112156104375782359167ffffffffffffffff8311610437576020838186019501011161043757565b610104359063ffffffff8216820361043757565b34610437576101206003193601126104375767ffffffffffffffff60243581811161043757610796903690600401610721565b6064929192356107a5816104d5565b60e435928311610437576104d1936107c46107e8943690600401610721565b9290916107cf61074f565b9460c4359260a43592608435926044359160043561357f565b6040519081529081906020820190565b9060a060031983011261043757600435916024359067ffffffffffffffff82116104375761082891600401610721565b9091604435906064359060843563ffffffff811681036104375790565b3461043757610853366107f8565b9490926001600160a01b0360029593955416330361043757858486836108b5836108b0816108ab6108cc9e7f97cc161fb90f5cdec9c65ba7aac2279e32df11368946590b82fd6fe8e76b39e09d886108c19c8f61508a565b615639565b614b44565b604051968796876147f5565b0390a1601054613718565b601055005b346104375760006003193601126104375760206805345cdf77eb68f44c54604051908152f35b9060c060031983011261043757600435916024359067ffffffffffffffff82116104375761092791600401610721565b9091604435906064359060843561093d816104d5565b9060a43563ffffffff811681036104375790565b346104375761095f366108f7565b929495936001600160a01b039291928060025416330361043757610a0a60008061099987876109948b8f8f84908c879361585a565b6158e1565b5a946040519060208201927fa9059cbb000000000000000000000000000000000000000000000000000000008452166024820152876044820152604481526109e081611d88565b519082897f8000000000000000000000000000000000000000000000000000000000000000f11590565b610a52575b6105e9610a4d887fcab6c1a18a9c89efaab5ea5a8c665ffe2c5aac9ddd9301ccad01fd4fed7c7e3d8b8a6108c18b8b8b878c60405197889788614a05565b601055565b610a6d610a66989497969395985a92613b6c565b603f900490565b11610a7f573896939194959296610a0f565b60046040517fdd629f86000000000000000000000000000000000000000000000000000000008152fd5b34610437576000600319360112610437576020610ac4613757565b6001600160a01b0360405191168152f35b600319606091011261043757600435610aed816104d5565b90602435610afa816104d5565b9060443590565b3461043757610b0f36610ad5565b8260601b91602092338452600c90637f5e9f2081178252603482209081549160018301610bb0575b506387a211a2915017815283812092835492838211610ba2577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef94826001600160a01b039503905560005284822081815401905584525160601c93169180a360405160018152602090f35b63f4d678b86000526004601cfd5b828611610bc757856387a211a29303905538610b37565b6313be252b6000526004601cfd5b34610437576020600319360112610437576001600160a01b03600435610bfa816104d5565b16600052600b6020526020604060002054604051908152f35b346104375760006003193601126104375760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461043757602060031936011261043757600435610c74816104d5565b6001600160a01b03610c84614394565b163303610437576105e990614a4a565b3461043757600060031936011261043757602060405160128152f35b34610437576020600319360112610437576001600160a01b03600435610cd5816104d5565b1660005260056020526020604060002054604051908152f35b34610437576101006003193601126104375760243567ffffffffffffffff811161043757610d20903690600401610721565b60443591610d2d836104d5565b60e4359063ffffffff82168203610437576104d1936107e89360c4359260a4359260843592606435926004356137ea565b3461043757600060031936011261043757602060a0610d7b613494565b828151910120604051907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8252838201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6604082015246606082015230608082015220604051908152f35b90816101409103126104375790565b60c060031936011261043757600467ffffffffffffffff813581811161043757610e239036908401610de7565b9160243591366084116104375760843590610e3d826104d5565b60a43590811161043757610e549036908401610721565b68929eee149b4bd2126894919492308454146110de573084558635966020810190610e89610e828383613abe565b3691612276565b9689600052600360205260ff610eb3602060409a8b600020828d519483868095519384920161045a565b8201908152030190205416156110b757610ed76001600160a01b0385161515613578565b610ee18633614ba4565b610f57610f46610f51610eff610ef9600f5460070b90565b60070b90565b610f4b610f0b82614c0b565b949092610f418d610f3c610f3582610f306805345cdf77eb68f44c54600c5490613b0f565b613b0f565b9182613b0f565b614d44565b614dc6565b613b32565b9061506d565b90613b9a565b98610f837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8b10613578565b610f92610a4d8b601054613bad565b610fb6610faa610faa6002546001600160a01b031690565b6001600160a01b031690565b803b156104375783600093610ffb8a998e958e519c8d97889687967f318987c40000000000000000000000000000000000000000000000000000000088528701613d44565b039134905af19081156110b2576104d199611088967f8c9503be4db35b4e3d31565a9616d1dc3f1b3024e5e9e9d65052de46a5149f1c93611099575b506110808a878b85018c61107361106b6110648686611056878d613abe565b4363ffffffff16939161508a565b9a89613abe565b939098613abe565b9290915197889788613d7f565b0390a16150ec565b389055519081529081906020820190565b806110a66110ac92611d6f565b8061042c565b38611037565b61374b565b87517f2c64c1b2000000000000000000000000000000000000000000000000000000008152fd5b8463ab143c06600052601cfd5b3461043757604060031936011261043757602061111660043561110d816104d5565b60243590613e12565b604051908152f35b346104375760e06003193601126104375767ffffffffffffffff60043581811161043757611150903690600401610721565b60243583811161043757611168903690600401610721565b93604435611175816104d5565b6064359182168203610437576105e99561118d61056f565b9360a4359561119b876104d5565b60c435976111a8896104d5565b613ef3565b9181601f840112156104375782359167ffffffffffffffff8311610437576020808501948460051b01011161043757565b90815180825260208080930193019160005b8281106111fe575050505090565b8351855293810193928101926001016111f0565b9060206112239281815201906111de565b90565b346104375760606003193601126104375767ffffffffffffffff60046024358281116104375761125990369083016111ad565b9190926044359081116104375761127390369083016111ad565b92903068929eee149b4bd2126854146117a0579293903068929eee149b4bd21268556112a0833533614ba4565b6112af610ef9600f5460070b90565b936112b86142ab565b916112c16142ab565b926000805b600382106115e5575b90610f516112e58261131d946010549003613e03565b610f4b6113188c610f418d610f3c6113106805345cdf77eb68f44c54610f30843591600c5490613b0f565b913582613b5f565b613b1c565b97611326614315565b9760009586975b60038810611408575b5050505050505050836113cf576104d1935061135c61135782600a54613b5f565b600a55565b600654908082116113c05750506113736000600655565b7f7a5919fae19c7104c67635ca0c2ded0d01316a06da859fd961851aa12ac6c71b604051806113a58533953583614373565b0390a23868929eee149b4bd212685560405191829182611212565b6113ca9103600655565b611373565b6040517f0289311f00000000000000000000000000000000000000000000000000000000815280830185815281906020010390fd5b0390fd5b909192939495969761142d610faa6114208b896142ff565b516001600160a01b031690565b156115df576114476114408a8985614330565b358d61506d565b9b8c1561153d578c61145891613b5f565b9b6114638a886142ff565b516001600160a01b03166000908152600560205260409020549085826114898d886142ff565b5161149393615258565b8061149f8c898d614330565b35116114f257916114db6114e192848f818f6114d16114208f9260019b6114c9826114d698614340565b5233936142ff565b6149bc565b613b9a565b90613b0f565b9801955b949392919096959661132d565b8b6114046115018d8a8e614330565b604080517f24557f0500000000000000000000000000000000000000000000000000000000815293840194855290356020850152919283920190565b9b509761154b818884614330565b356115b65761155b81868a614330565b3561156957600101956114e5565b6115758a91868a614330565b35906114046040519283927f24557f050000000000000000000000000000000000000000000000000000000084528301919060206040840193600081520152565b896040517fb8003bfa000000000000000000000000000000000000000000000000000000008152fd5b97611336565b90611612611605829b94959896979a9b6000526004602052604060002090565b546001600160a01b031690565b6001600160a01b038116801561179057906116a99161164382611635868a6142ff565b906001600160a01b03169052565b60208b611663846001600160a01b03166000526005602052604060002090565b549260405180809781947f70a08231000000000000000000000000000000000000000000000000000000008352309083019190916001600160a01b036020820193169052565b03915afa9182156110b2578c611720938f9592610f519360009261175d575b5061171991926116ff896116f96116f2856001600160a01b0316600052600b602052604060002090565b5487613b5f565b926142ff565b526001600160a01b0316600052600e602052604060002090565b5490613b5f565b80611739575b50506001019098979493959291986112c6565b9261174d61175392610f4160019596613b7d565b90613bad565b9190508938611726565b61171992506117839060203d602011611789575b61177b8183611da4565b810190613dc5565b916116c8565b503d611771565b50509098979493959291986112cf565b8263ab143c06600052601cfd5b346104375760806003193601126104375767ffffffffffffffff60048035828111610437576117e09036906004016111ad565b602493919335828111610437576117fe9094939436906004016111ad565b919093611809610586565b946119246064359561181a876104d5565b806001600160a01b03986118518a7f0000000000000000000000000000000000000000000000000000000000000000163314613578565b60008052600460205261188e8a6118877f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ec611605565b1615613578565b16670de0b6b3a76400006118a3818310613578565b031660070b6118e2817fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000600f54169067ffffffffffffffff1617600f55565b7fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff600f549160401b6fffffffffffffffff000000000000000016911617600f55565b61192c614315565b9660009485945b80861061198657897f7dde447513a4ed2580f1f8cd3caea2d0c14b2f46b036d72456a33457da14ed758a8a6119678b600a55565b61197081614acc565b611981604051928392169482614354565b0390a2005b909192939495886119a061199b89858a614330565b61438a565b916119ec836119b98b6000526004602052604060002090565b906001600160a01b03167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b6119f7898689614330565b3592611a04841515613578565b83611a22826001600160a01b03166000526005602052604060002090565b556040517f70a08231000000000000000000000000000000000000000000000000000000008152308882019081526020949192859284928390038401918391165afa80156110b2576001948b8f6114db94611a9d97600095611aa9575b50508391611a9791611a92841515613578565b614340565b52613b9a565b96019493929190611933565b611a97929395509081611ac792903d106117895761177b8183611da4565b93919038611a7f565b34610437576000600319360112610437576020610ac4614394565b3461043757600060031936011261043757602067ffffffffffffffff60075460901c16604051908152f35b3461043757608060031936011261043757600435611b33816104d5565b60243590611b40826104d5565b604435916064359168929eee149b4bd212689130835414611cf957308355611b8e611b88611b7b60075467ffffffffffffffff9060901c1690565b67ffffffffffffffff1690565b8661506d565b611ba2611b9b8288613b5f565b83856144db565b94858111611cbf5750947ff631545f16e5f3e54a6cf8b4e2bfe643a342d0d0bf8e224479382b8acdfd009a91611bf26104d197611be1843033896152be565b611bec8833856149bc565b85615317565b611c19611c12856001600160a01b03166000526005602052604060002090565b5483613b9a565b611c40611c39836001600160a01b03166000526005602052604060002090565b5488613b9a565b81811115611ca257611c58611c609261135792613b5f565b600a54613b5f565b604080516001600160a01b03958616815294909116602085015283015260608201849052339180608081015b0390a23890556040519081529081906020820190565b611cb261135791611cba93613b5f565b600a54613b0f565b611c60565b6040517f24557f05000000000000000000000000000000000000000000000000000000008152600481018790526024810191909152604490fd5b63ab143c066000526004601cfd5b3461043757602060031936011261043757611d20610558565b6001600160a01b03611d30614394565b163303610437576105e9906151e8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff8111611d8357604052565b611d40565b6080810190811067ffffffffffffffff821117611d8357604052565b90601f601f19910116810190811067ffffffffffffffff821117611d8357604052565b81601f820112156104375780359160209167ffffffffffffffff8411611d83578360051b9060405194611dfc85840187611da4565b85528380860192820101928311610437578301905b828210611e1f575050505090565b81358152908301908301611e11565b346104375760408060031936011261043757600480359160243567ffffffffffffffff811161043757611e65903690600401611dc7565b9268929eee149b4bd212689130835414611cf957308355611e868233614ba4565b611e95610ef9600f5460070b90565b93611e9e6142ab565b90611ea76142ab565b926000805b600381106120e8575b601054611ec29203613e03565b96611ecb614315565b9781611f02611eeb89610f306805345cdf77eb68f44c54600c5490613b0f565b611efd611ef88b83613b5f565b613b7d565b613e03565b806120d1575050905b600095611f17826143fe565b91875b60038110611fa8575b50505050505050506104d19450611f3f61135782600a54613b5f565b60065490808211611f99575050611f566000600655565b7f7a5919fae19c7104c67635ca0c2ded0d01316a06da859fd961851aa12ac6c71b60405180611f8786339583614373565b0390a238905560405191829182611212565b611fa39103600655565b611f56565b611fb5611420828a6142ff565b6001600160a01b038116156120cb578d90611fdd84610f41611fd786896142ff565b51613b7d565b9a611fe884876142ff565b519b808910612097575b50836120296120048e61202f94613b0f565b9d612022856001600160a01b03166000526005602052604060002090565b5490613e03565b93614340565b5182811161205a575081612054918f9361204c8660019796614340565b5233906149bc565b01611f1a565b88517f24557f05000000000000000000000000000000000000000000000000000000008152808b0184815260208101929092529081906040010390fd5b6120296120046120c161202f949f610f4b6113188d8f886120bc610f41928f9b613b5f565b61534b565b9e93505050611ff2565b50611f23565b6113186120e29392610f4b92614dc6565b90611f0b565b6120ff611605826000526004602052604060002090565b6001600160a01b0381169081156122475761211e81611635858a6142ff565b61213b816001600160a01b03166000526005602052604060002090565b5485517f70a082310000000000000000000000000000000000000000000000000000000081523088820190815290936020918291869182908190850103915afa80156110b2578d946121979260009261222a575b505082613b9a565b916121c86121c2836121bc846001600160a01b0316600052600b602052604060002090565b54613b9a565b84613b5f565b6121d2868c6142ff565b52826121e5575b50505050600101611eac565b92610f41611ef8600196979461221861174d956121bc61221e996001600160a01b0316600052600e602052604060002090565b90613b5f565b919050883880806121d9565b6122409250803d106117895761177b8183611da4565b388061218f565b5050611eb5565b3461043757600060031936011261043757602067ffffffffffffffff60085416604051908152f35b92919267ffffffffffffffff8211611d8357604051916122a06020601f19601f8401160184611da4565b829481845281830111610437578281602093846000960137010152565b346104375760406003193601126104375760243567ffffffffffffffff811161043757366023820112156104375761233c61232c602061230a6104d1943690602481600401359101612276565b600435600052600382526040600020826040519483868095519384920161045a565b8201908152030190205460ff1690565b60405190151581529081906020820190565b34610437576040600319360112610437576020611116600435612370816104d5565b60243590614418565b346104375760006003193601126104375760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b346104375760008060031936011261240b576020906001600160a01b0380600954161591826123f3575b50506040519015158152f35b604091925080805260048452205416151538806123e7565b80fd5b346104375760206003193601126104375760043561242b816104d5565b6387a211a2600c52600052602080600c2054604051908152f35b34610437576000600319360112610437576020600c54604051908152f35b34610437576020600319360112610437576001600160a01b03600435612488816104d5565b16600052600e6020526020604060002054604051908152f35b34610437576020600319360112610437576004356124be816104d5565b6338377508600c52600052602080600c2054604051908152f35b346104375760206111166124eb36610ad5565b916144db565b346104375760008060031936011261240b576009546001600160a01b0381163303612562577fffffffffffffffffffffffff0000000000000000000000000000000000000000166009557f7991f9d379def473bbda7d0d566449ae0c4b9edb031c33f829df029783f680a48180a180f35b5080fd5b3461043757602060031936011261043757600435600052600460205260206001600160a01b0360406000205416604051908152f35b3461043757600060031936011261043757602065ffffffffffff60075416604051908152f35b346104375760008060031936011261240b57604051908060018054906125e682613441565b80865292602092600181169081156126695750600114612611575b6104d1866104c581880382611da4565b9350600184527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b838510612656575050505081016020016104c5826104d138612601565b8054868601840152938201938101612639565b8796506104d1979450602093506104c59592507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091501682840152151560051b820101929338612601565b346104375760008060031936011261240b5780805b600382106126d9575b600a555080f35b6126f0611605836000526004602052604060002090565b906001600160a01b0382169081156127c1576040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152926020928390859060249082905afa9081156110b25761279561277b6114db9360019761279c978b926127a4575b5050611719846001600160a01b0316600052600b602052604060002090565b916001600160a01b03166000526005602052604060002090565b5490613b9a565b9101906126c9565b6127ba9250803d106117895761177b8183611da4565b388061275c565b9150506126d2565b346104375760006003193601126104375760206008546001600160a01b036040519160401c168152f35b3461043757602060031936011261043757600435600052600d60205260206001600160a01b0360406000205416604051908152f35b6044359060ff8216820361043757565b6084359060ff8216820361043757565b6101206003193601126104375767ffffffffffffffff60043581811161043757612876903690600401610de7565b90602435612883816104d5565b61288b612828565b9260c435612898816104d5565b60e4359061ffff821682036104375761010435948511610437576104d1956128c76107e8963690600401610721565b95909460a435926084359260643592614646565b34610437576040600319360112610437576004356128f8816104d5565b602435906387a211a2600c52336000526020600c208054808411610ba25783900390556000526020600c20818154019055602052600c5160601c337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a360405160018152602090f35b34610437576000600319360112610437576020600f5460070b604051908152f35b34610437576020806003193601126104375760043567ffffffffffffffff8111610437576129b79036906004016111ad565b9060009260208452826020528215612a3957909160051b90604082848237828101925b8151850191868460408401948035918291018637389085305af415612a305781848267ffffffffffffffe094603f945201933d90523d88606083013e3d0101169383821015612a2957936129da565b6040850186f35b863d81803e3d90fd5b604084f35b346104375760606003193601126104375760043560243567ffffffffffffffff811161043757612a72903690600401610721565b91604435928315158403610437576001600160a01b036009541633036104375760418103610437577fd3223b577ecd2d8632a35c20b084999e9f3d352d8924beb1d32f42e0bd66e9a593612b2391836000526003602052612b1782604060002060206040518092868b8337868201908152030190209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b6040519485948561472c565b0390a1005b3461043757608060031936011261043757604435606435612b48816104d5565b6001600160a01b03908160025416330361043757612b6981846004356158e1565b50612b78602435601054613bad565b60105516600052600e6020526040600020908154039055600080f35b346104375760006003193601126104375760206001600160a01b0360025416604051908152f35b346104375760c060031936011261043757600435612bd8816104d5565b60443590606435612be8816104d5565b60a43567ffffffffffffffff811161043757612c08903690600401610721565b6001600160a01b03600254163303610437576020612c2d60ff93612c4f933691612276565b608435600052600382526040600020826040519483868095519384920161045a565b820190815203019020541615612c9f57612c7b83612c9493612c7484836024356158e1565b50836149bc565b6001600160a01b0316600052600e602052604060002090565b908154039055600080f35b60046040517f2c64c1b2000000000000000000000000000000000000000000000000000000008152fd5b34610437576000600319360112610437576020611116600f5460070b612cf8612cf182614c0b565b50916143fe565b90614dc6565b346104375760006003193601126104375760206001600160a01b0360095416604051908152f35b346104375760e060031936011261043757600435612d42816104d5565b60243590612d4f826104d5565b6044359060643592612d5f612838565b92612d68613494565b90815160208093012090864211612ec857604051946001600160a01b0380911694169465383775081901600e5260009685885260c085600c209283549a7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82528782019687528b60408301977fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc689528b6060850199468b528c608087019330855260a08820602e527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9885252528789525260a082015220604e526042602c20885260ff16845260a43560405260c435606052838060808960015afa853d5103612ebb577f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259596979801905585777f5e9f200000000000000000000000000000000000000000176040526034602c2055a380f35b63ddafbaef88526004601cfd5b631a15a3cc6000526004601cfd5b3461043757604060031936011261043757600435612ef3816104d5565b602435612eff816104d5565b602052637f5e9f20600c5260005260206034600c2054604051908152f35b6101006003193601126104375767ffffffffffffffff60043581811161043757612f4b903690600401610de7565b9060243591612f59836104d5565b612f61612828565b60a435612f6d816104d5565b60c4359061ffff821682036104375760e435948511610437576104d195612f9b6107e8963690600401610721565b9590946084359260643592614753565b34610437576000600319360112610437576020601054604051908152f35b34610437576040806003193601126104375760049060043567ffffffffffffffff811161043757612ffe903690600401611dc7565b906024359268929eee149b4bd212689130835414611cf957308355613028610ef9600f5460070b90565b9160009182918391845b6003811061314f575b5050506130b26130ca94939261306d613068846130606113576130b897600a54613b0f565b600654613b0f565b600655565b6010549003926130806000821215613578565b6007546130999060901c67ffffffffffffffff16611b7b565b670de0b6b3a76400000302670de0b6b3a7640000900490565b926143fe565b916805345cdf77eb68f44c54906155ea565b91828411613112576104d193506130e18333614b44565b7f7dde447513a4ed2580f1f8cd3caea2d0c14b2f46b036d72456a33457da14ed7560405180611c8c33948783614373565b50506040517f24557f0500000000000000000000000000000000000000000000000000000000815260048101919091526024810191909152604490fd5b613166611605826000526004602052604060002090565b936001600160a01b0385169081156132d057613195866001600160a01b03166000526005602052604060002090565b54976131a1848d614340565b5185517f70a082310000000000000000000000000000000000000000000000000000000081523088820190815291946020928391839182908190850103915afa9081156110b2576131fc926000926132b3575b50508a613b9a565b8a6000918b81613269575b5085156132585761323e8694613245946132378f95610f41611ef86114db976114db60019f9e61324f9e9b613b9a565b9103613bad565b9b84613b9a565b96309033906152be565b01915b91613032565b505050975094505060010191613252565b61329d935091610f41611ef88361221861174d959f968f6121bc906001600160a01b0316600052600e602052604060002090565b978a6132ac81610f4184613b7d565b918b613207565b6132c99250803d106117895761177b8183611da4565b38806131f4565b94505061303b565b34610437576132e6366107f8565b906001600160a01b0360029694939654163303610437578583613334836108ab86827f8a49f1dbb0b988d0421183f74b9866ce7c88256f1b88cf865bf7f3a74706fe689c612b239a8d61508a565b50604051968796876147f5565b34610437576000600319360112610437576020600f546040519060401c60070b8152f35b3461043757613373366108f7565b94919590936001600160a01b036002541633036104375785858886846133ca84846109946133f39e83837fe6db00361b6a35af0ded81ba5696c1633e945a81008cd7da44fb8a78422a7d429f8f9d6133d79e61585a565b5060405197889788614a05565b0390a16001600160a01b03166000526005602052604060002090565b54026006548180821160001461342c5761340d9103600655565b600a549081019081101561342757506105e9600019600a55565b600a55005b501561340d5761343c6000600655565b61340d565b90600182811c9216801561348a575b602083101461345b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691613450565b60405190600082600054916134a883613441565b8083529260209060019081811690811561353557506001146134d5575b50506134d392500383611da4565b565b915092600080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563936000925b82841061351d57506134d394505050810160200138806134c5565b85548885018301529485019487945092810192613502565b9050602093506134d39592507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091501682840152151560051b82010138806134c5565b1561043757565b969197949098929a999568929eee149b4bd212689b308d5414611cf957308d556001600160a01b0380600254163303610437576135bd368c8e612276565b918a600052600360205260ff6135e76020604095866000208288519483868095519384920161045a565b82019081520301902054161561369f57600052600460205280826000205416926136129085856148fd565b9b61361e8d89866149bc565b82519b8c809c6101209182918152602001528c019061363c926136c8565b961690890152606088015260808701528760a087015260c086015284820360e0860152613668926136c8565b9063ffffffff16610100830152037f6b7977bd09a2e845fb431e372aac95edfb358014e167149b4f4d09021c87a79d91a191389055565b600483517f2c64c1b2000000000000000000000000000000000000000000000000000000008152fd5b601f8260209493601f19938186528686013760008582860101520116010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181039291600013801582851316918412161761373157565b6136e9565b908160209103126104375751611223816104d5565b6040513d6000823e3d90fd5b6040517f19b90b0d0000000000000000000000000000000000000000000000000000000081526020816004816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156110b2576000916137c1575090565b611223915060203d6020116137e3575b6137db8183611da4565b810190613736565b503d6137d1565b9390949691989792959868929eee149b4bd2126899308b5414611cf957308b556001600160a01b0360025416330361043757613827368389612276565b9086600052600360205260ff6138516020604094856000208287519483868095519384920161045a565b820190815203019020541615613a4957613888613873610ef9600f5460070b90565b9161388e61388084614c0b565b9381956143fe565b93613b9a565b926805345cdf77eb68f44c54906138a78486848b6155ea565b9d8e8111613a0757508d91849184613984575b50505050506138ce610a4d86601054613718565b8482111561395b578997946139559a61394d957f7af4b988c9949d39dbe6398b8332fa201574208c2656602a23f1624c428bfe9199956139438f9d9a966139398f979161393361131884610f4161392861393e9786614dc6565b94610f3c8d82613b5f565b9061594d565b615040565b615767565b5198899889613a72565b0390a1614b44565b91389055565b600483517f7c1e66d0000000000000000000000000000000000000000000000000000000008152fd5b83610f3061399f6139bd966114d66139af976139a996614dc6565b93600c5490613b0f565b90613e03565b670de0b6b3a7640000900490565b8082116139cd5782818e926138ba565b84517f24557f0500000000000000000000000000000000000000000000000000000000815260048101919091526024810191909152604490fd5b8e61140488519283927f24557f050000000000000000000000000000000000000000000000000000000084526004840160209093929193604081019481520152565b600482517f2c64c1b2000000000000000000000000000000000000000000000000000000008152fd5b9692613aa260c097936001600160a01b039263ffffffff98949c9b979c8b5260e060208c015260e08b01916136c8565b991660408801526060870152608086015260a085015216910152565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610437570180359067ffffffffffffffff82116104375760200191813603831361043757565b9190820180921161373157565b90670de0b6b3a764000091820391821161373157565b907ffffffffffffffffffffffffffffffffffffffffffffffffff21f494c589c0000820191821161373157565b9190820391821161373157565b908115600183800414171561373157565b90670de0b6b3a76400009182810292818404149015171561373157565b8181029291811591840414171561373157565b9190916000838201938412911290801582169115161761373157565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561043757016020813591019167ffffffffffffffff821161043757813603831361043757565b359065ffffffffffff8216820361043757565b35906bffffffffffffffffffffffff8216820361043757565b9061122390613c8f613c7461014085358452613c646020870187613bc9565b90918060208701528501916136c8565b613c816040860186613bc9565b9084830360408601526136c8565b9265ffffffffffff80613ca460608401613c19565b166060840152613cb660808301613c19565b1660808301526001600160a01b0360a0820135613cd2816104d5565b1660a0830152613ce460c08201613c2c565b6bffffffffffffffffffffffff80911660c0840152613d0560e08301613c2c565b1660e083015261010067ffffffffffffffff613d2282840161059d565b1690830152613d3561012080920161059d565b67ffffffffffffffff16910152565b9092613d5f611223969495939560c0845260c0840190613c45565b9460208301526040604481840137608082015260a08185039101526136c8565b9391613da490613db29460c097939a99989a875260e0602088015260e08701916136c8565b9184830360408601526136c8565b9460608201526040604460808301370152565b90816020910312610437575190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8115613e0d570490565b613dd4565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015291906020836024816001600160a01b0385165afa9283156110b257600093613ec3575b50613ea0613e8864e8d4a51000926001600160a01b03166000526005602052604060002090565b54613e98610ef9600f5460070b90565b908585615189565b92041015613eab5790565b670d2f13f7789f0000670de0b6b3a764000091020490565b64e8d4a51000919350613e88613eea613ea09260203d6020116117895761177b8183611da4565b94925050613e61565b95919396929490967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf6011329889548060038c556140b4575b50613f8292613f87959492613f7d926001600160a01b0390817fffffffffffffffffffffffff000000000000000000000000000000000000000093168360025416176002551690600954161760095561482b565b6151e8565b614a4a565b67ffffffffffffffff8411611d83578492600090613fae86613fa98454613441565b6140ee565b8190601f871160011461402a579580613fe192613fe89798859261401f575b50506000198260011b9260031b1c19161790565b90556141d5565b613fef5750565b6002905560016020527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602080a1565b013590503880613fcd565b600080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5639691601f198316845b818110614099575091613fe897989184600195941061407f575b505050811b0190556141d5565b60001960f88560031b161c19910135169055388080614072565b828401358a556001909901988a985060209283019201614058565b90999260018281979694971c14303b10156140e05760ff9190911b9290921b9892939092613f82613f29565b63f92ee8a96000526004601cfd5b601f81116140fa575050565b600090600080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563906020601f850160051c83019410614155575b601f0160051c01915b82811061414a57505050565b81815560010161413e565b9092508290614135565b90601f821161416c575050565b60019160009060016000527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6906020601f850160051c830194106141cb575b601f0160051c01915b8281106141c15750505050565b81815583016141b4565b90925082906141ab565b919067ffffffffffffffff8111611d83576001906141fc816141f78454613441565b61415f565b6000601f821160011461423057819061422c93949560009261401f5750506000198260011b9260031b1c19161790565b9055565b6001600052601f198216947fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf691805b8781106142955750838596971061427b575b505050811b019055565b60001960f88560031b161c19910135169055388080614271565b828201358455928501926020918201910161425f565b604051906060820182811067ffffffffffffffff821117611d83576040526060368337565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060038110156143105760051b0190565b6142d0565b6040519061432282611d88565b600382526060366020840137565b91908110156143105760051b0190565b80518210156143105760209160051b010190565b90604061122392670de0b6b3a7640000815281602082015201906111de565b6040906112239392815281602082015201906111de565b35611223816104d5565b6040517f8da5cb5b0000000000000000000000000000000000000000000000000000000081526020816004816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156110b2576000916137c1575090565b8015613e0d576ec097ce7bc90715b34b9f10000000000590565b906040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526020816024816001600160a01b0387165afa9081156110b2576000916144bc575b50614484836001600160a01b0316600052600b602052604060002090565b548103908111613731576144ae611223936001600160a01b03166000526005602052604060002090565b5490600f5460070b92615258565b6144d5915060203d6020116117895761177b8183611da4565b38614466565b6040517f70a0823100000000000000000000000000000000000000000000000000000000808252306004830152909493926001600160a01b039260209283886024818589165afa9788156110b257600098614625575b506040519081523060048201529383908590602490829086165afa80156110b2576145a861458e6145c39264e8d4a51000976145e597600092614608575b5050611719866001600160a01b0316600052600b602052604060002090565b926001600160a01b03166000526005602052604060002090565b54926001600160a01b03166000526005602052604060002090565b54906145e06145d7610ef9600f5460070b90565b80948a89615189565b615258565b930410156145ef57565b90670d2f13f7789f0000670de0b6b3a764000091020490565b61461e9250803d106117895761177b8183611da4565b388061456f565b8491985061463f90823d84116117895761177b8183611da4565b9790614531565b9099979496939198959268929eee149b4bd2126899308b5414611cf957308b5560ff61469d602061467c610e8282880188613abe565b8635600052600382526040600020826040519483868095519384920161045a565b820190815203019020541615612c9f576146c667ffffffffffffffff60075460901c168361506d565b92838303838111613731576146db908e613e12565b8a81106146f2575090899a9b9c6139559a92615450565b6040517f24557f050000000000000000000000000000000000000000000000000000000081526004810191909152602481018b9052604490fd5b929160409261474b9296959685526060602086015260608501916136c8565b931515910152565b989794919592989693909668929eee149b4bd2126899308b5414611cf957308b5560ff6147ab602061478a610e8282870187613abe565b8535600052600382526040600020826040519483868095519384920161045a565b820190815203019020541615612c9f576147d467ffffffffffffffff60075460901c168261506d565b9182820398828a11613731576147ed6139559a8c613e12565b9a8b92615450565b949060809497969263ffffffff9461481a92885260a0602089015260a08801916136c8565b966040860152606085015216910152565b67ffffffffffffffff811690670de0b6b3a76400008211610437577f5b7d342bae9633dbbf79ee2bcab48506e0662e0a74be62192960beab09d2fbc6916020917fffffffffffff0000000000000000ffffffffffffffffffffffffffffffffffff79ffffffffffffffff0000000000000000000000000000000000006007549260901b16911617600755604051908152a1565b600a54620151808165ffffffffffff60075460601c1642030204600654818111156148f8570390818111156148f1570390565b5050600090565b505090565b92916001600160a01b036149118386614418565b9416600052600560205261492a60406000205485613b9a565b600a54908082111561499257806149449203600a55615767565b8381116149585750610a4d90601054613718565b6040517f24557f05000000000000000000000000000000000000000000000000000000008152600481018590526024810191909152604490fd5b60046040517f7c1e66d0000000000000000000000000000000000000000000000000000000008152fd5b60109260209260145260345260446000938480936fa9059cbb00000000000000000000000082525af13d1560018351141716156149f857603452565b6390b8ec1890526004601cfd5b959094614a336001600160a01b039363ffffffff969a999560a0988a5260c060208b015260c08a01916136c8565b986040880152606087015216608085015216910152565b60207f9c1c6bae52abe079778f8408d952941f631f8b1d020e289245851e78f350c817916008547fffffffff0000000000000000000000000000000000000000ffffffffffffffff7bffffffffffffffffffffffffffffffffffffffff00000000000000008360401b169116176008556001600160a01b0360405191168152a1565b6805345cdf77eb68f44c908154670de0b6b3a764000092838201918210614b3657556387a211a2600c526000526020600c20818154019055602052600c5160601c60007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a3565b63e5cfe9576000526004601cfd5b6805345cdf77eb68f44c805490838201918210614b3657556387a211a2600c526000526020600c20818154019055602052600c5160601c60007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a3565b906387a211a2600c52816000526020600c2091825491828111610ba257600093816001600160a01b03940390556805345cdf77eb68f44c8181540390558352167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602083a3565b6000908190815b60038410614c2f575b505081614c2c916010549003613e03565b91565b9092614c48611605826000526004602052604060002090565b6001600160a01b0381168015614d3c57614c75826001600160a01b03166000526005602052604060002090565b546040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152916020908190849060249082905afa80156110b2578894614ce794610f51938993614d17575b5050611719906001600160a01b0316600052600e602052604060002090565b80614cf9575b50506001019290614c12565b9361174d614d0d92610f4160019597613b7d565b9290508338614ced565b6117199293509081614d3492903d106117895761177b8183611da4565b919038614cc8565b505092614c1b565b670de0b6b3a7640000907812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218111820215830215614d7957020490565b637c5f487d6000526004601cfd5b8181029291600082127f800000000000000000000000000000000000000000000000000000000000000082141661373157818405149015171561373157565b806fffffffffffffffffffffffffffffffff1060071b81811c67ffffffffffffffff1060061b1781811c63ffffffff1060051b1781811c61ffff1060041b1781811c60ff1060031b176000821315615032576112239282827ff8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff6f8421084210842108cc6318c6db6d54be61502d9661501f961c1c601f161a1890811b609f1c7ffffffffffffffff5f6af8f7b3396644f18e157960000000000000000000000006060917fffffffffffffffffffffffffffffffffffffff465fda27eb4d63ded474e5f832816c465772b2bbbb5f824b15207a30018202841d6d0388eaa27412d5aca026815d636e018202841d6d0df99ac502031bf953eff472fdcc018202841d6d13cdffb29d51d99322bdff5f2211018202841d6d0a0f742023def783a307a986912e018202841d6d01920d8043ca89b5239253284e42018202841d6c0b7a86d7375468fac667a0a5270193827ffffffffffffffffffffffffffffffffffffffdc7b88c420e53a9890533129f6f817fffffffffffffffffffffffffffffffffffffff73c0c716a594e00d54e3c4cbc9818080806c29508e458543d8aa4df2abee780102871d6d0139601a2efabe717e604cbb48940102861d6d02247f7a7b6594320649aa03aba10102851d0102831d0102901d01020105711340daa0d5f769dba1915cef59f0815a55060290609f037d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b302017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d614d87565b670de0b6b3a7640000900590565b61596f565b631615e6386000526004601cfd5b8060001904600211810261505f57670de0b6b3a76400009060011b0490565b63bac65e5b6000526004601cfd5b9080600019048211810261505f57670de0b6b3a764000091020490565b93906150e6927fffffffff0000000000000000000000000000000000000000000000000000000060649360405196848895602087019a8b378501936020850152604084015260e01b166060820152036044810184520182611da4565b51902090565b9081600052600d6020526001600160a01b036040600020541661515f5761514d91600052600d6020526040600020906001600160a01b03167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b600c5490810180911161373157600c55565b60046040517fed778779000000000000000000000000000000000000000000000000000000008152fd5b92909283018084116137315761519f9083613b9a565b670de0b6b3a7640000908181029080820483149015171561373157826151c491614dc6565b93806151d2575b5050505090565b6151dd930202614dc6565b9003388080806151cb565b67ffffffffffffffff16670a688906bd8b00008111610437576020817fb77b18869f1146e7805c1d2169aa277954f24055acb20c3e15ddc3de57eacb85927fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006008541617600855604051908152a1565b9092918361526591613b9a565b91670de0b6b3a76400009283810290808204851490151715613731578161528b91614dc6565b91820391808311613731576152a6612cf8916152ac9461534b565b916143fe565b8103908111613731576112239161506d565b601c600060649281946020966040519860605260405260601b602c526f23b872dd000000000000000000000000600c525af13d15600160005114171615615309576000606052604052565b637939f4246000526004601cfd5b67ffffffffffffffff60085416918261532f57505050565b6134d39261533c9161506d565b90615345613757565b906149bc565b670de0b6b3a7640000907812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218111820215830215614d79570290808204910615150190565b9461ffff946112239a989460ff6153b26001600160a01b03969c9a95610100808c528b0190613c45565b9b1660208901526040880152606087015260808601521660a08401521660c082015260e08185039101526136c8565b9a96918b61ffff9a966101209c989f9e9a9661541a6154289460ff986001600160a01b03976101409087528060208801528601916136c8565b9260408185039101526136c8565b9c1660608b015216608089015260a088015260c087015260e086015261010085015216910152565b9899909997969795939294956154706001600160a01b038a161515613578565b61549b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8710613578565b6154aa610a4d87601054613bad565b6154c2610faa610faa6002546001600160a01b031690565b906154cd8886613b5f565b91803b1561043757848c61551a8f968b958e8c996000996040519b8c9a8b998a997fd6512967000000000000000000000000000000000000000000000000000000008b5260048b01615388565b039134905af180156110b2576155d7575b50604088019661553b888a613abe565b8b6155468987613b5f565b884363ffffffff16936155589561585a565b908a6155648886613b5f565b9161556e936156f0565b61557a8230338c6152be565b615584858a615317565b6155916020890189613abe565b99909761559e908a613abe565b906040519b8c9b359a6155b19b8d6153e1565b037f39d3d3ef1ca8c28e2940bcde183fe626d94dca2df8d9ba3bdcd7a91caa31e36191a1565b806110a66155e492611d6f565b3861552b565b92909282018083116137315761560392610f4191614d44565b7ffffffffffffffffffffffffffffffffffffffffffffffffff21f494c589c00008101908113600116613731576112239161506d565b600052600d6020526040600020908154916001600160a01b038316928315610437577fffffffffffffffffffffffff0000000000000000000000000000000000000000169055600c5403600c5590565b90600090828252600d6020526001600160a01b03908160408420541661515f576040938352600d60205283832060017fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055168152600b60205220908154019055565b919091600091818352600d6020526001600160a01b03918260408520541661515f57604094615756918552600d602052858520906001600160a01b03167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b168152600b60205220908154019055565b600a549061579e8261579261578760075465ffffffffffff9060601c1690565b65ffffffffffff1690565b42030262015180900490565b906006548281111561580657906157b491613b0f565b0390811161499257600780547fffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff164260601b71ffffffffffff000000000000000000000000161790556134d390600655565b50809291501161499257600780547fffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff164260601b71ffffffffffff000000000000000000000000161790556134d390600655565b94917fffffffff000000000000000000000000000000000000000000000000000000006078937fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006150e6969760405198868a97602089019c8d378701956020870152604086015260601b16606084015260e01b166074820152036058810184520182611da4565b600052600d6020526040600020918254906001600160a01b038216938415610437577fffffffffffffffffffffffff0000000000000000000000000000000000000000615944931690556001600160a01b0316600052600b602052604060002090565b90815403905590565b81600019048111820261505f5702670de0b6b3a7640000808204910615150190565b7ffffffffffffffffffffffffffffffffffffffffffffffffdc0d0570925a462d7811315615b3657680755bf798b4a1bf1e5811215615b28576503782dace9d990604e1b0574029d9dc38563c32e5c2f6dc192ee70ef65f9978af36bb17217f7d1cf79abc9e3b3989179d835ebba824c98fb31b83b2ca45c0000000000000000000000006060916b8000000000000000000000008582851b0501831d94850290036e0587f503bb6ea29d25fcb740196450816c10fe68e7fd37d0007b713f7650810102841d936e05180bb14799ab47a8a8cb2a527d57837ffffffffffffffffffffffffffffffffffffd38dc772608b0ae56cce01296c0eb816db1bbb201f443cf962f1a1d3db4a5817fffffffffffffffffffffffffffffffffffffe5adedaa1cb095af9e4da10e363c816d0277594991cfc85f6e2461837cd9817fffffffffffffffffffffffffffffffffffffffdbf3ccf1604d263450f02a55048101028a1d0102881d0102861d0102841d0102821d01947ffffffffffffffffffffffffffffffffffffffe2c69812cf03b0763fd454a8f7e846d02d16720577bd19bf614176fe9ea830192010102901d01020105029060c3031c90565b63a37bfec96000526004601cfd5b5060009056fea26469706673582212205b620e5c19cd34ebff19ce0d6221c4cb20ca382d3b6b735234817ac60f7e822764736f6c6343000816003300000000000000000000000000000000e5e81e25aead7fccb4c9560c6b5b718f000000000000000000000000000000575b0d9cc6ddbd8990db4d845fe480281f

Deployed Bytecode

0x6080604052600436101561001257600080fd5b60003560e01c8063033597b21461042757806306fdde0314610422578063095ea7b31461041d57806309de4fea146104185780630bc350d714610413578063119f8e681461040e578063128581001461040957806313193e6a1461040457806316604f4f146103ff57806318160ddd146103fa5780631fd1602b146103f5578063202ad2c5146103f057806323b872dd146103eb57806324351d57146103e65780632dd31000146103e15780632f48ed57146103dc578063313ce567146103d757806332a3385e146103d257806334420dbd146103cd5780633644e515146103c857806336ae087e146103c357806337fd5135146103be57806339a391de146103b95780633b49580d146103b45780633cf183fa146103af5780634273601c146103aa57806346c3a2fd146103a5578063505b0c12146103a0578063586f98001461039b5780635c459a5b1461039657806364d1e1c01461039157806366ae79e61461038c5780636caea8a3146103875780636d0f691b146103825780636defbf801461037d57806370a08231146103785780637265563114610373578063739fc9bc1461036e5780637ecebe0014610369578063818431be1461036457806386f25e4d1461035f578063904e0a011461035a5780639584c8881461035557806395d89b4114610350578063994e1ada1461034b5780639cf80f20146103465780639d29db4514610341578063a57ecf4f1461033c578063a9059cbb14610337578063aa14786214610332578063ac9650d81461032d578063b22889e414610328578063b9ff629b14610323578063bf5086421461031e578063c86380d914610319578063c8d710d514610314578063c90da0131461030f578063d505accf1461030a578063dd62ed3e14610305578063de6bbd9d14610300578063e6b91aba146102fb578063e75552fb146102f6578063eb80cd11146102f1578063f71f4026146102ec5763f754fc35146102e757600080fd5b613365565b613341565b6132d8565b612fc9565b612fab565b612f1d565b612ed6565b612d25565b612cfe565b612cc9565b612bbb565b612b94565b612b28565b612a3e565b612985565b612964565b6128db565b612848565b6127f3565b6127c9565b6126b4565b6125c1565b61259b565b612566565b6124f1565b6124d8565b6124a1565b612463565b612445565b61240e565b6123bd565b612379565b61234e565b6122bd565b61224e565b611e2e565b611d07565b611b16565b611aeb565b611ad0565b6117ad565b611226565b61111e565b6110eb565b610df6565b610d5e565b610cee565b610cb0565b610c94565b610c57565b610c13565b610bd5565b610b01565b610aa9565b610951565b6108d1565b610845565b610763565b6106f8565b610611565b6105eb565b6105b2565b6104e6565b6104a9565b61043c565b600091031261043757565b600080fd5b34610437576000600319360112610437576020600a54604051908152f35b60005b83811061046d5750506000910152565b818101518382015260200161045d565b601f19601f604093602084526104a2815180928160208801526020888801910161045a565b0116010190565b34610437576000600319360112610437576104d16104c5613494565b6040519182918261047d565b0390f35b6001600160a01b0381160361043757565b3461043757604060031936011261043757600435610503816104d5565b60243590602052637f5e9f20600c5233600052806034600c2055600052602c5160601c337f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b92560206000a3602060405160018152f35b6004359067ffffffffffffffff8216820361043757565b6084359067ffffffffffffffff8216820361043757565b6044359067ffffffffffffffff8216820361043757565b359067ffffffffffffffff8216820361043757565b34610437576020600319360112610437576105cb610558565b6001600160a01b0360085460401c163303610437576105e99061482b565b005b346104375760006003193601126104375760206106066148be565b60011c604051908152f35b3461043757608060031936011261043757600435602435610631816104d5565b604435907f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff821015610437576001600160a01b039081600254163303610437576104d1937fd4228165b1b0dc50bd8ac0a74d2b5766000d5ee9f7488b9e74e183deb55c8f1d60606106e2946106a960643588876148fd565b966106b5888787615689565b60405191861682526020820152866040820152a26001600160a01b0316600052600e602052604060002090565b8054820190556040519081529081906020820190565b3461043757600060031936011261043757602065ffffffffffff60075460301c16604051908152f35b9181601f840112156104375782359167ffffffffffffffff8311610437576020838186019501011161043757565b610104359063ffffffff8216820361043757565b34610437576101206003193601126104375767ffffffffffffffff60243581811161043757610796903690600401610721565b6064929192356107a5816104d5565b60e435928311610437576104d1936107c46107e8943690600401610721565b9290916107cf61074f565b9460c4359260a43592608435926044359160043561357f565b6040519081529081906020820190565b9060a060031983011261043757600435916024359067ffffffffffffffff82116104375761082891600401610721565b9091604435906064359060843563ffffffff811681036104375790565b3461043757610853366107f8565b9490926001600160a01b0360029593955416330361043757858486836108b5836108b0816108ab6108cc9e7f97cc161fb90f5cdec9c65ba7aac2279e32df11368946590b82fd6fe8e76b39e09d886108c19c8f61508a565b615639565b614b44565b604051968796876147f5565b0390a1601054613718565b601055005b346104375760006003193601126104375760206805345cdf77eb68f44c54604051908152f35b9060c060031983011261043757600435916024359067ffffffffffffffff82116104375761092791600401610721565b9091604435906064359060843561093d816104d5565b9060a43563ffffffff811681036104375790565b346104375761095f366108f7565b929495936001600160a01b039291928060025416330361043757610a0a60008061099987876109948b8f8f84908c879361585a565b6158e1565b5a946040519060208201927fa9059cbb000000000000000000000000000000000000000000000000000000008452166024820152876044820152604481526109e081611d88565b519082897f8000000000000000000000000000000000000000000000000000000000000000f11590565b610a52575b6105e9610a4d887fcab6c1a18a9c89efaab5ea5a8c665ffe2c5aac9ddd9301ccad01fd4fed7c7e3d8b8a6108c18b8b8b878c60405197889788614a05565b601055565b610a6d610a66989497969395985a92613b6c565b603f900490565b11610a7f573896939194959296610a0f565b60046040517fdd629f86000000000000000000000000000000000000000000000000000000008152fd5b34610437576000600319360112610437576020610ac4613757565b6001600160a01b0360405191168152f35b600319606091011261043757600435610aed816104d5565b90602435610afa816104d5565b9060443590565b3461043757610b0f36610ad5565b8260601b91602092338452600c90637f5e9f2081178252603482209081549160018301610bb0575b506387a211a2915017815283812092835492838211610ba2577fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef94826001600160a01b039503905560005284822081815401905584525160601c93169180a360405160018152602090f35b63f4d678b86000526004601cfd5b828611610bc757856387a211a29303905538610b37565b6313be252b6000526004601cfd5b34610437576020600319360112610437576001600160a01b03600435610bfa816104d5565b16600052600b6020526020604060002054604051908152f35b346104375760006003193601126104375760206040516001600160a01b037f00000000000000000000000000000000e5e81e25aead7fccb4c9560c6b5b718f168152f35b3461043757602060031936011261043757600435610c74816104d5565b6001600160a01b03610c84614394565b163303610437576105e990614a4a565b3461043757600060031936011261043757602060405160128152f35b34610437576020600319360112610437576001600160a01b03600435610cd5816104d5565b1660005260056020526020604060002054604051908152f35b34610437576101006003193601126104375760243567ffffffffffffffff811161043757610d20903690600401610721565b60443591610d2d836104d5565b60e4359063ffffffff82168203610437576104d1936107e89360c4359260a4359260843592606435926004356137ea565b3461043757600060031936011261043757602060a0610d7b613494565b828151910120604051907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f8252838201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc6604082015246606082015230608082015220604051908152f35b90816101409103126104375790565b60c060031936011261043757600467ffffffffffffffff813581811161043757610e239036908401610de7565b9160243591366084116104375760843590610e3d826104d5565b60a43590811161043757610e549036908401610721565b68929eee149b4bd2126894919492308454146110de573084558635966020810190610e89610e828383613abe565b3691612276565b9689600052600360205260ff610eb3602060409a8b600020828d519483868095519384920161045a565b8201908152030190205416156110b757610ed76001600160a01b0385161515613578565b610ee18633614ba4565b610f57610f46610f51610eff610ef9600f5460070b90565b60070b90565b610f4b610f0b82614c0b565b949092610f418d610f3c610f3582610f306805345cdf77eb68f44c54600c5490613b0f565b613b0f565b9182613b0f565b614d44565b614dc6565b613b32565b9061506d565b90613b9a565b98610f837f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8b10613578565b610f92610a4d8b601054613bad565b610fb6610faa610faa6002546001600160a01b031690565b6001600160a01b031690565b803b156104375783600093610ffb8a998e958e519c8d97889687967f318987c40000000000000000000000000000000000000000000000000000000088528701613d44565b039134905af19081156110b2576104d199611088967f8c9503be4db35b4e3d31565a9616d1dc3f1b3024e5e9e9d65052de46a5149f1c93611099575b506110808a878b85018c61107361106b6110648686611056878d613abe565b4363ffffffff16939161508a565b9a89613abe565b939098613abe565b9290915197889788613d7f565b0390a16150ec565b389055519081529081906020820190565b806110a66110ac92611d6f565b8061042c565b38611037565b61374b565b87517f2c64c1b2000000000000000000000000000000000000000000000000000000008152fd5b8463ab143c06600052601cfd5b3461043757604060031936011261043757602061111660043561110d816104d5565b60243590613e12565b604051908152f35b346104375760e06003193601126104375767ffffffffffffffff60043581811161043757611150903690600401610721565b60243583811161043757611168903690600401610721565b93604435611175816104d5565b6064359182168203610437576105e99561118d61056f565b9360a4359561119b876104d5565b60c435976111a8896104d5565b613ef3565b9181601f840112156104375782359167ffffffffffffffff8311610437576020808501948460051b01011161043757565b90815180825260208080930193019160005b8281106111fe575050505090565b8351855293810193928101926001016111f0565b9060206112239281815201906111de565b90565b346104375760606003193601126104375767ffffffffffffffff60046024358281116104375761125990369083016111ad565b9190926044359081116104375761127390369083016111ad565b92903068929eee149b4bd2126854146117a0579293903068929eee149b4bd21268556112a0833533614ba4565b6112af610ef9600f5460070b90565b936112b86142ab565b916112c16142ab565b926000805b600382106115e5575b90610f516112e58261131d946010549003613e03565b610f4b6113188c610f418d610f3c6113106805345cdf77eb68f44c54610f30843591600c5490613b0f565b913582613b5f565b613b1c565b97611326614315565b9760009586975b60038810611408575b5050505050505050836113cf576104d1935061135c61135782600a54613b5f565b600a55565b600654908082116113c05750506113736000600655565b7f7a5919fae19c7104c67635ca0c2ded0d01316a06da859fd961851aa12ac6c71b604051806113a58533953583614373565b0390a23868929eee149b4bd212685560405191829182611212565b6113ca9103600655565b611373565b6040517f0289311f00000000000000000000000000000000000000000000000000000000815280830185815281906020010390fd5b0390fd5b909192939495969761142d610faa6114208b896142ff565b516001600160a01b031690565b156115df576114476114408a8985614330565b358d61506d565b9b8c1561153d578c61145891613b5f565b9b6114638a886142ff565b516001600160a01b03166000908152600560205260409020549085826114898d886142ff565b5161149393615258565b8061149f8c898d614330565b35116114f257916114db6114e192848f818f6114d16114208f9260019b6114c9826114d698614340565b5233936142ff565b6149bc565b613b9a565b90613b0f565b9801955b949392919096959661132d565b8b6114046115018d8a8e614330565b604080517f24557f0500000000000000000000000000000000000000000000000000000000815293840194855290356020850152919283920190565b9b509761154b818884614330565b356115b65761155b81868a614330565b3561156957600101956114e5565b6115758a91868a614330565b35906114046040519283927f24557f050000000000000000000000000000000000000000000000000000000084528301919060206040840193600081520152565b896040517fb8003bfa000000000000000000000000000000000000000000000000000000008152fd5b97611336565b90611612611605829b94959896979a9b6000526004602052604060002090565b546001600160a01b031690565b6001600160a01b038116801561179057906116a99161164382611635868a6142ff565b906001600160a01b03169052565b60208b611663846001600160a01b03166000526005602052604060002090565b549260405180809781947f70a08231000000000000000000000000000000000000000000000000000000008352309083019190916001600160a01b036020820193169052565b03915afa9182156110b2578c611720938f9592610f519360009261175d575b5061171991926116ff896116f96116f2856001600160a01b0316600052600b602052604060002090565b5487613b5f565b926142ff565b526001600160a01b0316600052600e602052604060002090565b5490613b5f565b80611739575b50506001019098979493959291986112c6565b9261174d61175392610f4160019596613b7d565b90613bad565b9190508938611726565b61171992506117839060203d602011611789575b61177b8183611da4565b810190613dc5565b916116c8565b503d611771565b50509098979493959291986112cf565b8263ab143c06600052601cfd5b346104375760806003193601126104375767ffffffffffffffff60048035828111610437576117e09036906004016111ad565b602493919335828111610437576117fe9094939436906004016111ad565b919093611809610586565b946119246064359561181a876104d5565b806001600160a01b03986118518a7f00000000000000000000000000000000e5e81e25aead7fccb4c9560c6b5b718f163314613578565b60008052600460205261188e8a6118877f17ef568e3e12ab5b9c7254a8d58478811de00f9e6eb34345acd53bf8fd09d3ec611605565b1615613578565b16670de0b6b3a76400006118a3818310613578565b031660070b6118e2817fffffffffffffffffffffffffffffffffffffffffffffffff0000000000000000600f54169067ffffffffffffffff1617600f55565b7fffffffffffffffffffffffffffffffff0000000000000000ffffffffffffffff600f549160401b6fffffffffffffffff000000000000000016911617600f55565b61192c614315565b9660009485945b80861061198657897f7dde447513a4ed2580f1f8cd3caea2d0c14b2f46b036d72456a33457da14ed758a8a6119678b600a55565b61197081614acc565b611981604051928392169482614354565b0390a2005b909192939495886119a061199b89858a614330565b61438a565b916119ec836119b98b6000526004602052604060002090565b906001600160a01b03167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b6119f7898689614330565b3592611a04841515613578565b83611a22826001600160a01b03166000526005602052604060002090565b556040517f70a08231000000000000000000000000000000000000000000000000000000008152308882019081526020949192859284928390038401918391165afa80156110b2576001948b8f6114db94611a9d97600095611aa9575b50508391611a9791611a92841515613578565b614340565b52613b9a565b96019493929190611933565b611a97929395509081611ac792903d106117895761177b8183611da4565b93919038611a7f565b34610437576000600319360112610437576020610ac4614394565b3461043757600060031936011261043757602067ffffffffffffffff60075460901c16604051908152f35b3461043757608060031936011261043757600435611b33816104d5565b60243590611b40826104d5565b604435916064359168929eee149b4bd212689130835414611cf957308355611b8e611b88611b7b60075467ffffffffffffffff9060901c1690565b67ffffffffffffffff1690565b8661506d565b611ba2611b9b8288613b5f565b83856144db565b94858111611cbf5750947ff631545f16e5f3e54a6cf8b4e2bfe643a342d0d0bf8e224479382b8acdfd009a91611bf26104d197611be1843033896152be565b611bec8833856149bc565b85615317565b611c19611c12856001600160a01b03166000526005602052604060002090565b5483613b9a565b611c40611c39836001600160a01b03166000526005602052604060002090565b5488613b9a565b81811115611ca257611c58611c609261135792613b5f565b600a54613b5f565b604080516001600160a01b03958616815294909116602085015283015260608201849052339180608081015b0390a23890556040519081529081906020820190565b611cb261135791611cba93613b5f565b600a54613b0f565b611c60565b6040517f24557f05000000000000000000000000000000000000000000000000000000008152600481018790526024810191909152604490fd5b63ab143c066000526004601cfd5b3461043757602060031936011261043757611d20610558565b6001600160a01b03611d30614394565b163303610437576105e9906151e8565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052604160045260246000fd5b67ffffffffffffffff8111611d8357604052565b611d40565b6080810190811067ffffffffffffffff821117611d8357604052565b90601f601f19910116810190811067ffffffffffffffff821117611d8357604052565b81601f820112156104375780359160209167ffffffffffffffff8411611d83578360051b9060405194611dfc85840187611da4565b85528380860192820101928311610437578301905b828210611e1f575050505090565b81358152908301908301611e11565b346104375760408060031936011261043757600480359160243567ffffffffffffffff811161043757611e65903690600401611dc7565b9268929eee149b4bd212689130835414611cf957308355611e868233614ba4565b611e95610ef9600f5460070b90565b93611e9e6142ab565b90611ea76142ab565b926000805b600381106120e8575b601054611ec29203613e03565b96611ecb614315565b9781611f02611eeb89610f306805345cdf77eb68f44c54600c5490613b0f565b611efd611ef88b83613b5f565b613b7d565b613e03565b806120d1575050905b600095611f17826143fe565b91875b60038110611fa8575b50505050505050506104d19450611f3f61135782600a54613b5f565b60065490808211611f99575050611f566000600655565b7f7a5919fae19c7104c67635ca0c2ded0d01316a06da859fd961851aa12ac6c71b60405180611f8786339583614373565b0390a238905560405191829182611212565b611fa39103600655565b611f56565b611fb5611420828a6142ff565b6001600160a01b038116156120cb578d90611fdd84610f41611fd786896142ff565b51613b7d565b9a611fe884876142ff565b519b808910612097575b50836120296120048e61202f94613b0f565b9d612022856001600160a01b03166000526005602052604060002090565b5490613e03565b93614340565b5182811161205a575081612054918f9361204c8660019796614340565b5233906149bc565b01611f1a565b88517f24557f05000000000000000000000000000000000000000000000000000000008152808b0184815260208101929092529081906040010390fd5b6120296120046120c161202f949f610f4b6113188d8f886120bc610f41928f9b613b5f565b61534b565b9e93505050611ff2565b50611f23565b6113186120e29392610f4b92614dc6565b90611f0b565b6120ff611605826000526004602052604060002090565b6001600160a01b0381169081156122475761211e81611635858a6142ff565b61213b816001600160a01b03166000526005602052604060002090565b5485517f70a082310000000000000000000000000000000000000000000000000000000081523088820190815290936020918291869182908190850103915afa80156110b2578d946121979260009261222a575b505082613b9a565b916121c86121c2836121bc846001600160a01b0316600052600b602052604060002090565b54613b9a565b84613b5f565b6121d2868c6142ff565b52826121e5575b50505050600101611eac565b92610f41611ef8600196979461221861174d956121bc61221e996001600160a01b0316600052600e602052604060002090565b90613b5f565b919050883880806121d9565b6122409250803d106117895761177b8183611da4565b388061218f565b5050611eb5565b3461043757600060031936011261043757602067ffffffffffffffff60085416604051908152f35b92919267ffffffffffffffff8211611d8357604051916122a06020601f19601f8401160184611da4565b829481845281830111610437578281602093846000960137010152565b346104375760406003193601126104375760243567ffffffffffffffff811161043757366023820112156104375761233c61232c602061230a6104d1943690602481600401359101612276565b600435600052600382526040600020826040519483868095519384920161045a565b8201908152030190205460ff1690565b60405190151581529081906020820190565b34610437576040600319360112610437576020611116600435612370816104d5565b60243590614418565b346104375760006003193601126104375760206040516001600160a01b037f000000000000000000000000000000575b0d9cc6ddbd8990db4d845fe480281f168152f35b346104375760008060031936011261240b576020906001600160a01b0380600954161591826123f3575b50506040519015158152f35b604091925080805260048452205416151538806123e7565b80fd5b346104375760206003193601126104375760043561242b816104d5565b6387a211a2600c52600052602080600c2054604051908152f35b34610437576000600319360112610437576020600c54604051908152f35b34610437576020600319360112610437576001600160a01b03600435612488816104d5565b16600052600e6020526020604060002054604051908152f35b34610437576020600319360112610437576004356124be816104d5565b6338377508600c52600052602080600c2054604051908152f35b346104375760206111166124eb36610ad5565b916144db565b346104375760008060031936011261240b576009546001600160a01b0381163303612562577fffffffffffffffffffffffff0000000000000000000000000000000000000000166009557f7991f9d379def473bbda7d0d566449ae0c4b9edb031c33f829df029783f680a48180a180f35b5080fd5b3461043757602060031936011261043757600435600052600460205260206001600160a01b0360406000205416604051908152f35b3461043757600060031936011261043757602065ffffffffffff60075416604051908152f35b346104375760008060031936011261240b57604051908060018054906125e682613441565b80865292602092600181169081156126695750600114612611575b6104d1866104c581880382611da4565b9350600184527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf65b838510612656575050505081016020016104c5826104d138612601565b8054868601840152938201938101612639565b8796506104d1979450602093506104c59592507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091501682840152151560051b820101929338612601565b346104375760008060031936011261240b5780805b600382106126d9575b600a555080f35b6126f0611605836000526004602052604060002090565b906001600160a01b0382169081156127c1576040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152926020928390859060249082905afa9081156110b25761279561277b6114db9360019761279c978b926127a4575b5050611719846001600160a01b0316600052600b602052604060002090565b916001600160a01b03166000526005602052604060002090565b5490613b9a565b9101906126c9565b6127ba9250803d106117895761177b8183611da4565b388061275c565b9150506126d2565b346104375760006003193601126104375760206008546001600160a01b036040519160401c168152f35b3461043757602060031936011261043757600435600052600d60205260206001600160a01b0360406000205416604051908152f35b6044359060ff8216820361043757565b6084359060ff8216820361043757565b6101206003193601126104375767ffffffffffffffff60043581811161043757612876903690600401610de7565b90602435612883816104d5565b61288b612828565b9260c435612898816104d5565b60e4359061ffff821682036104375761010435948511610437576104d1956128c76107e8963690600401610721565b95909460a435926084359260643592614646565b34610437576040600319360112610437576004356128f8816104d5565b602435906387a211a2600c52336000526020600c208054808411610ba25783900390556000526020600c20818154019055602052600c5160601c337fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a360405160018152602090f35b34610437576000600319360112610437576020600f5460070b604051908152f35b34610437576020806003193601126104375760043567ffffffffffffffff8111610437576129b79036906004016111ad565b9060009260208452826020528215612a3957909160051b90604082848237828101925b8151850191868460408401948035918291018637389085305af415612a305781848267ffffffffffffffe094603f945201933d90523d88606083013e3d0101169383821015612a2957936129da565b6040850186f35b863d81803e3d90fd5b604084f35b346104375760606003193601126104375760043560243567ffffffffffffffff811161043757612a72903690600401610721565b91604435928315158403610437576001600160a01b036009541633036104375760418103610437577fd3223b577ecd2d8632a35c20b084999e9f3d352d8924beb1d32f42e0bd66e9a593612b2391836000526003602052612b1782604060002060206040518092868b8337868201908152030190209060ff7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0083541691151516179055565b6040519485948561472c565b0390a1005b3461043757608060031936011261043757604435606435612b48816104d5565b6001600160a01b03908160025416330361043757612b6981846004356158e1565b50612b78602435601054613bad565b60105516600052600e6020526040600020908154039055600080f35b346104375760006003193601126104375760206001600160a01b0360025416604051908152f35b346104375760c060031936011261043757600435612bd8816104d5565b60443590606435612be8816104d5565b60a43567ffffffffffffffff811161043757612c08903690600401610721565b6001600160a01b03600254163303610437576020612c2d60ff93612c4f933691612276565b608435600052600382526040600020826040519483868095519384920161045a565b820190815203019020541615612c9f57612c7b83612c9493612c7484836024356158e1565b50836149bc565b6001600160a01b0316600052600e602052604060002090565b908154039055600080f35b60046040517f2c64c1b2000000000000000000000000000000000000000000000000000000008152fd5b34610437576000600319360112610437576020611116600f5460070b612cf8612cf182614c0b565b50916143fe565b90614dc6565b346104375760006003193601126104375760206001600160a01b0360095416604051908152f35b346104375760e060031936011261043757600435612d42816104d5565b60243590612d4f826104d5565b6044359060643592612d5f612838565b92612d68613494565b90815160208093012090864211612ec857604051946001600160a01b0380911694169465383775081901600e5260009685885260c085600c209283549a7f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82528782019687528b60408301977fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc689528b6060850199468b528c608087019330855260a08820602e527f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9885252528789525260a082015220604e526042602c20885260ff16845260a43560405260c435606052838060808960015afa853d5103612ebb577f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b9259596979801905585777f5e9f200000000000000000000000000000000000000000176040526034602c2055a380f35b63ddafbaef88526004601cfd5b631a15a3cc6000526004601cfd5b3461043757604060031936011261043757600435612ef3816104d5565b602435612eff816104d5565b602052637f5e9f20600c5260005260206034600c2054604051908152f35b6101006003193601126104375767ffffffffffffffff60043581811161043757612f4b903690600401610de7565b9060243591612f59836104d5565b612f61612828565b60a435612f6d816104d5565b60c4359061ffff821682036104375760e435948511610437576104d195612f9b6107e8963690600401610721565b9590946084359260643592614753565b34610437576000600319360112610437576020601054604051908152f35b34610437576040806003193601126104375760049060043567ffffffffffffffff811161043757612ffe903690600401611dc7565b906024359268929eee149b4bd212689130835414611cf957308355613028610ef9600f5460070b90565b9160009182918391845b6003811061314f575b5050506130b26130ca94939261306d613068846130606113576130b897600a54613b0f565b600654613b0f565b600655565b6010549003926130806000821215613578565b6007546130999060901c67ffffffffffffffff16611b7b565b670de0b6b3a76400000302670de0b6b3a7640000900490565b926143fe565b916805345cdf77eb68f44c54906155ea565b91828411613112576104d193506130e18333614b44565b7f7dde447513a4ed2580f1f8cd3caea2d0c14b2f46b036d72456a33457da14ed7560405180611c8c33948783614373565b50506040517f24557f0500000000000000000000000000000000000000000000000000000000815260048101919091526024810191909152604490fd5b613166611605826000526004602052604060002090565b936001600160a01b0385169081156132d057613195866001600160a01b03166000526005602052604060002090565b54976131a1848d614340565b5185517f70a082310000000000000000000000000000000000000000000000000000000081523088820190815291946020928391839182908190850103915afa9081156110b2576131fc926000926132b3575b50508a613b9a565b8a6000918b81613269575b5085156132585761323e8694613245946132378f95610f41611ef86114db976114db60019f9e61324f9e9b613b9a565b9103613bad565b9b84613b9a565b96309033906152be565b01915b91613032565b505050975094505060010191613252565b61329d935091610f41611ef88361221861174d959f968f6121bc906001600160a01b0316600052600e602052604060002090565b978a6132ac81610f4184613b7d565b918b613207565b6132c99250803d106117895761177b8183611da4565b38806131f4565b94505061303b565b34610437576132e6366107f8565b906001600160a01b0360029694939654163303610437578583613334836108ab86827f8a49f1dbb0b988d0421183f74b9866ce7c88256f1b88cf865bf7f3a74706fe689c612b239a8d61508a565b50604051968796876147f5565b34610437576000600319360112610437576020600f546040519060401c60070b8152f35b3461043757613373366108f7565b94919590936001600160a01b036002541633036104375785858886846133ca84846109946133f39e83837fe6db00361b6a35af0ded81ba5696c1633e945a81008cd7da44fb8a78422a7d429f8f9d6133d79e61585a565b5060405197889788614a05565b0390a16001600160a01b03166000526005602052604060002090565b54026006548180821160001461342c5761340d9103600655565b600a549081019081101561342757506105e9600019600a55565b600a55005b501561340d5761343c6000600655565b61340d565b90600182811c9216801561348a575b602083101461345b57565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052602260045260246000fd5b91607f1691613450565b60405190600082600054916134a883613441565b8083529260209060019081811690811561353557506001146134d5575b50506134d392500383611da4565b565b915092600080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563936000925b82841061351d57506134d394505050810160200138806134c5565b85548885018301529485019487945092810192613502565b9050602093506134d39592507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff0091501682840152151560051b82010138806134c5565b1561043757565b969197949098929a999568929eee149b4bd212689b308d5414611cf957308d556001600160a01b0380600254163303610437576135bd368c8e612276565b918a600052600360205260ff6135e76020604095866000208288519483868095519384920161045a565b82019081520301902054161561369f57600052600460205280826000205416926136129085856148fd565b9b61361e8d89866149bc565b82519b8c809c6101209182918152602001528c019061363c926136c8565b961690890152606088015260808701528760a087015260c086015284820360e0860152613668926136c8565b9063ffffffff16610100830152037f6b7977bd09a2e845fb431e372aac95edfb358014e167149b4f4d09021c87a79d91a191389055565b600483517f2c64c1b2000000000000000000000000000000000000000000000000000000008152fd5b601f8260209493601f19938186528686013760008582860101520116010190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601160045260246000fd5b8181039291600013801582851316918412161761373157565b6136e9565b908160209103126104375751611223816104d5565b6040513d6000823e3d90fd5b6040517f19b90b0d0000000000000000000000000000000000000000000000000000000081526020816004816001600160a01b037f00000000000000000000000000000000e5e81e25aead7fccb4c9560c6b5b718f165afa9081156110b2576000916137c1575090565b611223915060203d6020116137e3575b6137db8183611da4565b810190613736565b503d6137d1565b9390949691989792959868929eee149b4bd2126899308b5414611cf957308b556001600160a01b0360025416330361043757613827368389612276565b9086600052600360205260ff6138516020604094856000208287519483868095519384920161045a565b820190815203019020541615613a4957613888613873610ef9600f5460070b90565b9161388e61388084614c0b565b9381956143fe565b93613b9a565b926805345cdf77eb68f44c54906138a78486848b6155ea565b9d8e8111613a0757508d91849184613984575b50505050506138ce610a4d86601054613718565b8482111561395b578997946139559a61394d957f7af4b988c9949d39dbe6398b8332fa201574208c2656602a23f1624c428bfe9199956139438f9d9a966139398f979161393361131884610f4161392861393e9786614dc6565b94610f3c8d82613b5f565b9061594d565b615040565b615767565b5198899889613a72565b0390a1614b44565b91389055565b600483517f7c1e66d0000000000000000000000000000000000000000000000000000000008152fd5b83610f3061399f6139bd966114d66139af976139a996614dc6565b93600c5490613b0f565b90613e03565b670de0b6b3a7640000900490565b8082116139cd5782818e926138ba565b84517f24557f0500000000000000000000000000000000000000000000000000000000815260048101919091526024810191909152604490fd5b8e61140488519283927f24557f050000000000000000000000000000000000000000000000000000000084526004840160209093929193604081019481520152565b600482517f2c64c1b2000000000000000000000000000000000000000000000000000000008152fd5b9692613aa260c097936001600160a01b039263ffffffff98949c9b979c8b5260e060208c015260e08b01916136c8565b991660408801526060870152608086015260a085015216910152565b9035907fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe181360301821215610437570180359067ffffffffffffffff82116104375760200191813603831361043757565b9190820180921161373157565b90670de0b6b3a764000091820391821161373157565b907ffffffffffffffffffffffffffffffffffffffffffffffffff21f494c589c0000820191821161373157565b9190820391821161373157565b908115600183800414171561373157565b90670de0b6b3a76400009182810292818404149015171561373157565b8181029291811591840414171561373157565b9190916000838201938412911290801582169115161761373157565b90357fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe18236030181121561043757016020813591019167ffffffffffffffff821161043757813603831361043757565b359065ffffffffffff8216820361043757565b35906bffffffffffffffffffffffff8216820361043757565b9061122390613c8f613c7461014085358452613c646020870187613bc9565b90918060208701528501916136c8565b613c816040860186613bc9565b9084830360408601526136c8565b9265ffffffffffff80613ca460608401613c19565b166060840152613cb660808301613c19565b1660808301526001600160a01b0360a0820135613cd2816104d5565b1660a0830152613ce460c08201613c2c565b6bffffffffffffffffffffffff80911660c0840152613d0560e08301613c2c565b1660e083015261010067ffffffffffffffff613d2282840161059d565b1690830152613d3561012080920161059d565b67ffffffffffffffff16910152565b9092613d5f611223969495939560c0845260c0840190613c45565b9460208301526040604481840137608082015260a08185039101526136c8565b9391613da490613db29460c097939a99989a875260e0602088015260e08701916136c8565b9184830360408601526136c8565b9460608201526040604460808301370152565b90816020910312610437575190565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052601260045260246000fd5b8115613e0d570490565b613dd4565b6040517f70a0823100000000000000000000000000000000000000000000000000000000815230600482015291906020836024816001600160a01b0385165afa9283156110b257600093613ec3575b50613ea0613e8864e8d4a51000926001600160a01b03166000526005602052604060002090565b54613e98610ef9600f5460070b90565b908585615189565b92041015613eab5790565b670d2f13f7789f0000670de0b6b3a764000091020490565b64e8d4a51000919350613e88613eea613ea09260203d6020116117895761177b8183611da4565b94925050613e61565b95919396929490967fffffffffffffffffffffffffffffffffffffffffffffffffffffffffbf6011329889548060038c556140b4575b50613f8292613f87959492613f7d926001600160a01b0390817fffffffffffffffffffffffff000000000000000000000000000000000000000093168360025416176002551690600954161760095561482b565b6151e8565b614a4a565b67ffffffffffffffff8411611d83578492600090613fae86613fa98454613441565b6140ee565b8190601f871160011461402a579580613fe192613fe89798859261401f575b50506000198260011b9260031b1c19161790565b90556141d5565b613fef5750565b6002905560016020527fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602080a1565b013590503880613fcd565b600080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e5639691601f198316845b818110614099575091613fe897989184600195941061407f575b505050811b0190556141d5565b60001960f88560031b161c19910135169055388080614072565b828401358a556001909901988a985060209283019201614058565b90999260018281979694971c14303b10156140e05760ff9190911b9290921b9892939092613f82613f29565b63f92ee8a96000526004601cfd5b601f81116140fa575050565b600090600080527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563906020601f850160051c83019410614155575b601f0160051c01915b82811061414a57505050565b81815560010161413e565b9092508290614135565b90601f821161416c575050565b60019160009060016000527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6906020601f850160051c830194106141cb575b601f0160051c01915b8281106141c15750505050565b81815583016141b4565b90925082906141ab565b919067ffffffffffffffff8111611d83576001906141fc816141f78454613441565b61415f565b6000601f821160011461423057819061422c93949560009261401f5750506000198260011b9260031b1c19161790565b9055565b6001600052601f198216947fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf691805b8781106142955750838596971061427b575b505050811b019055565b60001960f88560031b161c19910135169055388080614271565b828201358455928501926020918201910161425f565b604051906060820182811067ffffffffffffffff821117611d83576040526060368337565b7f4e487b7100000000000000000000000000000000000000000000000000000000600052603260045260246000fd5b9060038110156143105760051b0190565b6142d0565b6040519061432282611d88565b600382526060366020840137565b91908110156143105760051b0190565b80518210156143105760209160051b010190565b90604061122392670de0b6b3a7640000815281602082015201906111de565b6040906112239392815281602082015201906111de565b35611223816104d5565b6040517f8da5cb5b0000000000000000000000000000000000000000000000000000000081526020816004816001600160a01b037f00000000000000000000000000000000e5e81e25aead7fccb4c9560c6b5b718f165afa9081156110b2576000916137c1575090565b8015613e0d576ec097ce7bc90715b34b9f10000000000590565b906040517f70a082310000000000000000000000000000000000000000000000000000000081523060048201526020816024816001600160a01b0387165afa9081156110b2576000916144bc575b50614484836001600160a01b0316600052600b602052604060002090565b548103908111613731576144ae611223936001600160a01b03166000526005602052604060002090565b5490600f5460070b92615258565b6144d5915060203d6020116117895761177b8183611da4565b38614466565b6040517f70a0823100000000000000000000000000000000000000000000000000000000808252306004830152909493926001600160a01b039260209283886024818589165afa9788156110b257600098614625575b506040519081523060048201529383908590602490829086165afa80156110b2576145a861458e6145c39264e8d4a51000976145e597600092614608575b5050611719866001600160a01b0316600052600b602052604060002090565b926001600160a01b03166000526005602052604060002090565b54926001600160a01b03166000526005602052604060002090565b54906145e06145d7610ef9600f5460070b90565b80948a89615189565b615258565b930410156145ef57565b90670d2f13f7789f0000670de0b6b3a764000091020490565b61461e9250803d106117895761177b8183611da4565b388061456f565b8491985061463f90823d84116117895761177b8183611da4565b9790614531565b9099979496939198959268929eee149b4bd2126899308b5414611cf957308b5560ff61469d602061467c610e8282880188613abe565b8635600052600382526040600020826040519483868095519384920161045a565b820190815203019020541615612c9f576146c667ffffffffffffffff60075460901c168361506d565b92838303838111613731576146db908e613e12565b8a81106146f2575090899a9b9c6139559a92615450565b6040517f24557f050000000000000000000000000000000000000000000000000000000081526004810191909152602481018b9052604490fd5b929160409261474b9296959685526060602086015260608501916136c8565b931515910152565b989794919592989693909668929eee149b4bd2126899308b5414611cf957308b5560ff6147ab602061478a610e8282870187613abe565b8535600052600382526040600020826040519483868095519384920161045a565b820190815203019020541615612c9f576147d467ffffffffffffffff60075460901c168261506d565b9182820398828a11613731576147ed6139559a8c613e12565b9a8b92615450565b949060809497969263ffffffff9461481a92885260a0602089015260a08801916136c8565b966040860152606085015216910152565b67ffffffffffffffff811690670de0b6b3a76400008211610437577f5b7d342bae9633dbbf79ee2bcab48506e0662e0a74be62192960beab09d2fbc6916020917fffffffffffff0000000000000000ffffffffffffffffffffffffffffffffffff79ffffffffffffffff0000000000000000000000000000000000006007549260901b16911617600755604051908152a1565b600a54620151808165ffffffffffff60075460601c1642030204600654818111156148f8570390818111156148f1570390565b5050600090565b505090565b92916001600160a01b036149118386614418565b9416600052600560205261492a60406000205485613b9a565b600a54908082111561499257806149449203600a55615767565b8381116149585750610a4d90601054613718565b6040517f24557f05000000000000000000000000000000000000000000000000000000008152600481018590526024810191909152604490fd5b60046040517f7c1e66d0000000000000000000000000000000000000000000000000000000008152fd5b60109260209260145260345260446000938480936fa9059cbb00000000000000000000000082525af13d1560018351141716156149f857603452565b6390b8ec1890526004601cfd5b959094614a336001600160a01b039363ffffffff969a999560a0988a5260c060208b015260c08a01916136c8565b986040880152606087015216608085015216910152565b60207f9c1c6bae52abe079778f8408d952941f631f8b1d020e289245851e78f350c817916008547fffffffff0000000000000000000000000000000000000000ffffffffffffffff7bffffffffffffffffffffffffffffffffffffffff00000000000000008360401b169116176008556001600160a01b0360405191168152a1565b6805345cdf77eb68f44c908154670de0b6b3a764000092838201918210614b3657556387a211a2600c526000526020600c20818154019055602052600c5160601c60007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a3565b63e5cfe9576000526004601cfd5b6805345cdf77eb68f44c805490838201918210614b3657556387a211a2600c526000526020600c20818154019055602052600c5160601c60007fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602080a3565b906387a211a2600c52816000526020600c2091825491828111610ba257600093816001600160a01b03940390556805345cdf77eb68f44c8181540390558352167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef602083a3565b6000908190815b60038410614c2f575b505081614c2c916010549003613e03565b91565b9092614c48611605826000526004602052604060002090565b6001600160a01b0381168015614d3c57614c75826001600160a01b03166000526005602052604060002090565b546040517f70a08231000000000000000000000000000000000000000000000000000000008152306004820152916020908190849060249082905afa80156110b2578894614ce794610f51938993614d17575b5050611719906001600160a01b0316600052600e602052604060002090565b80614cf9575b50506001019290614c12565b9361174d614d0d92610f4160019597613b7d565b9290508338614ced565b6117199293509081614d3492903d106117895761177b8183611da4565b919038614cc8565b505092614c1b565b670de0b6b3a7640000907812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218111820215830215614d7957020490565b637c5f487d6000526004601cfd5b8181029291600082127f800000000000000000000000000000000000000000000000000000000000000082141661373157818405149015171561373157565b806fffffffffffffffffffffffffffffffff1060071b81811c67ffffffffffffffff1060061b1781811c63ffffffff1060051b1781811c61ffff1060041b1781811c60ff1060031b176000821315615032576112239282827ff8f9f9faf9fdfafbf9fdfcfdfafbfcfef9fafdfafcfcfbfefafafcfbffffffff6f8421084210842108cc6318c6db6d54be61502d9661501f961c1c601f161a1890811b609f1c7ffffffffffffffff5f6af8f7b3396644f18e157960000000000000000000000006060917fffffffffffffffffffffffffffffffffffffff465fda27eb4d63ded474e5f832816c465772b2bbbb5f824b15207a30018202841d6d0388eaa27412d5aca026815d636e018202841d6d0df99ac502031bf953eff472fdcc018202841d6d13cdffb29d51d99322bdff5f2211018202841d6d0a0f742023def783a307a986912e018202841d6d01920d8043ca89b5239253284e42018202841d6c0b7a86d7375468fac667a0a5270193827ffffffffffffffffffffffffffffffffffffffdc7b88c420e53a9890533129f6f817fffffffffffffffffffffffffffffffffffffff73c0c716a594e00d54e3c4cbc9818080806c29508e458543d8aa4df2abee780102871d6d0139601a2efabe717e604cbb48940102861d6d02247f7a7b6594320649aa03aba10102851d0102831d0102901d01020105711340daa0d5f769dba1915cef59f0815a55060290609f037d0267a36c0c95b3975ab3ee5b203a7614a3f75373f047d803ae7b6687f2b302017d57115e47018c7177eebf7cd370a3356a1b7863008a5ae8028c72b88642840160ae1d614d87565b670de0b6b3a7640000900590565b61596f565b631615e6386000526004601cfd5b8060001904600211810261505f57670de0b6b3a76400009060011b0490565b63bac65e5b6000526004601cfd5b9080600019048211810261505f57670de0b6b3a764000091020490565b93906150e6927fffffffff0000000000000000000000000000000000000000000000000000000060649360405196848895602087019a8b378501936020850152604084015260e01b166060820152036044810184520182611da4565b51902090565b9081600052600d6020526001600160a01b036040600020541661515f5761514d91600052600d6020526040600020906001600160a01b03167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b600c5490810180911161373157600c55565b60046040517fed778779000000000000000000000000000000000000000000000000000000008152fd5b92909283018084116137315761519f9083613b9a565b670de0b6b3a7640000908181029080820483149015171561373157826151c491614dc6565b93806151d2575b5050505090565b6151dd930202614dc6565b9003388080806151cb565b67ffffffffffffffff16670a688906bd8b00008111610437576020817fb77b18869f1146e7805c1d2169aa277954f24055acb20c3e15ddc3de57eacb85927fffffffffffffffffffffffffffffffffffffffffffffffff00000000000000006008541617600855604051908152a1565b9092918361526591613b9a565b91670de0b6b3a76400009283810290808204851490151715613731578161528b91614dc6565b91820391808311613731576152a6612cf8916152ac9461534b565b916143fe565b8103908111613731576112239161506d565b601c600060649281946020966040519860605260405260601b602c526f23b872dd000000000000000000000000600c525af13d15600160005114171615615309576000606052604052565b637939f4246000526004601cfd5b67ffffffffffffffff60085416918261532f57505050565b6134d39261533c9161506d565b90615345613757565b906149bc565b670de0b6b3a7640000907812725dd1d243aba0e75fe645cc4873f9e65afe688c928e1f218111820215830215614d79570290808204910615150190565b9461ffff946112239a989460ff6153b26001600160a01b03969c9a95610100808c528b0190613c45565b9b1660208901526040880152606087015260808601521660a08401521660c082015260e08185039101526136c8565b9a96918b61ffff9a966101209c989f9e9a9661541a6154289460ff986001600160a01b03976101409087528060208801528601916136c8565b9260408185039101526136c8565b9c1660608b015216608089015260a088015260c087015260e086015261010085015216910152565b9899909997969795939294956154706001600160a01b038a161515613578565b61549b7f7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8710613578565b6154aa610a4d87601054613bad565b6154c2610faa610faa6002546001600160a01b031690565b906154cd8886613b5f565b91803b1561043757848c61551a8f968b958e8c996000996040519b8c9a8b998a997fd6512967000000000000000000000000000000000000000000000000000000008b5260048b01615388565b039134905af180156110b2576155d7575b50604088019661553b888a613abe565b8b6155468987613b5f565b884363ffffffff16936155589561585a565b908a6155648886613b5f565b9161556e936156f0565b61557a8230338c6152be565b615584858a615317565b6155916020890189613abe565b99909761559e908a613abe565b906040519b8c9b359a6155b19b8d6153e1565b037f39d3d3ef1ca8c28e2940bcde183fe626d94dca2df8d9ba3bdcd7a91caa31e36191a1565b806110a66155e492611d6f565b3861552b565b92909282018083116137315761560392610f4191614d44565b7ffffffffffffffffffffffffffffffffffffffffffffffffff21f494c589c00008101908113600116613731576112239161506d565b600052600d6020526040600020908154916001600160a01b038316928315610437577fffffffffffffffffffffffff0000000000000000000000000000000000000000169055600c5403600c5590565b90600090828252600d6020526001600160a01b03908160408420541661515f576040938352600d60205283832060017fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055168152600b60205220908154019055565b919091600091818352600d6020526001600160a01b03918260408520541661515f57604094615756918552600d602052858520906001600160a01b03167fffffffffffffffffffffffff0000000000000000000000000000000000000000825416179055565b168152600b60205220908154019055565b600a549061579e8261579261578760075465ffffffffffff9060601c1690565b65ffffffffffff1690565b42030262015180900490565b906006548281111561580657906157b491613b0f565b0390811161499257600780547fffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff164260601b71ffffffffffff000000000000000000000000161790556134d390600655565b50809291501161499257600780547fffffffffffffffffffffffffffff000000000000ffffffffffffffffffffffff164260601b71ffffffffffff000000000000000000000000161790556134d390600655565b94917fffffffff000000000000000000000000000000000000000000000000000000006078937fffffffffffffffffffffffffffffffffffffffff0000000000000000000000006150e6969760405198868a97602089019c8d378701956020870152604086015260601b16606084015260e01b166074820152036058810184520182611da4565b600052600d6020526040600020918254906001600160a01b038216938415610437577fffffffffffffffffffffffff0000000000000000000000000000000000000000615944931690556001600160a01b0316600052600b602052604060002090565b90815403905590565b81600019048111820261505f5702670de0b6b3a7640000808204910615150190565b7ffffffffffffffffffffffffffffffffffffffffffffffffdc0d0570925a462d7811315615b3657680755bf798b4a1bf1e5811215615b28576503782dace9d990604e1b0574029d9dc38563c32e5c2f6dc192ee70ef65f9978af36bb17217f7d1cf79abc9e3b3989179d835ebba824c98fb31b83b2ca45c0000000000000000000000006060916b8000000000000000000000008582851b0501831d94850290036e0587f503bb6ea29d25fcb740196450816c10fe68e7fd37d0007b713f7650810102841d936e05180bb14799ab47a8a8cb2a527d57837ffffffffffffffffffffffffffffffffffffd38dc772608b0ae56cce01296c0eb816db1bbb201f443cf962f1a1d3db4a5817fffffffffffffffffffffffffffffffffffffe5adedaa1cb095af9e4da10e363c816d0277594991cfc85f6e2461837cd9817fffffffffffffffffffffffffffffffffffffffdbf3ccf1604d263450f02a55048101028a1d0102881d0102861d0102841d0102821d01947ffffffffffffffffffffffffffffffffffffffe2c69812cf03b0763fd454a8f7e846d02d16720577bd19bf614176fe9ea830192010102901d01020105029060c3031c90565b63a37bfec96000526004601cfd5b5060009056fea26469706673582212205b620e5c19cd34ebff19ce0d6221c4cb20ca382d3b6b735234817ac60f7e822764736f6c63430008160033

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

00000000000000000000000000000000e5e81e25aead7fccb4c9560c6b5b718f000000000000000000000000000000575b0d9cc6ddbd8990db4d845fe480281f

-----Decoded View---------------
Arg [0] : factory_ (address): 0x00000000E5E81E25aeaD7fCCb4C9560C6b5b718F
Arg [1] : mathlib_ (address): 0x000000575b0D9cc6ddbd8990db4d845fe480281f

-----Encoded View---------------
2 Constructor Arguments found :
Arg [0] : 00000000000000000000000000000000e5e81e25aead7fccb4c9560c6b5b718f
Arg [1] : 000000000000000000000000000000575b0d9cc6ddbd8990db4d845fe480281f


Deployed Bytecode Sourcemap

2287:73514:11:-:0;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;:::i;:::-;;;;;;;:::o;:::-;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;6378:31:12;2287:73514:11;;;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;:::o;:::-;;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::o;:::-;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;-1:-1:-1;;;;;2287:73514:11;;;;;:::o;:::-;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;:::i;:::-;;;7568:413:3;2287:73514:11;7568:413:3;;;;;-1:-1:-1;7568:413:3;;;;;;-1:-1:-1;7568:413:3;;;;;;;2287:73514:11;-1:-1:-1;7568:413:3;2287:73514:11;;;7997:4:3;2287:73514:11;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;-1:-1:-1;;2287:73514:11;;;;;;;:::i;:::-;-1:-1:-1;;;;;18697:17:12;2287:73514:11;;;;18683:10:12;:31;2287:73514:11;;18784:3:12;;;:::i;:::-;2287:73514:11;;;;;;-1:-1:-1;;2287:73514:11;;;;;;8721:23;;:::i;:::-;8748:1;2287:73514;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;:::i;:::-;;;73973:29;73985:16;73973:29;;2287:73514;;;-1:-1:-1;;;;;2287:73514:11;;8467:15:12;2287:73514:11;;8453:10:12;:29;2287:73514:11;;;;34418:115:12;2287:73514:11;74482:39;2287:73514;34194:33:12;2287:73514:11;;34194:33:12;;;:::i;:::-;34377:15;;;;;;:::i;:::-;2287:73514:11;;;;;;;;2522:19:12;;2287:73514:11;2522:19:12;2287:73514:11;2522:19:12;;2287:73514:11;34418:115:12;-1:-1:-1;;;;;2287:73514:11;;;74482:30;2287:73514;;;;;;;74482:39;2287:73514;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;4765:35:12;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;;;52630:23;2287:73514;;;;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;52630:23;:::i;:::-;2287:73514;;;;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;;;;-1:-1:-1;;;;;8467:15:12;2287:73514:11;;;;;8453:10:12;:29;2287:73514:11;;31260:303:12;;;;31847:12;31260:303;31727:56;31260:303;;73428:25:11;31260:303:12;31894:145;31260:303;;31894:145;31260:303;;;:::i;:::-;31727:56;:::i;:::-;31847:12;:::i;:::-;2287:73514:11;;31894:145:12;;;;;:::i;:::-;;;;73428:25:11;2287:73514;73428:25;:::i;:::-;;2287:73514;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;6408:68:3;;2287:73514:11;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;:::o;:::-;;;;;;;:::i;:::-;;;;;-1:-1:-1;;;;;2287:73514:11;;;;8467:15:12;2287:73514:11;;8453:10:12;:29;2287:73514:11;;28696:8:12;-1:-1:-1;26731:410:12;27302:61;26731:410;;;;;;;;;;;;:::i;:::-;27302:61;:::i;:::-;27651:9;2287:73514:11;;;27974:83:12;;;;;;;;2287:73514:11;27974:83:12;;;2287:73514:11;;;;;;;27974:83:12;;;;;:::i;:::-;28067:504;;;;;;28696:8;;2287:73514:11;28696:8:12;28692:79;;2287:73514:11;71862:25;;2287:73514;28787:166:12;2287:73514:11;;28787:166:12;2287:73514:11;;;;;;;28787:166:12;;;;;:::i;71862:25:11:-;;2287:73514;;28692:79:12;28722:26;:21;28710:9;;;;;;;;28722:21;;:::i;:::-;28746:2;3138:4:11;;;;28722:26:12;-1:-1:-1;28706:65:12;;28692:79;;;;;;;;;;28706:65;2287:73514:11;;;28757:14:12;;;;2287:73514:11;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;:::i;:::-;-1:-1:-1;;;;;2287:73514:11;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;:::o;:::-;;;;;;;:::i;:::-;10164:1922:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;2287:73514:11;10164:1922:3;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;10164:1922:3;;;;-1:-1:-1;10164:1922:3;;;;;;;;;;;;;;;;;;;;2287:73514:11;;10164:1922:3;2287:73514:11;;;;;10164:1922:3;;-1:-1:-1;10164:1922:3;2287:73514:11;10164:1922:3;;;;;;;;;;;;;;;;;;;-1:-1:-1;10164:1922:3;2287:73514:11;10164:1922:3;;2287:73514:11;;;;;-1:-1:-1;;2287:73514:11;;;;;-1:-1:-1;;;;;2287:73514:11;;;;;:::i;:::-;;-1:-1:-1;2287:73514:11;6490:50:12;2287:73514:11;;;;-1:-1:-1;2287:73514:11;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;-1:-1:-1;;;;;3857:32:12;2287:73514:11;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;8241:14:12;;:::i;:::-;2287:73514:11;8227:10:12;:28;2287:73514:11;;18105:13:12;;;:::i;2287:73514:11:-;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;5933:2:3;2287:73514:11;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;-1:-1:-1;;;;;2287:73514:11;;;;;:::i;:::-;;-1:-1:-1;2287:73514:11;4534:42:12;2287:73514:11;;;;-1:-1:-1;2287:73514:11;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;;67407:28;2287:73514;;;;;;;;;;;;;;;67407:28;:::i;2287:73514::-;;;;;-1:-1:-1;;2287:73514:11;;;;;;16961:346:3;2287:73514:11;;:::i;:::-;;;;;;16884:24:3;16961:346;;;;;;;;;;;;;;;;;;;;;;;;;;;2287:73514:11;;;;;;;;;;;;;;;:::o;:::-;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;1575:245:8;;;;;;;;;;;;;;2287:73514:11;;58573:24;;;;;2287:73514;58573:24;;;;:::i;:::-;2287:73514;;;:::i;:::-;;;-1:-1:-1;2287:73514:11;8713:16:12;58573:24:11;2287:73514;;;58573:24;2287:73514;;;-1:-1:-1;2287:73514:11;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;8712:35:12;8708:67;;58817:35:11;-1:-1:-1;;;;;2287:73514:11;;58825:26;;58817:35;:::i;:::-;59089:11;59077:10;;59089:11;:::i;:::-;59722:336;59811:208;59727:331;59112:33;2287:73514;59133:12;2287:73514;;;;;;;;;;59112:33;59803:241;59365:29;;;:::i;:::-;6408:68:3;;;59657:46:11;6408:68:3;59682:16:11;59571:50;6408:68:3;59571:36:11;6408:68:3;;59587:20:11;2287:73514;59571:36;;:::i;:::-;:50;:::i;:::-;59682:16;;;:::i;:::-;59657:46;:::i;:::-;59811:208;:::i;:::-;59803:241;:::i;:::-;59727:331;;:::i;:::-;59722:336;;:::i;:::-;60222:29;60214:38;60234:16;60222:29;;60214:38;:::i;:::-;60296:25;;2287:73514;60296:25;2287:73514;60296:25;:::i;:::-;60393:64;:40;2287:73514;60417:15;2287:73514;-1:-1:-1;;;;;2287:73514:11;;;;-1:-1:-1;;;;;2287:73514:11;;;60393:64;:205;;;;;2287:73514;-1:-1:-1;2287:73514:11;60393:205;2287:73514;;;;;;60393:205;;;;;;;2287:73514;60393:205;;;;;:::i;:::-;;60465:9;;60393:205;;;;;;;;2287:73514;60393:205;61896:11;60393:205;61465:207;60393:205;;;2287:73514;61042:26;61465:207;61042:26;;;;;;61576;61538:24;61003:368;61042:26;;;;;;:::i;:::-;61298:12;2287:73514;;;61298:12;61003:368;:::i;:::-;61538:24;;;:::i;:::-;61576:26;;;;:::i;:::-;2287:73514;;;;61465:207;;;;;:::i;:::-;;;;61896:11;:::i;:::-;1883:75:8;;;2287:73514:11;;;;;;;;;;;;60393:205;;;;;;:::i;:::-;;;:::i;:::-;;;;;;:::i;8708:67:12:-;2287:73514:11;;8756:19:12;;;;1575:245:8;;;-1:-1:-1;1575:245:8;;;2287:73514:11;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;9628:715:12;2287:73514:11;;;:::i;:::-;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;9628:715:12;:::i;2287:73514:11:-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;-1:-1:-1;2287:73514:11;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;:::o;:::-;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;:::i;:::-;1575:245:8;;;;;;;;;;;;;;32880:11:11;2287:73514;;32868:10;32880:11;:::i;:::-;33064:33;2287:73514;33085:12;2287:73514;;;;;33064:33;3471:1:12;;;:::i;:::-;;;;:::i;:::-;33319:13:11;-1:-1:-1;33859:34:11;34148:14;3471:1:12;34148:14:11;;;;34136:1605;2287:73514;37233:332;36261:51;2287:73514;37228:337;2287:73514;36295:12;2287:73514;3471:1:12;;36261:51:11;:::i;:::-;37309:241;37341:208;6408:68:3;36827:46:11;6408:68:3;36852:16:11;36597:50;6408:68:3;;36597:36:11;2287:73514;;;36613:20;2287:73514;36597:36;;:::i;:50::-;2287:73514;;36852:16;;:::i;37341:208::-;37309:241;:::i;37228:337::-;37681:25;;;:::i;:::-;37753:10;-1:-1:-1;37716:22:11;;37748:1852;37765:15;3471:1:12;37765:15:11;;;;37748:1852;39721:6;;;;;;;;;39717:48;;2287:73514;;;39848:34;;2287:73514;39848:34;2287:73514;39848:34;:::i;:::-;;2287:73514;;39848:34;39896:17;2287:73514;;39896:35;;;;;39947:21;;;-1:-1:-1;39896:17:11;2287:73514;;39947:21;40197:47;2287:73514;;32868:10;40197:47;32868:10;;2287:73514;;40197:47;;:::i;:::-;;;;1883:75:8;1575:245;1883:75;2287:73514:11;;;;;;;:::i;39892:264::-;40096:35;3471:1:12;;39896:17:11;2287:73514;;40096:35;39892:264;;39717:48;2287:73514;;39736:29;;;;;;2287:73514;;;;;;;39736:29;;;;;;;37753:10;37911:16;;;;;;;;:30;:16;;;;;:::i;:::-;3471:1:12;-1:-1:-1;;;;;2287:73514:11;;3471:1:12;37911:30:11;;37907:41;;38032:46;38060:17;;;;;:::i;:::-;3471:1:12;38032:46:11;;:::i;:::-;38096:8;;;38092:503;;38608:8;;;;:::i;:::-;38766:16;;;;;:::i;:::-;3471:1:12;-1:-1:-1;;;;;2287:73514:11;;;;;34356:7;2287:73514;;;;;;38976:20;;;;;;;:::i;:::-;3471:1:12;38950:73:11;;;:::i;:::-;39095:10;;;;;;:::i;:::-;3471:1:12;39095:24:11;39091:80;;39234:25;39503;39485:43;39234:25;;;;;39360:16;;39234:25;;3471:1:12;39234:25:11;;;39390:11;39234:25;;:::i;:::-;3471:1:12;32868:10:11;39360:16;;:::i;:::-;39390:11;:::i;:::-;39503:25;:::i;:::-;39485:43;;:::i;:::-;3471:1:12;;37753:10:11;;;;;;;;;;;;39091:80;39160:10;39128:43;39160:10;;;;;:::i;:::-;2287:73514;;;39128:43;;;;;;2287:73514;;;3471:1:12;;;;;2287:73514:11;;;;;3471:1:12;;;38092:503:11;38250:17;;;;;;;;:::i;:::-;3471:1:12;38246:57:11;;38424:10;;;;;:::i;:::-;3471:1:12;38420:61:11;;3471:1:12;;38572:8:11;;;38420:61;38470:10;;;;;;:::i;:::-;3471:1:12;2287:73514:11;38448:33;2287:73514;;38448:33;;;;;;;;3471:1:12;;;;;;2287:73514:11;-1:-1:-1;2287:73514:11;;3471:1:12;2287:73514:11;3471:1:12;38246:57:11;2287:73514;;;38281:22;;;;37907:41;37943:5;;;34141;34203:17;;;;;;;;;;;;2287:73514;;;;;;;;;;34203:17;2287:73514;-1:-1:-1;;;;;2287:73514:11;;;34203:17;-1:-1:-1;;;;;2287:73514:11;;34246:19;;34242:30;;34294:23;34493:37;34294:23;;;;;;;:::i;:::-;3471:1:12;-1:-1:-1;;;;;2287:73514:11;3471:1:12;;;34294:23:11;2287:73514;34356:14;;;-1:-1:-1;;;;;2287:73514:11;;;34356:7;2287:73514;;;;;;;34356:14;2287:73514;;;;34493:37;;;;;2287:73514;34493:37;;1575:245:8;34493:37:11;;;2287:73514;;;-1:-1:-1;;;;;2287:73514:11;;;;;;;;34493:37;;;;;;;;;;;34934:53;34493:37;;;;34944:42;34493:37;-1:-1:-1;34493:37:11;;;34141:5;34764:22;34949:37;34764:22;;34737:49;34764:22;34759:27;34764:22;;-1:-1:-1;;;;;2287:73514:11;;;34764:15;2287:73514;;;;;;;34764:22;2287:73514;34759:27;;:::i;:::-;34737:49;;:::i;:::-;3471:1:12;-1:-1:-1;;;;;2287:73514:11;;;74482:30;2287:73514;;;;;;;34949:37;2287:73514;34944:42;;:::i;34934:53::-;35182:23;35178:461;;34141:5;3471:1:12;;;;34141:5:11;;;;;;;;;;;35178:461;35300:42;35239:231;35586:30;35300:42;;3471:1:12;35300:42:11;;;:::i;35239:231::-;35586:30;;:::i;:::-;35178:461;;;;;;;34493:37;34949;34493;;;;2287:73514;34493:37;2287:73514;34493:37;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;;;;;34242:30;34267:5;;;;;;;;;;;;;1575:245:8;;;-1:-1:-1;1575:245:8;;;2287:73514:11;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;6393:65;2287:73514;;;;;;:::i;:::-;;-1:-1:-1;;;;;4769:7:11;4747:30;4769:7;;2287:73514;4755:10;:21;4747:30;:::i;:::-;4874:1;2287:73514;;;;;4851:40;4859:17;;2287:73514;4859:17;2287:73514;4859:17;2287:73514;4859:31;4851:40;:::i;:::-;2287:73514;2458:4:4;5017:36:11;5025:27;;;5017:36;:::i;:::-;3471:1:12;2287:73514:11;;;6322:57;;2287:73514;6322:57;2287:73514;;;;;;6322:57;2287:73514;;6322:57;2287:73514;6322:57;2287:73514;;;;;;;;;6322:57;2287:73514;;6393:65;6556:25;;:::i;:::-;6675:10;-1:-1:-1;6591:27:11;;6670:1233;6687:16;;;;;;8131:34;8280:61;8131:34;;;;39848;2287:73514;;8131:34;8244:19;;;:::i;:::-;8280:61;2287:73514;;;;;;8280:61;;;:::i;:::-;;;;2287:73514;6675:10;6744;;;;;;;;;;;;;:::i;:::-;;:::i;:::-;6768:18;:33;:18;;;2287:73514;;;;;;;;;;6768:18;2287:73514;-1:-1:-1;;;;;2287:73514:11;;;;;;;;;6768:33;6833:11;;;;;:::i;:::-;3471:1:12;6866:11:11;6858:20;6866:11;;;6858:20;:::i;:::-;6934:21;;;-1:-1:-1;;;;;2287:73514:11;;;34356:7;2287:73514;;;;;;;6934:21;2287:73514;;;;7605:44;;7643:4;7605:44;;;2287:73514;;;;;;;;;;;7605:44;;;;;;2287:73514;;;7605:44;;;;;;3471:1:12;7605:44:11;;;7809:22;7605:44;7790:41;7605:44;-1:-1:-1;7605:44:11;;;6675:10;7671:18;;;;7740:35;7671:18;7663:27;7671:18;;;7663:27;:::i;:::-;7740:35;:::i;:::-;3471:1:12;7809:22:11;:::i;7790:41::-;3471:1:12;;6675:10:11;;;;;;;7605:44;7740:35;7605:44;;;;;;;;;;-1:-1:-1;7605:44:11;;;;;;:::i;:::-;;;;;;;2287:73514;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;:::i;:::-;;;;;-1:-1:-1;;2287:73514:11;;;;;;;5056:23:12;2287:73514:11;;;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;1575:245:8;;;;;;;;;;;;40887:43:11;;2287:73514;40920:9;2287:73514;;;;;;;;;;;;;40887:43;;;:::i;:::-;40986:47;41020:12;;;;:::i;:::-;40986:47;;;:::i;:::-;41116:12;;;;41112:56;;1575:245:8;;42150:54:11;1575:245:8;41628:3:11;2287:73514;1575:245:8;41469:6:11;1575:245:8;;41442:10:11;41469:6;;:::i;:::-;41536:3;41442:10;;41536:3;;:::i;:::-;41628;;:::i;:::-;41768:27;41777:18;;-1:-1:-1;;;;;2287:73514:11;;;34356:7;2287:73514;;;;;;;41777:18;2287:73514;41768:27;;:::i;:::-;41827:22;41833:16;;-1:-1:-1;;;;;2287:73514:11;;;34356:7;2287:73514;;;;;;;41833:16;2287:73514;41827:22;;:::i;:::-;41953:28;;;;;;42017;41997:48;42017:28;41997:48;42017:28;;:::i;:::-;41997:48;2287:73514;41997:48;:::i;:::-;2287:73514;;;-1:-1:-1;;;;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;41442:10;;2287:73514;;;;42150:54;;;;1883:75:8;;;2287:73514:11;;;;;;;;;;;;;41949:186;42096:28;42076:48;42096:28;42076:48;42096:28;;:::i;:::-;42076:48;2287:73514;42076:48;:::i;:::-;41949:186;;41112:56;2287:73514;;41137:31;;;2287:73514;41137:31;;2287:73514;;;3471:1:12;;;2287:73514:11;;;;3471:1:12;;39736:29:11;1575:245:8;;-1:-1:-1;1575:245:8;2287:73514:11;1575:245:8;;2287:73514:11;;;;;-1:-1:-1;;2287:73514:11;;;;;;;:::i;:::-;-1:-1:-1;;;;;8241:14:12;;:::i;:::-;2287:73514:11;8227:10:12;:28;2287:73514:11;;18414:3:12;;;:::i;2287:73514:11:-;;;;;;;;;;;;;;;;;;:::o;:::-;;:::i;:::-;;;;;;;;;;;;;;;:::o;:::-;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;1575:245:8;;;;;;;;;;;;23224:11:11;23212:10;;23224:11;:::i;:::-;23407:33;2287:73514;23428:12;2287:73514;;;;;23407:33;3471:1:12;;;:::i;:::-;;;;:::i;:::-;24134:10:11;-1:-1:-1;24003:34:11;24171:15;3471:1:12;24171:15:11;;;;24158:1347;26079:12;2287:73514;26045:52;;3471:1:12;26045:52:11;:::i;:::-;26282:25;;;:::i;:::-;6408:68:3;;26928:49:11;26841:50;6408:68:3;26841:36:11;6408:68:3;;26857:20:11;2287:73514;26841:36;;:::i;:50::-;26929:42;26930:16;;;;:::i;:::-;26929:42;:::i;:::-;26928:49;:::i;:::-;27212:16;;;:358;;;;-1:-1:-1;27651:20:11;;;;:::i;:::-;27686:10;;27698:15;3471:1:12;27698:15:11;;;;27681:3486;2287:73514;;;;;;;;;;;31241:34;;2287:73514;31241:34;2287:73514;31241:34;:::i;:::-;31289:17;2287:73514;;31289:35;;;;;31340:21;;;-1:-1:-1;39896:17:11;2287:73514;;31340:21;31585:47;2287:73514;;23212:10;31585:47;23212:10;;31585:47;;;:::i;:::-;;;;1883:75:8;;;2287:73514:11;;;;;;;:::i;31285:259::-;31484:35;3471:1:12;;39896:17:11;2287:73514;;31484:35;31285:259;;27686:10;27746:16;;;;;:::i;:::-;-1:-1:-1;;;;;2287:73514:11;;27780:19;27776:30;;28120:26;;28040:234;28120:26;:50;:26;;;;:::i;:::-;3471:1:12;28120:50:11;:::i;28040:234::-;28679:26;;;;;:::i;:::-;3471:1:12;28873:33:11;;;;28869:1456;;27686:10;30424:37;;30567;30424;;30730:10;30424:37;;:::i;:::-;30590:14;;;-1:-1:-1;;;;;2287:73514:11;;;34356:7;2287:73514;;;;;;;30590:14;2287:73514;30567:37;;:::i;:::-;30730:10;;:::i;:::-;3471:1:12;30774:33:11;;;30770:98;;30922:33;;31075:19;30922:33;;;;;3471:1:12;30922:33:11;;;:::i;:::-;3471:1:12;23212:10:11;31075:19;;:::i;:::-;3471:1:12;27686:10:11;;30770:98;2287:73514;;30816:52;;;;;;2287:73514;;;3471:1:12;;;2287:73514:11;;;;;;;3471:1:12;;39736:29:11;;;28869:1456;30567:37;30424;29716:594;30730:10;30101:33;;29803:489;29835:456;30101:33;;;;29966:245;30101:33;;;;:::i;:::-;29966:245;:::i;29716:594::-;28869:1456;;;;;;;27776:30;27801:5;;;27212:358;27361:194;27249:321;27361:194;;27329:227;27361:194;;:::i;27249:321::-;27212:358;;;24163:6;24223:18;;;2287:73514;;;;;;;;;;24223:18;-1:-1:-1;;;;;2287:73514:11;;24263:19;;;24259:30;;24307:24;;;;;;:::i;:::-;24366:14;;-1:-1:-1;;;;;2287:73514:11;;;34356:7;2287:73514;;;;;;;24366:14;2287:73514;;;;24520:37;;1575:245:8;24520:37:11;;;2287:73514;;;;;;;;;;;;;;;;;24520:37;;;;;;;;;;24511:46;24520:37;-1:-1:-1;24520:37:11;;;24163:6;24511:46;;;;:::i;:::-;24739:22;24718:52;24739:31;:22;;;-1:-1:-1;;;;;2287:73514:11;;;34764:15;2287:73514;;;;;;;24739:22;2287:73514;24739:31;:::i;:::-;24718:52;;:::i;:::-;24689:81;;;;:::i;:::-;3471:1:12;24931:23:11;24927:491;;24163:6;3471:1:12;;;;;;24163:6:11;;24927:491;25070:37;25048:93;25049:67;3471:1:12;25070:37:11;;;:46;24991:270;25070:37;;25369:30;25070:37;-1:-1:-1;;;;;2287:73514:11;;;74482:30;2287:73514;;;;;;;25070:46;25049:67;;:::i;25369:30::-;24927:491;;;;;;;;;24520:37;;;;;;-1:-1:-1;24520:37:11;;;;;;:::i;:::-;;;;;24259:30;24284:5;;;;2287:73514;;;;;-1:-1:-1;;2287:73514:11;;;;;;;5264:33:12;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;2287:73514:11;;;;;;:::o;:::-;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;4103:66:12;2287:73514:11;;;;;;;;;;;;;;;:::i;:::-;;;-1:-1:-1;2287:73514:11;4103:66:12;2287:73514:11;;;-1:-1:-1;2287:73514:11;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;4103:66:12;2287:73514:11;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;-1:-1:-1;;;;;7088:32:12;2287:73514:11;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;-1:-1:-1;;;;;2287:73514:11;12754:12:12;2287:73514:11;;12754:26:12;:61;;;;2287:73514:11;;;;;;;;;;;12754:61:12;2287:73514:11;;;;;;;;;;;;;12784:31:12;;12754:61;;;;2287:73514:11;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;:::i;:::-;6680:148:3;;;-1:-1:-1;6680:148:3;2287:73514:11;6680:148:3;;;;2287:73514:11;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;6602:35:12;2287:73514:11;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;-1:-1:-1;;;;;2287:73514:11;;;;;:::i;:::-;;-1:-1:-1;2287:73514:11;2510:65;2287:73514;;;;-1:-1:-1;2287:73514:11;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;:::i;:::-;12929:205:3;;;-1:-1:-1;12929:205:3;2287:73514:11;12929:205:3;;;;2287:73514:11;;;;;;;;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;-1:-1:-1;;2287:73514:11;;;;;11631:12:12;2287:73514:11;-1:-1:-1;;;;;2287:73514:11;;11617:10:12;:26;2287:73514:11;;;;11631:12:12;2287:73514:11;11712:13:12;;;;2287:73514:11;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;-1:-1:-1;2287:73514:11;;;;;-1:-1:-1;;;;;2287:73514:11;-1:-1:-1;2287:73514:11;;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;4727:31:12;2287:73514:11;;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;3713:7:12;;2287:73514:11;;;;;:::i;:::-;;;;;;;3713:7:12;2287:73514:11;;;3713:7:12;;;;2287:73514:11;;;;;;;;;;;;;;:::i;:::-;;;3713:7:12;2287:73514:11;;;;;;;;;-1:-1:-1;;;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;8944:23;;8994:15;3471:1:12;8994:15:11;;;;8977:314;39848:34;2287:73514;-1:-1:-1;2287:73514:11;;8982:10;9042:18;;;2287:73514;;;;;;;;;;9042:18;2287:73514;-1:-1:-1;;;;;2287:73514:11;;9078:19;;;9074:30;;2287:73514;;;9139:37;;9170:4;2287:73514;9139:37;;2287:73514;;9139:37;;;;2287:73514;;;;;;9139:37;;;;;;;9205:14;9139:62;9138:81;9139:37;3471:1:12;9139:37:11;9119:100;9139:37;;;;;8982:10;9179:22;;;;-1:-1:-1;;;;;2287:73514:11;;;34764:15;2287:73514;;;;;;;9139:62;9205:14;-1:-1:-1;;;;;2287:73514:11;;;34356:7;2287:73514;;;;;;;9205:14;2287:73514;9138:81;;:::i;9119:100::-;3471:1:12;;8982:10:11;;;9139:37;;;;;;-1:-1:-1;9139:37:11;;;;;;:::i;:::-;;;;;9074:30;9099:5;;;;;2287:73514;;;;;-1:-1:-1;;2287:73514:11;;;;;;5407:32:12;2287:73514:11;-1:-1:-1;;;;;2287:73514:11;;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;-1:-1:-1;2287:73514:11;6736:48:12;2287:73514:11;;;-1:-1:-1;;;;;2287:73514:11;-1:-1:-1;2287:73514:11;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;:::o;:::-;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;49144:9;2287:73514;;;;;;:::i;:::-;;;;;;;;;;;;49144:9;;:::i;2287:73514::-;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;:::i;:::-;;;8378:1143:3;;;;;-1:-1:-1;8378:1143:3;2287:73514:11;8378:1143:3;;;;;;;;;;;;;;-1:-1:-1;8378:1143:3;2287:73514:11;8378:1143:3;;;;;;;;2287:73514:11;8378:1143:3;;;;;;;2287:73514:11;8378:1143:3;;2287:73514:11;;10164:1922:3;2287:73514:11;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;3283:25;2287:73514;;;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;1903:2157:7;2287:73514:11;1903:2157:7;;;2287:73514:11;1903:2157:7;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;2287:73514:11;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;-1:-1:-1;;;;;10975:12:12;2287:73514:11;;10961:10:12;:26;2287:73514:11;;11040:2:12;11022:20;;2287:73514:11;;11160:40:12;2287:73514:11;11160:40:12;2287:73514:11;;-1:-1:-1;2287:73514:11;11100:16:12;2287:73514:11;;11100:44:12;2287:73514:11;;-1:-1:-1;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;11100:44:12;2287:73514:11;;11160:40:12;;;;;:::i;:::-;;;;2287:73514:11;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;2287:73514:11;;8467:15:12;2287:73514:11;;8453:10:12;:29;2287:73514:11;;36211:58:12;2287:73514:11;;;;36211:58:12;:::i;:::-;;75457:25:11;2287:73514;;75457:25;2287:73514;75457:25;:::i;:::-;;2287:73514;;-1:-1:-1;2287:73514:11;75723:30;2287:73514;;;-1:-1:-1;2287:73514:11;;;;3471:1:12;2287:73514:11;;-1:-1:-1;2287:73514:11;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;-1:-1:-1;;;;;3895:30:12;2287:73514:11;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;;;;8467:15:12;2287:73514:11;;8453:10:12;:29;2287:73514:11;;;;;;;;;;;:::i;:::-;;;-1:-1:-1;2287:73514:11;8713:16:12;2287:73514:11;;;-1:-1:-1;2287:73514:11;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;8712:35:12;8708:67;;35506:12;2287:73514:11;74951:43;2287:73514;35314:58:12;2287:73514:11;;;;35314:58:12;:::i;:::-;;35506:12;;:::i;:::-;-1:-1:-1;;;;;2287:73514:11;;;74482:30;2287:73514;;;;;;;74951:43;2287:73514;;;3471:1:12;2287:73514:11;;-1:-1:-1;2287:73514:11;;8708:67:12;2287:73514:11;;;8756:19:12;;;;2287:73514:11;;;;;-1:-1:-1;;2287:73514:11;;;;;;57051:195;56892:12;2287:73514;;;57212:20;56943:29;;;:::i;:::-;57212:20;;;:::i;:::-;57051:195;;:::i;2287:73514::-;;;;;-1:-1:-1;;2287:73514:11;;;;;;-1:-1:-1;;;;;5714:27:12;2287:73514:11;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;:::i;:::-;;;;:::i;:::-;;;;;;;;13704:24:3;13781:2752;;;;;;2287:73514:11;13781:2752:3;;-1:-1:-1;;;;;13781:2752:3;;;;;;;;;-1:-1:-1;13781:2752:3;;;;2287:73514:11;13781:2752:3;;;;;;;;;;;;;;;;;2287:73514:11;13781:2752:3;;;;;;;2287:73514:11;13781:2752:3;;;;;;;2287:73514:11;13781:2752:3;;;;;;2287:73514:11;13781:2752:3;;;;;;;;;;;;;2287:73514:11;13781:2752:3;;;;;;;;;;;;;;;2287:73514:11;;;13781:2752:3;2287:73514:11;;;13781:2752:3;;;2287:73514:11;13781:2752:3;13669:22;13781:2752;;;;;;;;;;;;;;;;;;;2287:73514:11;13781:2752:3;;;;;;2287:73514:11;;13781:2752:3;;;;2287:73514:11;13781:2752:3;;;;-1:-1:-1;13781:2752:3;2287:73514:11;13781:2752:3;;2287:73514:11;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;:::i;:::-;;;;;;:::i;:::-;;7114:184:3;;;;-1:-1:-1;7114:184:3;2287:73514:11;7114:184:3;;;;2287:73514:11;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;:::i;:::-;;;:::i;:::-;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;46806:9;2287:73514;;;;;;:::i;:::-;;;;;;;;;46806:9;;:::i;2287:73514::-;;;;;-1:-1:-1;;2287:73514:11;;;;;;3538:26;2287:73514;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;1575:245:8;;;;;;;;;;;;14324:33:11;2287:73514;14345:12;2287:73514;;;;;14324:33;14647:11;-1:-1:-1;15731:34:11;;15779:27;;15825:10;;15837:15;3471:1:12;15837:15:11;;;;15820:3453;2287:73514;;;21590:75;21873:98;2287:73514;;;19616:36;;2287:73514;19354:35;;21728:20;2287:73514;19354:35;2287:73514;19354:35;:::i;:::-;19616:36;2287:73514;19616:36;:::i;:::-;39896:17;2287:73514;;19616:36;20245:12;2287:73514;3471:1:12;;20965:9:11;20957:18;-1:-1:-1;20965:9:11;;;20957:18;:::i;:::-;21632:9;2287:73514;21608:33;;2287:73514;;;;;;21608:33;2458:4:4;3471:1:12;3185:5:11;2458:4:4;3138::11;;;;21590:75;21728:20;;:::i;:::-;6408:68:3;;;21873:98:11;;:::i;:::-;22080:20;;;;22076:72;;2287:73514;19045:10;;22241:11;19045:10;;22241:11;:::i;:::-;22303:51;2287:73514;;19045:10;22303:51;19045:10;22303:51;;;;:::i;22076:72::-;-1:-1:-1;;2287:73514:11;;22109:39;;;2287:73514;22109:39;;2287:73514;;;;3471:1:12;;;2287:73514:11;;;;3471:1:12;;39736:29:11;15825:10;15889:18;;;2287:73514;;;;;;;;;;15889:18;2287:73514;-1:-1:-1;;;;;2287:73514:11;;15929:19;;;15925:30;;15990:14;;-1:-1:-1;;;;;2287:73514:11;;;34356:7;2287:73514;;;;;;;15990:14;2287:73514;16044:16;;;;;:::i;:::-;3471:1:12;2287:73514:11;;;16217:37;;1575:245:8;16217:37:11;;;2287:73514;;;3471:1:12;;2287:73514:11;;;;;;;;;;;;16217:37;;;;;;;;;16208:46;16217:37;-1:-1:-1;16217:37:11;;;15825:10;16208:46;;;;:::i;:::-;16578:14;-1:-1:-1;16618:23:11;;;16614:864;;15825:10;17855:16;;;17851:175;;18598:239;18681:20;;18890:39;18681:20;18606:231;18681:20;;18659:67;18660:41;18909:20;18681;;3471:1:12;18681:20:11;;19112:11;18681:20;;;:::i;18606:231::-;3471:1:12;;18598:239:11;:::i;:::-;18909:20;;;:::i;18890:39::-;1575:245:8;;19045:10:11;;19112:11;;:::i;:::-;3471:1:12;15825:10:11;;;;;17851:175;3471:1:12;;;;;;;;;;17995:8:11;;;16614:864;17161:30;16846:37;;;16824:93;16825:67;16846:37;:46;16763:282;16846:37;;;;;;-1:-1:-1;;;;;2287:73514:11;;;74482:30;2287:73514;;;;;;;17161:30;17285:42;;17224:231;17285:42;;;;:::i;17224:231::-;16614:864;;;;16217:37;;;;;;-1:-1:-1;16217:37:11;;;;;;:::i;:::-;;;;;15925:30;15950:5;;;;;2287:73514;;;;;;;:::i;:::-;;-1:-1:-1;;;;;8467:15:12;2287:73514:11;;;;;;8453:10:12;:29;2287:73514:11;;29974:303:12;;30288:56;29974:303;;;;30390:145;29974:303;30390:145;29974:303;;;:::i;30288:56::-;;2287:73514:11;;30390:145:12;;;;;:::i;2287:73514:11:-;;;;;-1:-1:-1;;2287:73514:11;;;;;;3314:33;2287:73514;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;-1:-1:-1;;;;;8467:15:12;2287:73514:11;;8453:10:12;:29;2287:73514:11;;24587:410:12;;;;;25008:61;24587:410;;;69404:20:11;24587:410:12;;;25120:166;24587:410;;;25120:166;24587:410;;:::i;25008:61::-;;2287:73514:11;;25120:166:12;;;;;:::i;:::-;;;;-1:-1:-1;;;;;2287:73514:11;;;34356:7;2287:73514;;;;;;;69404:20;2287:73514;3185:5;69452:17;2287:73514;69573:28;;;;69569:430;69573:28;;;69621:48;3471:1:12;;39896:17:11;2287:73514;;69621:48;70295:16;2287:73514;;;;;70561:13;;;;;70594:36;;-1:-1:-1;;70295:16:11;2287:73514;;70557:153;39848:34;2287:73514;;69569:430;69756:7;69752:247;69569:430;69752:247;69963:21;-1:-1:-1;39896:17:11;2287:73514;;69963:21;69569:430;;2287:73514;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;:::o;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;2287:73514:11;;-1:-1:-1;;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;1490:474:8:-;;;;;;;;;;;1575:245;;;;;;;;;;;-1:-1:-1;;;;;2287:73514:11;8467:15:12;2287:73514:11;;8453:10:12;:29;2287:73514:11;;;;;;;:::i;:::-;;;;;8713:16:12;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;8712:35:12;8708:67;;2287:73514:11;;52760:14;2287:73514;;;;;;;;52816:79;;;;;;:::i;:::-;52995:15;;;;;;:::i;:::-;2287:73514;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;53027:237;;;;1883:75:8;;;;1490:474::o;8708:67:12:-;8756:19;2287:73514:11;;8756:19:12;;;;2287:73514:11;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;-1:-1:-1;2287:73514:11;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;-1:-1:-1;2287:73514:11;;;;;;;;;;;;;:::o;:::-;;:::i;:::-;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;7792:154:12;2287:73514:11;;;7884:55:12;;;7903:7;7884:55;7903:7;-1:-1:-1;;;;;7903:7:12;2287:73514:11;7884:55:12;;;;;;;;;;;7877:62;7792:154;:::o;7884:55::-;;;;;;;;;;;;;;;:::i;:::-;;;;;:::i;:::-;;;;;1490:474:8;;;;;;;;;;;1575:245;;;;;;;;;;;-1:-1:-1;;;;;8467:15:12;2287:73514:11;;8453:10:12;:29;2287:73514:11;;;;;;;:::i;:::-;;;;;8713:16:12;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;8712:35:12;8708:67;;63182:20:11;62861:33;2287:73514;62882:12;2287:73514;;;;;62861:33;63114:29;63245:20;63114:29;;;:::i;:::-;63182:20;;;;:::i;:::-;63245;;:::i;:::-;6408:68:3;;;63390:86:11;;;;;;;:::i;:::-;63561:28;;;;63557:88;;63736:22;;;;;;63732:841;;1490:474:8;2287:73514:11;;;;;64619:25;;2287:73514;64619:25;2287:73514;64619:25;:::i;:::-;64984:26;;;;64980:61;;65133:225;;;67760:20;65133:225;67587:102;65133:225;67587:102;65133:225;;65892:44;65133:225;;;;65081:712;65133:225;;;65377:402;65409:369;65133:225;65513:206;65133:225;65892:44;65133:225;;;:::i;:::-;65625:25;;;;;:::i;65377:402::-;65081:712;;:::i;:::-;65892:44;:::i;:::-;;:::i;:::-;2287:73514;67587:102;;;;;:::i;:::-;;;;67760:20;:::i;:::-;1883:75:8;;;;1490:474::o;64980:61:11:-;65019:22;2287:73514;;65019:22;;;;63732:841;63801:273;64372:36;64345:22;64343:105;63801:273;;64344:79;63801:273;64372:50;63801:273;;:::i;64345:22::-;2287:73514;64388:20;2287:73514;64372:36;;:::i;:50::-;64344:79;;:::i;:::-;2458:4:4;3138::11;;;;64343:105;64466:34;;;64462:100;;63732:841;;;;;;64462:100;2287:73514;;64509:53;;;;;;2287:73514;;;;3471:1:12;;;2287:73514:11;;;;3471:1:12;;39736:29:11;63557:88;2287:73514;63598:47;2287:73514;;63598:47;;;;;;;;;3471:1:12;;;;;;;;;2287:73514:11;;;3471:1:12;2287:73514:11;3471:1:12;8708:67;8756:19;2287:73514:11;;8756:19:12;;;;2287:73514:11;;;;;;;-1:-1:-1;;;;;2287:73514:11;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;2458:4:4:-;;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;28742:1:12;2458:4:4;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;2287:73514:11;;;;2458:4:4;;;;:::o;:::-;;;;;;;;;;:::o;:::-;;;;;;;2287:73514:11;;;;2458:4:4;;;;;;:::i;:::-;;;;;;;;;;;;:::i;:::-;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;2287:73514:11;;2458:4:4;;;;;;:::i;:::-;2287:73514:11;2458:4:4;;;2287:73514:11;2458:4:4;;;;;:::i;:::-;2287:73514:11;2458:4:4;;;2287:73514:11;-1:-1:-1;;;;;2458:4:4;;;2287:73514:11;;;;:::i;:::-;;2458:4:4;;;2287:73514:11;2458:4:4;;;;;:::i;:::-;;;;;;;;;;;;;;:::i;:::-;;;;;;;2287:73514:11;;2458:4:4;;;2287:73514:11;:::i;:::-;;2458:4:4;;;2287:73514:11;;2458:4:4;;;;2287:73514:11;:::i;:::-;;;2458:4:4;;2287:73514:11;;2458:4:4;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;2287:73514:11;2458:4:4;2287:73514:11;2458:4:4;;;;;;;2287:73514:11;2458:4:4;;;;;;;;:::i;:::-;;;;;;;;;;;;;;2287:73514:11;;2458:4:4;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;2287:73514:11;2458:4:4;2287:73514:11;2458:4:4;;;;;2287:73514:11;2458:4:4:o;2287:73514:11:-;;;;;;;;;;;:::o;3138:4::-;;;;;;;;;;;;;;;;;:::o;:::-;;:::i;9801:1163::-;2287:73514;;;10019:41;;10054:4;10019:41;;;2287:73514;;;10019:41;2287:73514;;;-1:-1:-1;;;;;2287:73514:11;;10019:41;;;;;;;-1:-1:-1;10019:41:11;;;9801:1163;10082:18;10363:47;10082:18;3138:4;10082:18;-1:-1:-1;;;;;2287:73514:11;;;34356:7;2287:73514;;;;;;;10082:18;2287:73514;10363:47;2287:73514;10397:12;2287:73514;;;;;10363:47;;;;;:::i;:::-;3138:4;;10838:28;;10834:86;;9801:1163;:::o;10834:86::-;3185:5;2458:4:4;3185:5:11;;3138:4;10868:52;:::o;10019:41::-;3138:4;10019:41;;;10082:18;10019:41;10363:47;10019:41;;;;;;;;;;;:::i;:::-;;;;;;;3081:1113:5;;;;;;;;;2001:66;3207:622;;;;;;;;;3081:1113;2287:73514:11;10185:13:12;2287:73514:11;10230:16:12;2287:73514:11;;;10148:8:12;2287:73514:11;-1:-1:-1;;;;;2287:73514:11;;;;;;10056:32:12;2287:73514:11;;;10056:32:12;2287:73514:11;;;10098:26:12;2287:73514:11;;;10098:26:12;2287:73514:11;10148:8:12;:::i;:::-;10185:13;:::i;:::-;10230:16;:::i;:::-;2287:73514:11;;;;;-1:-1:-1;;;2287:73514:11;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;:::i;:::-;3892:296:5;;3081:1113;:::o;3892:296::-;10056:32:12;3892:296:5;;10319:17:12;3892:296:5;;;;;;3081:1113::o;2287:73514:11:-;;;;-1:-1:-1;2287:73514:11;;;;;10296:13:12;2287:73514:11;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;-1:-1:-1;;2287:73514:11;;3207:622:5;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;2287:73514:11;;;;;;;;3207:622:5;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;10185:13:12;3207:622:5;;;;-1:-1:-1;3207:622:5;;;;2287:73514:11;;;;;;;;:::o;:::-;10296:13:12;2287:73514:11;10296:13:12;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;-1:-1:-1;2287:73514:11;;;;;;;;;;;;;:::o;:::-;10319:17:12;-1:-1:-1;;2287:73514:11;10319:17:12;-1:-1:-1;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;;;;;;;;;;-1:-1:-1;2287:73514:11;;;;;;;;;;;;10319:17:12;2287:73514:11;;;;;;;:::i;:::-;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;:::o;:::-;10319:17:12;2287:73514:11;;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;-1:-1:-1;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;3471:1:12;2287:73514:11;;;3471:1:12;2287:73514:11;;;;;;;;;;;;;3471:1:12;;;;:::o;:::-;;;;;;;;;;;;;;;;;;;;;;:::o;:::-;;:::i;:::-;2287:73514:11;;;;;;:::i;:::-;;3471:1:12;;2287:73514:11;3471:1:12;2287:73514:11;3471:1:12;;;:::o;:::-;;;;;;;;;;;;:::o;:::-;2287:73514:11;;3471:1:12;;;;;;;;;;;;:::o;:::-;;;;;2458:4:4;2287:73514:11;;3471:1:12;;;;;;;;:::i;:::-;;;;;;2287:73514:11;;3471:1:12;;;;;;;;:::i;2287:73514:11:-;;;;;:::i;7577:111:12:-;2287:73514:11;;;7657:24:12;;;7665:7;7657:24;7665:7;-1:-1:-1;;;;;7665:7:12;2287:73514:11;7657:24:12;;;;;;;;;;;7650:31;7577:111;:::o;84:4:28:-;;;;;;;;:::o;11290:703:11:-;;2287:73514;;;11522:39;;11555:4;11522:39;;;2287:73514;11522:39;2287:73514;;;-1:-1:-1;;;;;2287:73514:11;;11522:39;;;;;;;-1:-1:-1;11522:39:11;;;11290:703;11564:24;;;-1:-1:-1;;;;;2287:73514:11;;;34764:15;2287:73514;;;;;;;11564:24;2287:73514;2458:4:4;;;;;;;11610:16:11;11943:43;11610:16;-1:-1:-1;;;;;2287:73514:11;;;34356:7;2287:73514;;;;;;;11610:16;2287:73514;;11973:12;2287:73514;;;11943:43;;:::i;11522:39::-;;;;;;;;;;;;;;:::i;:::-;;;;12536:903;2287:73514;;;12709:41;;;12744:4;12709:41;;;2287:73514;;;;12536:903;-1:-1:-1;;;;;2287:73514:11;12709:41;;;2287:73514;;;;;;12709:41;;;;;;;-1:-1:-1;12709:41:11;;;12536:903;-1:-1:-1;2287:73514:11;;12772:39;;;12744:4;12709:41;12772:39;;2287:73514;;;;;;;;;;;;12772:39;;;;;;12862:18;12772:66;12904:16;12772:39;3138:4;12772:39;12983:91;12772:39;-1:-1:-1;12772:39:11;;;12536:903;12814:24;;;;-1:-1:-1;;;;;2287:73514:11;;;34764:15;2287:73514;;;;;;;12772:66;12862:18;-1:-1:-1;;;;;2287:73514:11;;;34356:7;2287:73514;;;;;;;12862:18;2287:73514;12904:16;-1:-1:-1;;;;;2287:73514:11;;;34356:7;2287:73514;;;;;;;12904:16;2287:73514;;13004:48;12930:33;2287:73514;12951:12;2287:73514;;;;;12930:33;13004:48;;;;;:::i;:::-;12983:91;:::i;:::-;3138:4;;13335:28;;13331:91;;12536:903::o;13331:91::-;3185:5;;2458:4:4;3185:5:11;;3138:4;13365:57;:::o;12772:39::-;;;;;;-1:-1:-1;12772:39:11;;;;;;:::i;:::-;;;;;12709:41;;;;;;;;;;;;;;;;;:::i;:::-;;;;;1490:474:8;;;;;;;;;;;1575:245;;;;;;;;;;;2287:73514:11;;49083:24;2287:73514;49083:24;;;;;;:::i;2287:73514::-;;;;;8713:16:12;2287:73514:11;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;8712:35:12;8708:67;;49215:43:11;2287:73514;49248:9;2287:73514;;;;49215:43;;:::i;:::-;2458:4:4;;;;;;;;;49312:38:11;;;;:::i;:::-;49365:8;;;49361:48;;49773:9;;;;;;;;;;:::i;49361:48::-;2287:73514;;49382:27;;;;;;2287:73514;;;;3471:1:12;;;2287:73514:11;;;3471:1:12;;39736:29:11;2287:73514;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;:::o;1490:474:8:-;;;;;;;;;;;;1575:245;;;;;;;;;;;2287:73514:11;;46745:24;2287:73514;46745:24;;;;;;:::i;2287:73514::-;;;;;8713:16:12;2287:73514:11;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;8712:35:12;8708:67;;46877:43:11;2287:73514;46910:9;2287:73514;;;;46877:43;;:::i;:::-;2458:4:4;;;;;;;;;;46974:38:11;47307:9;46974:38;;;:::i;:::-;47307:9;;;;:::i;2287:73514::-;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;:::o;17361:187:12:-;2287:73514:11;;;17422:28:12;2458:4:4;17422:28:12;;2287:73514:11;;17525:16:12;2287:73514:11;;;;;17495:15:12;2287:73514:11;;;;;;;;17495:15:12;2287:73514:11;;;;;;17525:16:12;17361:187::o;12970:1638::-;13068:16;2287:73514:11;2900:6:12;2287:73514:11;;13400:26:12;2287:73514:11;;;;13382:15:12;3471:1;3185:5:11;3138:4;13871:17:12;2287:73514:11;14099:26:12;;;;14095:42;;3471:1;14310:32;;;;;14306:46;;3471:1;12970:1638;:::o;14306:46::-;14344:8;;-1:-1:-1;14344:8:12;:::o;14095:42::-;14127:10;;;:::o;50352:1017:11:-;;;-1:-1:-1;;;;;50651:28:11;;;;:::i;:::-;2287:73514;;-1:-1:-1;2287:73514:11;50799:7;2287:73514;;50781:34;2287:73514;-1:-1:-1;2287:73514:11;;50781:34;;:::i;:::-;50829:16;2287:73514;50829:38;;;;;50825:73;;3471:1:12;51100:18:11;3471:1:12;;50829:16:11;2287:73514;51100:18;:::i;:::-;51201:24;;;51197:80;;2287:73514;51337:25;2287:73514;51337:25;2287:73514;51337:25;:::i;51197:80::-;2287:73514;;51234:43;;;;;;2287:73514;;;3471:1:12;;;2287:73514:11;;;;3471:1:12;;39736:29:11;50825:73;50876:22;2287:73514;;50876:22;;;;11375:939:9;11502:806;11375:939;11502:806;11375:939;11502:806;;;;;;;;;;;;;;;;;;;;;;;;;;;;11375:939::o;11502:806::-;;;;;;;2287:73514:11;;;;;-1:-1:-1;;;;;2287:73514:11;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;:::o;17066:161:12:-;2287:73514:11;17186:34:12;17066:161;17138:33;2287:73514:11;;;;;;;;;;17138:33:12;2287:73514:11;-1:-1:-1;;;;;2287:73514:11;;;;;;17186:34:12;17066:161::o;17717:1172:3:-;17884:946;;;;2458:4:4;17884:946:3;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;17717:1172::o;17884:946::-;;;;;;;17717:1172;17884:946;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;17717:1172::o;19295:1119::-;;19466:887;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;19466:887:3;;;;;;;;;;;;;;;;;;19295:1119::o;54321:1955:11:-;2287:73514;;;;;54753:15;3471:1:12;54753:15:11;;;;54744:941;2287:73514;;;56149:52;2287:73514;56183:12;2287:73514;3471:1:12;;56149:52:11;:::i;:::-;54321:1955;:::o;54749:2::-;54801:18;;;;;2287:73514;;;;;;;;;;54801:18;-1:-1:-1;;;;;2287:73514:11;;54837:19;;54833:30;;54894:14;;-1:-1:-1;;;;;2287:73514:11;;;34356:7;2287:73514;;;;;;;54894:14;2287:73514;;;;54962:37;;54993:4;54801:14;54962:37;;2287:73514;;54962:37;;;;2287:73514;;;;;;54962:37;;;;;;;;54952:88;54962:37;:77;:37;;;;;54749:2;55002:37;;;;-1:-1:-1;;;;;2287:73514:11;;;74482:30;2287:73514;;;;;;;54952:88;55211:23;55207:407;;54749:2;3471:1:12;;;;54749:2:11;;;;55207:407;55312:42;55259:210;55569:30;55312:42;;3471:1:12;55312:42:11;;;:::i;55569:30::-;55207:407;;;;;;;54962:37;55002;54962;;;;;;;;;-1:-1:-1;54962:37:11;;;;;;:::i;:::-;;;;;;54833:30;54858:5;;;;;5293:468:4;5418:337;;;;;;;;;;;;;;;5293:468;:::o;5418:337::-;;;;;;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;;;:::o;7938:186:4:-;11948:3810;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;-1:-1:-1;11948:3810:4;;;;;8081:36;11948:3810;;;;;8088:28;11948:3810;8089:12;11948:3810;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8089:12;:::i;:::-;2458:4;84::28;;;;8088:28:4;8081:36;:::i;11948:3810::-;;-1:-1:-1;11948:3810:4;;;;2809:424;2934:293;-1:-1:-1;;2934:293:4;8467:15:12;2934:293:4;;;;;;;;;;2809:424;:::o;2934:293::-;;;;;;;2809:424;;2934:293;-1:-1:-1;;2934:293:4;;;;;;;;;;;2809:424;:::o;33263:586:12:-;;;2287:73514:11;33263:586:12;2287:73514:11;;33263:586:12;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;33460:382:12;;33263:586;:::o;21083:460::-;;2287:73514:11;-1:-1:-1;2287:73514:11;21235:13:12;2287:73514:11;;-1:-1:-1;;;;;2287:73514:11;-1:-1:-1;2287:73514:11;;;21231:80:12;;21321:47;2287:73514:11;-1:-1:-1;2287:73514:11;21235:13:12;2287:73514:11;;;-1:-1:-1;2287:73514:11;;-1:-1:-1;;;;;2287:73514:11;;;;;;;;;21321:47:12;21501:35;2287:73514:11;;;;;;;;;21501:35:12;2287:73514:11;21083:460:12:o;21231:80::-;21290:21;2287:73514:11;;21290:21:12;;;;911:1089:14;;;;2287:73514:11;;;;;;;1203:15:14;;;;:::i;:::-;2458:4:4;;;;;;;;;;;;;;;;;1158:180:14;;;;:::i;:::-;1543:6;;1539:372;;911:1089;1929:20;;;;911:1089;:::o;1539:372::-;1693:193;3185:5:11;;;1693:193:14;:::i;:::-;3471:1:12;;1539:372:14;;;;;;17686:221:12;2287:73514:11;;415:5:10;17752:31:12;;2287:73514:11;;;415:5:10;17879:21:12;415:5:10;2287:73514:11;17839:25:12;415:5:10;2287:73514:11;;17839:25:12;415:5:10;2287:73514:11;;;;;17879:21:12;17686:221::o;2657:952:14:-;;;;;3052:5;2657:952;3052:5;:::i;:::-;2458:4:4;;;;;;;;;;;;;;;;;;3003:182:14;;;;:::i;:::-;2458:4:4;;;;;;;;;3401:54:14;3539:20;3401:54;3348:230;3401:54;;:::i;:::-;3539:20;;:::i;3348:230::-;2458:4:4;;;;;;;3213:389:14;;;:::i;8181:1139:9:-;8326:988;;;8181:1139;;;8326:988;8181:1139;8326:988;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;8181:1139::o;8326:988::-;;;;;;;18973:468:12;2287:73514:11;19091:19:12;2287:73514:11;;19197:23:12;;19193:242;;18973:468;;;:::o;19193:242::-;19404:19;19266:60;;;;:::i;:::-;19376:26;;;:::i;:::-;19404:19;;:::i;6959:512:4:-;7086:379;;;;;;;;;;;;;;;;;;;;;;;6959:512;:::o;2287:73514:11:-;;;;;;;;;;-1:-1:-1;;;;;2287:73514:11;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;-1:-1:-1;;;;;2287:73514:11;;;;;;;;;;;;;;:::i;:::-;;;;;;;;;;:::i;:::-;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::o;42283:3105::-;;;;;;;;;;;;;42788:35;-1:-1:-1;;;;;2287:73514:11;;42796:26;;42788:35;:::i;:::-;42967:38;42987:16;42975:29;;42967:38;:::i;:::-;43046:25;;2287:73514;43046:25;2287:73514;43046:25;:::i;:::-;43159:60;:40;2287:73514;43183:15;2287:73514;-1:-1:-1;;;;;2287:73514:11;;;43159:60;43342:12;;;;;:::i;:::-;43159:287;;;;;;2287:73514;;43159:287;2287:73514;;;;;;;42820:1;2287:73514;;;43159:287;;;;;;;2287:73514;43159:287;;;;;;:::i;:::-;;43227:9;;43159:287;;;;;;;;;42283:3105;43882:26;2287:73514;43882:26;;;;;;;:::i;:::-;44050:12;;;;;:::i;:::-;44219;;2287:73514;;43847:445;;;;:::i;:::-;44526:12;;;;;;:::i;:::-;;;;;:::i;:::-;44850:6;44823:10;44843:4;44823:10;44850:6;;:::i;:::-;44927:3;;;;:::i;:::-;45150:24;;;;;;:::i;:::-;45188:26;;;;;;;:::i;:::-;2287:73514;;;;;;;45081:300;;;;;:::i;:::-;;;;;42283:3105::o;43159:287::-;;;;;;:::i;:::-;;;;6274:791:14;;;;2287:73514:11;;;;;;;6607:366:14;6711:203;;;;:::i;6607:366::-;2287:73514:11;;;;;;;;;;6454:575:14;;;:::i;22755:768:12:-;-1:-1:-1;2287:73514:11;22918:13:12;2287:73514:11;;;-1:-1:-1;2287:73514:11;;;;;-1:-1:-1;;;;;2287:73514:11;;23022:26:12;;;2287:73514:11;;;;;;23440:36:12;2287:73514:11;3471:1:12;23440:36;2287:73514:11;22755:768:12;:::o;20073:431::-;;-1:-1:-1;2287:73514:11;;;;20239:13:12;2287:73514:11;;-1:-1:-1;;;;;2287:73514:11;;;;;;;20235:77:12;;2287:73514:11;;;;20239:13:12;2287:73514:11;;;;;;;;;;;;;;;;20451:15:12;2287:73514:11;;;;;;;;;20073:431:12:o;:::-;;;;-1:-1:-1;2287:73514:11;;;;20239:13:12;2287:73514:11;;-1:-1:-1;;;;;2287:73514:11;;;;;;;20235:77:12;;2287:73514:11;;20322:43:12;2287:73514:11;;;20239:13:12;2287:73514:11;;;;;;-1:-1:-1;;;;;2287:73514:11;;;;;;;;;20322:43:12;2287:73514:11;;;20451:15:12;2287:73514:11;;;;;;;;;20073:431:12:o;14889:2052::-;14966:16;2287:73514:11;;15702:34:12;2287:73514:11;15280:44:12;2287:73514:11;15298:26:12;2287:73514:11;;;;;;;;;;;;;15280:44:12;:15;3471:1;3185:5:11;2900:6:12;3138:4:11;;;;15702:34:12;2287:73514:11;15770:17:12;2287:73514:11;15897:26:12;;;;15893:378;;16428:10;;;;:::i;:::-;3471:1;16721:17;;;16717:52;;15298:26;2287:73514:11;;;;15280:15:12;2287:73514:11;;;;;;;16903:31:12;;39896:17:11;2287:73514;;15893:378:12;16028:11;;;;;;16024:46;;15298:26;2287:73514:11;;;;15280:15:12;2287:73514:11;;;;;;;16215:25:12;;39896:17:11;2287:73514;;32307:696:12;;;2287:73514:11;;32307:696:12;2287:73514:11;;32307:696:12;;2287:73514:11;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;:::i;21755:788:12:-;-1:-1:-1;2287:73514:11;21939:13:12;2287:73514:11;;;-1:-1:-1;2287:73514:11;;;;;-1:-1:-1;;;;;2287:73514:11;;22039:26:12;;;2287:73514:11;;;22444:28:12;2287:73514:11;;;;-1:-1:-1;;;;;2287:73514:11;;;34764:15;2287:73514;;;;;;;22444:28:12;2287:73514:11;;;3471:1:12;2287:73514:11;;21755:788:12;:::o;4437:468:4:-;4564:335;-1:-1:-1;;4564:335:4;;;;;;;;;;;;;;;;;4437:468;:::o;8260:3442::-;8498:21;8493:26;;;8489:40;;8591:405;;;;;;9274:7;2287:73514:11;;;;11601:49:4;9569:29;2287:73514:11;10154:40:4;2287:73514:11;;9601:7:4;2287:73514:11;;;;;;;;;;;3471:1:12;;10092:35:4;2287:73514:11;9892:31:4;2287:73514:11;;;;;3471:1:12;10671:35:4;3471:1:12;;;10530:34:4;3471:1:12;;;10393:32:4;3471:1:12;;;;2287:73514:11;;;;;;;3471:1:12;2287:73514:11;;;;;;;3471:1:12;2287:73514:11;;;;;3471:1:12;2287:73514:11;9959:32:4;2287:73514:11;;;;3471:1:12;2287:73514:11;;;;;;10768:329:4;3185:5:11;3471:1:12;11663:3:4;3471:1:12;2287:73514:11;8260:3442:4;:::o;8591:405::-;;2287:73514:11;8591:405:4;;;;8489:40;8521:8;2287:73514:11;8521:8:4;:::o

Swarm Source

ipfs://5b620e5c19cd34ebff19ce0d6221c4cb20ca382d3b6b735234817ac60f7e8227

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
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  ]

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.