ETH Price: $3,844.05 (+2.42%)

Contract

0x6B61D8680C4F9E560c8306807908553f95c749C5
 

Overview

ETH Balance

0 ETH

Eth Value

$0.00

More Info

Private Name Tags

TokenTracker

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To
Approve236284212025-10-21 20:50:592 days ago1761079859IN
0x6B61D868...f95c749C5
0 ETH0.000011480.20079043
Approve235639862025-10-12 20:19:2311 days ago1760300363IN
0x6B61D868...f95c749C5
0 ETH0.000067981.82182365
Approve235639832025-10-12 20:18:4711 days ago1760300327IN
0x6B61D868...f95c749C5
0 ETH0.000068331.83128441
Approve235457782025-10-10 7:15:2313 days ago1760080523IN
0x6B61D868...f95c749C5
0 ETH0.000094371.6493432
Approve234825542025-10-01 11:07:4722 days ago1759316867IN
0x6B61D868...f95c749C5
0 ETH0.000073821.2902505
Approve234501092025-09-26 22:16:3526 days ago1758924995IN
0x6B61D868...f95c749C5
0 ETH0.000065891.15168878
Approve234373932025-09-25 3:32:4728 days ago1758771167IN
0x6B61D868...f95c749C5
0 ETH0.000105741.84806472
Approve234357492025-09-24 22:02:1129 days ago1758751331IN
0x6B61D868...f95c749C5
0 ETH0.000017160.3
Approve233552182025-09-13 16:03:2340 days ago1757779403IN
0x6B61D868...f95c749C5
0 ETH0.000022950.40331582
Approve233547042025-09-13 14:20:1140 days ago1757773211IN
0x6B61D868...f95c749C5
0 ETH0.00003330.58520753
Approve233220142025-09-09 0:40:4744 days ago1757378447IN
0x6B61D868...f95c749C5
0 ETH0.000012230.35010299
Approve232775972025-09-02 19:46:1151 days ago1756842371IN
0x6B61D868...f95c749C5
0 ETH0.000048041.28734518
Approve232775952025-09-02 19:45:4751 days ago1756842347IN
0x6B61D868...f95c749C5
0 ETH0.000048461.29883439
Approve232775932025-09-02 19:45:2351 days ago1756842323IN
0x6B61D868...f95c749C5
0 ETH0.000048431.29790307
Approve232775912025-09-02 19:44:5951 days ago1756842299IN
0x6B61D868...f95c749C5
0 ETH0.00004811.28894767
Approve232775892025-09-02 19:44:3551 days ago1756842275IN
0x6B61D868...f95c749C5
0 ETH0.000075281.31577143
Approve232775562025-09-02 19:37:5951 days ago1756841879IN
0x6B61D868...f95c749C5
0 ETH0.000076581.33848359
Approve232092632025-08-24 6:49:5960 days ago1756018199IN
0x6B61D868...f95c749C5
0 ETH0.000009770.17170401
Approve231946642025-08-22 5:56:4762 days ago1755842207IN
0x6B61D868...f95c749C5
0 ETH0.000122942.16002563
Approve231859922025-08-21 0:51:2363 days ago1755737483IN
0x6B61D868...f95c749C5
0 ETH0.000011940.20994063
Approve231851902025-08-20 22:10:4764 days ago1755727847IN
0x6B61D868...f95c749C5
0 ETH0.000023440.40969291
Approve231851212025-08-20 21:56:5964 days ago1755727019IN
0x6B61D868...f95c749C5
0 ETH0.000021410.37630057
Approve231834432025-08-20 16:19:5964 days ago1755706799IN
0x6B61D868...f95c749C5
0 ETH0.000131282.29441451
Approve231695312025-08-18 17:47:5966 days ago1755539279IN
0x6B61D868...f95c749C5
0 ETH0.00001780.4472522
Approve231674422025-08-18 10:46:4766 days ago1755514007IN
0x6B61D868...f95c749C5
0 ETH0.000013780.24217842
View all transactions

Latest 1 internal transaction

Advanced mode:
Parent Transaction Hash Method Block
From
To
0x61022060224283332025-05-07 0:48:59169 days ago1746578939  Contract Creation0 ETH
Cross-Chain Transactions

Block Transaction Difficulty Gas Used Reward
View All Blocks Produced

Validator Index Block Amount
View All Withdrawals

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

Contract Source Code Verified (Exact Match)

Contract Name:
QuantAMMWeightedPool

Compiler Version
v0.8.26+commit.8a97fa7a

Optimization Enabled:
Yes with 850 runs

Other Settings:
cancun EvmVersion
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity >=0.8.24;

import {
    IWeightedPool,
    WeightedPoolDynamicData,
    WeightedPoolImmutableData
} from "@balancer-labs/v3-interfaces/contracts/pool-weighted/IWeightedPool.sol";
import { ISwapFeePercentageBounds } from "@balancer-labs/v3-interfaces/contracts/vault/ISwapFeePercentageBounds.sol";
import {
    IUnbalancedLiquidityInvariantRatioBounds
} from "@balancer-labs/v3-interfaces/contracts/vault/IUnbalancedLiquidityInvariantRatioBounds.sol";
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";
import { IVaultErrors } from "@balancer-labs/v3-interfaces/contracts/vault/IVaultErrors.sol";
import {
    SwapKind,
    PoolSwapParams,
    PoolConfig,
    Rounding
} from "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol";
import { IBasePool } from "@balancer-labs/v3-interfaces/contracts/vault/IBasePool.sol";

import { VaultGuard } from "@balancer-labs/v3-vault/contracts/VaultGuard.sol";
import { BalancerPoolToken } from "@balancer-labs/v3-vault/contracts/BalancerPoolToken.sol";
import { PoolInfo } from "@balancer-labs/v3-pool-utils/contracts/PoolInfo.sol";
import { FixedPoint } from "@balancer-labs/v3-solidity-utils/contracts/math/FixedPoint.sol";
import { WeightedMath } from "@balancer-labs/v3-solidity-utils/contracts/math/WeightedMath.sol";
import { Version } from "@balancer-labs/v3-solidity-utils/contracts/helpers/Version.sol";
//CODEHAWKS INFO /s/703 remove dupe import
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IUpdateRule } from "@balancer-labs/v3-interfaces/contracts/pool-quantamm/IUpdateRule.sol"; // Ensure this path is correct
import { IQuantAMMWeightedPool } from "@balancer-labs/v3-interfaces/contracts/pool-quantamm/IQuantAMMWeightedPool.sol";
import { ScalarQuantAMMBaseStorage } from "./QuantAMMStorage.sol";
import { UpdateWeightRunner } from "./UpdateWeightRunner.sol";
import "@prb/math/contracts/PRBMathSD59x18.sol";
import "@openzeppelin/contracts/proxy/utils/Initializable.sol";
import { QuantAMMWeightedPoolFactory } from "./QuantAMMWeightedPoolFactory.sol";

/**
 * @notice QuantAMM Base Weighted pool. One per pool.
 * @dev QuantAMM pools are in effect more advanced managed pools. They are fixed to run with the QuantAMM UpdateWeightRunner.
 *
 * UpdateWeightRunner is reponsible for running automated strategies that determine weight changes in QuantAMM pools.
 * Given that all the logic is in update weight runner, setWeights is the fundamental access point between the two.
 *
 * QuantAMM weighted pools define the last set weight time and weight and a block multiplier.
 *
 * This block multiplier is used to interpolate between the last set weight and the current weight for a given block.
 *
 * Older mechanisms defined a target weight and a target block index. Like this by storing times instead of weights
 * we save on SLOADs during weight calculations. It also allows more nuanced weight changes where you carry on a vector
 * until you either hit a guard rail or call a new setWeight.
 *
 * Fees for these pools are set in hooks.
 *
 * Pool Registration will be gated by the QuantAMM team to begin with for security reasons.
 *
 * At any given block the pool is a fixed weighted balancer pool.
 *
 * We store weights differently to the standard balancer pool. We store them as a 32 bit int, with the first 16 bits being the weight
 * and the second 16 bits being the block multiplier. This allows us to store 8 weights in a single 256 bit int.
 * Changing to a less precise storage has been shown in simulations to have a negligible impact on overall performance of the strategy
 * while drastically reducing the gas cost.
 *
 */
contract QuantAMMWeightedPool is
    IQuantAMMWeightedPool,
    IBasePool,
    BalancerPoolToken,
    PoolInfo,
    Version,
    ScalarQuantAMMBaseStorage,
    Initializable
{
    using FixedPoint for uint256;

    // Fees are 18-decimal, floating point values, which will be stored in the Vault using 24 bits.
    // This means they have 0.00001% resolution (i.e., any non-zero bits < 1e11 will cause precision loss).
    // Minimum values help make the math well-behaved (i.e., the swap fee should overwhelm any rounding error).
    // Maximum values protect users by preventing permissioned actors from setting excessively high swap fees.
    uint256 private constant _MIN_SWAP_FEE_PERCENTAGE = 0.001e16; // 0.001%
    uint256 private constant _MAX_SWAP_FEE_PERCENTAGE = 10e16; // 10%
    uint256 private constant _FIX_WINDOW = 3 * 365 * 24 * 60 * 60;

    uint256 private immutable _totalTokens;

    ///@dev First elem = category, second elem is name, third variable type, fourth elem detail
    string[][] private poolDetails;

    /// @dev The weights are stored as 32-bit integers, packed into 256-bit integers. 9 d.p. was shown to have roughly same performance
    // packed: [weight1,weight2,weight3,weight4,multiplier1,multiplier2,multiplier3,multiplier4]
    int256 internal _normalizedFirstFourWeights;
    //packed: [weight5,weight6,weight7,weight8,multiplier5,multiplier6,multiplier7,multiplier8]
    int256 internal _normalizedSecondFourWeights;

    UpdateWeightRunner public updateWeightRunner;

    uint256 public immutable deploymentTime;
    address internal immutable quantammAdmin;

    /// @notice the pool settings for getting weights and assets keyed by pool
    QuantAMMBaseGetWeightData poolSettings;

    /// @notice the pool settings for setting weights keyed by pool
    /// @param name The name of the pool
    /// @param symbol The symbol of the pool
    /// @param numTokens The number of tokens in the pool
    /// @param version The version of the pool
    /// @param updateWeightRunner The address of the update weight runner
    /// @param poolRegistry The settings of admin functionality of pools
    /// @param poolDetails The details of the pool. dynamic user driven descriptive data
    struct NewPoolParams {
        string name;
        string symbol;
        uint256 numTokens;
        string version;
        address updateWeightRunner;
        uint256 poolRegistry;
        string[][] poolDetails;
    }

    ///@dev Emitted when the weights of the pool are updated
    /// @notice The information regarding the weight update. A second event is sent with finalised weights from the updateWeightRunner with precisions used for trading.
    /// @param poolAddress The address of the pool
    /// @param calculatedWeightsAndMultipliers The weights and multipliers submitted to be saved. These are in 18dp. Trade precision is in 9dp.
    /// @param lastInterpolationTimePossible The last time the weights can be interpolated
    /// @param lastUpdateTime The last time the weights were updated
    event WeightsUpdated(address indexed poolAddress, int256[] calculatedWeightsAndMultipliers, uint40 lastInterpolationTimePossible, uint40 lastUpdateTime);

    /// @notice Emitted when the update weight runner is updated. This is during break glass situations.
    /// @param oldAddress The old address of the update weight runner
    /// @param newAddress The new address of the update weight runner
    event UpdateWeightRunnerAddressUpdated(address indexed oldAddress, address indexed newAddress);

    /// @notice Emitted when the pool is set in the update weight runner
    /// @param rule The rule to use for the pool
    /// @param poolOracles The oracles to use for the pool. [asset oracle][backup oracles for that asset]
    /// @param lambda The decay parameter for the rule
    /// @param ruleParameters The parameters for the rule
    /// @param epsilonMax The parameter that controls maximum allowed delta for a weight update
    /// @param absoluteWeightGuardRail The parameter that controls minimum allowed absolute weight allowed
    /// @param updateInterval The time between updates
    /// @param poolManager The address of the pool manager
    /// @param creatorAddress The address of the creator of the pool
    event PoolRuleSet(
        address rule,
        address[][] poolOracles,
        uint64[] lambda,
        int256[][] ruleParameters,
        uint64 epsilonMax,
        uint64 absoluteWeightGuardRail,
        uint40 updateInterval,
        address poolManager,
        address creatorAddress
    );


    /// @dev Indicates that the maximum allowed trade size has been exceeded.
    error maxTradeSizeRatioExceeded();

    /**
     * @notice `getRate` from `IRateProvider` was called on a Weighted Pool.
     * @dev It is not safe to nest Weighted Pools as WITH_RATE tokens in other pools, where they function as their own
     * rate provider. The default `getRate` implementation from `BalancerPoolToken` computes the BPT rate using the
     * invariant, which has a non-trivial (and non-linear) error. Without the ability to specify a rounding direction,
     * the rate could be manipulable.
     *
     * It is fine to nest Weighted Pools as STANDARD tokens, or to use them with external rate providers that are
     * stable and have at most 1 wei of rounding error (e.g., oracle-based).
     */
    error WeightedPoolBptRateUnsupported();

    ///@dev The parameters for the rule, validated in each rule separately during set rule
    int256[][] public ruleParameters;

    ///@dev Decay parameter for exponentially-weighted moving average (0 < λ < 1)
    uint64[] public lambda;

    ///@dev Maximum allowed delta for a weight update, stored as SD59x18 number
    uint64 public epsilonMax; // Maximum allowed delta for a weight update, stored as SD59x18 number

    ///@dev Minimum absolute weight allowed. CODEHAWKS INFO /s/611
    uint64 public absoluteWeightGuardRail;

    ///@dev maximum trade size allowed as a fraction of the pool
    uint256 internal maxTradeSizeRatio;

    ///@dev Minimum amount of seconds between two updates
    uint64 public updateInterval;

    ///@dev the maximum amount of time that an oracle an be stale.
    uint oracleStalenessThreshold;

    ///@dev the admin functionality enabled for this pool.
    uint256 public immutable poolRegistry;

    constructor(
        NewPoolParams memory params,
        IVault vault
    ) BalancerPoolToken(vault, params.name, params.symbol) PoolInfo(vault) Version(params.version) {
        _totalTokens = params.numTokens;
        updateWeightRunner = UpdateWeightRunner(params.updateWeightRunner);
        quantammAdmin = updateWeightRunner.quantammAdmin();
        deploymentTime = block.timestamp;

        poolRegistry = params.poolRegistry;
        poolDetails = params.poolDetails;
    }

    /// @inheritdoc IBasePool
    function computeBalance(
        uint256[] memory balancesLiveScaled18,
        uint256 tokenInIndex,
        uint256 invariantRatio
    ) external view returns (uint256 newBalance) {
        uint40 multiplierTime = uint40(block.timestamp);
        //CODEHAWKS INFO /s/234
        InteropDetails memory variables = poolSettings.interopDetails;

        if (block.timestamp >= variables.lastInteropTime) {
            //we have gone beyond the first variable hitting the guard rail. We cannot interpolate any further and an update is needed
            multiplierTime = variables.lastInteropTime;
        }

        //CODEHAWKS INFO /s/4
        //Lifted outside of unchecked in case of race condition of combined bad user setting last update in the future and chain downtime
        uint256 timeSinceLastUpdate = uint256(
            multiplierTime - variables.lastUpdateTime
        );

        unchecked {
            return
                WeightedMath.computeBalanceOutGivenInvariant(
                    balancesLiveScaled18[tokenInIndex],
                    _getNormalizedWeight(tokenInIndex, timeSinceLastUpdate, _totalTokens),
                    invariantRatio
                );
        }
    }

    /// @inheritdoc IQuantAMMWeightedPool
    function getPoolDetail(string memory category, string memory name) external view returns (string memory, string memory) {
        for(uint i = 0; i < poolDetails.length; i++){
            string[] memory detail = poolDetails[i];
            if(keccak256(abi.encodePacked(detail[0])) == keccak256(abi.encodePacked(category)) 
                && keccak256(abi.encodePacked(detail[1])) == keccak256(abi.encodePacked(name))){
                return (detail[2], detail[3]);
            }
        }
        
        return ("", "");
    }

    /// @inheritdoc IBasePool
    function computeInvariant(uint256[] memory balancesLiveScaled18, Rounding rounding) public view returns (uint256) {
        function(uint256[] memory, uint256[] memory) internal pure returns (uint256) _upOrDown = rounding ==
            Rounding.ROUND_UP
            ? WeightedMath.computeInvariantUp
            : WeightedMath.computeInvariantDown;

        return _upOrDown(_getNormalizedWeights(), balancesLiveScaled18);
    }

    /// @inheritdoc IQuantAMMWeightedPool
    function getNormalizedWeights() external view returns (uint256[] memory) {
        return _getNormalizedWeights();
    }

    /// @inheritdoc IBasePool
    function onSwap(PoolSwapParams memory request) public view onlyVault returns (uint256) {
        InteropDetails memory variables = poolSettings.interopDetails;

        uint256 tokenInWeight;
        uint256 tokenOutWeight;
        uint256 totalTokens = _totalTokens;
        uint40 multiplierTime = uint40(block.timestamp);

        if (block.timestamp >= variables.lastInteropTime) {
            //we have gone beyond the first variable hitting the guard rail. We cannot interpolate any further and an update is needed
            multiplierTime = variables.lastInteropTime;
        }

        uint256 timeSinceLastUpdate = uint256(multiplierTime - variables.lastUpdateTime);

        // if both tokens are within the first storage elem
        if ((request.indexIn < 4 && request.indexOut < 4) || (request.indexIn >= 4 && request.indexOut >= 4)) {
            QuantAMMNormalisedTokenPair memory tokenWeights = _getNormalisedWeightPair(
                request.indexIn,
                request.indexOut,
                timeSinceLastUpdate,
                totalTokens
            );
            tokenInWeight = tokenWeights.firstTokenWeight;
            tokenOutWeight = tokenWeights.secondTokenWeight;
        } else {
            tokenInWeight = _getNormalizedWeight(request.indexIn, timeSinceLastUpdate, totalTokens);
            tokenOutWeight = _getNormalizedWeight(request.indexOut, timeSinceLastUpdate, totalTokens);
        }

        if (request.kind == SwapKind.EXACT_IN) {            
            if (request.amountGivenScaled18 > request.balancesScaled18[request.indexIn].mulDown(maxTradeSizeRatio)) {
                revert maxTradeSizeRatioExceeded();
            }

            uint256 amountOutScaled18 = WeightedMath.computeOutGivenExactIn(
                request.balancesScaled18[request.indexIn],
                tokenInWeight,
                request.balancesScaled18[request.indexOut],
                tokenOutWeight,
                request.amountGivenScaled18
            );
            //CODEHAWKS M-09 check amountOUTScaled18 
            if (amountOutScaled18 > request.balancesScaled18[request.indexOut].mulDown(maxTradeSizeRatio)) {
                revert maxTradeSizeRatioExceeded();
            }

            return amountOutScaled18;
        } else {
            // Cannot exceed maximum out ratio
            if (request.amountGivenScaled18 > request.balancesScaled18[request.indexOut].mulDown(maxTradeSizeRatio)) {
                revert maxTradeSizeRatioExceeded();
            }

            uint256 amountInScaled18 = WeightedMath.computeInGivenExactOut(
                request.balancesScaled18[request.indexIn],
                tokenInWeight,
                request.balancesScaled18[request.indexOut],
                tokenOutWeight,
                request.amountGivenScaled18
            );

            //CODEHAWKS M-09 check amountInScaled18 
            if (amountInScaled18 > request.balancesScaled18[request.indexIn].mulDown(maxTradeSizeRatio)) {
                revert maxTradeSizeRatioExceeded();
            }

            // Fees are added after scaling happens, to reduce the complexity of the rounding direction analysis.
            return amountInScaled18;
        }
    }

    struct QuantAMMNormalisedTokenPair {
        uint256 firstTokenWeight;
        uint256 secondTokenWeight;
    }

    /// @notice Get the normalised weights for a pair of tokens
    /// @param tokenIndexOne The index of the first token
    /// @param tokenIndexTwo The index of the second token
    /// @param timeSinceLastUpdate The time since the last update
    /// @param totalTokens The total number of tokens in the pool
    function _getNormalisedWeightPair(
        uint256 tokenIndexOne,
        uint256 tokenIndexTwo,
        uint256 timeSinceLastUpdate,
        uint256 totalTokens
    ) internal view virtual returns (QuantAMMNormalisedTokenPair memory) {
        uint256 firstTokenIndex = tokenIndexOne;
        uint256 secondTokenIndex = tokenIndexTwo;
        uint256 totalTokensInPacked = totalTokens;
        int256 targetWrappedToken;
        //because we are in get pair we can assume that we are in the same storage int
        if (tokenIndexTwo >= 4) {
            firstTokenIndex = tokenIndexOne - 4;
            secondTokenIndex = tokenIndexTwo - 4;
            totalTokensInPacked -= 4;
            targetWrappedToken = _normalizedSecondFourWeights;
        } else {
            if (totalTokens > 4) {
                totalTokensInPacked = 4;
            }
            targetWrappedToken = _normalizedFirstFourWeights;
        }

        unchecked {
            int256[] memory normalizedWeights = quantAMMUnpack32(targetWrappedToken);
            uint256 firstTokenWeight = _calculateCurrentBlockWeight(
                normalizedWeights,
                firstTokenIndex,
                timeSinceLastUpdate,
                totalTokensInPacked
            );

            uint256 secondTokenWeight = _calculateCurrentBlockWeight(
                normalizedWeights,
                secondTokenIndex,
                timeSinceLastUpdate,
                totalTokensInPacked
            );

            return QuantAMMNormalisedTokenPair(firstTokenWeight, secondTokenWeight);
        }
    }

    /// @notice Calculate the current block weight
    /// @param tokenWeights The token weights
    /// @param tokenIndex The index of the token
    /// @param timeSinceLastUpdate The time since the last update
    /// @param tokensInTokenWeights The number of tokens in the specific storage int
    function _calculateCurrentBlockWeight(
        int256[] memory tokenWeights,
        uint256 tokenIndex,
        uint256 timeSinceLastUpdate,
        uint256 tokensInTokenWeights
    ) internal pure returns (uint256) {
        unchecked {
            int256 blockMultiplier = tokenWeights[tokenIndex + (tokensInTokenWeights)];

            return calculateBlockNormalisedWeight(tokenWeights[tokenIndex], blockMultiplier, timeSinceLastUpdate);
        }
    }

    /// @notice Gets the normalised weight for a token
    /// @param tokenIndex The index of the token
    /// @param timeSinceLastUpdate The time since the last update
    /// @param totalTokens The total number of tokens in the pool
    function _getNormalizedWeight(
        uint256 tokenIndex,
        uint256 timeSinceLastUpdate,
        uint256 totalTokens
    ) internal view virtual returns (uint256) {
        uint256 index = tokenIndex;
        int256 targetWrappedToken;
        uint256 tokenIndexInPacked = totalTokens;

        if (tokenIndex >= 4) {
            //get the index in the second storage int
            index = tokenIndex - 4;
            targetWrappedToken = _normalizedSecondFourWeights;
            tokenIndexInPacked -= 4;
        } else {
            if (totalTokens > 4) {
                tokenIndexInPacked = 4;
            }
            targetWrappedToken = _normalizedFirstFourWeights;
        }

        int256[] memory unwrappedWeightsAndMultipliers = quantAMMUnpack32(targetWrappedToken);

        return
            _calculateCurrentBlockWeight(
                unwrappedWeightsAndMultipliers,
                index,
                timeSinceLastUpdate,
                tokenIndexInPacked
            );
    }

    /// @notice gets the normalised weights for the pool
    function _getNormalizedWeights() internal view virtual returns (uint256[] memory) {
        uint256 totalTokens = _totalTokens;
        uint256[] memory normalizedWeights = new uint256[](totalTokens);

        uint40 multiplierTime = uint40(block.timestamp);
        uint40 lastInterpolationTime = poolSettings.interopDetails.lastInteropTime;

        if (block.timestamp >= lastInterpolationTime) {
            //we have gone beyond the first variable hitting the guard rail. We cannot interpolate any further and an update is needed
            multiplierTime = lastInterpolationTime;
        }

        unchecked {
            uint256 timeSinceLastUpdate = uint256(
                multiplierTime - poolSettings.interopDetails.lastUpdateTime
            );

            int256[] memory firstFourWeights = quantAMMUnpack32(_normalizedFirstFourWeights);
            uint256 tokenIndex = totalTokens;
            if (totalTokens > 4) {
                tokenIndex = 4;
            }

            //not using _calculateCurrentBlockWeight as hardcoded you avoid 1 calc gas
            normalizedWeights[0] = calculateBlockNormalisedWeight(
                firstFourWeights[0],
                firstFourWeights[tokenIndex],
                timeSinceLastUpdate
            );
            normalizedWeights[1] = calculateBlockNormalisedWeight(
                firstFourWeights[1],
                firstFourWeights[tokenIndex + 1],
                timeSinceLastUpdate
            );

            if (totalTokens > 2) {
                normalizedWeights[2] = calculateBlockNormalisedWeight(
                    firstFourWeights[2],
                    firstFourWeights[tokenIndex + 2],
                    timeSinceLastUpdate
                );
            } else {
                return normalizedWeights;
            }
            if (totalTokens > 3) {
                normalizedWeights[3] = calculateBlockNormalisedWeight(
                    firstFourWeights[3],
                    firstFourWeights[tokenIndex + 3],
                    timeSinceLastUpdate
                );
            } else {
                return normalizedWeights;
            }

            //avoid unneccessary SLOAD
            if (totalTokens == 4) {
                return normalizedWeights;
            }

            int256[] memory secondFourWeights = quantAMMUnpack32(_normalizedSecondFourWeights);
            uint256 moreThan4Tokens = totalTokens - 4;

            if (totalTokens > 4) {
                normalizedWeights[4] = calculateBlockNormalisedWeight(
                    secondFourWeights[0],
                    secondFourWeights[moreThan4Tokens],
                    timeSinceLastUpdate
                );
            } else {
                return normalizedWeights;
            }
            if (totalTokens > 5) {
                normalizedWeights[5] = calculateBlockNormalisedWeight(
                    secondFourWeights[1],
                    secondFourWeights[moreThan4Tokens + 1],
                    timeSinceLastUpdate
                );
            } else {
                return normalizedWeights;
            }
            if (totalTokens > 6) {
                normalizedWeights[6] = calculateBlockNormalisedWeight(
                    secondFourWeights[2],
                    secondFourWeights[moreThan4Tokens + 2],
                    timeSinceLastUpdate
                );
            } else {
                return normalizedWeights;
            }
            if (totalTokens > 7) {
                normalizedWeights[7] = calculateBlockNormalisedWeight(
                    secondFourWeights[3],
                    secondFourWeights[moreThan4Tokens + 3],
                    timeSinceLastUpdate
                );
            } else {
                return normalizedWeights;
            }

            return normalizedWeights;
        }
    }

    /// @notice Calculate the normalised weight for a token
    /// @param weight The weight of the token
    /// @param multiplier The multiplier for the token
    /// @param timeSinceLastUpdate The time since the last update
    function calculateBlockNormalisedWeight(
        int256 weight,
        int256 multiplier,
        uint256 timeSinceLastUpdate
    ) internal pure returns (uint256) {
        //multiplier is always below 1 which is int128, we multiply by 1e18 for rounding as muldown / 1e18 at the end.
        int256 multiplierScaled18 = multiplier * 1e18;
        if (multiplier > 0) {
            return uint256(weight) + FixedPoint.mulDown(uint256(multiplierScaled18), timeSinceLastUpdate);
        } else {
            //CYFRIN H02
            //CODEHAWKS /s/866,542
            return uint256(weight) - FixedPoint.mulDown(uint256(-multiplierScaled18), timeSinceLastUpdate);
        }
    }

    /// @inheritdoc ISwapFeePercentageBounds
    function getMinimumSwapFeePercentage() external pure returns (uint256) {
        return _MIN_SWAP_FEE_PERCENTAGE;
    }

    /// @inheritdoc ISwapFeePercentageBounds
    function getMaximumSwapFeePercentage() external pure returns (uint256) {
        return _MAX_SWAP_FEE_PERCENTAGE;
    }

    /// @inheritdoc IUnbalancedLiquidityInvariantRatioBounds
    function getMinimumInvariantRatio() external pure returns (uint256) {
        return WeightedMath._MIN_INVARIANT_RATIO;
    }

    /// @inheritdoc IUnbalancedLiquidityInvariantRatioBounds
    function getMaximumInvariantRatio() external pure returns (uint256) {
        return WeightedMath._MAX_INVARIANT_RATIO;
    }

    /// @inheritdoc IQuantAMMWeightedPool
    function getQuantAMMWeightedPoolDynamicData() external view returns (QuantAMMWeightedPoolDynamicData memory data) {
        data.balancesLiveScaled18 = _vault.getCurrentLiveBalances(address(this));
        (, data.tokenRates) = _vault.getPoolTokenRates(address(this));
        data.totalSupply = totalSupply();

        PoolConfig memory poolConfig = _vault.getPoolConfig(address(this));
        data.isPoolInitialized = poolConfig.isPoolInitialized;
        data.isPoolPaused = poolConfig.isPoolPaused;
        data.isPoolInRecoveryMode = poolConfig.isPoolInRecoveryMode;

        data.firstFourWeightsAndMultipliers = quantAMMUnpack32(_normalizedFirstFourWeights);
        data.secondFourWeightsAndMultipliers = quantAMMUnpack32(_normalizedSecondFourWeights);

        //just a get but still more efficient to do it here
        InteropDetails memory interpolationDetails = poolSettings.interopDetails;
        data.lastUpdateTime = interpolationDetails.lastUpdateTime;
        data.lastInteropTime = interpolationDetails.lastInteropTime;
    }

    /// @inheritdoc IQuantAMMWeightedPool
    function getQuantAMMWeightedPoolImmutableData()
        external
        view
        returns (QuantAMMWeightedPoolImmutableData memory data)
    {
        data.tokens = _vault.getPoolTokens(address(this));
        data.oracleStalenessThreshold = oracleStalenessThreshold;
        data.poolRegistry = poolRegistry;
        data.ruleParameters = ruleParameters;
        data.lambda = lambda;
        data.epsilonMax = epsilonMax;
        data.absoluteWeightGuardRail = absoluteWeightGuardRail;
        data.maxTradeSizeRatio = maxTradeSizeRatio;
        data.updateInterval = updateInterval;
    }

    function getWithinFixWindow() external override view returns (bool) {
        return block.timestamp - deploymentTime < _FIX_WINDOW;
    }

    /// @notice the main function to update target weights and multipliers from the update weight runner
    /// @param _weights the target weights and their block multipliers
    /// @param _address the target pool address
    /// @param _lastInteropTime the last time the weights can be interpolated
    function setWeights(
        int256[] calldata _weights,
        address _address,
        uint40 _lastInteropTime
    ) external override {
        require(msg.sender == address(updateWeightRunner), "XUR");
        require(_weights.length == _totalTokens * 2, "WD"); //weight length different

        if (_weights.length > 8) {
            int256[][] memory splitWeights = _splitWeightAndMultipliers(_weights);
            _normalizedFirstFourWeights = quantAMMPack32Array(splitWeights[0])[0];
            _normalizedSecondFourWeights = quantAMMPack32Array(splitWeights[1])[0];
        } else {
            _normalizedFirstFourWeights = quantAMMPack32Array(_weights)[0];
        }

        //struct allows one SSTORE
        poolSettings.interopDetails = InteropDetails({
            lastInteropTime: _lastInteropTime,
            lastUpdateTime: uint40(block.timestamp)
        });

        emit WeightsUpdated(_address, _weights, _lastInteropTime, uint40(block.timestamp));
    }

    /// @notice the initialising function during registration of the pool with the vault to set the initial weights
    /// @param _weights the target weights
    function _setInitialWeights(int256[] memory _weights) internal {
        require(_normalizedFirstFourWeights == 0, "init");

        int256[] memory _weightsAndBlockMultiplier = new int256[](_weights.length * 2);
        for (uint i; i < _weights.length; ) {
            _weightsAndBlockMultiplier[i] = _weights[i];
            //Initially register pool with no movement, first update will come and set block multiplier.
            _weightsAndBlockMultiplier[i + _weights.length] = int256(0);
            unchecked {
                ++i;
            }
        }

        if (_weightsAndBlockMultiplier.length > 8) {
            int256[][] memory splitWeights = _splitWeightAndMultipliers(_weightsAndBlockMultiplier);
            _normalizedFirstFourWeights = quantAMMPack32Array(splitWeights[0])[0];
            _normalizedSecondFourWeights = quantAMMPack32Array(splitWeights[1])[0];
        } else {
            _normalizedFirstFourWeights = quantAMMPack32Array(_weightsAndBlockMultiplier)[0];
        }

        //struct allows one SSTORE
        poolSettings.interopDetails = InteropDetails({
            lastInteropTime: uint40(block.timestamp), //given muliplier is 0 on start
            lastUpdateTime: uint40(block.timestamp)
        });

        //CODEHAWKS L-05 emit weights and multiplier
        emit WeightsUpdated(address(this), _weightsAndBlockMultiplier, uint40(block.timestamp), uint40(block.timestamp));
    }

    /// @notice Initialize the pool
    /// @param params parameters defined by the factory
    function initialize(
        QuantAMMWeightedPoolFactory.CreationNewPoolParams memory params
    ) public initializer {        
        oracleStalenessThreshold = params._oracleStalenessThreshold;
        updateInterval = params._poolSettings.updateInterval;
        _setRule(params);

        _setInitialWeights(params._initialWeights);

        //CODEHAWKS L-09 emit during creation rather than setruleforpool with creator address also
        // emit event for easier tracking of rule changes
        emit PoolRuleSet(
            address(params._poolSettings.rule),
            params._poolSettings.oracles,
            params._poolSettings.lambda,
            params._poolSettings.ruleParameters,
            params._poolSettings.epsilonMax,
            params._poolSettings.absoluteWeightGuardRail,
            params._poolSettings.updateInterval,
            params._poolSettings.poolManager,
            msg.sender //this should be the factory and only factory sent creations should be listened to.
        );
    }

    /// @notice Split the weights and multipliers into two arrays
    /// @param weights The weights and multipliers to split
    /// @dev Update weight runner gives all weights in a single array shaped like [w1,w2,w3,w4,w5,w6,w7,w8,m1,m2,m3,m4,m5,m6,m7,m8], we need it to be [w1,w2,w3,w4,m1,m2,m3,m4,w5,w6,w7,w8,m5,m6,m7,m8]
    function _splitWeightAndMultipliers(
        int256[] memory weights
    ) internal pure returns (int256[][] memory splitWeights) {
        uint256 tokenLength = weights.length / 2;
        splitWeights = new int256[][](2);
        splitWeights[0] = new int256[](8);
        for (uint i; i < 4; ) {
            splitWeights[0][i] = weights[i];
            splitWeights[0][i + 4] = weights[i + tokenLength];

            unchecked {
                i++;
            }
        }

        splitWeights[1] = new int256[](8);

        uint256 moreThan4Tokens = tokenLength - 4;
        for (uint i = 0; i < moreThan4Tokens; ) {
            uint256 i4 = i + 4;
            splitWeights[1][i] = weights[i4];
            splitWeights[1][i + moreThan4Tokens] = weights[i4 + tokenLength];

            unchecked {
                i++;
            }
        }
    }

    /// @notice Set the rule for this pool
    /// @param params parameters defined by the factory creation process
    function _setRule(
        QuantAMMWeightedPoolFactory.CreationNewPoolParams memory params
    ) internal {
        lambda = params._poolSettings.lambda;
        epsilonMax = params._poolSettings.epsilonMax;
        absoluteWeightGuardRail = params._poolSettings.absoluteWeightGuardRail;
        maxTradeSizeRatio = params._poolSettings.maxTradeSizeRatio;

        ruleParameters = params._poolSettings.ruleParameters;

        params._poolSettings.rule.initialisePoolRuleIntermediateValues(
            address(this),
            params._initialMovingAverages,
            params._initialIntermediateValues,
            params._initialWeights.length
        );

        updateWeightRunner.setRuleForPool(params._poolSettings);
    }

    /// @inheritdoc IQuantAMMWeightedPool
    function getOracleStalenessThreshold() external view override returns (uint) {
        return oracleStalenessThreshold;
    }

    /// @inheritdoc IQuantAMMWeightedPool
    function setUpdateWeightRunnerAddress(address _updateWeightRunner) external override {
        require(msg.sender == quantammAdmin, "XAD");

        require(
            block.timestamp - deploymentTime < _FIX_WINDOW,
            "XIM"
        );

        address oldAddress = address(updateWeightRunner);

        updateWeightRunner = UpdateWeightRunner(_updateWeightRunner);
        //CODEHAWKS L-04
        emit UpdateWeightRunnerAddressUpdated(oldAddress, _updateWeightRunner);
    }

    function getRate() public pure override returns (uint256) {
        revert WeightedPoolBptRateUnsupported();
    }
}

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

import "@openzeppelin/contracts/utils/Context.sol";
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "@prb/math/contracts/PRBMathSD59x18.sol";
import "./IUpdateRule.sol";

/// @title the main central quantammBase containing records and balances of all pools. Contains all user-pool interaction functions.
interface IQuantAMMWeightedPool {
    /**
     * @notice Snapshot of current Weighted Pool data that can change.
     * @dev Note that live balances will not necessarily be accurate if the pool is in Recovery Mode. Withdrawals
     * in Recovery Mode do not make external calls (including those necessary for updating live balances), so if
     * there are withdrawals, raw and live balances will be out of sync until Recovery Mode is disabled.
     *
     * @param balancesLiveScaled18 Token balances after paying yield fees, applying decimal scaling and rates
     * @param tokenRates 18-decimal FP values for rate tokens (e.g., yield-bearing), or FP(1) for standard tokens
     * @param totalSupply The current total supply of the pool tokens (BPT)
     * @param isPoolInitialized If false, the pool has not been seeded with initial liquidity, so operations will revert
     * @param isPoolPaused If true, the pool is paused, and all non-recovery-mode state-changing operations will revert
     * @param isPoolInRecoveryMode If true, Recovery Mode withdrawals are enabled, and live balances may be inaccurate
     * @param weightsAtLastUpdateInterval The weights of the pool at the last update interval
     * @param weightBlockMultipliers The block multipliers for the weights
     * @param lastUpdateIntervalTime The last time the pool was updated
     * @param lastInterpolationTimePossible The last time that the weights can be updated given the block multiplier before one weight hits the guardrail
     */
    struct QuantAMMWeightedPoolDynamicData {
        uint256[] balancesLiveScaled18;
        uint256[] tokenRates;
        uint256 totalSupply;
        bool isPoolInitialized;
        bool isPoolPaused;
        bool isPoolInRecoveryMode;
        int256[] firstFourWeightsAndMultipliers;
        int256[] secondFourWeightsAndMultipliers;
        uint40 lastUpdateTime;
        uint40 lastInteropTime;
    }
    
    /**
     * @notice Weighted Pool data that cannot change after deployment.
        * @param tokens Pool tokens, sorted in pool registration order
        * @param oracleStalenessThreshold The acceptable number of blocks behind the current that an oracle value can be
        * @param poolRegistry The address of the pool registry
        * @param ruleParameters The parameters for the rule, validated in each rule separately during set rule
        * @param lambda Decay parameter for exponentially-weighted moving average (0 < λ < 1)
        * @param epsilonMax Maximum allowed delta for a weight update, stored as SD59x18 number
        * @param absoluteWeightGuardRail Minimum absolute weight allowed. CODEHAWKS INFO /s/611
        * @param maxTradeSizeRatio maximum trade size allowed as a fraction of the pool
        * @param updateInterval Minimum amount of seconds between two updates
     */
    struct QuantAMMWeightedPoolImmutableData {
        IERC20[] tokens;
        uint oracleStalenessThreshold;
        uint256 poolRegistry;
        int256[][] ruleParameters;
        uint64[] lambda;
        uint64 epsilonMax;
        uint64 absoluteWeightGuardRail;
        uint64 updateInterval;
        uint256 maxTradeSizeRatio;
    }

    ///@notice the time variables used for interpolation
    ///@param lastUpdateTime the last time the pool was updated, this is the time of the weights prior to multiplier being added to it.abi
    ///@param lastPossibleInterpolationTime the last time that the weights can be updated given the block multiplier before one weight hits the guardrail
    struct InteropDetails {
        uint40 lastUpdateTime;
        uint40 lastInteropTime;
    }

    ///@notice the data needed to get the weights of the pool
    ///@param quantAMMBaseInterpolationDetails the time variables used for interpolation
    ///@param assets the assets of the pool

    ///@notice the data needed to get the weights of the pool
    ///@param interopDetails the time variables used for interpolation
    ///@param assets the assets of the pool
    ///@dev this would be more populated for v2 of the pool but the structure is kept for other areas
    struct QuantAMMBaseGetWeightData {
        InteropDetails interopDetails;
    }

    /// @notice Settings needed to create and initialise a pool
    /// @param assets the assets of the pool
    /// @param rule the rule to use for the pool
    /// @param oracles the oracles to use for the pool. [asset oracle][backup oracles for that asset]
    /// @param updateInterval the time between updates
    /// @param lambda the decay parameter for the rule
    /// @param epsilonMax the maximum allowed delta for a weight update
    /// @param absoluteWeightGuardRail the maximum allowed absolute weight allowed
    /// @param maxTradeSizeRatio the maximum trade size allowed as a fraction of the pool
    /// @param ruleParameters the parameters for the rule
    /// @param poolManager the address of the pool manager
    struct PoolSettings {
        IERC20[] assets;
        IUpdateRule rule;
        address[][] oracles;
        uint40 updateInterval;
        uint64[] lambda;
        uint64 epsilonMax;
        uint64 absoluteWeightGuardRail;
        uint64 maxTradeSizeRatio;
        int256[][] ruleParameters;
        address poolManager;
    }

    /// @notice function called to set weights and weight block multipliers
    /// @param _weights the weights to set that sum to 1
    /// @param _poolAddress the address of the pool to set the weights for
    /// @param _lastInterpolationTimePossible the last time that the weights can be updated given the block multiplier before one weight hits the guardrail
    function setWeights(
        int256[] calldata _weights,
        address _poolAddress,
        uint40 _lastInterpolationTimePossible
    ) external;
    
    /// @notice get pool details such as strategy name and description
    /// @param category the category of detail
    /// @param name the name of the detail to be retrieved
    function getPoolDetail(string memory category, string memory name) external view returns (string memory, string memory);

    /// @notice the acceptable number of blocks behind the current that an oracle value can be
    function getOracleStalenessThreshold() external view returns (uint);

    ///@notice returns the normalized weights of the pool for the current block
    function getNormalizedWeights() external view returns (uint256[] memory);
    
    ///@notice returns the fix window for the pool
    function getWithinFixWindow() external view returns (bool);
    /**
     * @notice Get dynamic pool data relevant to swap/add/remove calculations.
     * @return data A struct containing all dynamic QuantAMM weighted pool parameters
     */
    function getQuantAMMWeightedPoolDynamicData() external view returns (QuantAMMWeightedPoolDynamicData memory data);

    /**
     * @notice Get immutable pool data relevant to swap/add/remove calculations.
     * @return data A struct containing all immutable weighted pool parameters
     */
    function getQuantAMMWeightedPoolImmutableData() external view returns (QuantAMMWeightedPoolImmutableData memory data);

    /// @notice this is to update the runner for the pool. This is for hotfixes and is timelock protected.
    function setUpdateWeightRunnerAddress(address _updateWeightRunner) external;
}

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

/// @title The implementations required for a new update rule to run on robodex
interface IUpdateRule {
    /// @param _prevWeights the weights at the current block time
    /// @param _data the data from the oracles called in getdata in update weight runner
    /// @param _pool the pool address
    /// @param _parameters any parameters required by the rule apart from lambda
    /// @param _lambdaStore lambda values either 1 for all constituents or one per constituent
    /// @param _epsilonMax the maximum trade size possible for the pool
    /// @param _absoluteWeightGuardRail the minimum weight possible for the pool CODEHAWKS INFO /s/611
    function CalculateNewWeights(
        int256[] calldata _prevWeights,
        int256[]  memory _data,
        address _pool,
        int256[][] calldata _parameters,
        uint64[] calldata _lambdaStore,
        uint64 _epsilonMax,
        uint64 _absoluteWeightGuardRail
    ) external returns (int256[] memory updatedWeights);

    /// @notice Called on pool creation to preset all the neccessary rule state
    /// @param _poolAddress address of pool being initialised
    /// @param _newMovingAverages array of initial moving averages
    /// @param _newInitialValues the initial intermediate values provided
    /// @param _numberOfAssets number of assets in the pool
    function initialisePoolRuleIntermediateValues(
        address _poolAddress,
        //CODEHAWKS INFO /s/321 /s/516
        int256[] memory _newMovingAverages,
        int256[] memory _newInitialValues,
        uint _numberOfAssets
    ) external;

    /// @notice Check if the given parameters are valid for the rule
    /// @param _parameters the parameters to check
    function validParameters(int256[][] calldata _parameters) external view returns (bool);
}

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

import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@prb/math/contracts/PRBMathSD59x18.sol";
import "./IQuantAMMWeightedPool.sol";
import "./IUpdateRule.sol";
import "./OracleWrapper.sol";

import {
    IWeightedPool
} from "../pool-weighted/IWeightedPool.sol";
/*
ARCHITECTURE DESIGN NOTES

The update weight runner is a singleton contract that is responsible for running all weight updates. It is a singleton contract as it is responsible for managing the update rule state of all pools.

The update weight runner is responsible for:
- Managing the state of all update rules
- Managing the state of all pools related to update rules
- Managing the state of all oracles related to update rules
- Managing the state of all quantAMM weight runners
- Managing the state of the ETH/USD oracle - important for exit fee calculations
- Managing the state of all approved oracles
- Managing the state of all oracle staleness thresholds
- Managing the state of all pool last run times
- Managing the state of all pool rule settings
- Managing the state of all pool primary oracles
- Managing the state of all pool backup oracles
- Managing the state of all pool rules

As all QuantAMM pools are based on the TFMM approach, core aspects of running a periodic strategy
update are shared. This allows for appropriate centralisation of the process in a single update weight
runner.
What benefits are achieved by such centralisation? Efficiency of external contract calls is a great benefit
however security should always come before efficiency. A single runner allows for a gated approach
where pool contracts can be built, however only when registered with the quantammAdmin and update weight
runner can they be considered to be ”approved” and running within the QuantAMM umbrella.
Centralised common logic also allows for ease of protecting the interaction between quantammAdmin and update
weight runner, while reducing the pool specific code that will require n number of audits per n pools
designs. Such logic includes a single heavily tested implementation for oracle fall backs, triggering of
updates and guard rails.


 */


/// @title UpdateWeightRunner singleton contract that is responsible for running all weight updates

interface IUpdateWeightRunner {
    
    // Store the current execution context for callbacks
    struct ExecutionData {
        address pool;
        uint96 subpoolIndex;
    }
    struct PoolTimingSettings {
        uint40 lastPoolUpdateRun;
        uint40 updateInterval;
    }

    struct PoolRuleSettings {
        uint64[] lambda;
        PoolTimingSettings timingSettings;
        uint64 epsilonMax;
        uint64 absoluteWeightGuardRail;
        int256[][] ruleParameters;
        address poolManager;
    }

    /// @notice Struct for caching oracle replies
    /// @dev Data types chosen such that only one slot is used
    struct OracleData {
        int216 data;
        uint40 timestamp;
    }

    /// @notice Get the happy path primary oracles for the constituents of a pool
    /// @param _poolAddress Address of the pool
    function getOptimisedPoolOracle(
        address _poolAddress
    ) external view returns (address[] memory oracles);
    
    /// @notice Get the data for a pool from the oracles and return it in the same order as the assets in the pool
    /// @param _pool Pool to get data for
    function getData(
        address _pool
    ) external view returns (int256[] memory outputData);

    /// @notice Get the backup oracles for the constituents of a pool
    /// @param _poolAddress Address of the pool
    function getPoolOracleAndBackups(
        address _poolAddress
    ) external view returns (address[][] memory oracles);

    /// @notice Get the rule settings for a pool
    /// @param _poolAddress Address of the pool
    function getPoolRuleSettings(
        address _poolAddress
    ) external view returns (PoolRuleSettings memory oracles);

    /// @notice Get the rule for a pool
    /// @param _poolAddress Address of the pool
    function getPoolRule(
        address _poolAddress
    ) external view returns (IUpdateRule rule);

    /// @notice Add a new oracle to the available oracles
    /// @param _oracle Oracle to add
    function addOracle(OracleWrapper _oracle) external;

    /// @notice Removes an existing oracle from the approved oracles
    /// @param _oracleToRemove The oracle to remove
    function removeOracle(OracleWrapper _oracleToRemove) external;

    /// @notice Set a rule for a pool, called by the pool
    /// @param _poolSettings Settings for the pool
    function setRuleForPool(IQuantAMMWeightedPool.PoolSettings memory _poolSettings) external;

    /// @notice Run the update for the provided rule. Last update must be performed more than or equal (CODEHAWKS INFO /s/228) to updateInterval seconds ago.
    function performUpdate(address _pool) external;

    /// @notice Change the ETH/USD oracle
    /// @param _ethUsdOracle The new oracle address to use for ETH/USD 
    function setETHUSDOracle(address _ethUsdOracle) external;

    /// @notice Set the quantAMM swap fee % amount allocated to the protocol for running costs
    /// @param _quantAMMSwapFeeTake The new swap fee % amount allocated to the protocol for running costs
    function setQuantAMMSwapFeeTake(uint256 _quantAMMSwapFeeTake) external;

    /// @notice Get the quantAMM swap fee % amount allocated to the protocol for running costs
    function getQuantAMMSwapFeeTake() external view returns (uint256);

    /// @notice Set the quantAMM uplift fee % amount allocated to the protocol for running costs
    /// @param _quantAMMUpliftFeeTake The new uplift fee % amount allocated to the protocol for running costs
    function setQuantAMMUpliftFeeTake(uint256 _quantAMMUpliftFeeTake) external;

    /// @notice Get the quantAMM uplift fee % amount allocated to the protocol for running costs
    function getQuantAMMUpliftFeeTake() external view returns (uint256);

    /// @notice get the confirmedquantAMMAdmin address
    function getQuantAMMAdmin() external view returns (address); 

    /// @notice Sets the timestamp of when an update was last run for a pool. Can by used as a breakgrass measure to retrigger an update.
    /// @param _poolAddress the target pool address
    /// @param _time the time to initialise the last update run to
    function InitialisePoolLastRunTime(
        address _poolAddress,
        uint40 _time
    ) external ;

    /// @notice Breakglass function to allow the admin or the pool manager to set the quantammAdmins weights manually
    /// @param _weights the new weights
    /// @param _poolAddress the target pool
    /// @param _lastInterpolationTimePossible the last time that the interpolation will work
    /// @param _numberOfAssets the number of assets in the pool
    function setWeightsManually(
        int256[] calldata _weights,
        address _poolAddress,
        uint40 _lastInterpolationTimePossible,
        uint _numberOfAssets
    ) external ;

    /// @notice Breakglass function to allow the admin or the pool manager to set the intermediate values of the rule manually
    /// @param _poolAddress the target pool
    /// @param _newMovingAverages manual new moving averages
    /// @param _newParameters manual new parameters
    /// @param _numberOfAssets number of assets in the pool
    function setIntermediateValuesManually(
        address _poolAddress,
        int256[] memory _newMovingAverages,
        int256[] memory _newParameters,
        uint _numberOfAssets
    ) external ;
}

File 5 of 72 : OracleWrapper.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.24;

/// @notice Generic wrapper around an arbitrary oracle that returns normalized values.
abstract contract OracleWrapper {
    //CODEHAWKS INFO /s/294 remove unused enum

    /// @notice Get the data of the underlying oracle, interpretation of data depends on oracle type
    /// @param data The underlying data (can be negative), normalized to 18 decimals
    /// @return data Retrieved oracle data
    /// @return timestamp Last update timestamp
    function getData() public view returns (int216 data, uint40 timestamp) {
        (data, timestamp) = _getData();
        require(timestamp > 0, "INVORCLVAL"); // Sanity check in case oracle returns invalid values
    }

    /// @notice Get data from oracle, to be implemented by child contracts. Needs to return data with 18 decimals
    function _getData() internal view virtual returns (int216 data, uint40 timestamp) {}
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

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

import { TokenInfo } from "../vault/VaultTypes.sol";

/**
 * @notice Convenience interface for pools, to get easy access to information stored in the Vault.
 * Intended mostly for off-chain requests; pools do not need to implement this to work properly.
 */
interface IPoolInfo {
    /**
     * @notice Gets the tokens registered in the pool.
     * @return tokens List of tokens in the pool, sorted in registration order
     */
    function getTokens() external view returns (IERC20[] memory tokens);

    /**
     * @notice Gets the raw data for the pool: tokens, token info, raw balances, and last live balances.
     * @return tokens Pool tokens, sorted in token registration order
     * @return tokenInfo Token info structs (type, rate provider, yield flag), sorted in token registration order
     * @return balancesRaw Current native decimal balances of the pool tokens, sorted in token registration order
     * @return lastBalancesLiveScaled18 Last saved live balances, sorted in token registration order
     */
    function getTokenInfo()
        external
        view
        returns (
            IERC20[] memory tokens,
            TokenInfo[] memory tokenInfo,
            uint256[] memory balancesRaw,
            uint256[] memory lastBalancesLiveScaled18
        );

    /**
     * @notice Gets the current live balances of the pool as fixed point, 18-decimal numbers.
     * @dev Note that live balances will not necessarily be accurate if the pool is in Recovery Mode.
     * Withdrawals in Recovery Mode do not make external calls (including those necessary for updating live balances),
     * so if there are withdrawals, raw and live balances will be out of sync until Recovery Mode is disabled.
     *
     * @return balancesLiveScaled18 Token balances after paying yield fees, applying decimal scaling and rates
     */
    function getCurrentLiveBalances() external view returns (uint256[] memory balancesLiveScaled18);

    /**
     * @notice Fetches the static swap fee percentage for the pool.
     * @return staticSwapFeePercentage 18-decimal FP value of the static swap fee percentage
     */
    function getStaticSwapFeePercentage() external view returns (uint256 staticSwapFeePercentage);

    /**
     * @notice Gets the aggregate swap and yield fee percentages for a pool.
     * @dev These are determined by the current protocol and pool creator fees, set in the `ProtocolFeeController`.
     * @return aggregateSwapFeePercentage The aggregate percentage fee applied to swaps
     * @return aggregateYieldFeePercentage The aggregate percentage fee applied to yield
     */
    function getAggregateFeePercentages()
        external
        view
        returns (uint256 aggregateSwapFeePercentage, uint256 aggregateYieldFeePercentage);
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

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

import { IBasePool } from "../vault/IBasePool.sol";

/**
 * @notice Weighted Pool data that cannot change after deployment.
 * @param tokens Pool tokens, sorted in token registration order
 * @param decimalScalingFactors Conversion factor used to adjust for token decimals for uniform precision in
 * calculations. FP(1) for 18-decimal tokens
 * @param normalizedWeights The token weights, sorted in token registration order
 */
struct WeightedPoolImmutableData {
    IERC20[] tokens;
    uint256[] decimalScalingFactors;
    uint256[] normalizedWeights;
}

/**
 * @notice Snapshot of current Weighted Pool data that can change.
 * @dev Note that live balances will not necessarily be accurate if the pool is in Recovery Mode. Withdrawals
 * in Recovery Mode do not make external calls (including those necessary for updating live balances), so if
 * there are withdrawals, raw and live balances will be out of sync until Recovery Mode is disabled.
 *
 * @param balancesLiveScaled18 Token balances after paying yield fees, applying decimal scaling and rates
 * @param tokenRates 18-decimal FP values for rate tokens (e.g., yield-bearing), or FP(1) for standard tokens
 * @param staticSwapFeePercentage 18-decimal FP value of the static swap fee percentage
 * @param totalSupply The current total supply of the pool tokens (BPT)
 * @param isPoolInitialized If false, the pool has not been seeded with initial liquidity, so operations will revert
 * @param isPoolPaused If true, the pool is paused, and all non-recovery-mode state-changing operations will revert
 * @param isPoolInRecoveryMode If true, Recovery Mode withdrawals are enabled, and live balances may be inaccurate
 */
struct WeightedPoolDynamicData {
    uint256[] balancesLiveScaled18;
    uint256[] tokenRates;
    uint256 staticSwapFeePercentage;
    uint256 totalSupply;
    bool isPoolInitialized;
    bool isPoolPaused;
    bool isPoolInRecoveryMode;
}

/// @notice Full Weighted pool interface.
interface IWeightedPool is IBasePool {
    /**
     * @notice Get the normalized weights.
     * @return normalizedWeights The normalized weights, sorted in token registration order
     */
    function getNormalizedWeights() external view returns (uint256[] memory normalizedWeights);

    /**
     * @notice Get dynamic pool data relevant to swap/add/remove calculations.
     * @return data A struct containing all dynamic weighted pool parameters
     */
    function getWeightedPoolDynamicData() external view returns (WeightedPoolDynamicData memory data);

    /**
     * @notice Get immutable pool data relevant to swap/add/remove calculations.
     * @return data A struct containing all immutable weighted pool parameters
     */
    function getWeightedPoolImmutableData() external view returns (WeightedPoolImmutableData memory data);
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

/// @notice Simple interface for permissioned calling of external functions.
interface IAuthentication {
    /// @notice The sender does not have permission to call a function.
    error SenderNotAllowed();

    /**
     * @notice Returns the action identifier associated with the external function described by `selector`.
     * @param selector The 4-byte selector of the permissioned function
     * @return actionId The computed actionId
     */
    function getActionId(bytes4 selector) external view returns (bytes32 actionId);
}

File 9 of 72 : IPoolVersion.sol
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

/// @notice Simple interface to retrieve the version of pools deployed by a pool factory.
interface IPoolVersion {
    /**
     * @notice Returns a JSON representation of the deployed pool version containing name, version number and task ID.
     * @dev This is typically only useful in complex Pool deployment schemes, where multiple subsystems need to know
     * about each other. Note that this value will only be set at factory creation time.
     *
     * @return poolVersion A string representation of the pool version
     */
    function getPoolVersion() external view returns (string memory poolVersion);
}

File 10 of 72 : IRateProvider.sol
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

/// @notice General interface for token exchange rates.
interface IRateProvider {
    /**
     * @notice An 18 decimal fixed point number representing the exchange rate of one token to another related token.
     * @dev The meaning of this rate depends on the context. Note that there may be an error associated with a token
     * rate, and the caller might require a certain rounding direction to ensure correctness. This (legacy) interface
     * does not take a rounding direction or return an error, so great care must be taken when interpreting and using
     * rates in downstream computations.
     *
     * @return rate The current token rate
     */
    function getRate() external view returns (uint256 rate);
}

File 11 of 72 : IVersion.sol
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

/// @notice Simple interface to retrieve the version of a deployed contract.
interface IVersion {
    /**
     * @notice Return arbitrary text representing the version of a contract.
     * @dev For standard Balancer contracts, returns a JSON representation of the contract version containing name,
     * version number and task ID. See real examples in the deployment repo; local tests just use plain text strings.
     *
     * @return version The version string corresponding to the current deployed contract
     */
    function version() external view returns (string memory);
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

/// @notice Interface to the Vault's permission system.
interface IAuthorizer {
    /**
     * @notice Returns true if `account` can perform the action described by `actionId` in the contract `where`.
     * @param actionId Identifier for the action to be performed
     * @param account Account trying to perform the action
     * @param where Target contract for the action
     * @return success True if the action is permitted
     */
    function canPerform(bytes32 actionId, address account, address where) external view returns (bool success);
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

import { IUnbalancedLiquidityInvariantRatioBounds } from "./IUnbalancedLiquidityInvariantRatioBounds.sol";
import { ISwapFeePercentageBounds } from "./ISwapFeePercentageBounds.sol";
import { PoolSwapParams, Rounding, SwapKind } from "./VaultTypes.sol";

/**
 * @notice Base interface for a Balancer Pool.
 * @dev All pool types should implement this interface. Note that it also requires implementation of:
 * - `ISwapFeePercentageBounds` to specify the minimum and maximum swap fee percentages.
 * - `IUnbalancedLiquidityInvariantRatioBounds` to specify how much the invariant can change during an unbalanced
 * liquidity operation.
 */
interface IBasePool is ISwapFeePercentageBounds, IUnbalancedLiquidityInvariantRatioBounds {
    /***************************************************************************
                                   Invariant
    ***************************************************************************/

    /**
     * @notice Computes the pool's invariant.
     * @dev This function computes the invariant based on current balances (and potentially other pool state).
     * The rounding direction must be respected for the Vault to round in the pool's favor when calling this function.
     * If the invariant computation involves no precision loss (e.g. simple sum of balances), the same result can be
     * returned for both rounding directions.
     *
     * You can think of the invariant as a measure of the "value" of the pool, which is related to the total liquidity
     * (i.e., the "BPT rate" is `invariant` / `totalSupply`). Two critical properties must hold:
     *
     * 1) The invariant should not change due to a swap. In practice, it can *increase* due to swap fees, which
     * effectively add liquidity after the swap - but it should never decrease.
     *
     * 2) The invariant must be "linear"; i.e., increasing the balances proportionally must increase the invariant in
     * the same proportion: inv(a * n, b * n, c * n) = inv(a, b, c) * n
     *
     * Property #1 is required to prevent "round trip" paths that drain value from the pool (and all LP shareholders).
     * Intuitively, an accurate pricing algorithm ensures the user gets an equal value of token out given token in, so
     * the total value should not change.
     *
     * Property #2 is essential for the "fungibility" of LP shares. If it did not hold, then different users depositing
     * the same total value would get a different number of LP shares. In that case, LP shares would not be
     * interchangeable, as they must be in a fair DEX.
     *
     * @param balancesLiveScaled18 Token balances after paying yield fees, applying decimal scaling and rates
     * @param rounding Rounding direction to consider when computing the invariant
     * @return invariant The calculated invariant of the pool, represented as a uint256
     */
    function computeInvariant(
        uint256[] memory balancesLiveScaled18,
        Rounding rounding
    ) external view returns (uint256 invariant);

    /**
     * @notice Computes a new token balance, given the invariant growth ratio and all other balances.
     * @dev Similar to V2's `_getTokenBalanceGivenInvariantAndAllOtherBalances` in StableMath.
     * The pool must round up for the Vault to round in the protocol's favor when calling this function.
     *
     * @param balancesLiveScaled18 Token balances after paying yield fees, applying decimal scaling and rates
     * @param tokenInIndex The index of the token we're computing the balance for, sorted in token registration order
     * @param invariantRatio The ratio of the new invariant (after an operation) to the old
     * @return newBalance The new balance of the selected token, after the operation
     */
    function computeBalance(
        uint256[] memory balancesLiveScaled18,
        uint256 tokenInIndex,
        uint256 invariantRatio
    ) external view returns (uint256 newBalance);

    /***************************************************************************
                                       Swaps
    ***************************************************************************/

    /**
     * @notice Execute a swap in the pool.
     * @param params Swap parameters (see above for struct definition)
     * @return amountCalculatedScaled18 Calculated amount for the swap operation
     */
    function onSwap(PoolSwapParams calldata params) external returns (uint256 amountCalculatedScaled18);
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

import { IAuthentication } from "../solidity-utils/helpers/IAuthentication.sol";

/**
 * @notice Base interface for a Balancer Pool Factory.
 * @dev All pool factories should be derived from `BasePoolFactory` to enable common behavior for all pool types
 * (e.g., address prediction, tracking deployed pools, and governance-facilitated migration).
 */
interface IBasePoolFactory is IAuthentication {
    /**
     * @notice A pool was deployed.
     * @param pool The address of the new pool
     */
    event PoolCreated(address indexed pool);

    /// @notice The factory was disabled by governance.
    event FactoryDisabled();

    /// @notice Attempted pool creation after the factory was disabled.
    error Disabled();

    /// @notice A pool index is beyond the current bounds of the array.
    error IndexOutOfBounds();

    /**
     * @notice Check whether a pool was deployed by this factory.
     * @param pool The pool to check
     * @return success True if `pool` was created by this factory
     */
    function isPoolFromFactory(address pool) external view returns (bool success);

    /**
     * @notice Return the total number of pools deployed by this factory.
     * @dev This can then be used to "paginate" calls to `getPools` to control gas costs.
     * @return poolCount The number of pools deployed by this factory
     */
    function getPoolCount() external view returns (uint256 poolCount);

    /**
     * @notice Return a subset of the list of pools deployed by this factory.
     * @dev `start` must be a valid index, but if `count` exceeds the total length, it will not revert, but simply
     * stop at the end and return fewer results than requested.
     *
     * @param start The index of the first pool to return
     * @param count The maximum number of pools to return
     * @return pools The list of pools deployed by this factory, starting at `start` and returning up to `count` pools
     */
    function getPoolsInRange(uint256 start, uint256 count) external view returns (address[] memory pools);

    /**
     * @notice Return the complete list of pools deployed by this factory.
     * @return pools The list of pools deployed by this factory
     */
    function getPools() external view returns (address[] memory pools);

    /**
     * @notice Return the address where a new pool will be deployed, based on the factory address and salt.
     * @param constructorArgs The arguments used to create the pool
     * @param salt The salt used to deploy the pool
     * @return deploymentAddress The predicted address of the pool, given the salt
     */
    function getDeploymentAddress(
        bytes memory constructorArgs,
        bytes32 salt
    ) external view returns (address deploymentAddress);

    /**
     * @notice Check whether this factory has been disabled by governance.
     * @return success True if this factory was disabled
     */
    function isDisabled() external view returns (bool success);

    /**
     * @notice Disable the factory, preventing the creation of more pools.
     * @dev Existing pools are unaffected. Once a factory is disabled, it cannot be re-enabled.
     */
    function disable() external;
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

// Explicitly import VaultTypes structs because we expect this interface to be heavily used by external developers.
// Internally, when this list gets too long, we usually just do a simple import to keep things tidy.
import {
    TokenConfig,
    LiquidityManagement,
    PoolSwapParams,
    AfterSwapParams,
    HookFlags,
    AddLiquidityKind,
    RemoveLiquidityKind,
    SwapKind
} from "./VaultTypes.sol";

/**
 * @notice Interface for pool hooks.
 * @dev Hooks are functions invoked by the Vault at specific points in the flow of each operation. This guarantees that
 * they are called in the correct order, and with the correct arguments. To maintain this security, these functions
 * should only be called by the Vault. The recommended way to do this is to derive the hook contract from `BaseHooks`,
 * then use the `onlyVault` modifier from `VaultGuard`. (See the examples in /pool-hooks.)
 */
interface IHooks {
    /***************************************************************************
                                   Register
    ***************************************************************************/

    /**
     * @notice Hook executed when a pool is registered with a non-zero hooks contract.
     * @dev Returns true if registration was successful, and false to revert the pool registration.
     * Make sure this function is properly implemented (e.g. check the factory, and check that the
     * given pool is from the factory). The Vault address will be msg.sender.
     *
     * @param factory Address of the pool factory (contract deploying the pool)
     * @param pool Address of the pool
     * @param tokenConfig An array of descriptors for the tokens the pool will manage
     * @param liquidityManagement Liquidity management flags indicating which functions are enabled
     * @return success True if the hook allowed the registration, false otherwise
     */
    function onRegister(
        address factory,
        address pool,
        TokenConfig[] memory tokenConfig,
        LiquidityManagement calldata liquidityManagement
    ) external returns (bool success);

    /**
     * @notice Return the set of hooks implemented by the contract.
     * @dev The Vault will only call hooks the pool says it supports, and of course only if a hooks contract is defined
     * (i.e., the `poolHooksContract` in `PoolRegistrationParams` is non-zero).
     * `onRegister` is the only "mandatory" hook.
     *
     * @return hookFlags Flags indicating which hooks the contract supports
     */
    function getHookFlags() external view returns (HookFlags memory hookFlags);

    /***************************************************************************
                                   Initialize
    ***************************************************************************/

    /**
     * @notice Hook executed before pool initialization.
     * @dev Called if the `shouldCallBeforeInitialize` flag is set in the configuration. Hook contracts should use
     * the `onlyVault` modifier to guarantee this is only called by the Vault.
     *
     * @param exactAmountsIn Exact amounts of input tokens
     * @param userData Optional, arbitrary data sent with the encoded request
     * @return success True if the pool wishes to proceed with initialization
     */
    function onBeforeInitialize(uint256[] memory exactAmountsIn, bytes memory userData) external returns (bool success);

    /**
     * @notice Hook to be executed after pool initialization.
     * @dev Called if the `shouldCallAfterInitialize` flag is set in the configuration. Hook contracts should use
     * the `onlyVault` modifier to guarantee this is only called by the Vault.
     *
     * @param exactAmountsIn Exact amounts of input tokens
     * @param bptAmountOut Amount of pool tokens minted during initialization
     * @param userData Optional, arbitrary data sent with the encoded request
     * @return success True if the pool accepts the initialization results
     */
    function onAfterInitialize(
        uint256[] memory exactAmountsIn,
        uint256 bptAmountOut,
        bytes memory userData
    ) external returns (bool success);

    /***************************************************************************
                                   Add Liquidity
    ***************************************************************************/

    /**
     * @notice Hook to be executed before adding liquidity.
     * @dev Called if the `shouldCallBeforeAddLiquidity` flag is set in the configuration. Hook contracts should use
     * the `onlyVault` modifier to guarantee this is only called by the Vault.
     *
     * @param router The address (usually a router contract) that initiated an add liquidity operation on the Vault
     * @param pool Pool address, used to fetch pool information from the Vault (pool config, tokens, etc.)
     * @param kind The add liquidity operation type (e.g., proportional, custom)
     * @param maxAmountsInScaled18 Maximum amounts of input tokens
     * @param minBptAmountOut Minimum amount of output pool tokens
     * @param balancesScaled18 Current pool balances, sorted in token registration order
     * @param userData Optional, arbitrary data sent with the encoded request
     * @return success True if the pool wishes to proceed with settlement
     */
    function onBeforeAddLiquidity(
        address router,
        address pool,
        AddLiquidityKind kind,
        uint256[] memory maxAmountsInScaled18,
        uint256 minBptAmountOut,
        uint256[] memory balancesScaled18,
        bytes memory userData
    ) external returns (bool success);

    /**
     * @notice Hook to be executed after adding liquidity.
     * @dev Called if the `shouldCallAfterAddLiquidity` flag is set in the configuration. The Vault will ignore
     * `hookAdjustedAmountsInRaw` unless `enableHookAdjustedAmounts` is true. Hook contracts should use the
     * `onlyVault` modifier to guarantee this is only called by the Vault.
     *
     * @param router The address (usually a router contract) that initiated an add liquidity operation on the Vault
     * @param pool Pool address, used to fetch pool information from the Vault (pool config, tokens, etc.)
     * @param kind The add liquidity operation type (e.g., proportional, custom)
     * @param amountsInScaled18 Actual amounts of tokens added, sorted in token registration order
     * @param amountsInRaw Actual amounts of tokens added, sorted in token registration order
     * @param bptAmountOut Amount of pool tokens minted
     * @param balancesScaled18 Current pool balances, sorted in token registration order
     * @param userData Additional (optional) data provided by the user
     * @return success True if the pool wishes to proceed with settlement
     * @return hookAdjustedAmountsInRaw New amountsInRaw, potentially modified by the hook
     */
    function onAfterAddLiquidity(
        address router,
        address pool,
        AddLiquidityKind kind,
        uint256[] memory amountsInScaled18,
        uint256[] memory amountsInRaw,
        uint256 bptAmountOut,
        uint256[] memory balancesScaled18,
        bytes memory userData
    ) external returns (bool success, uint256[] memory hookAdjustedAmountsInRaw);

    /***************************************************************************
                                 Remove Liquidity
    ***************************************************************************/

    /**
     * @notice Hook to be executed before removing liquidity.
     * @dev Called if the `shouldCallBeforeRemoveLiquidity` flag is set in the configuration. Hook contracts should use
     * the `onlyVault` modifier to guarantee this is only called by the Vault.
     *
     * @param router The address (usually a router contract) that initiated a remove liquidity operation on the Vault
     * @param pool Pool address, used to fetch pool information from the Vault (pool config, tokens, etc.)
     * @param kind The type of remove liquidity operation (e.g., proportional, custom)
     * @param maxBptAmountIn Maximum amount of input pool tokens
     * @param minAmountsOutScaled18 Minimum output amounts, sorted in token registration order
     * @param balancesScaled18 Current pool balances, sorted in token registration order
     * @param userData Optional, arbitrary data sent with the encoded request
     * @return success True if the pool wishes to proceed with settlement
     */
    function onBeforeRemoveLiquidity(
        address router,
        address pool,
        RemoveLiquidityKind kind,
        uint256 maxBptAmountIn,
        uint256[] memory minAmountsOutScaled18,
        uint256[] memory balancesScaled18,
        bytes memory userData
    ) external returns (bool success);

    /**
     * @notice Hook to be executed after removing liquidity.
     * @dev Called if the `shouldCallAfterRemoveLiquidity` flag is set in the configuration. The Vault will ignore
     * `hookAdjustedAmountsOutRaw` unless `enableHookAdjustedAmounts` is true. Hook contracts should use the
     * `onlyVault` modifier to guarantee this is only called by the Vault.
     *
     * @param router The address (usually a router contract) that initiated a remove liquidity operation on the Vault
     * @param pool Pool address, used to fetch pool information from the Vault (pool config, tokens, etc.)
     * @param kind The type of remove liquidity operation (e.g., proportional, custom)
     * @param bptAmountIn Amount of pool tokens to burn
     * @param amountsOutScaled18 Scaled amount of tokens to receive, sorted in token registration order
     * @param amountsOutRaw Actual amount of tokens to receive, sorted in token registration order
     * @param balancesScaled18 Current pool balances, sorted in token registration order
     * @param userData Additional (optional) data provided by the user
     * @return success True if the pool wishes to proceed with settlement
     * @return hookAdjustedAmountsOutRaw New amountsOutRaw, potentially modified by the hook
     */
    function onAfterRemoveLiquidity(
        address router,
        address pool,
        RemoveLiquidityKind kind,
        uint256 bptAmountIn,
        uint256[] memory amountsOutScaled18,
        uint256[] memory amountsOutRaw,
        uint256[] memory balancesScaled18,
        bytes memory userData
    ) external returns (bool success, uint256[] memory hookAdjustedAmountsOutRaw);

    /***************************************************************************
                                    Swap
    ***************************************************************************/

    /**
     * @notice Called before a swap to give the Pool an opportunity to perform actions.
     * @dev Called if the `shouldCallBeforeSwap` flag is set in the configuration. Hook contracts should use the
     * `onlyVault` modifier to guarantee this is only called by the Vault.
     *
     * @param params Swap parameters (see PoolSwapParams for struct definition)
     * @param pool Pool address, used to get pool information from the Vault (poolData, token config, etc.)
     * @return success True if the pool wishes to proceed with settlement
     */
    function onBeforeSwap(PoolSwapParams calldata params, address pool) external returns (bool success);

    /**
     * @notice Called after a swap to perform further actions once the balances have been updated by the swap.
     * @dev Called if the `shouldCallAfterSwap` flag is set in the configuration. The Vault will ignore
     * `hookAdjustedAmountCalculatedRaw` unless `enableHookAdjustedAmounts` is true. Hook contracts should
     * use the `onlyVault` modifier to guarantee this is only called by the Vault.
     *
     * @param params Swap parameters (see above for struct definition)
     * @return success True if the pool wishes to proceed with settlement
     * @return hookAdjustedAmountCalculatedRaw New amount calculated, potentially modified by the hook
     */
    function onAfterSwap(
        AfterSwapParams calldata params
    ) external returns (bool success, uint256 hookAdjustedAmountCalculatedRaw);

    /**
     * @notice Called after `onBeforeSwap` and before the main swap operation, if the pool has dynamic fees.
     * @dev Called if the `shouldCallComputeDynamicSwapFee` flag is set in the configuration. Hook contracts should use
     * the `onlyVault` modifier to guarantee this is only called by the Vault.
     *
     * @param params Swap parameters (see PoolSwapParams for struct definition)
     * @param pool Pool address, used to get pool information from the Vault (poolData, token config, etc.)
     * @param staticSwapFeePercentage 18-decimal FP value of the static swap fee percentage, for reference
     * @return success True if the pool wishes to proceed with settlement
     * @return dynamicSwapFeePercentage Value of the swap fee percentage, as an 18-decimal FP value
     */
    function onComputeDynamicSwapFeePercentage(
        PoolSwapParams calldata params,
        address pool,
        uint256 staticSwapFeePercentage
    ) external view returns (bool success, uint256 dynamicSwapFeePercentage);
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

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

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

/// @notice Contract that handles protocol and pool creator fees for the Vault.
interface IProtocolFeeController {
    /**
     * @notice Emitted when the protocol swap fee percentage is updated.
     * @param swapFeePercentage The updated protocol swap fee percentage
     */
    event GlobalProtocolSwapFeePercentageChanged(uint256 swapFeePercentage);

    /**
     * @notice Emitted when the protocol yield fee percentage is updated.
     * @param yieldFeePercentage The updated protocol yield fee percentage
     */
    event GlobalProtocolYieldFeePercentageChanged(uint256 yieldFeePercentage);

    /**
     * @notice Emitted when the protocol swap fee percentage is updated for a specific pool.
     * @param pool The pool whose protocol swap fee will be changed
     * @param swapFeePercentage The updated protocol swap fee percentage
     */
    event ProtocolSwapFeePercentageChanged(address indexed pool, uint256 swapFeePercentage);

    /**
     * @notice Emitted when the protocol yield fee percentage is updated for a specific pool.
     * @param pool The pool whose protocol yield fee will be changed
     * @param yieldFeePercentage The updated protocol yield fee percentage
     */
    event ProtocolYieldFeePercentageChanged(address indexed pool, uint256 yieldFeePercentage);

    /**
     * @notice Emitted when the pool creator swap fee percentage of a pool is updated.
     * @param pool The pool whose pool creator swap fee will be changed
     * @param poolCreatorSwapFeePercentage The new pool creator swap fee percentage for the pool
     */
    event PoolCreatorSwapFeePercentageChanged(address indexed pool, uint256 poolCreatorSwapFeePercentage);

    /**
     * @notice Emitted when the pool creator yield fee percentage of a pool is updated.
     * @param pool The pool whose pool creator yield fee will be changed
     * @param poolCreatorYieldFeePercentage The new pool creator yield fee percentage for the pool
     */
    event PoolCreatorYieldFeePercentageChanged(address indexed pool, uint256 poolCreatorYieldFeePercentage);

    /**
     * @notice Logs the collection of protocol swap fees in a specific token and amount.
     * @dev Note that since charging protocol fees (i.e., distributing tokens between pool and fee balances) occurs
     * in the Vault, but fee collection happens in the ProtocolFeeController, the swap fees reported here may encompass
     * multiple operations.
     *
     * @param pool The pool on which the swap fee was charged
     * @param token The token in which the swap fee was charged
     * @param amount The amount of the token collected in fees
     */
    event ProtocolSwapFeeCollected(address indexed pool, IERC20 indexed token, uint256 amount);

    /**
     * @notice Logs the collection of protocol yield fees in a specific token and amount.
     * @dev Note that since charging protocol fees (i.e., distributing tokens between pool and fee balances) occurs
     * in the Vault, but fee collection happens in the ProtocolFeeController, the yield fees reported here may encompass
     * multiple operations.
     *
     * @param pool The pool on which the yield fee was charged
     * @param token The token in which the yield fee was charged
     * @param amount The amount of the token collected in fees
     */
    event ProtocolYieldFeeCollected(address indexed pool, IERC20 indexed token, uint256 amount);

    /**
     * @notice Logs the withdrawal of protocol fees in a specific token and amount.
     * @param pool The pool from which protocol fees are being withdrawn
     * @param token The token being withdrawn
     * @param recipient The recipient of the funds
     * @param amount The amount of the fee token that was withdrawn
     */
    event ProtocolFeesWithdrawn(address indexed pool, IERC20 indexed token, address indexed recipient, uint256 amount);

    /**
     * @notice Logs the withdrawal of pool creator fees in a specific token and amount.
     * @param pool The pool from which pool creator fees are being withdrawn
     * @param token The token being withdrawn
     * @param recipient The recipient of the funds (the pool creator if permissionless, or another account)
     * @param amount The amount of the fee token that was withdrawn
     */
    event PoolCreatorFeesWithdrawn(
        address indexed pool,
        IERC20 indexed token,
        address indexed recipient,
        uint256 amount
    );

    /**
     * @notice Error raised when the protocol swap fee percentage exceeds the maximum allowed value.
     * @dev Note that this is checked for both the global and pool-specific protocol swap fee percentages.
     */
    error ProtocolSwapFeePercentageTooHigh();

    /**
     * @notice Error raised when the protocol yield fee percentage exceeds the maximum allowed value.
     * @dev Note that this is checked for both the global and pool-specific protocol yield fee percentages.
     */
    error ProtocolYieldFeePercentageTooHigh();

    /**
     * @notice Error raised if there is no pool creator on a withdrawal attempt from the given pool.
     * @param pool The pool with no creator
     */
    error PoolCreatorNotRegistered(address pool);

    /**
     * @notice Error raised if the wrong account attempts to withdraw pool creator fees.
     * @param caller The account attempting to withdraw pool creator fees
     * @param pool The pool the caller tried to withdraw from
     */
    error CallerIsNotPoolCreator(address caller, address pool);

    /// @notice Error raised when the pool creator swap or yield fee percentage exceeds the maximum allowed value.
    error PoolCreatorFeePercentageTooHigh();

    /**
     * @notice Get the address of the main Vault contract.
     * @return vault The Vault address
     */
    function vault() external view returns (IVault);

    /**
     * @notice Collects aggregate fees from the Vault for a given pool.
     * @param pool The pool with aggregate fees
     */
    function collectAggregateFees(address pool) external;

    /**
     * @notice Getter for the current global protocol swap fee.
     * @return protocolSwapFeePercentage The global protocol swap fee percentage
     */
    function getGlobalProtocolSwapFeePercentage() external view returns (uint256 protocolSwapFeePercentage);

    /**
     * @notice Getter for the current global protocol yield fee.
     * @return protocolYieldFeePercentage The global protocol yield fee percentage
     */
    function getGlobalProtocolYieldFeePercentage() external view returns (uint256 protocolYieldFeePercentage);

    /**
     * @notice Getter for the current protocol swap fee for a given pool.
     * @param pool The address of the pool
     * @return protocolSwapFeePercentage The global protocol swap fee percentage
     * @return isOverride True if the protocol fee has been overridden
     */
    function getPoolProtocolSwapFeeInfo(
        address pool
    ) external view returns (uint256 protocolSwapFeePercentage, bool isOverride);

    /**
     * @notice Getter for the current protocol yield fee for a given pool.
     * @param pool The address of the pool
     * @return protocolYieldFeePercentage The global protocol yield fee percentage
     * @return isOverride True if the protocol fee has been overridden
     */
    function getPoolProtocolYieldFeeInfo(
        address pool
    ) external view returns (uint256 protocolYieldFeePercentage, bool isOverride);

    /**
     * @notice Returns the amount of each pool token allocated to the protocol for withdrawal.
     * @dev Includes both swap and yield fees.
     * @param pool The address of the pool on which fees were collected
     * @return feeAmounts The total amounts of each token available for withdrawal, sorted in token registration order
     */
    function getProtocolFeeAmounts(address pool) external view returns (uint256[] memory feeAmounts);

    /**
     * @notice Returns the amount of each pool token allocated to the pool creator for withdrawal.
     * @dev Includes both swap and yield fees.
     * @param pool The address of the pool on which fees were collected
     * @return feeAmounts The total amounts of each token available for withdrawal, sorted in token registration order
     */
    function getPoolCreatorFeeAmounts(address pool) external view returns (uint256[] memory feeAmounts);

    /**
     * @notice Returns a calculated aggregate percentage from protocol and pool creator fee percentages.
     * @dev Not tied to any particular pool; this just performs the low-level "additive fee" calculation. Note that
     * pool creator fees are calculated based on creatorAndLpFees, and not in totalFees. Since aggregate fees are
     * stored in the Vault with 24-bit precision, this will truncate any values that require greater precision.
     * It is expected that pool creators will negotiate with the DAO and agree on reasonable values for these fee
     * components, but the truncation ensures it will not revert for any valid set of fee percentages.
     *
     * See example below:
     *
     * tokenOutAmount = 10000; poolSwapFeePct = 10%; protocolFeePct = 40%; creatorFeePct = 60%
     * totalFees = tokenOutAmount * poolSwapFeePct = 10000 * 10% = 1000
     * protocolFees = totalFees * protocolFeePct = 1000 * 40% = 400
     * creatorAndLpFees = totalFees - protocolFees = 1000 - 400 = 600
     * creatorFees = creatorAndLpFees * creatorFeePct = 600 * 60% = 360
     * lpFees (will stay in the pool) = creatorAndLpFees - creatorFees = 600 - 360 = 240
     *
     * @param protocolFeePercentage The protocol portion of the aggregate fee percentage
     * @param poolCreatorFeePercentage The pool creator portion of the aggregate fee percentage
     * @return aggregateFeePercentage The computed aggregate percentage
     */
    function computeAggregateFeePercentage(
        uint256 protocolFeePercentage,
        uint256 poolCreatorFeePercentage
    ) external pure returns (uint256 aggregateFeePercentage);

    /**
     * @notice Override the protocol swap fee percentage for a specific pool.
     * @dev This is a permissionless call, and will set the pool's fee to the current global fee, if it is different
     * from the current value, and the fee is not controlled by governance (i.e., has never been overridden).
     *
     * @param pool The pool for which we are setting the protocol swap fee
     */
    function updateProtocolSwapFeePercentage(address pool) external;

    /**
     * @notice Override the protocol yield fee percentage for a specific pool.
     * @dev This is a permissionless call, and will set the pool's fee to the current global fee, if it is different
     * from the current value, and the fee is not controlled by governance (i.e., has never been overridden).
     *
     * @param pool The pool for which we are setting the protocol yield fee
     */
    function updateProtocolYieldFeePercentage(address pool) external;

    /***************************************************************************
                                Permissioned Functions
    ***************************************************************************/

    /**
     * @notice Add pool-specific entries to the protocol swap and yield percentages.
     * @dev This must be called from the Vault during pool registration. It will initialize the pool to the global
     * protocol fee percentage values (or 0, if the `protocolFeeExempt` flags is set), and return the initial aggregate
     * fee percentages, based on an initial pool creator fee of 0.
     *
     * @param pool The address of the pool being registered
     * @param poolCreator The address of the pool creator (or 0 if there won't be a pool creator fee)
     * @param protocolFeeExempt If true, the pool is initially exempt from protocol fees
     * @return aggregateSwapFeePercentage The initial aggregate swap fee percentage
     * @return aggregateYieldFeePercentage The initial aggregate yield fee percentage
     */
    function registerPool(
        address pool,
        address poolCreator,
        bool protocolFeeExempt
    ) external returns (uint256 aggregateSwapFeePercentage, uint256 aggregateYieldFeePercentage);

    /**
     * @notice Set the global protocol swap fee percentage, used by standard pools.
     * @param newProtocolSwapFeePercentage The new protocol swap fee percentage
     */
    function setGlobalProtocolSwapFeePercentage(uint256 newProtocolSwapFeePercentage) external;

    /**
     * @notice Set the global protocol yield fee percentage, used by standard pools.
     * @param newProtocolYieldFeePercentage The new protocol yield fee percentage
     */
    function setGlobalProtocolYieldFeePercentage(uint256 newProtocolYieldFeePercentage) external;

    /**
     * @notice Override the protocol swap fee percentage for a specific pool.
     * @param pool The address of the pool for which we are setting the protocol swap fee
     * @param newProtocolSwapFeePercentage The new protocol swap fee percentage for the pool
     */
    function setProtocolSwapFeePercentage(address pool, uint256 newProtocolSwapFeePercentage) external;

    /**
     * @notice Override the protocol yield fee percentage for a specific pool.
     * @param pool The address of the pool for which we are setting the protocol yield fee
     * @param newProtocolYieldFeePercentage The new protocol yield fee percentage for the pool
     */
    function setProtocolYieldFeePercentage(address pool, uint256 newProtocolYieldFeePercentage) external;

    /**
     * @notice Assigns a new pool creator swap fee percentage to the specified pool.
     * @dev Fees are divided between the protocol, pool creator, and LPs. The pool creator percentage is applied to
     * the "net" amount after protocol fees, and divides the remainder between the pool creator and LPs. If the
     * pool creator fee is near 100%, almost none of the fee amount remains in the pool for LPs.
     *
     * @param pool The address of the pool for which the pool creator fee will be changed
     * @param poolCreatorSwapFeePercentage The new pool creator swap fee percentage to apply to the pool
     */
    function setPoolCreatorSwapFeePercentage(address pool, uint256 poolCreatorSwapFeePercentage) external;

    /**
     * @notice Assigns a new pool creator yield fee percentage to the specified pool.
     * @dev Fees are divided between the protocol, pool creator, and LPs. The pool creator percentage is applied to
     * the "net" amount after protocol fees, and divides the remainder between the pool creator and LPs. If the
     * pool creator fee is near 100%, almost none of the fee amount remains in the pool for LPs.
     *
     * @param pool The address of the pool for which the pool creator fee will be changed
     * @param poolCreatorYieldFeePercentage The new pool creator yield fee percentage to apply to the pool
     */
    function setPoolCreatorYieldFeePercentage(address pool, uint256 poolCreatorYieldFeePercentage) external;

    /**
     * @notice Withdraw collected protocol fees for a given pool (all tokens). This is a permissioned function.
     * @dev Sends swap and yield protocol fees to the recipient.
     * @param pool The pool on which fees were collected
     * @param recipient Address to send the tokens
     */
    function withdrawProtocolFees(address pool, address recipient) external;

    /**
     * @notice Withdraw collected protocol fees for a given pool and a given token. This is a permissioned function.
     * @dev Sends swap and yield protocol fees to the recipient.
     * @param pool The pool on which fees were collected
     * @param recipient Address to send the tokens
     * @param token Token to withdraw
     */
    function withdrawProtocolFeesForToken(address pool, address recipient, IERC20 token) external;

    /**
     * @notice Withdraw collected pool creator fees for a given pool. This is a permissioned function.
     * @dev Sends swap and yield pool creator fees to the recipient.
     * @param pool The pool on which fees were collected
     * @param recipient Address to send the tokens
     */
    function withdrawPoolCreatorFees(address pool, address recipient) external;

    /**
     * @notice Withdraw collected pool creator fees for a given pool.
     * @dev Sends swap and yield pool creator fees to the registered poolCreator. Since this is a known and immutable
     * value, this function is permissionless.
     *
     * @param pool The pool on which fees were collected
     */
    function withdrawPoolCreatorFees(address pool) external;
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

/**
 * @notice Return the minimum/maximum swap fee percentages for a pool.
 * @dev The Vault does not enforce bounds on swap fee percentages; `IBasePool` implements this interface to ensure
 * that new pool developers think about and set these bounds according to their specific pool type.
 *
 * A minimum swap fee might be necessary to ensure mathematical soundness (e.g., Weighted Pools, which use the power
 * function in the invariant). A maximum swap fee is general protection for users. With no limits at the Vault level,
 * a pool could specify a near 100% swap fee, effectively disabling trading. Though there are some use cases, such as
 * LVR/MEV strategies, where a very high fee makes sense.
 *
 * Note that the Vault does ensure that dynamic and aggregate fees are less than 100% to prevent attempting to allocate
 * more fees than were collected by the operation. The true `MAX_FEE_PERCENTAGE` is defined in VaultTypes.sol, and is
 * the highest value below 100% that satisfies the precision requirements.
 */
interface ISwapFeePercentageBounds {
    /// @return minimumSwapFeePercentage The minimum swap fee percentage for a pool
    function getMinimumSwapFeePercentage() external view returns (uint256 minimumSwapFeePercentage);

    /// @return maximumSwapFeePercentage The maximum swap fee percentage for a pool
    function getMaximumSwapFeePercentage() external view returns (uint256 maximumSwapFeePercentage);
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

/**
 * @notice Return the minimum/maximum invariant ratios allowed during an unbalanced liquidity operation.
 * @dev The Vault does not enforce any "baseline" bounds on invariant ratios, since such bounds are highly specific
 * and dependent on the math of each pool type. Instead, the Vault reads invariant ratio bounds from the pools.
 * `IBasePool` implements this interface to ensure that new pool developers think about and set these bounds according
 * to their pool type's math.
 *
 * For instance, Balancer Weighted Pool math involves exponentiation (the `pow` function), which uses natural
 * logarithms and a discrete Taylor series expansion to compute x^y values for the 18-decimal floating point numbers
 * used in all Vault computations. See `LogExpMath` and `WeightedMath` for a derivation of the bounds for these pools.
 */
interface IUnbalancedLiquidityInvariantRatioBounds {
    /// @return minimumInvariantRatio The minimum invariant ratio for a pool during unbalanced remove liquidity
    function getMinimumInvariantRatio() external view returns (uint256 minimumInvariantRatio);

    /// @return maximumInvariantRatio The maximum invariant ratio for a pool during unbalanced add liquidity
    function getMaximumInvariantRatio() external view returns (uint256 maximumInvariantRatio);
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

import { IAuthentication } from "../solidity-utils/helpers/IAuthentication.sol";
import { IVaultExtension } from "./IVaultExtension.sol";
import { IVaultErrors } from "./IVaultErrors.sol";
import { IVaultEvents } from "./IVaultEvents.sol";
import { IVaultAdmin } from "./IVaultAdmin.sol";
import { IVaultMain } from "./IVaultMain.sol";

/// @notice Composite interface for all Vault operations: swap, add/remove liquidity, and associated queries.
interface IVault is IVaultMain, IVaultExtension, IVaultAdmin, IVaultErrors, IVaultEvents, IAuthentication {
    /// @return vault The main Vault address.
    function vault() external view override(IVaultAdmin, IVaultExtension) returns (IVault);
}

File 20 of 72 : IVaultAdmin.sol
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

import { IERC4626 } from "@openzeppelin/contracts/interfaces/IERC4626.sol";

import { IProtocolFeeController } from "./IProtocolFeeController.sol";
import { IAuthorizer } from "./IAuthorizer.sol";
import { IVault } from "./IVault.sol";

/**
 * @notice Interface for functions defined on the `VaultAdmin` contract.
 * @dev `VaultAdmin` is the Proxy extension of `VaultExtension`, and handles the least critical operations,
 * as two delegate calls add gas to each call. Most of the permissioned calls are here.
 */
interface IVaultAdmin {
    /*******************************************************************************
                               Constants and immutables
    *******************************************************************************/

    /**
     * @notice Returns the main Vault address.
     * @dev The main Vault contains the entrypoint and main liquidity operation implementations.
     * @return vault The address of the main Vault
     */
    function vault() external view returns (IVault);

    /**
     * @notice Returns the Vault's pause window end time.
     * @dev This value is immutable, and represents the timestamp after which the Vault can no longer be paused
     * by governance. Balancer timestamps are 32 bits.
     *
     * @return pauseWindowEndTime The timestamp when the Vault's pause window ends
     */
    function getPauseWindowEndTime() external view returns (uint32 pauseWindowEndTime);

    /**
     * @notice Returns the Vault's buffer period duration.
     * @dev This value is immutable. It represents the period during which, if paused, the Vault will remain paused.
     * This ensures there is time available to address whatever issue caused the Vault to be paused. Balancer
     * timestamps are 32 bits.
     *
     * @return bufferPeriodDuration The length of the buffer period in seconds
     */
    function getBufferPeriodDuration() external view returns (uint32 bufferPeriodDuration);

    /**
     * @notice Returns the Vault's buffer period end time.
     * @dev This value is immutable. If already paused, the Vault can be unpaused until this timestamp. Balancer
     * timestamps are 32 bits.
     *
     * @return bufferPeriodEndTime The timestamp after which the Vault remains permanently unpaused
     */
    function getBufferPeriodEndTime() external view returns (uint32 bufferPeriodEndTime);

    /**
     * @notice Get the minimum number of tokens in a pool.
     * @dev We expect the vast majority of pools to be 2-token.
     * @return minTokens The minimum token count of a pool
     */
    function getMinimumPoolTokens() external pure returns (uint256 minTokens);

    /**
     * @notice Get the maximum number of tokens in a pool.
     * @return maxTokens The maximum token count of a pool
     */
    function getMaximumPoolTokens() external pure returns (uint256 maxTokens);

    /**
     * @notice Get the minimum total supply of pool tokens (BPT) for an initialized pool.
     * @dev This prevents pools from being completely drained. When the pool is initialized, this minimum amount of BPT
     * is minted to the zero address. This is an 18-decimal floating point number; BPT are always 18 decimals.
     *
     * @return poolMinimumTotalSupply The minimum total supply a pool can have after initialization
     */
    function getPoolMinimumTotalSupply() external pure returns (uint256 poolMinimumTotalSupply);

    /**
     * @notice Get the minimum total supply of an ERC4626 wrapped token buffer in the Vault.
     * @dev This prevents buffers from being completely drained. When the buffer is initialized, this minimum number
     * of shares is added to the shares resulting from the initial deposit. Buffer total supply accounting is internal
     * to the Vault, as buffers are not tokenized.
     *
     * @return bufferMinimumTotalSupply The minimum total supply a buffer can have after initialization
     */
    function getBufferMinimumTotalSupply() external pure returns (uint256 bufferMinimumTotalSupply);

    /**
     * @notice Get the minimum trade amount in a pool operation.
     * @dev This limit is applied to the 18-decimal "upscaled" amount in any operation (swap, add/remove liquidity).
     * @return minimumTradeAmount The minimum trade amount as an 18-decimal floating point number
     */
    function getMinimumTradeAmount() external view returns (uint256 minimumTradeAmount);

    /**
     * @notice Get the minimum wrap amount in a buffer operation.
     * @dev This limit is applied to the wrap operation amount, in native underlying token decimals.
     * @return minimumWrapAmount The minimum wrap amount in native underlying token decimals
     */
    function getMinimumWrapAmount() external view returns (uint256 minimumWrapAmount);

    /*******************************************************************************
                                    Vault Pausing
    *******************************************************************************/

    /**
     * @notice Indicates whether the Vault is paused.
     * @dev If the Vault is paused, all non-Recovery Mode state-changing operations on pools will revert. Note that
     * ERC4626 buffers and the Vault have separate and independent pausing mechanisms. Pausing the Vault does not
     * also pause buffers (though we anticipate they would likely be paused and unpaused together). Call
     * `areBuffersPaused` to check the pause state of the buffers.
     *
     * @return vaultPaused True if the Vault is paused
     */
    function isVaultPaused() external view returns (bool vaultPaused);

    /**
     * @notice Returns the paused status, and end times of the Vault's pause window and buffer period.
     * @dev Balancer timestamps are 32 bits.
     * @return vaultPaused True if the Vault is paused
     * @return vaultPauseWindowEndTime The timestamp of the end of the Vault's pause window
     * @return vaultBufferPeriodEndTime The timestamp of the end of the Vault's buffer period
     */
    function getVaultPausedState()
        external
        view
        returns (bool vaultPaused, uint32 vaultPauseWindowEndTime, uint32 vaultBufferPeriodEndTime);

    /**
     * @notice Pause the Vault: an emergency action which disables all operational state-changing functions on pools.
     * @dev This is a permissioned function that will only work during the Pause Window set during deployment.
     * Note that ERC4626 buffer operations have an independent pause mechanism, which is not affected by pausing
     * the Vault. Custom routers could still wrap/unwrap using buffers while the Vault is paused, unless buffers
     * are also paused (with `pauseVaultBuffers`).
     */
    function pauseVault() external;

    /**
     * @notice Reverse a `pause` operation, and restore Vault pool operations to normal functionality.
     * @dev This is a permissioned function that will only work on a paused Vault within the Buffer Period set during
     * deployment. Note that the Vault will automatically unpause after the Buffer Period expires. As noted above,
     * ERC4626 buffers and Vault operations on pools are independent. Unpausing the Vault does not reverse
     * `pauseVaultBuffers`. If buffers were also paused, they will remain in that state until explicitly unpaused.
     */
    function unpauseVault() external;

    /*******************************************************************************
                                    Pool Pausing
    *******************************************************************************/

    /**
     * @notice Pause the Pool: an emergency action which disables all pool functions.
     * @dev This is a permissioned function that will only work during the Pause Window set during pool factory
     * deployment.
     *
     * @param pool The pool being paused
     */
    function pausePool(address pool) external;

    /**
     * @notice Reverse a `pause` operation, and restore the Pool to normal functionality.
     * @dev This is a permissioned function that will only work on a paused Pool within the Buffer Period set during
     * deployment. Note that the Pool will automatically unpause after the Buffer Period expires.
     *
     * @param pool The pool being unpaused
     */
    function unpausePool(address pool) external;

    /*******************************************************************************
                                         Fees
    *******************************************************************************/

    /**
     * @notice Assigns a new static swap fee percentage to the specified pool.
     * @dev This is a permissioned function, disabled if the pool is paused. The swap fee percentage must be within
     * the bounds specified by the pool's implementation of `ISwapFeePercentageBounds`.
     * Emits the SwapFeePercentageChanged event.
     *
     * @param pool The address of the pool for which the static swap fee will be changed
     * @param swapFeePercentage The new swap fee percentage to apply to the pool
     */
    function setStaticSwapFeePercentage(address pool, uint256 swapFeePercentage) external;

    /**
     * @notice Collects accumulated aggregate swap and yield fees for the specified pool.
     * @dev Fees are sent to the ProtocolFeeController address.
     * @param pool The pool on which all aggregate fees should be collected
     * @return swapFeeAmounts An array with the total swap fees collected, sorted in token registration order
     * @return yieldFeeAmounts An array with the total yield fees collected, sorted in token registration order
     */
    function collectAggregateFees(
        address pool
    ) external returns (uint256[] memory swapFeeAmounts, uint256[] memory yieldFeeAmounts);

    /**
     * @notice Update an aggregate swap fee percentage.
     * @dev Can only be called by the current protocol fee controller. Called when governance overrides a protocol fee
     * for a specific pool, or to permissionlessly update a pool to a changed global protocol fee value (if the pool's
     * fee has not previously been set by governance). Ensures the aggregate percentage <= FixedPoint.ONE, and also
     * that the final value does not lose precision when stored in 24 bits (see `FEE_BITLENGTH` in VaultTypes.sol).
     * Emits an `AggregateSwapFeePercentageChanged` event.
     *
     * @param pool The pool whose swap fee percentage will be updated
     * @param newAggregateSwapFeePercentage The new aggregate swap fee percentage
     */
    function updateAggregateSwapFeePercentage(address pool, uint256 newAggregateSwapFeePercentage) external;

    /**
     * @notice Update an aggregate yield fee percentage.
     * @dev Can only be called by the current protocol fee controller. Called when governance overrides a protocol fee
     * for a specific pool, or to permissionlessly update a pool to a changed global protocol fee value (if the pool's
     * fee has not previously been set by governance). Ensures the aggregate percentage <= FixedPoint.ONE, and also
     * that the final value does not lose precision when stored in 24 bits (see `FEE_BITLENGTH` in VaultTypes.sol).
     * Emits an `AggregateYieldFeePercentageChanged` event.
     *
     * @param pool The pool whose yield fee percentage will be updated
     * @param newAggregateYieldFeePercentage The new aggregate yield fee percentage
     */
    function updateAggregateYieldFeePercentage(address pool, uint256 newAggregateYieldFeePercentage) external;

    /**
     * @notice Sets a new Protocol Fee Controller for the Vault.
     * @dev This is a permissioned call. Emits a `ProtocolFeeControllerChanged` event.
     * @param newProtocolFeeController The address of the new Protocol Fee Controller
     */
    function setProtocolFeeController(IProtocolFeeController newProtocolFeeController) external;

    /*******************************************************************************
                                    Recovery Mode
    *******************************************************************************/

    /**
     * @notice Enable recovery mode for a pool.
     * @dev This is a permissioned function. It enables a safe proportional withdrawal, with no external calls.
     * Since there are no external calls, ensuring that entering Recovery Mode cannot fail, we cannot compute and so
     * must forfeit any yield fees between the last operation and enabling Recovery Mode. For the same reason, live
     * balances cannot be updated while in Recovery Mode, as doing so might cause withdrawals to fail.
     *
     * @param pool The address of the pool
     */
    function enableRecoveryMode(address pool) external;

    /**
     * @notice Disable recovery mode for a pool.
     * @dev This is a permissioned function. It re-syncs live balances (which could not be updated during
     * Recovery Mode), forfeiting any yield fees that accrued while enabled. It makes external calls, and could
     * potentially fail if there is an issue with any associated Rate Providers.
     *
     * @param pool The address of the pool
     */
    function disableRecoveryMode(address pool) external;

    /*******************************************************************************
                                  Query Functionality
    *******************************************************************************/

    /**
     * @notice Disables query functionality on the Vault. Can only be called by governance.
     * @dev The query functions rely on a specific EVM feature to detect static calls. Query operations are exempt from
     * settlement constraints, so it's critical that no state changes can occur. We retain the ability to disable
     * queries in the unlikely event that EVM changes violate its assumptions (perhaps on an L2).
     * This function can be acted upon as an emergency measure in ambiguous contexts where it's not 100% clear whether
     * disabling queries is completely necessary; queries can still be re-enabled after this call.
     */
    function disableQuery() external;

    /**
     * @notice Disables query functionality permanently on the Vault. Can only be called by governance.
     * @dev Shall only be used when there is no doubt that queries pose a fundamental threat to the system.
     */
    function disableQueryPermanently() external;

    /**
     * @notice Enables query functionality on the Vault. Can only be called by governance.
     * @dev Only works if queries are not permanently disabled.
     */
    function enableQuery() external;

    /*******************************************************************************
                                  ERC4626 Buffers
    *******************************************************************************/

    /**
     * @notice Indicates whether the Vault buffers are paused.
     * @dev When buffers are paused, all buffer operations (i.e., calls on the Router with `isBuffer` true)
     * will revert. Pausing buffers is reversible. Note that ERC4626 buffers and the Vault have separate and
     * independent pausing mechanisms. Pausing the Vault does not also pause buffers (though we anticipate they
     * would likely be paused and unpaused together). Call `isVaultPaused` to check the pause state of the Vault.
     *
     * @return buffersPaused True if the Vault buffers are paused
     */
    function areBuffersPaused() external view returns (bool buffersPaused);

    /**
     * @notice Pauses native vault buffers globally.
     * @dev When buffers are paused, it's not possible to add liquidity or wrap/unwrap tokens using the Vault's
     * `erc4626BufferWrapOrUnwrap` primitive. However, it's still possible to remove liquidity. Currently it's not
     * possible to pause vault buffers individually.
     *
     * This is a permissioned call, and is reversible (see `unpauseVaultBuffers`). Note that the Vault has a separate
     * and independent pausing mechanism. It is possible to pause the Vault (i.e. pool operations), without affecting
     * buffers, and vice versa.
     */
    function pauseVaultBuffers() external;

    /**
     * @notice Unpauses native vault buffers globally.
     * @dev When buffers are paused, it's not possible to add liquidity or wrap/unwrap tokens using the Vault's
     * `erc4626BufferWrapOrUnwrap` primitive. However, it's still possible to remove liquidity. As noted above,
     * ERC4626 buffers and Vault operations on pools are independent. Unpausing buffers does not reverse `pauseVault`.
     * If the Vault was also paused, it will remain in that state until explicitly unpaused.
     *
     * This is a permissioned call.
     */
    function unpauseVaultBuffers() external;

    /**
     * @notice Initializes buffer for the given wrapped token.
     * @param wrappedToken Address of the wrapped token that implements IERC4626
     * @param amountUnderlyingRaw Amount of underlying tokens that will be deposited into the buffer
     * @param amountWrappedRaw Amount of wrapped tokens that will be deposited into the buffer
     * @param minIssuedShares Minimum amount of shares to receive from the buffer, expressed in underlying token
     * native decimals
     * @param sharesOwner Address that will own the deposited liquidity. Only this address will be able to remove
     * liquidity from the buffer
     * @return issuedShares the amount of tokens sharesOwner has in the buffer, expressed in underlying token amounts.
     * (it is the BPT of an internal ERC4626 buffer). It is expressed in underlying token native decimals.
     */
    function initializeBuffer(
        IERC4626 wrappedToken,
        uint256 amountUnderlyingRaw,
        uint256 amountWrappedRaw,
        uint256 minIssuedShares,
        address sharesOwner
    ) external returns (uint256 issuedShares);

    /**
     * @notice Adds liquidity to an internal ERC4626 buffer in the Vault, proportionally.
     * @dev The buffer needs to be initialized beforehand.
     * @param wrappedToken Address of the wrapped token that implements IERC4626
     * @param maxAmountUnderlyingInRaw Maximum amount of underlying tokens to add to the buffer. It is expressed in
     * underlying token native decimals
     * @param maxAmountWrappedInRaw Maximum amount of wrapped tokens to add to the buffer. It is expressed in wrapped
     * token native decimals
     * @param exactSharesToIssue The value in underlying tokens that `sharesOwner` wants to add to the buffer,
     * in underlying token decimals
     * @param sharesOwner Address that will own the deposited liquidity. Only this address will be able to remove
     * liquidity from the buffer
     * @return amountUnderlyingRaw Amount of underlying tokens deposited into the buffer
     * @return amountWrappedRaw Amount of wrapped tokens deposited into the buffer
     */
    function addLiquidityToBuffer(
        IERC4626 wrappedToken,
        uint256 maxAmountUnderlyingInRaw,
        uint256 maxAmountWrappedInRaw,
        uint256 exactSharesToIssue,
        address sharesOwner
    ) external returns (uint256 amountUnderlyingRaw, uint256 amountWrappedRaw);

    /**
     * @notice Removes liquidity from an internal ERC4626 buffer in the Vault.
     * @dev Only proportional exits are supported, and the sender has to be the owner of the shares.
     * This function unlocks the Vault just for this operation; it does not work with a Router as an entrypoint.
     *
     * Pre-conditions:
     * - The buffer needs to be initialized.
     * - sharesOwner is the original msg.sender, it needs to be checked in the Router. That's why
     *   this call is authenticated; only routers approved by the DAO can remove the liquidity of a buffer.
     * - The buffer needs to have some liquidity and have its asset registered in `_bufferAssets` storage.
     *
     * @param wrappedToken Address of the wrapped token that implements IERC4626
     * @param sharesToRemove Amount of shares to remove from the buffer. Cannot be greater than sharesOwner's
     * total shares. It is expressed in underlying token native decimals
     * @param minAmountUnderlyingOutRaw Minimum amount of underlying tokens to receive from the buffer. It is expressed
     * in underlying token native decimals
     * @param minAmountWrappedOutRaw Minimum amount of wrapped tokens to receive from the buffer. It is expressed in
     * wrapped token native decimals
     * @return removedUnderlyingBalanceRaw Amount of underlying tokens returned to the user
     * @return removedWrappedBalanceRaw Amount of wrapped tokens returned to the user
     */
    function removeLiquidityFromBuffer(
        IERC4626 wrappedToken,
        uint256 sharesToRemove,
        uint256 minAmountUnderlyingOutRaw,
        uint256 minAmountWrappedOutRaw
    ) external returns (uint256 removedUnderlyingBalanceRaw, uint256 removedWrappedBalanceRaw);

    /**
     * @notice Returns the asset registered for a given wrapped token.
     * @dev The asset can never change after buffer initialization.
     * @param wrappedToken Address of the wrapped token that implements IERC4626
     * @return underlyingToken Address of the underlying token registered for the wrapper; `address(0)` if the buffer
     * has not been initialized.
     */
    function getBufferAsset(IERC4626 wrappedToken) external view returns (address underlyingToken);

    /**
     * @notice Returns the shares (internal buffer BPT) of a liquidity owner: a user that deposited assets
     * in the buffer.
     *
     * @param wrappedToken Address of the wrapped token that implements IERC4626
     * @param liquidityOwner Address of the user that owns liquidity in the wrapped token's buffer
     * @return ownerShares Amount of shares allocated to the liquidity owner, in native underlying token decimals
     */
    function getBufferOwnerShares(
        IERC4626 wrappedToken,
        address liquidityOwner
    ) external view returns (uint256 ownerShares);

    /**
     * @notice Returns the supply shares (internal buffer BPT) of the ERC4626 buffer.
     * @param wrappedToken Address of the wrapped token that implements IERC4626
     * @return bufferShares Amount of supply shares of the buffer, in native underlying token decimals
     */
    function getBufferTotalShares(IERC4626 wrappedToken) external view returns (uint256 bufferShares);

    /**
     * @notice Returns the amount of underlying and wrapped tokens deposited in the internal buffer of the Vault.
     * @dev All values are in native token decimals of the wrapped or underlying tokens.
     * @param wrappedToken Address of the wrapped token that implements IERC4626
     * @return underlyingBalanceRaw Amount of underlying tokens deposited into the buffer, in native token decimals
     * @return wrappedBalanceRaw Amount of wrapped tokens deposited into the buffer, in native token decimals
     */
    function getBufferBalance(
        IERC4626 wrappedToken
    ) external view returns (uint256 underlyingBalanceRaw, uint256 wrappedBalanceRaw);

    /*******************************************************************************
                                Authentication
    *******************************************************************************/

    /**
     * @notice Sets a new Authorizer for the Vault.
     * @dev This is a permissioned call. Emits an `AuthorizerChanged` event.
     * @param newAuthorizer The address of the new authorizer
     */
    function setAuthorizer(IAuthorizer newAuthorizer) external;
}

File 21 of 72 : IVaultErrors.sol
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

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

/// @notice Errors are declared inside an interface (namespace) to improve DX with Typechain.
interface IVaultErrors {
    /*******************************************************************************
                            Registration and Initialization
    *******************************************************************************/

    /**
     * @notice A pool has already been registered. `registerPool` may only be called once.
     * @param pool The already registered pool
     */
    error PoolAlreadyRegistered(address pool);

    /**
     * @notice A pool has already been initialized. `initialize` may only be called once.
     * @param pool The already initialized pool
     */
    error PoolAlreadyInitialized(address pool);

    /**
     * @notice A pool has not been registered.
     * @param pool The unregistered pool
     */
    error PoolNotRegistered(address pool);

    /**
     * @notice A referenced pool has not been initialized.
     * @param pool The uninitialized pool
     */
    error PoolNotInitialized(address pool);

    /**
     * @notice A hook contract rejected a pool on registration.
     * @param poolHooksContract Address of the hook contract that rejected the pool registration
     * @param pool Address of the rejected pool
     * @param poolFactory Address of the pool factory
     */
    error HookRegistrationFailed(address poolHooksContract, address pool, address poolFactory);

    /**
     * @notice A token was already registered (i.e., it is a duplicate in the pool).
     * @param token The duplicate token
     */
    error TokenAlreadyRegistered(IERC20 token);

    /// @notice The token count is below the minimum allowed.
    error MinTokens();

    /// @notice The token count is above the maximum allowed.
    error MaxTokens();

    /// @notice Invalid tokens (e.g., zero) cannot be registered.
    error InvalidToken();

    /// @notice The token type given in a TokenConfig during pool registration is invalid.
    error InvalidTokenType();

    /// @notice The data in a TokenConfig struct is inconsistent or unsupported.
    error InvalidTokenConfiguration();

    /// @notice Tokens with more than 18 decimals are not supported.
    error InvalidTokenDecimals();

    /**
     * @notice The token list passed into an operation does not match the pool tokens in the pool.
     * @param pool Address of the pool
     * @param expectedToken The correct token at a given index in the pool
     * @param actualToken The actual token found at that index
     */
    error TokensMismatch(address pool, address expectedToken, address actualToken);

    /*******************************************************************************
                                 Transient Accounting
    *******************************************************************************/

    /// @notice A transient accounting operation completed with outstanding token deltas.
    error BalanceNotSettled();

    /// @notice A user called a Vault function (swap, add/remove liquidity) outside the lock context.
    error VaultIsNotUnlocked();

    /// @notice The pool has returned false to the beforeSwap hook, indicating the transaction should revert.
    error DynamicSwapFeeHookFailed();

    /// @notice The pool has returned false to the beforeSwap hook, indicating the transaction should revert.
    error BeforeSwapHookFailed();

    /// @notice The pool has returned false to the afterSwap hook, indicating the transaction should revert.
    error AfterSwapHookFailed();

    /// @notice The pool has returned false to the beforeInitialize hook, indicating the transaction should revert.
    error BeforeInitializeHookFailed();

    /// @notice The pool has returned false to the afterInitialize hook, indicating the transaction should revert.
    error AfterInitializeHookFailed();

    /// @notice The pool has returned false to the beforeAddLiquidity hook, indicating the transaction should revert.
    error BeforeAddLiquidityHookFailed();

    /// @notice The pool has returned false to the afterAddLiquidity hook, indicating the transaction should revert.
    error AfterAddLiquidityHookFailed();

    /// @notice The pool has returned false to the beforeRemoveLiquidity hook, indicating the transaction should revert.
    error BeforeRemoveLiquidityHookFailed();

    /// @notice The pool has returned false to the afterRemoveLiquidity hook, indicating the transaction should revert.
    error AfterRemoveLiquidityHookFailed();

    /// @notice An unauthorized Router tried to call a permissioned function (i.e., using the Vault's token allowance).
    error RouterNotTrusted();

    /*******************************************************************************
                                        Swaps
    *******************************************************************************/

    /// @notice The user tried to swap zero tokens.
    error AmountGivenZero();

    /// @notice The user attempted to swap a token for itself.
    error CannotSwapSameToken();

    /**
     * @notice The user attempted to operate with a token that is not in the pool.
     * @param token The unregistered token
     */
    error TokenNotRegistered(IERC20 token);

    /**
     * @notice An amount in or out has exceeded the limit specified in the swap request.
     * @param amount The total amount in or out
     * @param limit The amount of the limit that has been exceeded
     */
    error SwapLimit(uint256 amount, uint256 limit);

    /**
     * @notice A hook adjusted amount in or out has exceeded the limit specified in the swap request.
     * @param amount The total amount in or out
     * @param limit The amount of the limit that has been exceeded
     */
    error HookAdjustedSwapLimit(uint256 amount, uint256 limit);

    /// @notice The amount given or calculated for an operation is below the minimum limit.
    error TradeAmountTooSmall();

    /*******************************************************************************
                                    Add Liquidity
    *******************************************************************************/

    /// @notice Add liquidity kind not supported.
    error InvalidAddLiquidityKind();

    /**
     * @notice A required amountIn exceeds the maximum limit specified for the operation.
     * @param tokenIn The incoming token
     * @param amountIn The total token amount in
     * @param maxAmountIn The amount of the limit that has been exceeded
     */
    error AmountInAboveMax(IERC20 tokenIn, uint256 amountIn, uint256 maxAmountIn);

    /**
     * @notice A hook adjusted amountIn exceeds the maximum limit specified for the operation.
     * @param tokenIn The incoming token
     * @param amountIn The total token amount in
     * @param maxAmountIn The amount of the limit that has been exceeded
     */
    error HookAdjustedAmountInAboveMax(IERC20 tokenIn, uint256 amountIn, uint256 maxAmountIn);

    /**
     * @notice The BPT amount received from adding liquidity is below the minimum specified for the operation.
     * @param amountOut The total BPT amount out
     * @param minAmountOut The amount of the limit that has been exceeded
     */
    error BptAmountOutBelowMin(uint256 amountOut, uint256 minAmountOut);

    /// @notice Pool does not support adding liquidity with a customized input.
    error DoesNotSupportAddLiquidityCustom();

    /// @notice Pool does not support adding liquidity through donation.
    error DoesNotSupportDonation();

    /*******************************************************************************
                                    Remove Liquidity
    *******************************************************************************/

    /// @notice Remove liquidity kind not supported.
    error InvalidRemoveLiquidityKind();

    /**
     * @notice The actual amount out is below the minimum limit specified for the operation.
     * @param tokenOut The outgoing token
     * @param amountOut The total BPT amount out
     * @param minAmountOut The amount of the limit that has been exceeded
     */
    error AmountOutBelowMin(IERC20 tokenOut, uint256 amountOut, uint256 minAmountOut);

    /**
     * @notice The hook adjusted amount out is below the minimum limit specified for the operation.
     * @param tokenOut The outgoing token
     * @param amountOut The total BPT amount out
     * @param minAmountOut The amount of the limit that has been exceeded
     */
    error HookAdjustedAmountOutBelowMin(IERC20 tokenOut, uint256 amountOut, uint256 minAmountOut);

    /**
     * @notice The required BPT amount in exceeds the maximum limit specified for the operation.
     * @param amountIn The total BPT amount in
     * @param maxAmountIn The amount of the limit that has been exceeded
     */
    error BptAmountInAboveMax(uint256 amountIn, uint256 maxAmountIn);

    /// @notice Pool does not support removing liquidity with a customized input.
    error DoesNotSupportRemoveLiquidityCustom();

    /*******************************************************************************
                                     Fees
    *******************************************************************************/

    /**
     * @notice Error raised when there is an overflow in the fee calculation.
     * @dev This occurs when the sum of the parts (aggregate swap or yield fee) is greater than the whole
     * (total swap or yield fee). Also validated when the protocol fee controller updates aggregate fee
     * percentages in the Vault.
     */
    error ProtocolFeesExceedTotalCollected();

    /**
     * @notice Error raised when the swap fee percentage is less than the minimum allowed value.
     * @dev The Vault itself does not impose a universal minimum. Rather, it validates against the
     * range specified by the `ISwapFeePercentageBounds` interface. and reverts with this error
     * if it is below the minimum value returned by the pool.
     *
     * Pools with dynamic fees do not check these limits.
     */
    error SwapFeePercentageTooLow();

    /**
     * @notice Error raised when the swap fee percentage is greater than the maximum allowed value.
     * @dev The Vault itself does not impose a universal minimum. Rather, it validates against the
     * range specified by the `ISwapFeePercentageBounds` interface. and reverts with this error
     * if it is above the maximum value returned by the pool.
     *
     * Pools with dynamic fees do not check these limits.
     */
    error SwapFeePercentageTooHigh();

    /**
     * @notice Primary fee percentages result in an aggregate fee that cannot be stored with the required precision.
     * @dev Primary fee percentages are 18-decimal values, stored here in 64 bits, and calculated with full 256-bit
     * precision. However, the resulting aggregate fees are stored in the Vault with 24-bit precision, which
     * corresponds to 0.00001% resolution (i.e., a fee can be 1%, 1.00001%, 1.00002%, but not 1.000005%).
     * Disallow setting fees such that there would be precision loss in the Vault, leading to a discrepancy between
     * the aggregate fee calculated here and that stored in the Vault.
     */
    error FeePrecisionTooHigh();

    /// @notice A given percentage is above the maximum (usually a value close to FixedPoint.ONE, or 1e18 wei).
    error PercentageAboveMax();

    /*******************************************************************************
                                    Queries
    *******************************************************************************/

    /// @notice A user tried to execute a query operation when they were disabled.
    error QueriesDisabled();

    /// @notice An admin tried to re-enable queries, but they were disabled permanently.
    error QueriesDisabledPermanently();

    /*******************************************************************************
                                Recovery Mode
    *******************************************************************************/

    /**
     * @notice Cannot enable recovery mode when already enabled.
     * @param pool The pool
     */
    error PoolInRecoveryMode(address pool);

    /**
     * @notice Cannot disable recovery mode when not enabled.
     * @param pool The pool
     */
    error PoolNotInRecoveryMode(address pool);

    /*******************************************************************************
                                Authentication
    *******************************************************************************/

    /**
     * @notice Error indicating the sender is not the Vault (e.g., someone is trying to call a permissioned function).
     * @param sender The account attempting to call a permissioned function
     */
    error SenderIsNotVault(address sender);

    /*******************************************************************************
                                        Pausing
    *******************************************************************************/

    /// @notice The caller specified a pause window period longer than the maximum.
    error VaultPauseWindowDurationTooLarge();

    /// @notice The caller specified a buffer period longer than the maximum.
    error PauseBufferPeriodDurationTooLarge();

    /// @notice A user tried to perform an operation while the Vault was paused.
    error VaultPaused();

    /// @notice Governance tried to unpause the Vault when it was not paused.
    error VaultNotPaused();

    /// @notice Governance tried to pause the Vault after the pause period expired.
    error VaultPauseWindowExpired();

    /**
     * @notice A user tried to perform an operation involving a paused Pool.
     * @param pool The paused pool
     */
    error PoolPaused(address pool);

    /**
     * @notice Governance tried to unpause the Pool when it was not paused.
     * @param pool The unpaused pool
     */
    error PoolNotPaused(address pool);

    /**
     * @notice Governance tried to pause a Pool after the pause period expired.
     * @param pool The pool
     */
    error PoolPauseWindowExpired(address pool);

    /*******************************************************************************
                                ERC4626 token buffers
    *******************************************************************************/

    /**
     * @notice The buffer for the given wrapped token was already initialized.
     * @param wrappedToken The wrapped token corresponding to the buffer
     */
    error BufferAlreadyInitialized(IERC4626 wrappedToken);

    /**
     * @notice The buffer for the given wrapped token was not initialized.
     * @param wrappedToken The wrapped token corresponding to the buffer
     */
    error BufferNotInitialized(IERC4626 wrappedToken);

    /// @notice The user is trying to remove more than their allocated shares from the buffer.
    error NotEnoughBufferShares();

    /**
     * @notice The wrapped token asset does not match the underlying token.
     * @dev This should never happen, but a malicious wrapper contract might not return the correct address.
     * Legitimate wrapper contracts should make the asset a constant or immutable value.
     *
     * @param wrappedToken The wrapped token corresponding to the buffer
     * @param underlyingToken The underlying token returned by `asset`
     */
    error WrongUnderlyingToken(IERC4626 wrappedToken, address underlyingToken);

    /**
     * @notice A wrapped token reported the zero address as its underlying token asset.
     * @dev This should never happen, but a malicious wrapper contract might do this (e.g., in an attempt to
     * re-initialize the buffer).
     *
     * @param wrappedToken The wrapped token corresponding to the buffer
     */
    error InvalidUnderlyingToken(IERC4626 wrappedToken);

    /**
     * @notice The amount given to wrap/unwrap was too small, which can introduce rounding issues.
     * @param wrappedToken The wrapped token corresponding to the buffer
     */
    error WrapAmountTooSmall(IERC4626 wrappedToken);

    /// @notice Buffer operation attempted while vault buffers are paused.
    error VaultBuffersArePaused();

    /// @notice Buffer shares were minted to the zero address.
    error BufferSharesInvalidReceiver();

    /// @notice Buffer shares were burned from the zero address.
    error BufferSharesInvalidOwner();

    /**
     * @notice The total supply of a buffer can't be lower than the absolute minimum.
     * @param totalSupply The total supply value that was below the minimum
     */
    error BufferTotalSupplyTooLow(uint256 totalSupply);

    /// @dev A wrap/unwrap operation consumed more or returned less underlying tokens than it should.
    error NotEnoughUnderlying(IERC4626 wrappedToken, uint256 expectedUnderlyingAmount, uint256 actualUnderlyingAmount);

    /// @dev A wrap/unwrap operation consumed more or returned less wrapped tokens than it should.
    error NotEnoughWrapped(IERC4626 wrappedToken, uint256 expectedWrappedAmount, uint256 actualWrappedAmount);

    /// @dev Shares issued during initialization are below the requested amount.
    error IssuedSharesBelowMin(uint256 issuedShares, uint256 minIssuedShares);

    /*******************************************************************************
                                    Miscellaneous
    *******************************************************************************/

    /// @notice Pool does not support adding / removing liquidity with an unbalanced input.
    error DoesNotSupportUnbalancedLiquidity();

    /// @notice The contract should not receive ETH.
    error CannotReceiveEth();

    /**
     * @notice The `VaultExtension` contract was called by an account directly.
     * @dev It can only be called by the Vault via delegatecall.
     */
    error NotVaultDelegateCall();

    /// @notice The `VaultExtension` contract was configured with an incorrect Vault address.
    error WrongVaultExtensionDeployment();

    /// @notice The `ProtocolFeeController` contract was configured with an incorrect Vault address.
    error WrongProtocolFeeControllerDeployment();

    /// @notice The `VaultAdmin` contract was configured with an incorrect Vault address.
    error WrongVaultAdminDeployment();

    /// @notice Quote reverted with a reserved error code.
    error QuoteResultSpoofed();
}

File 22 of 72 : IVaultEvents.sol
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

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

import { IProtocolFeeController } from "./IProtocolFeeController.sol";
import { IAuthorizer } from "./IAuthorizer.sol";
import { IHooks } from "./IHooks.sol";
import "./VaultTypes.sol";

/// @dev Events are declared inside an interface (namespace) to improve DX with Typechain.
interface IVaultEvents {
    /**
     * @notice A Pool was registered by calling `registerPool`.
     * @param pool The pool being registered
     * @param factory The factory creating the pool
     * @param tokenConfig An array of descriptors for the tokens the pool will manage
     * @param swapFeePercentage The static swap fee of the pool
     * @param pauseWindowEndTime The pool's pause window end time
     * @param roleAccounts Addresses the Vault will allow to change certain pool settings
     * @param hooksConfig Flags indicating which hooks the pool supports and address of hooks contract
     * @param liquidityManagement Supported liquidity management hook flags
     */
    event PoolRegistered(
        address indexed pool,
        address indexed factory,
        TokenConfig[] tokenConfig,
        uint256 swapFeePercentage,
        uint32 pauseWindowEndTime,
        PoolRoleAccounts roleAccounts,
        HooksConfig hooksConfig,
        LiquidityManagement liquidityManagement
    );

    /**
     * @notice A Pool was initialized by calling `initialize`.
     * @param pool The pool being initialized
     */
    event PoolInitialized(address indexed pool);

    /**
     * @notice A swap has occurred.
     * @param pool The pool with the tokens being swapped
     * @param tokenIn The token entering the Vault (balance increases)
     * @param tokenOut The token leaving the Vault (balance decreases)
     * @param amountIn Number of tokenIn tokens
     * @param amountOut Number of tokenOut tokens
     * @param swapFeePercentage Swap fee percentage applied (can differ if dynamic)
     * @param swapFeeAmount Swap fee amount paid
     */
    event Swap(
        address indexed pool,
        IERC20 indexed tokenIn,
        IERC20 indexed tokenOut,
        uint256 amountIn,
        uint256 amountOut,
        uint256 swapFeePercentage,
        uint256 swapFeeAmount
    );

    /**
     * @notice A wrap operation has occurred.
     * @param wrappedToken The wrapped token address
     * @param depositedUnderlying Number of underlying tokens deposited
     * @param mintedShares Number of shares (wrapped tokens) minted
     * @param bufferBalances The final buffer balances, packed in 128-bit words (underlying, wrapped)
     */
    event Wrap(
        IERC4626 indexed wrappedToken,
        uint256 depositedUnderlying,
        uint256 mintedShares,
        bytes32 bufferBalances
    );

    /**
     * @notice An unwrap operation has occurred.
     * @param wrappedToken The wrapped token address
     * @param burnedShares Number of shares (wrapped tokens) burned
     * @param withdrawnUnderlying Number of underlying tokens withdrawn
     * @param bufferBalances The final buffer balances, packed in 128-bit words (underlying, wrapped)
     */
    event Unwrap(
        IERC4626 indexed wrappedToken,
        uint256 burnedShares,
        uint256 withdrawnUnderlying,
        bytes32 bufferBalances
    );

    /**
     * @notice Liquidity has been added to a pool (including initialization).
     * @param pool The pool with liquidity added
     * @param liquidityProvider The user performing the operation
     * @param kind The add liquidity operation type (e.g., proportional, custom)
     * @param totalSupply The total supply of the pool after the operation
     * @param amountsAddedRaw The amount of each token that was added, sorted in token registration order
     * @param swapFeeAmountsRaw The total swap fees charged, sorted in token registration order
     */
    event LiquidityAdded(
        address indexed pool,
        address indexed liquidityProvider,
        AddLiquidityKind indexed kind,
        uint256 totalSupply,
        uint256[] amountsAddedRaw,
        uint256[] swapFeeAmountsRaw
    );

    /**
     * @notice Liquidity has been removed from a pool.
     * @param pool The pool with liquidity removed
     * @param liquidityProvider The user performing the operation
     * @param kind The remove liquidity operation type (e.g., proportional, custom)
     * @param totalSupply The total supply of the pool after the operation
     * @param amountsRemovedRaw The amount of each token that was removed, sorted in token registration order
     * @param swapFeeAmountsRaw The total swap fees charged, sorted in token registration order
     */
    event LiquidityRemoved(
        address indexed pool,
        address indexed liquidityProvider,
        RemoveLiquidityKind indexed kind,
        uint256 totalSupply,
        uint256[] amountsRemovedRaw,
        uint256[] swapFeeAmountsRaw
    );

    /**
     * @notice The Vault's pause status has changed.
     * @param paused True if the Vault was paused
     */
    event VaultPausedStateChanged(bool paused);

    /// @notice `disableQuery` has been called on the Vault, disabling query functionality.
    event VaultQueriesDisabled();

    /// @notice `enableQuery` has been called on the Vault, enabling query functionality.
    event VaultQueriesEnabled();

    /**
     * @notice A Pool's pause status has changed.
     * @param pool The pool that was just paused or unpaused
     * @param paused True if the pool was paused
     */
    event PoolPausedStateChanged(address indexed pool, bool paused);

    /**
     * @notice Emitted when the swap fee percentage of a pool is updated.
     * @param swapFeePercentage The new swap fee percentage for the pool
     */
    event SwapFeePercentageChanged(address indexed pool, uint256 swapFeePercentage);

    /**
     * @notice Recovery mode has been enabled or disabled for a pool.
     * @param pool The pool
     * @param recoveryMode True if recovery mode was enabled
     */
    event PoolRecoveryModeStateChanged(address indexed pool, bool recoveryMode);

    /**
     * @notice A protocol or pool creator fee has changed, causing an update to the aggregate swap fee.
     * @dev The `ProtocolFeeController` will emit an event with the underlying change.
     * @param pool The pool whose aggregate swap fee percentage changed
     * @param aggregateSwapFeePercentage The new aggregate swap fee percentage
     */
    event AggregateSwapFeePercentageChanged(address indexed pool, uint256 aggregateSwapFeePercentage);

    /**
     * @notice A protocol or pool creator fee has changed, causing an update to the aggregate yield fee.
     * @dev The `ProtocolFeeController` will emit an event with the underlying change.
     * @param pool The pool whose aggregate yield fee percentage changed
     * @param aggregateYieldFeePercentage The new aggregate yield fee percentage
     */
    event AggregateYieldFeePercentageChanged(address indexed pool, uint256 aggregateYieldFeePercentage);

    /**
     * @notice A new authorizer is set by `setAuthorizer`.
     * @param newAuthorizer The address of the new authorizer
     */
    event AuthorizerChanged(IAuthorizer indexed newAuthorizer);

    /**
     * @notice A new protocol fee controller is set by `setProtocolFeeController`.
     * @param newProtocolFeeController The address of the new protocol fee controller
     */
    event ProtocolFeeControllerChanged(IProtocolFeeController indexed newProtocolFeeController);

    /**
     * @notice Liquidity was added to an ERC4626 buffer corresponding to the given wrapped token.
     * @dev The underlying token can be derived from the wrapped token, so it's not included here.
     *
     * @param wrappedToken The wrapped token that identifies the buffer
     * @param amountUnderlying The amount of the underlying token that was deposited
     * @param amountWrapped The amount of the wrapped token that was deposited
     * @param bufferBalances The final buffer balances, packed in 128-bit words (underlying, wrapped)
     */
    event LiquidityAddedToBuffer(
        IERC4626 indexed wrappedToken,
        uint256 amountUnderlying,
        uint256 amountWrapped,
        bytes32 bufferBalances
    );

    /**
     * @notice Buffer shares were minted for an ERC4626 buffer corresponding to a given wrapped token.
     * @dev The shares are not tokenized like pool BPT, but accounted for in the Vault. `getBufferOwnerShares`
     * retrieves the current total shares for a given buffer and address, and `getBufferTotalShares` returns the
     * "totalSupply" of a buffer.
     *
     * @param wrappedToken The wrapped token that identifies the buffer
     * @param to The owner of the minted shares
     * @param issuedShares The amount of "internal BPT" shares created
     */
    event BufferSharesMinted(IERC4626 indexed wrappedToken, address indexed to, uint256 issuedShares);

    /**
     * @notice Buffer shares were burned for an ERC4626 buffer corresponding to a given wrapped token.
     * @dev The shares are not tokenized like pool BPT, but accounted for in the Vault. `getBufferOwnerShares`
     * retrieves the current total shares for a given buffer and address, and `getBufferTotalShares` returns the
     * "totalSupply" of a buffer.
     *
     * @param wrappedToken The wrapped token that identifies the buffer
     * @param from The owner of the burned shares
     * @param burnedShares The amount of "internal BPT" shares burned
     */
    event BufferSharesBurned(IERC4626 indexed wrappedToken, address indexed from, uint256 burnedShares);

    /**
     * @notice Liquidity was removed from an ERC4626 buffer.
     * @dev The underlying token can be derived from the wrapped token, so it's not included here.
     * @param wrappedToken The wrapped token that identifies the buffer
     * @param amountUnderlying The amount of the underlying token that was withdrawn
     * @param amountWrapped The amount of the wrapped token that was withdrawn
     * @param bufferBalances The final buffer balances, packed in 128-bit words (underlying, wrapped)
     */
    event LiquidityRemovedFromBuffer(
        IERC4626 indexed wrappedToken,
        uint256 amountUnderlying,
        uint256 amountWrapped,
        bytes32 bufferBalances
    );

    /**
     * @notice The Vault buffers pause status has changed.
     * @dev If buffers all paused, all buffer operations (i.e., all calls through the Router with `isBuffer`
     * set to true) will revert.
     *
     * @param paused True if the Vault buffers were paused
     */
    event VaultBuffersPausedStateChanged(bool paused);

    /**
     * @notice Pools can use this event to emit event data from the Vault.
     * @param pool Pool address
     * @param eventKey Event key
     * @param eventData Encoded event data
     */
    event VaultAuxiliary(address indexed pool, bytes32 indexed eventKey, bytes eventData);
}

File 23 of 72 : IVaultExtension.sol
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

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

import { IAuthorizer } from "./IAuthorizer.sol";
import { IProtocolFeeController } from "./IProtocolFeeController.sol";
import { IVault } from "./IVault.sol";
import { IHooks } from "./IHooks.sol";
import "./VaultTypes.sol";

/**
 * @notice Interface for functions defined on the `VaultExtension` contract.
 * @dev `VaultExtension` handles less critical or frequently used functions, since delegate calls through
 * the Vault are more expensive than direct calls. The main Vault contains the core code for swaps and
 * liquidity operations.
 */
interface IVaultExtension {
    /*******************************************************************************
                              Constants and immutables
    *******************************************************************************/

    /**
     * @notice Returns the main Vault address.
     * @dev The main Vault contains the entrypoint and main liquidity operation implementations.
     * @return vault The address of the main Vault
     */
    function vault() external view returns (IVault);

    /**
     * @notice Returns the VaultAdmin contract address.
     * @dev The VaultAdmin contract mostly implements permissioned functions.
     * @return vaultAdmin The address of the Vault admin
     */
    function getVaultAdmin() external view returns (address vaultAdmin);

    /*******************************************************************************
                              Transient Accounting
    *******************************************************************************/

    /**
     * @notice Returns whether the Vault is unlocked (i.e., executing an operation).
     * @dev The Vault must be unlocked to perform state-changing liquidity operations.
     * @return unlocked True if the Vault is unlocked, false otherwise
     */
    function isUnlocked() external view returns (bool unlocked);

    /**
     *  @notice Returns the count of non-zero deltas.
     *  @return nonzeroDeltaCount The current value of `_nonzeroDeltaCount`
     */
    function getNonzeroDeltaCount() external view returns (uint256 nonzeroDeltaCount);

    /**
     * @notice Retrieves the token delta for a specific token.
     * @dev This function allows reading the value from the `_tokenDeltas` mapping.
     * @param token The token for which the delta is being fetched
     * @return tokenDelta The delta of the specified token
     */
    function getTokenDelta(IERC20 token) external view returns (int256 tokenDelta);

    /**
     * @notice Retrieves the reserve (i.e., total Vault balance) of a given token.
     * @param token The token for which to retrieve the reserve
     * @return reserveAmount The amount of reserves for the given token
     */
    function getReservesOf(IERC20 token) external view returns (uint256 reserveAmount);

    /**
     * @notice This flag is used to detect and tax "round-trip" interactions (adding and removing liquidity in the
     * same pool).
     * @dev Taxing remove liquidity proportional whenever liquidity was added in the same `unlock` call adds an extra
     * layer of security, discouraging operations that try to undo others for profit. Remove liquidity proportional
     * is the only standard way to exit a position without fees, and this flag is used to enable fees in that case.
     * It also discourages indirect swaps via unbalanced add and remove proportional, as they are expected to be worse
     * than a simple swap for every pool type.
     *
     * @param pool Address of the pool to check
     * @return liquidityAdded True if liquidity has been added to this pool in the current transaction
     
     * Note that there is no `sessionId` argument; it always returns the value for the current (i.e., latest) session.
     */
    function getAddLiquidityCalledFlag(address pool) external view returns (bool liquidityAdded);

    /*******************************************************************************
                                    Pool Registration
    *******************************************************************************/

    /**
     * @notice Registers a pool, associating it with its factory and the tokens it manages.
     * @dev A pool can opt-out of pausing by providing a zero value for the pause window, or allow pausing indefinitely
     * by providing a large value. (Pool pause windows are not limited by the Vault maximums.) The vault defines an
     * additional buffer period during which a paused pool will stay paused. After the buffer period passes, a paused
     * pool will automatically unpause. Balancer timestamps are 32 bits.
     *
     * A pool can opt out of Balancer governance pausing by providing a custom `pauseManager`. This might be a
     * multi-sig contract or an arbitrary smart contract with its own access controls, that forwards calls to
     * the Vault.
     *
     * If the zero address is provided for the `pauseManager`, permissions for pausing the pool will default to the
     * authorizer.
     *
     * @param pool The address of the pool being registered
     * @param tokenConfig An array of descriptors for the tokens the pool will manage
     * @param swapFeePercentage The initial static swap fee percentage of the pool
     * @param pauseWindowEndTime The timestamp after which it is no longer possible to pause the pool
     * @param protocolFeeExempt If true, the pool's initial aggregate fees will be set to 0
     * @param roleAccounts Addresses the Vault will allow to change certain pool settings
     * @param poolHooksContract Contract that implements the hooks for the pool
     * @param liquidityManagement Liquidity management flags with implemented methods
     */
    function registerPool(
        address pool,
        TokenConfig[] memory tokenConfig,
        uint256 swapFeePercentage,
        uint32 pauseWindowEndTime,
        bool protocolFeeExempt,
        PoolRoleAccounts calldata roleAccounts,
        address poolHooksContract,
        LiquidityManagement calldata liquidityManagement
    ) external;

    /**
     * @notice Checks whether a pool is registered.
     * @param pool Address of the pool to check
     * @return registered True if the pool is registered, false otherwise
     */
    function isPoolRegistered(address pool) external view returns (bool registered);

    /**
     * @notice Initializes a registered pool by adding liquidity; mints BPT tokens for the first time in exchange.
     * @param pool Address of the pool to initialize
     * @param to Address that will receive the output BPT
     * @param tokens Tokens used to seed the pool (must match the registered tokens)
     * @param exactAmountsIn Exact amounts of input tokens
     * @param minBptAmountOut Minimum amount of output pool tokens
     * @param userData Additional (optional) data required for adding initial liquidity
     * @return bptAmountOut Output pool token amount
     */
    function initialize(
        address pool,
        address to,
        IERC20[] memory tokens,
        uint256[] memory exactAmountsIn,
        uint256 minBptAmountOut,
        bytes memory userData
    ) external returns (uint256 bptAmountOut);

    /*******************************************************************************
                                    Pool Information
    *******************************************************************************/

    /**
     * @notice Checks whether a pool is initialized.
     * @dev An initialized pool can be considered registered as well.
     * @param pool Address of the pool to check
     * @return initialized True if the pool is initialized, false otherwise
     */
    function isPoolInitialized(address pool) external view returns (bool initialized);

    /**
     * @notice Gets the tokens registered to a pool.
     * @param pool Address of the pool
     * @return tokens List of tokens in the pool
     */
    function getPoolTokens(address pool) external view returns (IERC20[] memory tokens);

    /**
     * @notice Gets pool token rates.
     * @dev This function performs external calls if tokens are yield-bearing. All returned arrays are in token
     * registration order.
     *
     * @param pool Address of the pool
     * @return decimalScalingFactors Conversion factor used to adjust for token decimals for uniform precision in
     * calculations. FP(1) for 18-decimal tokens
     * @return tokenRates 18-decimal FP values for rate tokens (e.g., yield-bearing), or FP(1) for standard tokens
     */
    function getPoolTokenRates(
        address pool
    ) external view returns (uint256[] memory decimalScalingFactors, uint256[] memory tokenRates);

    /**
     * @notice Returns comprehensive pool data for the given pool.
     * @dev This contains the pool configuration (flags), tokens and token types, rates, scaling factors, and balances.
     * @param pool The address of the pool
     * @return poolData The `PoolData` result
     */
    function getPoolData(address pool) external view returns (PoolData memory poolData);

    /**
     * @notice Gets the raw data for a pool: tokens, raw balances, scaling factors.
     * @param pool Address of the pool
     * @return tokens The pool tokens, sorted in registration order
     * @return tokenInfo Token info structs (type, rate provider, yield flag), sorted in token registration order
     * @return balancesRaw Current native decimal balances of the pool tokens, sorted in token registration order
     * @return lastBalancesLiveScaled18 Last saved live balances, sorted in token registration order
     */
    function getPoolTokenInfo(
        address pool
    )
        external
        view
        returns (
            IERC20[] memory tokens,
            TokenInfo[] memory tokenInfo,
            uint256[] memory balancesRaw,
            uint256[] memory lastBalancesLiveScaled18
        );

    /**
     * @notice Gets current live balances of a given pool (fixed-point, 18 decimals), corresponding to its tokens in
     * registration order.
     *
     * @param pool Address of the pool
     * @return balancesLiveScaled18 Token balances after paying yield fees, applying decimal scaling and rates
     */
    function getCurrentLiveBalances(address pool) external view returns (uint256[] memory balancesLiveScaled18);

    /**
     * @notice Gets the configuration parameters of a pool.
     * @dev The `PoolConfig` contains liquidity management and other state flags, fee percentages, the pause window.
     * @param pool Address of the pool
     * @return poolConfig The pool configuration as a `PoolConfig` struct
     */
    function getPoolConfig(address pool) external view returns (PoolConfig memory poolConfig);

    /**
     * @notice Gets the hooks configuration parameters of a pool.
     * @dev The `HooksConfig` contains flags indicating which pool hooks are implemented.
     * @param pool Address of the pool
     * @return hooksConfig The hooks configuration as a `HooksConfig` struct
     */
    function getHooksConfig(address pool) external view returns (HooksConfig memory hooksConfig);

    /**
     * @notice The current rate of a pool token (BPT) = invariant / totalSupply.
     * @param pool Address of the pool
     * @return rate BPT rate
     */
    function getBptRate(address pool) external view returns (uint256 rate);

    /*******************************************************************************
                                 Balancer Pool Tokens
    *******************************************************************************/

    /**
     * @notice Gets the total supply of a given ERC20 token.
     * @param token The token address
     * @return tokenTotalSupply Total supply of the token
     */
    function totalSupply(address token) external view returns (uint256 tokenTotalSupply);

    /**
     * @notice Gets the balance of an account for a given ERC20 token.
     * @param token Address of the token
     * @param account Address of the account
     * @return tokenBalance Token balance of the account
     */
    function balanceOf(address token, address account) external view returns (uint256 tokenBalance);

    /**
     * @notice Gets the allowance of a spender for a given ERC20 token and owner.
     * @param token Address of the token
     * @param owner Address of the owner
     * @param spender Address of the spender
     * @return tokenAllowance Amount of tokens the spender is allowed to spend
     */
    function allowance(address token, address owner, address spender) external view returns (uint256 tokenAllowance);

    /**
     * @notice Approves a spender to spend pool tokens on behalf of sender.
     * @dev Notice that the pool token address is not included in the params. This function is exclusively called by
     * the pool contract, so msg.sender is used as the token address.
     *
     * @param owner Address of the owner
     * @param spender Address of the spender
     * @param amount Amount of tokens to approve
     * @return success True if successful, false otherwise
     */
    function approve(address owner, address spender, uint256 amount) external returns (bool success);

    /*******************************************************************************
                                     Pool Pausing
    *******************************************************************************/

    /**
     * @notice Indicates whether a pool is paused.
     * @dev If a pool is paused, all non-Recovery Mode state-changing operations will revert.
     * @param pool The pool to be checked
     * @return poolPaused True if the pool is paused
     */
    function isPoolPaused(address pool) external view returns (bool poolPaused);

    /**
     * @notice Returns the paused status, and end times of the Pool's pause window and buffer period.
     * @dev Note that even when set to a paused state, the pool will automatically unpause at the end of
     * the buffer period. Balancer timestamps are 32 bits.
     *
     * @param pool The pool whose data is requested
     * @return poolPaused True if the Pool is paused
     * @return poolPauseWindowEndTime The timestamp of the end of the Pool's pause window
     * @return poolBufferPeriodEndTime The timestamp after which the Pool unpauses itself (if paused)
     * @return pauseManager The pause manager, or the zero address
     */
    function getPoolPausedState(
        address pool
    )
        external
        view
        returns (bool poolPaused, uint32 poolPauseWindowEndTime, uint32 poolBufferPeriodEndTime, address pauseManager);

    /*******************************************************************************
                                   ERC4626 Buffers
    *******************************************************************************/

    /**
     * @notice Checks if the wrapped token has an initialized buffer in the Vault.
     * @dev An initialized buffer should have an asset registered in the Vault.
     * @param wrappedToken Address of the wrapped token that implements IERC4626
     * @return isBufferInitialized True if the ERC4626 buffer is initialized
     */
    function isERC4626BufferInitialized(IERC4626 wrappedToken) external view returns (bool isBufferInitialized);

    /**
     * @notice Gets the registered asset for a given buffer.
     * @dev To avoid malicious wrappers (e.g., that might potentially change their asset after deployment), routers
     * should never call `wrapper.asset()` directly, at least without checking it against the asset registered with
     * the Vault on initialization.
     *
     * @param wrappedToken The wrapped token specifying the buffer
     * @return asset The underlying asset of the wrapped token
     */
    function getERC4626BufferAsset(IERC4626 wrappedToken) external view returns (address asset);

    /*******************************************************************************
                                          Fees
    *******************************************************************************/

    /**
     * @notice Returns the accumulated swap fees (including aggregate fees) in `token` collected by the pool.
     * @param pool The address of the pool for which aggregate fees have been collected
     * @param token The address of the token in which fees have been accumulated
     * @return swapFeeAmount The total amount of fees accumulated in the specified token
     */
    function getAggregateSwapFeeAmount(address pool, IERC20 token) external view returns (uint256 swapFeeAmount);

    /**
     * @notice Returns the accumulated yield fees (including aggregate fees) in `token` collected by the pool.
     * @param pool The address of the pool for which aggregate fees have been collected
     * @param token The address of the token in which fees have been accumulated
     * @return yieldFeeAmount The total amount of fees accumulated in the specified token
     */
    function getAggregateYieldFeeAmount(address pool, IERC20 token) external view returns (uint256 yieldFeeAmount);

    /**
     * @notice Fetches the static swap fee percentage for a given pool.
     * @param pool The address of the pool whose static swap fee percentage is being queried
     * @return swapFeePercentage The current static swap fee percentage for the specified pool
     */
    function getStaticSwapFeePercentage(address pool) external view returns (uint256 swapFeePercentage);

    /**
     * @notice Fetches the role accounts for a given pool (pause manager, swap manager, pool creator)
     * @param pool The address of the pool whose roles are being queried
     * @return roleAccounts A struct containing the role accounts for the pool (or 0 if unassigned)
     */
    function getPoolRoleAccounts(address pool) external view returns (PoolRoleAccounts memory roleAccounts);

    /**
     * @notice Query the current dynamic swap fee percentage of a pool, given a set of swap parameters.
     * @dev Reverts if the hook doesn't return the success flag set to `true`.
     * @param pool The pool
     * @param swapParams The swap parameters used to compute the fee
     * @return dynamicSwapFeePercentage The dynamic swap fee percentage
     */
    function computeDynamicSwapFeePercentage(
        address pool,
        PoolSwapParams memory swapParams
    ) external view returns (uint256 dynamicSwapFeePercentage);

    /**
     * @notice Returns the Protocol Fee Controller address.
     * @return protocolFeeController Address of the ProtocolFeeController
     */
    function getProtocolFeeController() external view returns (IProtocolFeeController protocolFeeController);

    /*******************************************************************************
                                     Recovery Mode
    *******************************************************************************/

    /**
     * @notice Checks whether a pool is in Recovery Mode.
     * @dev Recovery Mode enables a safe proportional withdrawal path, with no external calls.
     * @param pool Address of the pool to check
     * @return inRecoveryMode True if the pool is in Recovery Mode, false otherwise
     */
    function isPoolInRecoveryMode(address pool) external view returns (bool inRecoveryMode);

    /**
     * @notice Remove liquidity from a pool specifying exact pool tokens in, with proportional token amounts out.
     * The request is implemented by the Vault without any interaction with the pool, ensuring that
     * it works the same for all pools, and cannot be disabled by a new pool type.
     *
     * @param pool Address of the pool
     * @param from Address of user to burn pool tokens from
     * @param exactBptAmountIn Input pool token amount
     * @param minAmountsOut Minimum amounts of tokens to be received, sorted in token registration order
     * @return amountsOut Actual calculated amounts of output tokens, sorted in token registration order
     */
    function removeLiquidityRecovery(
        address pool,
        address from,
        uint256 exactBptAmountIn,
        uint256[] memory minAmountsOut
    ) external returns (uint256[] memory amountsOut);

    /*******************************************************************************
                                    Queries
    *******************************************************************************/

    /**
     * @notice Performs a callback on msg.sender with arguments provided in `data`.
     * @dev Used to query a set of operations on the Vault. Only off-chain eth_call are allowed,
     * anything else will revert.
     *
     * Allows querying any operation on the Vault that has the `onlyWhenUnlocked` modifier.
     *
     * Allows the external calling of a function via the Vault contract to
     * access Vault's functions guarded by `onlyWhenUnlocked`.
     * `transient` modifier ensuring balances changes within the Vault are settled.
     *
     * @param data Contains function signature and args to be passed to the msg.sender
     * @return result Resulting data from the call
     */
    function quote(bytes calldata data) external returns (bytes memory result);

    /**
     * @notice Performs a callback on msg.sender with arguments provided in `data`.
     * @dev Used to query a set of operations on the Vault. Only off-chain eth_call are allowed,
     * anything else will revert.
     *
     * Allows querying any operation on the Vault that has the `onlyWhenUnlocked` modifier.
     *
     * Allows the external calling of a function via the Vault contract to
     * access Vault's functions guarded by `onlyWhenUnlocked`.
     * `transient` modifier ensuring balances changes within the Vault are settled.
     *
     * This call always reverts, returning the result in the revert reason.
     *
     * @param data Contains function signature and args to be passed to the msg.sender
     */
    function quoteAndRevert(bytes calldata data) external;

    /**
     * @notice Returns true if queries are disabled on the Vault.
     * @dev If true, queries might either be disabled temporarily or permanently.
     * @return queryDisabled True if query functionality is reversibly disabled
     */
    function isQueryDisabled() external view returns (bool queryDisabled);

    /**
     * @notice Returns true if queries are disabled permanently; false if they are enabled.
     * @dev This is a one-way switch. Once queries are disabled permanently, they can never be re-enabled.
     * @return queryDisabledPermanently True if query functionality is permanently disabled
     */
    function isQueryDisabledPermanently() external view returns (bool queryDisabledPermanently);

    /**
     * @notice Pools can use this event to emit event data from the Vault.
     * @param eventKey Event key
     * @param eventData Encoded event data
     */
    function emitAuxiliaryEvent(bytes32 eventKey, bytes calldata eventData) external;

    /*******************************************************************************
                                Authentication
    *******************************************************************************/

    /**
     * @notice Returns the Authorizer address.
     * @dev The authorizer holds the permissions granted by governance. It is set on Vault deployment,
     * and can be changed through a permissioned call.
     *
     * @return authorizer Address of the authorizer contract
     */
    function getAuthorizer() external view returns (IAuthorizer authorizer);
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

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

import "./VaultTypes.sol";

/**
 * @notice Interface for functions defined on the main Vault contract.
 * @dev These are generally "critical path" functions (swap, add/remove liquidity) that are in the main contract
 * for technical or performance reasons.
 */
interface IVaultMain {
    /*******************************************************************************
                              Transient Accounting
    *******************************************************************************/

    /**
     * @notice Creates a context for a sequence of operations (i.e., "unlocks" the Vault).
     * @dev Performs a callback on msg.sender with arguments provided in `data`. The Callback is `transient`,
     * meaning all balances for the caller have to be settled at the end.
     *
     * @param data Contains function signature and args to be passed to the msg.sender
     * @return result Resulting data from the call
     */
    function unlock(bytes calldata data) external returns (bytes memory result);

    /**
     * @notice Settles deltas for a token; must be successful for the current lock to be released.
     * @dev Protects the caller against leftover dust in the Vault for the token being settled. The caller
     * should know in advance how many tokens were paid to the Vault, so it can provide it as a hint to discard any
     * excess in the Vault balance.
     *
     * If the given hint is equal to or higher than the difference in reserves, the difference in reserves is given as
     * credit to the caller. If it's higher, the caller sent fewer tokens than expected, so settlement would fail.
     *
     * If the given hint is lower than the difference in reserves, the hint is given as credit to the caller.
     * In this case, the excess would be absorbed by the Vault (and reflected correctly in the reserves), but would
     * not affect settlement.
     *
     * The credit supplied by the Vault can be calculated as `min(reserveDifference, amountHint)`, where the reserve
     * difference equals current balance of the token minus existing reserves of the token when the function is called.
     *
     * @param token Address of the token
     * @param amountHint Amount paid as reported by the caller
     * @return credit Credit received in return of the payment
     */
    function settle(IERC20 token, uint256 amountHint) external returns (uint256 credit);

    /**
     * @notice Sends tokens to a recipient.
     * @dev There is no inverse operation for this function. Transfer funds to the Vault and call `settle` to cancel
     * debts.
     *
     * @param token Address of the token
     * @param to Recipient address
     * @param amount Amount of tokens to send
     */
    function sendTo(IERC20 token, address to, uint256 amount) external;

    /***************************************************************************
                                       Swaps
    ***************************************************************************/

    /**
     * @notice Swaps tokens based on provided parameters.
     * @dev All parameters are given in raw token decimal encoding.
     * @param vaultSwapParams Parameters for the swap (see above for struct definition)
     * @return amountCalculatedRaw Calculated swap amount
     * @return amountInRaw Amount of input tokens for the swap
     * @return amountOutRaw Amount of output tokens from the swap
     */
    function swap(
        VaultSwapParams memory vaultSwapParams
    ) external returns (uint256 amountCalculatedRaw, uint256 amountInRaw, uint256 amountOutRaw);

    /***************************************************************************
                                   Add Liquidity
    ***************************************************************************/

    /**
     * @notice Adds liquidity to a pool.
     * @dev Caution should be exercised when adding liquidity because the Vault has the capability
     * to transfer tokens from any user, given that it holds all allowances.
     *
     * @param params Parameters for the add liquidity (see above for struct definition)
     * @return amountsIn Actual amounts of input tokens
     * @return bptAmountOut Output pool token amount
     * @return returnData Arbitrary (optional) data with an encoded response from the pool
     */
    function addLiquidity(
        AddLiquidityParams memory params
    ) external returns (uint256[] memory amountsIn, uint256 bptAmountOut, bytes memory returnData);

    /***************************************************************************
                                 Remove Liquidity
    ***************************************************************************/

    /**
     * @notice Removes liquidity from a pool.
     * @dev Trusted routers can burn pool tokens belonging to any user and require no prior approval from the user.
     * Untrusted routers require prior approval from the user. This is the only function allowed to call
     * _queryModeBalanceIncrease (and only in a query context).
     *
     * @param params Parameters for the remove liquidity (see above for struct definition)
     * @return bptAmountIn Actual amount of BPT burned
     * @return amountsOut Actual amounts of output tokens
     * @return returnData Arbitrary (optional) data with an encoded response from the pool
     */
    function removeLiquidity(
        RemoveLiquidityParams memory params
    ) external returns (uint256 bptAmountIn, uint256[] memory amountsOut, bytes memory returnData);

    /*******************************************************************************
                                    Pool Information
    *******************************************************************************/

    /**
     * @notice Gets the index of a token in a given pool.
     * @dev Reverts if the pool is not registered, or if the token does not belong to the pool.
     * @param pool Address of the pool
     * @param token Address of the token
     * @return tokenCount Number of tokens in the pool
     * @return index Index corresponding to the given token in the pool's token list
     */
    function getPoolTokenCountAndIndexOfToken(
        address pool,
        IERC20 token
    ) external view returns (uint256 tokenCount, uint256 index);

    /*******************************************************************************
                                 Balancer Pool Tokens
    *******************************************************************************/

    /**
     * @notice Transfers pool token from owner to a recipient.
     * @dev Notice that the pool token address is not included in the params. This function is exclusively called by
     * the pool contract, so msg.sender is used as the token address.
     *
     * @param owner Address of the owner
     * @param to Address of the recipient
     * @param amount Amount of tokens to transfer
     * @return success True if successful, false otherwise
     */
    function transfer(address owner, address to, uint256 amount) external returns (bool);

    /**
     * @notice Transfers pool token from a sender to a recipient using an allowance.
     * @dev Notice that the pool token address is not included in the params. This function is exclusively called by
     * the pool contract, so msg.sender is used as the token address.
     *
     * @param spender Address allowed to perform the transfer
     * @param from Address of the sender
     * @param to Address of the recipient
     * @param amount Amount of tokens to transfer
     * @return success True if successful, false otherwise
     */
    function transferFrom(address spender, address from, address to, uint256 amount) external returns (bool success);

    /*******************************************************************************
                                  ERC4626 Buffers
    *******************************************************************************/

    /**
     * @notice Wraps/unwraps tokens based on the parameters provided.
     * @dev All parameters are given in raw token decimal encoding. It requires the buffer to be initialized,
     * and uses the internal wrapped token buffer when it has enough liquidity to avoid external calls.
     *
     * @param params Parameters for the wrap/unwrap operation (see struct definition)
     * @return amountCalculatedRaw Calculated swap amount
     * @return amountInRaw Amount of input tokens for the swap
     * @return amountOutRaw Amount of output tokens from the swap
     */
    function erc4626BufferWrapOrUnwrap(
        BufferWrapOrUnwrapParams memory params
    ) external returns (uint256 amountCalculatedRaw, uint256 amountInRaw, uint256 amountOutRaw);

    /*******************************************************************************
                                     Miscellaneous
    *******************************************************************************/

    /**
     * @notice Returns the VaultExtension contract address.
     * @dev Function is in the main Vault contract. The VaultExtension handles less critical or frequently used
     * functions, since delegate calls through the Vault are more expensive than direct calls.
     *
     * @return vaultExtension Address of the VaultExtension
     */
    function getVaultExtension() external view returns (address vaultExtension);
}

File 25 of 72 : VaultTypes.sol
// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

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

import { IRateProvider } from "../solidity-utils/helpers/IRateProvider.sol";

/**
 * @notice Represents a pool's liquidity management configuration.
 * @param disableUnbalancedLiquidity If set, liquidity can only be added or removed proportionally
 * @param enableAddLiquidityCustom If set, the pool has implemented `onAddLiquidityCustom`
 * @param enableRemoveLiquidityCustom If set, the pool has implemented `onRemoveLiquidityCustom`
 * @param enableDonation If set, the pool will not revert if liquidity is added with AddLiquidityKind.DONATION
 */
struct LiquidityManagement {
    bool disableUnbalancedLiquidity;
    bool enableAddLiquidityCustom;
    bool enableRemoveLiquidityCustom;
    bool enableDonation;
}

// @notice Custom type to store the entire configuration of the pool.
type PoolConfigBits is bytes32;

/**
 * @notice Represents a pool's configuration (hooks configuration are separated in another struct).
 * @param liquidityManagement Flags related to adding/removing liquidity
 * @param staticSwapFeePercentage The pool's native swap fee
 * @param aggregateSwapFeePercentage The total swap fee charged, including protocol and pool creator components
 * @param aggregateYieldFeePercentage The total swap fee charged, including protocol and pool creator components
 * @param tokenDecimalDiffs Compressed storage of the token decimals of each pool token
 * @param pauseWindowEndTime Timestamp after which the pool cannot be paused
 * @param isPoolRegistered If true, the pool has been registered with the Vault
 * @param isPoolInitialized If true, the pool has been initialized with liquidity, and is available for trading
 * @param isPoolPaused If true, the pool has been paused (by governance or the pauseManager)
 * @param isPoolInRecoveryMode If true, the pool has been placed in recovery mode, enabling recovery mode withdrawals
 */
struct PoolConfig {
    LiquidityManagement liquidityManagement;
    uint256 staticSwapFeePercentage;
    uint256 aggregateSwapFeePercentage;
    uint256 aggregateYieldFeePercentage;
    uint40 tokenDecimalDiffs;
    uint32 pauseWindowEndTime;
    bool isPoolRegistered;
    bool isPoolInitialized;
    bool isPoolPaused;
    bool isPoolInRecoveryMode;
}

/**
 * @notice The flag portion of the `HooksConfig`.
 * @dev `enableHookAdjustedAmounts` must be true for all contracts that modify the `amountCalculated`
 * in after hooks. Otherwise, the Vault will ignore any "hookAdjusted" amounts. Setting any "shouldCall"
 * flags to true will cause the Vault to call the corresponding hook during operations.
 */
struct HookFlags {
    bool enableHookAdjustedAmounts;
    bool shouldCallBeforeInitialize;
    bool shouldCallAfterInitialize;
    bool shouldCallComputeDynamicSwapFee;
    bool shouldCallBeforeSwap;
    bool shouldCallAfterSwap;
    bool shouldCallBeforeAddLiquidity;
    bool shouldCallAfterAddLiquidity;
    bool shouldCallBeforeRemoveLiquidity;
    bool shouldCallAfterRemoveLiquidity;
}

/// @notice Represents a hook contract configuration for a pool (HookFlags + hooksContract address).
struct HooksConfig {
    bool enableHookAdjustedAmounts;
    bool shouldCallBeforeInitialize;
    bool shouldCallAfterInitialize;
    bool shouldCallComputeDynamicSwapFee;
    bool shouldCallBeforeSwap;
    bool shouldCallAfterSwap;
    bool shouldCallBeforeAddLiquidity;
    bool shouldCallAfterAddLiquidity;
    bool shouldCallBeforeRemoveLiquidity;
    bool shouldCallAfterRemoveLiquidity;
    address hooksContract;
}

/**
 * @notice Represents temporary state used during a swap operation.
 * @param indexIn The zero-based index of tokenIn
 * @param indexOut The zero-based index of tokenOut
 * @param amountGivenScaled18 The amountGiven (i.e., tokenIn for ExactIn), adjusted for token decimals
 * @param swapFeePercentage The swap fee to be applied (might be static or dynamic)
 */
struct SwapState {
    uint256 indexIn;
    uint256 indexOut;
    uint256 amountGivenScaled18;
    uint256 swapFeePercentage;
}

/**
 * @notice Represents the Vault's configuration.
 * @param isQueryDisabled If set to true, disables query functionality of the Vault. Can be modified by governance
 * @param isVaultPaused If set to true, swaps and add/remove liquidity operations are halted
 * @param areBuffersPaused If set to true, the Vault wrap/unwrap primitives associated with buffers will be disabled
 */
struct VaultState {
    bool isQueryDisabled;
    bool isVaultPaused;
    bool areBuffersPaused;
}

/**
 * @notice Represents the accounts holding certain roles for a given pool. This is passed in on pool registration.
 * @param pauseManager Account empowered to pause/unpause the pool (note that governance can always pause a pool)
 * @param swapFeeManager Account empowered to set static swap fees for a pool (or 0 to delegate to governance)
 * @param poolCreator Account empowered to set the pool creator fee (or 0 if all fees go to the protocol and LPs)
 */
struct PoolRoleAccounts {
    address pauseManager;
    address swapFeeManager;
    address poolCreator;
}

/*******************************************************************************
                                   Tokens
*******************************************************************************/

// Note that the following tokens are unsupported by the Vault. This list is not meant to be exhaustive, but covers
// many common types of tokens that will not work with the Vault architecture. (See https://github.com/d-xo/weird-erc20
// for examples of token features that are problematic for many protocols.)
//
// * Rebasing tokens (e.g., aDAI). The Vault keeps track of token balances in its internal accounting; any token whose
//   balance changes asynchronously (i.e., outside a swap or liquidity operation), would get out-of-sync with this
//   internal accounting. This category would also include "airdrop" tokens, whose balances can change unexpectedly.
//
// * Double entrypoint (e.g., old Synthetix tokens, now fixed). These could likewise bypass internal accounting by
//   registering the token under one address, then accessing it through another. This is especially troublesome
//   in v3, with the introduction of ERC4626 buffers.
//
// * Fee on transfer (e.g., PAXG). The Vault issues credits and debits according to given and calculated token amounts,
//   and settlement assumes that the send/receive transfer functions transfer exactly the given number of tokens.
//   If this is not the case, transactions will not settle. Unlike with the other types, which are fundamentally
//   incompatible, it would be possible to design a Router to handle this - but we didn't try it. In any case, it's
//   not supported in the current Routers.
//
// * Tokens with more than 18 decimals (e.g., YAM-V2). The Vault handles token scaling: i.e., handling I/O for
//   amounts in native token decimals, but doing calculations with full 18-decimal precision. This requires reading
//   and storing the decimals for each token. Since virtually all tokens are 18 or fewer decimals, and we have limited
//   storage space, 18 was a reasonable maximum. Unlike the other types, this is enforceable by the Vault. Attempting
//   to register such tokens will revert with `InvalidTokenDecimals`. Of course, we must also be able to read the token
//   decimals, so the Vault only supports tokens that implement `IERC20Metadata.decimals`, and return a value less than
//   or equal to 18.
//
//  * Token decimals are checked and stored only once, on registration. Valid tokens store their decimals as immutable
//    variables or constants. Malicious tokens that don't respect this basic property would not work anywhere in DeFi.
//
// These types of tokens are supported but discouraged, as they don't tend to play well with AMMs generally.
//
// * Very low-decimal tokens (e.g., GUSD). The Vault has been extensively tested with 6-decimal tokens (e.g., USDC),
//   but going much below that may lead to unanticipated effects due to precision loss, especially with smaller trade
//   values.
//
// * Revert on zero value approval/transfer. The Vault has been tested against these, but peripheral contracts, such
//   as hooks, might not have been designed with this in mind.
//
// * Other types from "weird-erc20," such as upgradeable, pausable, or tokens with blocklists. We have seen cases
//   where a token upgrade fails, "bricking" the token - and many operations on pools containing that token. Any
//   sort of "permissioned" token that can make transfers fail can cause operations on pools containing them to
//   revert. Even Recovery Mode cannot help then, as it does a proportional withdrawal of all tokens. If one of
//   them is bricked, the whole operation will revert. Since v3 does not have "internal balances" like v2, there
//   is no recourse.
//
//   Of course, many tokens in common use have some of these "features" (especially centralized stable coins), so
//   we have to support them anyway. Working with common centralized tokens is a risk common to all of DeFi.

/**
 * @notice Token types supported by the Vault.
 * @dev In general, pools may contain any combination of these tokens.
 *
 * STANDARD tokens (e.g., BAL, WETH) have no rate provider.
 * WITH_RATE tokens (e.g., wstETH) require a rate provider. These may be tokens like wstETH, which need to be wrapped
 * because the underlying stETH token is rebasing, and such tokens are unsupported by the Vault. They may also be
 * tokens like sEUR, which track an underlying asset, but are not yield-bearing. Finally, this encompasses
 * yield-bearing ERC4626 tokens, which can be used to facilitate swaps without requiring wrapping or unwrapping
 * in most cases. The `paysYieldFees` flag can be used to indicate whether a token is yield-bearing (e.g., waDAI),
 * not yield-bearing (e.g., sEUR), or yield-bearing but exempt from fees (e.g., in certain nested pools, where
 * yield fees are charged elsewhere).
 *
 * NB: STANDARD must always be the first enum element, so that newly initialized data structures default to Standard.
 */
enum TokenType {
    STANDARD,
    WITH_RATE
}

/**
 * @notice Encapsulate the data required for the Vault to support a token of the given type.
 * @dev For STANDARD tokens, the rate provider address must be 0, and paysYieldFees must be false. All WITH_RATE tokens
 * need a rate provider, and may or may not be yield-bearing.
 *
 * At registration time, it is useful to include the token address along with the token parameters in the structure
 * passed to `registerPool`, as the alternative would be parallel arrays, which would be error prone and require
 * validation checks. `TokenConfig` is only used for registration, and is never put into storage (see `TokenInfo`).
 *
 * @param token The token address
 * @param tokenType The token type (see the enum for supported types)
 * @param rateProvider The rate provider for a token (see further documentation above)
 * @param paysYieldFees Flag indicating whether yield fees should be charged on this token
 */
struct TokenConfig {
    IERC20 token;
    TokenType tokenType;
    IRateProvider rateProvider;
    bool paysYieldFees;
}

/**
 * @notice This data structure is stored in `_poolTokenInfo`, a nested mapping from pool -> (token -> TokenInfo).
 * @dev Since the token is already the key of the nested mapping, it would be redundant (and an extra SLOAD) to store
 * it again in the struct. When we construct PoolData, the tokens are separated into their own array.
 *
 * @param tokenType The token type (see the enum for supported types)
 * @param rateProvider The rate provider for a token (see further documentation above)
 * @param paysYieldFees Flag indicating whether yield fees should be charged on this token
 */
struct TokenInfo {
    TokenType tokenType;
    IRateProvider rateProvider;
    bool paysYieldFees;
}

/**
 * @notice Data structure used to represent the current pool state in memory
 * @param poolConfigBits Custom type to store the entire configuration of the pool.
 * @param tokens Pool tokens, sorted in token registration order
 * @param tokenInfo Configuration data for each token, sorted in token registration order
 * @param balancesRaw Token balances in native decimals
 * @param balancesLiveScaled18 Token balances after paying yield fees, applying decimal scaling and rates
 * @param tokenRates 18-decimal FP values for rate tokens (e.g., yield-bearing), or FP(1) for standard tokens
 * @param decimalScalingFactors Conversion factor used to adjust for token decimals for uniform precision in
 * calculations. It is 1e18 (FP 1) for 18-decimal tokens
 */
struct PoolData {
    PoolConfigBits poolConfigBits;
    IERC20[] tokens;
    TokenInfo[] tokenInfo;
    uint256[] balancesRaw;
    uint256[] balancesLiveScaled18;
    uint256[] tokenRates;
    uint256[] decimalScalingFactors;
}

enum Rounding {
    ROUND_UP,
    ROUND_DOWN
}

/*******************************************************************************
                                    Swaps
*******************************************************************************/

enum SwapKind {
    EXACT_IN,
    EXACT_OUT
}

// There are two "SwapParams" structs defined below. `VaultSwapParams` corresponds to the external swap API defined
// in the Router contracts, which uses explicit token addresses, the amount given and limit on the calculated amount
// expressed in native token decimals, and optional user data passed in from the caller.
//
// `PoolSwapParams` passes some of this information through (kind, userData), but "translates" the parameters to fit
// the internal swap API used by `IBasePool`. It scales amounts to full 18-decimal precision, adds the token balances,
// converts the raw token addresses to indices, and adds the address of the Router originating the request. It does
// not need the limit, since this is checked at the Router level.

/**
 * @notice Data passed into primary Vault `swap` operations.
 * @param kind Type of swap (Exact In or Exact Out)
 * @param pool The pool with the tokens being swapped
 * @param tokenIn The token entering the Vault (balance increases)
 * @param tokenOut The token leaving the Vault (balance decreases)
 * @param amountGivenRaw Amount specified for tokenIn or tokenOut (depending on the type of swap)
 * @param limitRaw Minimum or maximum value of the calculated amount (depending on the type of swap)
 * @param userData Additional (optional) user data
 */
struct VaultSwapParams {
    SwapKind kind;
    address pool;
    IERC20 tokenIn;
    IERC20 tokenOut;
    uint256 amountGivenRaw;
    uint256 limitRaw;
    bytes userData;
}

/**
 * @notice Data for a swap operation, used by contracts implementing `IBasePool`.
 * @param kind Type of swap (exact in or exact out)
 * @param amountGivenScaled18 Amount given based on kind of the swap (e.g., tokenIn for EXACT_IN)
 * @param balancesScaled18 Current pool balances
 * @param indexIn Index of tokenIn
 * @param indexOut Index of tokenOut
 * @param router The address (usually a router contract) that initiated a swap operation on the Vault
 * @param userData Additional (optional) data required for the swap
 */
struct PoolSwapParams {
    SwapKind kind;
    uint256 amountGivenScaled18;
    uint256[] balancesScaled18;
    uint256 indexIn;
    uint256 indexOut;
    address router;
    bytes userData;
}

/**
 * @notice Data for the hook after a swap operation.
 * @param kind Type of swap (exact in or exact out)
 * @param tokenIn Token to be swapped from
 * @param tokenOut Token to be swapped to
 * @param amountInScaled18 Amount of tokenIn (entering the Vault)
 * @param amountOutScaled18 Amount of tokenOut (leaving the Vault)
 * @param tokenInBalanceScaled18 Updated (after swap) balance of tokenIn
 * @param tokenOutBalanceScaled18 Updated (after swap) balance of tokenOut
 * @param amountCalculatedScaled18 Token amount calculated by the swap
 * @param amountCalculatedRaw Token amount calculated by the swap
 * @param router The address (usually a router contract) that initiated a swap operation on the Vault
 * @param pool Pool address
 * @param userData Additional (optional) data required for the swap
 */
struct AfterSwapParams {
    SwapKind kind;
    IERC20 tokenIn;
    IERC20 tokenOut;
    uint256 amountInScaled18;
    uint256 amountOutScaled18;
    uint256 tokenInBalanceScaled18;
    uint256 tokenOutBalanceScaled18;
    uint256 amountCalculatedScaled18;
    uint256 amountCalculatedRaw;
    address router;
    address pool;
    bytes userData;
}

/*******************************************************************************
                                Add liquidity
*******************************************************************************/

enum AddLiquidityKind {
    PROPORTIONAL,
    UNBALANCED,
    SINGLE_TOKEN_EXACT_OUT,
    DONATION,
    CUSTOM
}

/**
 * @notice Data for an add liquidity operation.
 * @param pool Address of the pool
 * @param to Address of user to mint to
 * @param maxAmountsIn Maximum amounts of input tokens
 * @param minBptAmountOut Minimum amount of output pool tokens
 * @param kind Add liquidity kind
 * @param userData Optional user data
 */
struct AddLiquidityParams {
    address pool;
    address to;
    uint256[] maxAmountsIn;
    uint256 minBptAmountOut;
    AddLiquidityKind kind;
    bytes userData;
}

/*******************************************************************************
                                Remove liquidity
*******************************************************************************/

enum RemoveLiquidityKind {
    PROPORTIONAL,
    SINGLE_TOKEN_EXACT_IN,
    SINGLE_TOKEN_EXACT_OUT,
    CUSTOM
}

/**
 * @notice Data for an remove liquidity operation.
 * @param pool Address of the pool
 * @param from Address of user to burn from
 * @param maxBptAmountIn Maximum amount of input pool tokens
 * @param minAmountsOut Minimum amounts of output tokens
 * @param kind Remove liquidity kind
 * @param userData Optional user data
 */
struct RemoveLiquidityParams {
    address pool;
    address from;
    uint256 maxBptAmountIn;
    uint256[] minAmountsOut;
    RemoveLiquidityKind kind;
    bytes userData;
}

/*******************************************************************************
                                Remove liquidity
*******************************************************************************/

enum WrappingDirection {
    WRAP,
    UNWRAP
}

/**
 * @notice Data for a wrap/unwrap operation.
 * @param kind Type of swap (Exact In or Exact Out)
 * @param direction Direction of the wrapping operation (Wrap or Unwrap)
 * @param wrappedToken Wrapped token, compatible with interface ERC4626
 * @param amountGivenRaw Amount specified for tokenIn or tokenOut (depends on the type of swap and wrapping direction)
 * @param limitRaw Minimum or maximum amount specified for the other token (depends on the type of swap and wrapping
 * direction)
 */
struct BufferWrapOrUnwrapParams {
    SwapKind kind;
    WrappingDirection direction;
    IERC4626 wrappedToken;
    uint256 amountGivenRaw;
    uint256 limitRaw;
}

// Protocol Fees are 24-bit values. We transform them by multiplying by 1e11, so that they can be set to any value
// between 0% and 100% (step 0.00001%). Protocol and pool creator fees are set in the `ProtocolFeeController`, and
// ensure both constituent and aggregate fees do not exceed this precision.
uint256 constant FEE_BITLENGTH = 24;
uint256 constant FEE_SCALING_FACTOR = 1e11;
// Used to ensure the safety of fee-related math (e.g., pools or hooks don't set it greater than 100%).
// This value should work for practical purposes and is well within the max precision requirements.
uint256 constant MAX_FEE_PERCENTAGE = 99.9999e16; // 99.9999%

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

import { IBasePoolFactory } from "@balancer-labs/v3-interfaces/contracts/vault/IBasePoolFactory.sol";
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";
import {
    TokenConfig,
    PoolRoleAccounts,
    LiquidityManagement
} from "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol";

import { BaseSplitCodeFactory } from "@balancer-labs/v3-solidity-utils/contracts/helpers/BaseSplitCodeFactory.sol";
import { FactoryWidePauseWindow } from "@balancer-labs/v3-solidity-utils/contracts/helpers/FactoryWidePauseWindow.sol";
import { SingletonAuthentication } from "@balancer-labs/v3-vault/contracts/SingletonAuthentication.sol";

/**
 * @notice Base contract for Pool factories.
 *
 * Pools are deployed from factories to allow third parties to more easily reason about them. Unknown Pools may have
 * arbitrary logic: being able to assert that a Pool's behavior follows certain rules (those imposed by the contracts
 * created by the factory) is very powerful.
 *
 * Note that in v3, the factory alone is not enough to ensure the safety of a pool. v3 pools can have arbitrary hook
 * contracts, rate providers, complex tokens, and configuration that significantly impacts pool behavior. Specialty
 * factories can be designed to limit their pools range of behavior (e.g., weighted 80/20 factories where the token
 * count and weights are fixed).
 *
 * Since we expect to release new versions of pool types regularly - and the blockchain is forever - versioning will
 * become increasingly important. Governance can deprecate a factory by calling `disable`, which will permanently
 * prevent the creation of any future pools from the factory.
 *
 * Use of factories is also important for security. Calls to `registerPool` or `initialize` made directly on the Vault
 * could potentially be frontrun. In the case of registration, a DoS attack could register a pool with malicious
 * parameters, causing the legitimate registration transaction to fail. The standard Balancer factories avoid this by
 * deploying and registering in a single `create` function.
 *
 * It would also be possible to frontrun `initialize` (e.g., with unbalanced liquidity), and cause the intended
 * initialization to fail. Like registration, initialization only happens once. The Balancer standard factories do not
 * initialize on create, as this would be more complex (e.g., requiring token approvals), and it's very common for the
 * deployment and funding to be performed from different accounts. Also, frontrunning `initialize` doesn't have serious
 * consequences, beyond being a DoS.
 *
 * Nevertheless, this is a factor to consider when launching new pools. To avoid any possibility of frontrunning,
 * the best practice would be to create (i.e., deploy and register) and initialize in the same transaction.
 */
abstract contract BasePoolFactory is
    IBasePoolFactory,
    BaseSplitCodeFactory,
    SingletonAuthentication,
    FactoryWidePauseWindow
{
    mapping(address pool => bool isFromFactory) private _isPoolFromFactory;
    address[] private _pools;

    bool private _disabled;

    /// @notice A pool creator was specified for a pool from a Balancer core pool type.
    error StandardPoolWithCreator();

    constructor(
        IVault vault,
        uint32 pauseWindowDuration,
        bytes memory creationCode
    ) BaseSplitCodeFactory(creationCode) SingletonAuthentication(vault) FactoryWidePauseWindow(pauseWindowDuration) {
        // solhint-disable-previous-line no-empty-blocks
    }

    /// @inheritdoc IBasePoolFactory
    function isPoolFromFactory(address pool) external view returns (bool) {
        return _isPoolFromFactory[pool];
    }

    /// @inheritdoc IBasePoolFactory
    function getPoolCount() external view returns (uint256) {
        return _pools.length;
    }

    /// @inheritdoc IBasePoolFactory
    function getPools() external view returns (address[] memory) {
        return _pools;
    }

    /// @inheritdoc IBasePoolFactory
    function getPoolsInRange(uint256 start, uint256 count) external view returns (address[] memory pools) {
        uint256 length = _pools.length;
        if (start >= length) {
            revert IndexOutOfBounds();
        }

        // If `count` requests more pools than we have available, stop at the end of the array.
        uint256 end = start + count;
        if (end > length) {
            count = length - start;
        }

        pools = new address[](count);
        for (uint256 i = 0; i < count; i++) {
            pools[i] = _pools[start + i];
        }
    }

    /// @inheritdoc IBasePoolFactory
    function isDisabled() public view returns (bool) {
        return _disabled;
    }

    /// @inheritdoc IBasePoolFactory
    function getDeploymentAddress(bytes memory constructorArgs, bytes32 salt) public view returns (address) {
        bytes32 finalSalt = _computeFinalSalt(salt);

        return _getDeploymentAddress(constructorArgs, finalSalt);
    }

    /// @inheritdoc IBasePoolFactory
    function disable() external authenticate {
        _ensureEnabled();

        _disabled = true;

        emit FactoryDisabled();
    }

    function _ensureEnabled() internal view {
        if (isDisabled()) {
            revert Disabled();
        }
    }

    function _registerPoolWithFactory(address pool) internal virtual {
        _ensureEnabled();

        _isPoolFromFactory[pool] = true;
        _pools.push(pool);

        emit PoolCreated(pool);
    }

    /**
     * @dev Factories that require a custom-calculated salt can override to replace this default salt processing.
     * By default, the pool address determinants include the sender and chain id, as well as the user-provided salt,
     * so contracts will generally not have the same address on different L2s.
     */
    function _computeFinalSalt(bytes32 salt) internal view virtual returns (bytes32) {
        return keccak256(abi.encode(msg.sender, block.chainid, salt));
    }

    function _create(bytes memory constructorArgs, bytes32 salt) internal returns (address pool) {
        bytes32 finalSalt = _computeFinalSalt(salt);
        pool = _create2(constructorArgs, finalSalt);

        _registerPoolWithFactory(pool);
    }

    function _registerPoolWithVault(
        address pool,
        TokenConfig[] memory tokens,
        uint256 swapFeePercentage,
        bool protocolFeeExempt,
        PoolRoleAccounts memory roleAccounts,
        address poolHooksContract,
        LiquidityManagement memory liquidityManagement
    ) internal {
        getVault().registerPool(
            pool,
            tokens,
            swapFeePercentage,
            getNewPoolPauseWindowEndTime(),
            protocolFeeExempt,
            roleAccounts,
            poolHooksContract,
            liquidityManagement
        );
    }

    /// @notice A common place to retrieve a default hooks contract. Currently set to address(0) (i.e. no hooks).
    function getDefaultPoolHooksContract() public pure returns (address) {
        return address(0);
    }

    /**
     * @notice Convenience function for constructing a LiquidityManagement object.
     * @dev Users can call this to create a structure with all false arguments, then set the ones they need to true.
     * @return liquidityManagement Liquidity management flags, all initialized to false
     */
    function getDefaultLiquidityManagement() public pure returns (LiquidityManagement memory liquidityManagement) {
        // solhint-disable-previous-line no-empty-blocks
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

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

import { TokenInfo, PoolConfig } from "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol";
import { IPoolInfo } from "@balancer-labs/v3-interfaces/contracts/pool-utils/IPoolInfo.sol";
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";

/**
 * @notice Standard implementation of the `IPoolInfo` interface.
 * @dev Balancer standard pools inherit from this optional interface to provide a standard off-chain interface for
 * commonly requested data.
 */
contract PoolInfo is IPoolInfo {
    IVault private immutable _vault;

    constructor(IVault vault) {
        _vault = vault;
    }

    /// @inheritdoc IPoolInfo
    function getTokens() external view returns (IERC20[] memory tokens) {
        return _vault.getPoolTokens(address(this));
    }

    /// @inheritdoc IPoolInfo
    function getTokenInfo()
        external
        view
        returns (
            IERC20[] memory tokens,
            TokenInfo[] memory tokenInfo,
            uint256[] memory balancesRaw,
            uint256[] memory lastBalancesLiveScaled18
        )
    {
        return _vault.getPoolTokenInfo(address(this));
    }

    /// @inheritdoc IPoolInfo
    function getCurrentLiveBalances() external view returns (uint256[] memory balancesLiveScaled18) {
        return _vault.getCurrentLiveBalances(address(this));
    }

    /// @inheritdoc IPoolInfo
    function getStaticSwapFeePercentage() external view returns (uint256) {
        return _vault.getStaticSwapFeePercentage((address(this)));
    }

    /// @inheritdoc IPoolInfo
    function getAggregateFeePercentages()
        external
        view
        returns (uint256 aggregateSwapFeePercentage, uint256 aggregateYieldFeePercentage)
    {
        PoolConfig memory poolConfig = _vault.getPoolConfig(address(this));

        aggregateSwapFeePercentage = poolConfig.aggregateSwapFeePercentage;
        aggregateYieldFeePercentage = poolConfig.aggregateYieldFeePercentage;
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

import { IAuthentication } from "@balancer-labs/v3-interfaces/contracts/solidity-utils/helpers/IAuthentication.sol";

/**
 * @notice Building block for performing access control on external functions.
 * @dev This contract is used via the `authenticate` modifier (or the `_authenticateCaller` function), which can be
 * applied to external functions to make them only callable by authorized accounts.
 *
 * Derived contracts must implement the `_canPerform` function, which holds the actual access control logic.
 */
abstract contract Authentication is IAuthentication {
    bytes32 private immutable _actionIdDisambiguator;

    /**
     * @dev The main purpose of the `actionIdDisambiguator` is to prevent accidental function selector collisions in
     * multi-contract systems.
     *
     * There are two main uses for it:
     *  - if the contract is a singleton, any unique identifier can be used to make the associated action identifiers
     *    unique. The contract's own address is a good option.
     *  - if the contract belongs to a family that shares action identifiers for the same functions, an identifier
     *    shared by the entire family (and no other contract) should be used instead.
     */
    constructor(bytes32 actionIdDisambiguator) {
        _actionIdDisambiguator = actionIdDisambiguator;
    }

    /// @dev Reverts unless the caller is allowed to call this function. Should only be applied to external functions.
    modifier authenticate() {
        _authenticateCaller();
        _;
    }

    /// @dev Reverts unless the caller is allowed to call the entry point function.
    function _authenticateCaller() internal view {
        bytes32 actionId = getActionId(msg.sig);

        if (!_canPerform(actionId, msg.sender)) {
            revert SenderNotAllowed();
        }
    }

    /// @inheritdoc IAuthentication
    function getActionId(bytes4 selector) public view override returns (bytes32) {
        // Each external function is dynamically assigned an action identifier as the hash of the disambiguator and the
        // function selector. Disambiguation is necessary to avoid potential collisions in the function selectors of
        // multiple contracts.
        return keccak256(abi.encodePacked(_actionIdDisambiguator, selector));
    }

    /**
     * @dev Derived contracts must implement this function to perform the actual access control logic.
     * @param actionId The action identifier associated with an external function
     * @param user The account performing the action
     * @return success True if the action is permitted
     */
    function _canPerform(bytes32 actionId, address user) internal view virtual returns (bool);
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

import "./CodeDeployer.sol";
import { Create2 } from "@openzeppelin/contracts/utils/Create2.sol";

/**
 * @dev Base factory for contracts whose creation code is so large that the factory cannot hold it. This happens when
 * the contract's creation code grows close to 24kB.
 *
 * Note that this factory cannot help with contracts that have a *runtime* (deployed) bytecode larger than 24kB.
 */
contract BaseSplitCodeFactory {
    // The contract's creation code is stored as code in two separate addresses, and retrieved via `extcodecopy`. This
    // means this factory supports contracts with creation code of up to 48kB.
    // We rely on inline-assembly to achieve this, both to make the entire operation highly gas efficient, and because
    // `extcodecopy` is not available in Solidity.

    // solhint-disable no-inline-assembly

    address private immutable _creationCodeContractA;
    uint256 private immutable _creationCodeSizeA;

    address private immutable _creationCodeContractB;
    uint256 private immutable _creationCodeSizeB;

    /**
     * @dev The creation code of a contract Foo can be obtained inside Solidity with `type(Foo).creationCode`.
     */
    constructor(bytes memory creationCode) {
        uint256 creationCodeSize = creationCode.length;

        // We are going to deploy two contracts: one with approximately the first half of `creationCode`'s contents
        // (A), and another with the remaining half (B).
        // We store the lengths in both immutable and stack variables, since immutable variables cannot be read during
        // construction.
        uint256 creationCodeSizeA = creationCodeSize / 2;
        _creationCodeSizeA = creationCodeSizeA;

        uint256 creationCodeSizeB = creationCodeSize - creationCodeSizeA;
        _creationCodeSizeB = creationCodeSizeB;

        // To deploy the contracts, we're going to use `CodeDeployer.deploy()`, which expects a memory array with
        // the code to deploy. Note that we cannot simply create arrays for A and B's code by copying or moving
        // `creationCode`'s contents as they are expected to be very large (> 24kB), so we must operate in-place.

        // Memory: [ code length ] [ A.data ] [ B.data ]

        // Creating A's array is simple: we simply replace `creationCode`'s length with A's length. We'll later restore
        // the original length.

        bytes memory creationCodeA;
        assembly {
            creationCodeA := creationCode
            mstore(creationCodeA, creationCodeSizeA)
        }

        // Memory: [ A.length ] [ A.data ] [ B.data ]
        //         ^ creationCodeA

        // The first half is the beginning of the real contract (as opposed to the second half, which could be
        // anything), so we don't strictly need to protect the A contract. Fork tests should test both,
        // for completeness.
        bool preventExecution = false;

        _creationCodeContractA = CodeDeployer.deploy(creationCodeA, preventExecution);

        // Creating B's array is a bit more involved: since we cannot move B's contents, we are going to create a 'new'
        // memory array starting at A's last 32 bytes, which will be replaced with B's length. We'll back-up this last
        // byte to later restore it.

        bytes memory creationCodeB;
        bytes32 lastByteA;

        assembly {
            // `creationCode` points to the array's length, not data, so by adding A's length to it we arrive at A's
            // last 32 bytes.
            creationCodeB := add(creationCode, creationCodeSizeA)
            lastByteA := mload(creationCodeB)
            mstore(creationCodeB, creationCodeSizeB)
        }

        // Memory: [ A.length ] [ A.data[ : -1] ] [ B.length ][ B.data ]
        //         ^ creationCodeA                ^ creationCodeB

        // The code for contract B starts at a random point, and could accidentally contain valid opcodes.
        // The `preventExecution` flag prepends an invalid opcode to ensure the  "contract" cannot be accidentally
        // (or maliciously) executed. We need to remove this extra byte when reassembling the creation code.
        preventExecution = true;
        _creationCodeContractB = CodeDeployer.deploy(creationCodeB, preventExecution);

        // We now restore the original contents of `creationCode` by writing back the original length and A's last byte.
        assembly {
            mstore(creationCodeA, creationCodeSize)
            mstore(creationCodeB, lastByteA)
        }
    }

    /// @dev Returns the two addresses where the creation code of the contract created by this factory is stored.
    function getCreationCodeContracts() public view returns (address contractA, address contractB) {
        return (_creationCodeContractA, _creationCodeContractB);
    }

    /// @dev Returns the creation code of the contract this factory creates.
    function getCreationCode() public view returns (bytes memory) {
        return _getCreationCodeWithArgs("");
    }

    /**
     * @dev Returns the creation code that will result in a contract being deployed with `constructorArgs`.
     */
    function _getCreationCodeWithArgs(bytes memory constructorArgs) private view returns (bytes memory code) {
        // This function exists because `abi.encode()` cannot be instructed to place its result at a specific address.
        // We need for the ABI-encoded constructor arguments to be located immediately after the creation code, but
        // cannot rely on `abi.encodePacked()` to perform concatenation as that would involve copying the creation code,
        // which would be prohibitively expensive.
        // Instead, we compute the creation code in a pre-allocated array that is large enough to hold *both* the
        // creation code and the constructor arguments, and then copy the ABI-encoded arguments (which should not be
        // overly long) right after the end of the creation code.

        // Immutable variables cannot be used in assembly, so we store them in the stack first.
        address creationCodeContractA = _creationCodeContractA;
        uint256 creationCodeSizeA = _creationCodeSizeA;
        address creationCodeContractB = _creationCodeContractB;
        uint256 creationCodeSizeB = _creationCodeSizeB;

        uint256 creationCodeSize = creationCodeSizeA + creationCodeSizeB;
        uint256 constructorArgsSize = constructorArgs.length;

        uint256 codeSize = creationCodeSize + constructorArgsSize;

        assembly {
            // First, we allocate memory for `code` by retrieving the free memory pointer and then moving it ahead of
            // `code` by the size of the creation code plus constructor arguments, and 32 bytes for the array length.
            code := mload(0x40)
            mstore(0x40, add(code, add(codeSize, 32)))

            // We now store the length of the code plus constructor arguments.
            mstore(code, codeSize)

            // Next, we concatenate the creation code stored in A and B.
            let dataStart := add(code, 32)
            extcodecopy(creationCodeContractA, dataStart, 0, creationCodeSizeA)
            // Start at offset 1 in contract B, to skip the inserted invalid opcode.
            extcodecopy(creationCodeContractB, add(dataStart, creationCodeSizeA), 1, creationCodeSizeB)
        }

        // Finally, we copy the constructorArgs to the end of the array. Unfortunately there is no way to avoid this
        // copy, as it is not possible to tell Solidity where to store the result of `abi.encode()`.
        uint256 constructorArgsDataPtr;
        uint256 constructorArgsCodeDataPtr;
        assembly {
            constructorArgsDataPtr := add(constructorArgs, 32)
            constructorArgsCodeDataPtr := add(add(code, 32), creationCodeSize)
        }

        // solhint-disable-next-line no-inline-assembly
        assembly ("memory-safe") {
            mcopy(constructorArgsCodeDataPtr, constructorArgsDataPtr, constructorArgsSize)
        }
    }

    /**
     * @dev Deploys a contract with constructor arguments and a user-provided salt, using the create2 opcode.
     * To create `constructorArgs`, call `abi.encode()` with the contract's constructor arguments, in order.
     */
    function _create2(bytes memory constructorArgs, bytes32 salt) internal virtual returns (address) {
        bytes memory creationCode = _getCreationCodeWithArgs(constructorArgs);

        return Create2.deploy(0, salt, creationCode);
    }

    function _getDeploymentAddress(bytes memory constructorArgs, bytes32 salt) internal view returns (address) {
        bytes memory creationCode = _getCreationCodeWithArgs(constructorArgs);

        return Create2.computeAddress(salt, keccak256(creationCode));
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

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

/// @notice Library of helper functions related to typecasting arrays.
library CastingHelpers {
    /// @dev Returns a native array of addresses as an IERC20[] array.
    function asIERC20(address[] memory addresses) internal pure returns (IERC20[] memory tokens) {
        // solhint-disable-next-line no-inline-assembly
        assembly ("memory-safe") {
            tokens := addresses
        }
    }

    /// @dev Returns an IERC20[] array as an address[] array.
    function asAddress(IERC20[] memory tokens) internal pure returns (address[] memory addresses) {
        // solhint-disable-next-line no-inline-assembly
        assembly ("memory-safe") {
            addresses := tokens
        }
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

/**
 * @dev Library used to deploy contracts with specific code. This can be used for long-term storage of immutable data as
 * contract code, which can be retrieved via the `extcodecopy` opcode.
 */
library CodeDeployer {
    error CodeDeploymentFailed();

    // During contract construction, the full code supplied exists as code, and can be accessed via `codesize` and
    // `codecopy`. This is not the contract's final code however: whatever the constructor returns is what will be
    // stored as its code.
    //
    // We use this mechanism to have a simple constructor that stores whatever is appended to it. The following opcode
    // sequence corresponds to the creation code of the following equivalent Solidity contract, plus padding to make the
    // full code 32 bytes long:
    //
    // contract CodeDeployer {
    //     constructor() payable {
    //         uint256 size;
    //         assembly {
    //             size := sub(codesize(), 32) // size of appended data, as constructor is 32 bytes long
    //             codecopy(0, 32, size) // copy all appended data to memory at position 0
    //             return(0, size) // return appended data for it to be stored as code
    //         }
    //     }
    // }
    //
    // More specifically, it is composed of the following opcodes (plus padding):
    //
    // [1] PUSH1 0x20
    // [2] CODESIZE
    // [3] SUB
    // [4] DUP1
    // [6] PUSH1 0x20
    // [8] PUSH1 0x00
    // [9] CODECOPY
    // [11] PUSH1 0x00
    // [12] RETURN
    //
    // The padding is just the 0xfe sequence (invalid opcode). It is important as it lets us work in-place, avoiding
    // memory allocation and copying.

    bytes32 private constant _DEPLOYER_CREATION_CODE =
        0x602038038060206000396000f3fefefefefefefefefefefefefefefefefefefe;

    // Sometimes (e.g., when deploying the second or "B" half of the creation code in BaseSplitCodeFactory), we need to
    // protect the bare contract from being accidentally (or maliciously) executed, in case the bytes at the boundary
    // happen to be valid opcodes. It's especially dangerous if the bytes contained the selfdestruct opcode, which would
    // destroy the contract (and, if it's a factory, effectively disable it and prevent further pool creation).
    //
    // To guard against this, if the "preventExecution" flag is set, we prepend an invalid opcode to the contract,
    // to ensure that it cannot be executed, regardless of its content.
    //
    // This corresponds to the following contract:
    //
    // contract CodeDeployer {
    //     constructor() payable {
    //         uint256 size;
    //         assembly {
    //             mstore8(0, 0xfe) // store invalid opcode at position 0
    //             size := sub(codesize(), 32) // size of appended data, as constructor is 32 bytes long
    //             codecopy(1, 32, size) // copy all appended data to memory at position 1
    //             return(0, add(size, 1)) // return appended data (plus the extra byte) for it to be stored as code
    //         }
    //     }
    // }
    //
    // More specifically, it is composed of the following opcodes (plus padding, described above):
    //
    // [1] PUSH1 0xfe
    // [3] PUSH1 0x00
    // [4] MSTORE8
    // [6] PUSH1 0x20
    // [7] CODESIZE
    // [8] SUB
    // [9] DUP1
    // [11] PUSH1 0x20
    // [13] PUSH1 0x01
    // [14] CODECOPY
    // [16] PUSH1 0x01
    // [17] ADD
    // [19] PUSH1 0x00
    // [20] RETURN

    // solhint-disable max-line-length
    bytes32 private constant _PROTECTED_DEPLOYER_CREATION_CODE =
        0x60fe600053602038038060206001396001016000f3fefefefefefefefefefefe;

    /**
     * @dev Deploys a contract with `code` as its code, returning the destination address.
     * If preventExecution is set, prepend an invalid opcode to ensure the "contract" cannot be executed.
     * Rather than add a flag, we could simply always prepend the opcode, but there might be use cases where fidelity
     * is required.
     *
     * Reverts if deployment fails.
     */
    function deploy(bytes memory code, bool preventExecution) internal returns (address destination) {
        bytes32 deployerCreationCode = preventExecution ? _PROTECTED_DEPLOYER_CREATION_CODE : _DEPLOYER_CREATION_CODE;

        // We need to concatenate the deployer creation code and `code` in memory, but want to avoid copying all of
        // `code` (which could be quite long) into a new memory location. Therefore, we operate in-place using
        // assembly.

        // solhint-disable-next-line no-inline-assembly
        assembly {
            let codeLength := mload(code)

            // `code` is composed of length and data. We've already stored its length in `codeLength`, so we simply
            // replace it with the deployer creation code (which is exactly 32 bytes long).
            mstore(code, deployerCreationCode)

            // At this point, `code` now points to the deployer creation code immediately followed by `code`'s data
            // contents. This is exactly what the deployer expects to receive when created.
            destination := create(0, code, add(codeLength, 32))

            // Finally, we restore the original length in order to not mutate `code`.
            mstore(code, codeLength)
        }

        // The create opcode returns the zero address when contract creation fails, so we revert if this happens.
        if (destination == address(0)) {
            revert CodeDeploymentFailed();
        }
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

/**
 * @notice Base contract for v3 factories to support pause windows for pools based on the factory deployment time.
 * @dev Each pool deployment calls `getPauseWindowDuration` on the factory so that all Pools created by this factory
 * will share the same Pause Window end time, after which both old and new Pools will not be pausable.
 *
 * All pools are reversibly pausable until the pause window expires. Afterward, there is an additional buffer
 * period, set to the same duration as the Vault's buffer period. If a pool was paused, it will remain paused
 * through this buffer period, and cannot be unpaused.
 *
 * When the buffer period expires, it will unpause automatically, and remain permissionless forever after.
 */
contract FactoryWidePauseWindow {
    // This contract relies on timestamps - the usual caveats apply.
    // solhint-disable not-rely-on-time

    // The pause window end time is stored in 32 bits.
    uint32 private constant _MAX_TIMESTAMP = type(uint32).max;

    uint32 private immutable _pauseWindowDuration;

    // Time when the pause window for all created Pools expires.
    uint32 private immutable _poolsPauseWindowEndTime;

    /// @notice The factory deployer gave a duration that would overflow the Unix timestamp.
    error PoolPauseWindowDurationOverflow();

    constructor(uint32 pauseWindowDuration) {
        uint256 pauseWindowEndTime = block.timestamp + pauseWindowDuration;

        if (pauseWindowEndTime > _MAX_TIMESTAMP) {
            revert PoolPauseWindowDurationOverflow();
        }

        _pauseWindowDuration = pauseWindowDuration;

        // Direct cast is safe, as it was checked above.
        _poolsPauseWindowEndTime = uint32(pauseWindowEndTime);
    }

    /**
     * @notice Return the pause window duration. This is the time pools will be pausable after factory deployment.
     * @return pauseWindowDuration The duration in seconds
     */
    function getPauseWindowDuration() external view returns (uint32) {
        return _pauseWindowDuration;
    }

    /**
     * @notice Returns the original factory pauseWindowEndTime, regardless of the current time.
     * @return pauseWindowEndTime The end time as a timestamp
     */
    function getOriginalPauseWindowEndTime() external view returns (uint32) {
        return _poolsPauseWindowEndTime;
    }

    /**
     * @notice Returns the current pauseWindowEndTime that will be applied to Pools created by this factory.
     * @dev We intend for all pools deployed by this factory to have the same pause window end time (i.e., after
     * this date, all future pools will be unpausable). This function will return `_poolsPauseWindowEndTime`
     * until it passes, after which it will return 0.
     *
     * @return pauseWindowEndTime The resolved pause window end time (0 indicating it's no longer pausable)
     */
    function getNewPoolPauseWindowEndTime() public view returns (uint32) {
        // We know _poolsPauseWindowEndTime <= _MAX_TIMESTAMP (checked above).
        // Do not truncate timestamp; it should still return 0 after _MAX_TIMESTAMP.
        return (block.timestamp < _poolsPauseWindowEndTime) ? _poolsPauseWindowEndTime : 0;
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

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

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

library InputHelpers {
    /// @notice Arrays passed to a function and intended to be parallel have different lengths.
    error InputLengthMismatch();

    /**
     * @notice More than one non-zero value was given for a single token operation.
     * @dev Input arrays for single token add/remove liquidity operations are expected to have only one non-zero value,
     * corresponding to the token being added or removed. This error results if there are multiple non-zero entries.
     */
    error MultipleNonZeroInputs();

    /**
     * @notice No valid input was given for a single token operation.
     * @dev Input arrays for single token add/remove liquidity operations are expected to have one non-zero value,
     * corresponding to the token being added or removed. This error results if all entries are zero.
     */
    error AllZeroInputs();

    /**
     * @notice The tokens supplied to an array argument were not sorted in numerical order.
     * @dev Tokens are not sorted by address on registration. This is an optimization so that off-chain processes can
     * predict the token order without having to query the Vault. (It is also legacy v2 behavior.)
     */
    error TokensNotSorted();

    function ensureInputLengthMatch(uint256 a, uint256 b) internal pure {
        if (a != b) {
            revert InputLengthMismatch();
        }
    }

    function ensureInputLengthMatch(uint256 a, uint256 b, uint256 c) internal pure {
        if (a != b || b != c) {
            revert InputLengthMismatch();
        }
    }

    // Find the single non-zero input; revert if there is not exactly one such value.
    function getSingleInputIndex(uint256[] memory maxAmountsIn) internal pure returns (uint256 inputIndex) {
        uint256 length = maxAmountsIn.length;
        inputIndex = length;

        for (uint256 i = 0; i < length; ++i) {
            if (maxAmountsIn[i] != 0) {
                if (inputIndex != length) {
                    revert MultipleNonZeroInputs();
                }
                inputIndex = i;
            }
        }

        if (inputIndex >= length) {
            revert AllZeroInputs();
        }

        return inputIndex;
    }

    /**
     * @dev Sort an array of tokens, mutating in place (and also returning them).
     * This assumes the tokens have been (or will be) validated elsewhere for length
     * and non-duplication. All this does is the sorting.
     *
     * A bubble sort should be gas- and bytecode-efficient enough for such small arrays.
     * Could have also done "manual" comparisons for each of the cases, but this is
     * about the same number of operations, and more concise.
     *
     * This is less efficient for larger token count (i.e., above 4), but such pools should
     * be rare. And in any case, sorting is only done on-chain in test code.
     */
    function sortTokens(IERC20[] memory tokens) internal pure returns (IERC20[] memory) {
        for (uint256 i = 0; i < tokens.length - 1; ++i) {
            for (uint256 j = 0; j < tokens.length - i - 1; ++j) {
                if (tokens[j] > tokens[j + 1]) {
                    // Swap if they're out of order.
                    (tokens[j], tokens[j + 1]) = (tokens[j + 1], tokens[j]);
                }
            }
        }

        return tokens;
    }

    /// @dev Ensure an array of tokens is sorted. As above, does not validate length or uniqueness.
    function ensureSortedTokens(IERC20[] memory tokens) internal pure {
        if (tokens.length < 2) {
            return;
        }

        IERC20 previous = tokens[0];

        for (uint256 i = 1; i < tokens.length; ++i) {
            IERC20 current = tokens[i];

            if (previous > current) {
                revert TokensNotSorted();
            }

            previous = current;
        }
    }

    /// @dev Ensure an array of amounts is sorted. As above, does not validate length or uniqueness.
    function ensureSortedAmounts(uint256[] memory amounts) internal pure {
        if (amounts.length < 2) {
            return;
        }

        uint256 previous = amounts[0];

        for (uint256 i = 1; i < amounts.length; ++i) {
            uint256 current = amounts[i];

            if (previous > current) {
                revert TokensNotSorted();
            }

            previous = current;
        }
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

import { IVersion } from "@balancer-labs/v3-interfaces/contracts/solidity-utils/helpers/IVersion.sol";

/**
 * @notice Retrieves a contract's version from storage.
 * @dev The version is set at deployment time and cannot be changed. It would be immutable, but immutable strings
 * are not yet supported.
 *
 * Contracts like factories and pools should have versions. These typically take the form of JSON strings containing
 * detailed information about the deployment. For instance:
 *
 * `{name: 'ChildChainGaugeFactory', version: 2, deployment: '20230316-child-chain-gauge-factory-v2'}`
 */
contract Version is IVersion {
    string private _version;

    constructor(string memory version_) {
        _setVersion(version_);
    }

    /**
     * @notice Getter for the version.
     * @return version The stored contract version
     */
    function version() external view returns (string memory) {
        return _version;
    }

    /// @dev Internal setter that allows this contract to be used in proxies.
    function _setVersion(string memory newVersion) internal {
        _version = newVersion;
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

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

/// @notice Support 18-decimal fixed point arithmetic. All Vault calculations use this for high and uniform precision.
library FixedPoint {
    /// @notice Attempted division by zero.
    error ZeroDivision();

    // solhint-disable no-inline-assembly
    // solhint-disable private-vars-leading-underscore

    uint256 internal constant ONE = 1e18; // 18 decimal places
    uint256 internal constant TWO = 2 * ONE;
    uint256 internal constant FOUR = 4 * ONE;
    uint256 internal constant MAX_POW_RELATIVE_ERROR = 10000; // 10^(-14)

    function mulDown(uint256 a, uint256 b) internal pure returns (uint256) {
        // Multiplication overflow protection is provided by Solidity 0.8.x.
        uint256 product = a * b;

        return product / ONE;
    }

    function mulUp(uint256 a, uint256 b) internal pure returns (uint256 result) {
        // Multiplication overflow protection is provided by Solidity 0.8.x.
        uint256 product = a * b;

        // Equivalent to:
        // result = product == 0 ? 0 : ((product - 1) / FixedPoint.ONE) + 1
        assembly ("memory-safe") {
            result := mul(iszero(iszero(product)), add(div(sub(product, 1), ONE), 1))
        }
    }

    function divDown(uint256 a, uint256 b) internal pure returns (uint256) {
        // Solidity 0.8 reverts with a Panic code (0x11) if the multiplication overflows.
        uint256 aInflated = a * ONE;

        // Solidity 0.8 reverts with a "Division by Zero" Panic code (0x12) if b is zero
        return aInflated / b;
    }

    function divUp(uint256 a, uint256 b) internal pure returns (uint256 result) {
        return mulDivUp(a, ONE, b);
    }

    /// @dev Return (a * b) / c, rounding up.
    function mulDivUp(uint256 a, uint256 b, uint256 c) internal pure returns (uint256 result) {
        // This check is required because Yul's `div` doesn't revert on c==0.
        if (c == 0) {
            revert ZeroDivision();
        }

        // Multiple overflow protection is done by Solidity 0.8.x.
        uint256 product = a * b;

        // The traditional divUp formula is:
        // divUp(x, y) := (x + y - 1) / y
        // To avoid intermediate overflow in the addition, we distribute the division and get:
        // divUp(x, y) := (x - 1) / y + 1
        // Note that this requires x != 0, if x == 0 then the result is zero
        //
        // Equivalent to:
        // result = a == 0 ? 0 : (a * b - 1) / c + 1
        assembly ("memory-safe") {
            result := mul(iszero(iszero(product)), add(div(sub(product, 1), c), 1))
        }
    }

    /**
     * @dev Version of divUp when the input is raw (i.e., already "inflated"). For instance,
     * invariant * invariant (36 decimals) vs. invariant.mulDown(invariant) (18 decimal FP).
     * This can occur in calculations with many successive multiplications and divisions, and
     * we want to minimize the number of operations by avoiding unnecessary scaling by ONE.
     */
    function divUpRaw(uint256 a, uint256 b) internal pure returns (uint256 result) {
        // This check is required because Yul's `div` doesn't revert on b==0.
        if (b == 0) {
            revert ZeroDivision();
        }

        // Equivalent to:
        // result = a == 0 ? 0 : 1 + (a - 1) / b
        assembly ("memory-safe") {
            result := mul(iszero(iszero(a)), add(1, div(sub(a, 1), b)))
        }
    }

    /**
     * @dev Returns x^y, assuming both are fixed point numbers, rounding down. The result is guaranteed to not be above
     * the true value (that is, the error function expected - actual is always positive).
     */
    function powDown(uint256 x, uint256 y) internal pure returns (uint256) {
        // Optimize for when y equals 1.0, 2.0 or 4.0, as those are very simple to implement and occur often in 50/50
        // and 80/20 Weighted Pools
        if (y == ONE) {
            return x;
        } else if (y == TWO) {
            return mulDown(x, x);
        } else if (y == FOUR) {
            uint256 square = mulDown(x, x);
            return mulDown(square, square);
        } else {
            uint256 raw = LogExpMath.pow(x, y);
            uint256 maxError = mulUp(raw, MAX_POW_RELATIVE_ERROR) + 1;

            if (raw < maxError) {
                return 0;
            } else {
                unchecked {
                    return raw - maxError;
                }
            }
        }
    }

    /**
     * @dev Returns x^y, assuming both are fixed point numbers, rounding up. The result is guaranteed to not be below
     * the true value (that is, the error function expected - actual is always negative).
     */
    function powUp(uint256 x, uint256 y) internal pure returns (uint256) {
        // Optimize for when y equals 1.0, 2.0 or 4.0, as those are very simple to implement and occur often in 50/50
        // and 80/20 Weighted Pools
        if (y == ONE) {
            return x;
        } else if (y == TWO) {
            return mulUp(x, x);
        } else if (y == FOUR) {
            uint256 square = mulUp(x, x);
            return mulUp(square, square);
        } else {
            uint256 raw = LogExpMath.pow(x, y);
            uint256 maxError = mulUp(raw, MAX_POW_RELATIVE_ERROR) + 1;

            return raw + maxError;
        }
    }

    /**
     * @dev Returns the complement of a value (1 - x), capped to 0 if x is larger than 1.
     *
     * Useful when computing the complement for values with some level of relative error, as it strips this error and
     * prevents intermediate negative values.
     */
    function complement(uint256 x) internal pure returns (uint256 result) {
        // Equivalent to:
        // result = (x < ONE) ? (ONE - x) : 0
        assembly ("memory-safe") {
            result := mul(lt(x, ONE), sub(ONE, x))
        }
    }
}

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.24;

// solhint-disable

/**
 * @dev Exponentiation and logarithm functions for 18 decimal fixed point numbers (both base and exponent/argument).
 *
 * Exponentiation and logarithm with arbitrary bases (x^y and log_x(y)) are implemented by conversion to natural
 * exponentiation and logarithm (where the base is Euler's number).
 *
 * All math operations are unchecked in order to save gas.
 *
 * @author Fernando Martinelli - @fernandomartinelli
 * @author Sergio Yuhjtman     - @sergioyuhjtman
 * @author Daniel Fernandez    - @dmf7z
 */
library LogExpMath {
    /// @notice This error is thrown when a base is not within an acceptable range.
    error BaseOutOfBounds();

    /// @notice This error is thrown when a exponent is not within an acceptable range.
    error ExponentOutOfBounds();

    /// @notice This error is thrown when the exponent * ln(base) is not within an acceptable range.
    error ProductOutOfBounds();

    /// @notice This error is thrown when an exponent used in the exp function is not within an acceptable range.
    error InvalidExponent();

    /// @notice This error is thrown when a variable or result is not within the acceptable bounds defined in the function.
    error OutOfBounds();

    // All fixed point multiplications and divisions are inlined. This means we need to divide by ONE when multiplying
    // two numbers, and multiply by ONE when dividing them.

    // All arguments and return values are 18 decimal fixed point numbers.
    int256 constant ONE_18 = 1e18;

    // Internally, intermediate values are computed with higher precision as 20 decimal fixed point numbers, and in the
    // case of ln36, 36 decimals.
    int256 constant ONE_20 = 1e20;
    int256 constant ONE_36 = 1e36;

    // The domain of natural exponentiation is bound by the word size and number of decimals used.
    //
    // Because internally the result will be stored using 20 decimals, the largest possible result is
    // (2^255 - 1) / 10^20, which makes the largest exponent ln((2^255 - 1) / 10^20) = 130.700829182905140221.
    // The smallest possible result is 10^(-18), which makes largest negative argument
    // ln(10^(-18)) = -41.446531673892822312.
    // We use 130.0 and -41.0 to have some safety margin.
    int256 constant MAX_NATURAL_EXPONENT = 130e18;
    int256 constant MIN_NATURAL_EXPONENT = -41e18;

    // Bounds for ln_36's argument. Both ln(0.9) and ln(1.1) can be represented with 36 decimal places in a fixed point
    // 256 bit integer.
    int256 constant LN_36_LOWER_BOUND = ONE_18 - 1e17;
    int256 constant LN_36_UPPER_BOUND = ONE_18 + 1e17;

    uint256 constant MILD_EXPONENT_BOUND = 2 ** 254 / uint256(ONE_20);

    // 18 decimal constants
    int256 constant x0 = 128000000000000000000; // 2ˆ7
    int256 constant a0 = 38877084059945950922200000000000000000000000000000000000; // eˆ(x0) (no decimals)
    int256 constant x1 = 64000000000000000000; // 2ˆ6
    int256 constant a1 = 6235149080811616882910000000; // eˆ(x1) (no decimals)

    // 20 decimal constants
    int256 constant x2 = 3200000000000000000000; // 2ˆ5
    int256 constant a2 = 7896296018268069516100000000000000; // eˆ(x2)
    int256 constant x3 = 1600000000000000000000; // 2ˆ4
    int256 constant a3 = 888611052050787263676000000; // eˆ(x3)
    int256 constant x4 = 800000000000000000000; // 2ˆ3
    int256 constant a4 = 298095798704172827474000; // eˆ(x4)
    int256 constant x5 = 400000000000000000000; // 2ˆ2
    int256 constant a5 = 5459815003314423907810; // eˆ(x5)
    int256 constant x6 = 200000000000000000000; // 2ˆ1
    int256 constant a6 = 738905609893065022723; // eˆ(x6)
    int256 constant x7 = 100000000000000000000; // 2ˆ0
    int256 constant a7 = 271828182845904523536; // eˆ(x7)
    int256 constant x8 = 50000000000000000000; // 2ˆ-1
    int256 constant a8 = 164872127070012814685; // eˆ(x8)
    int256 constant x9 = 25000000000000000000; // 2ˆ-2
    int256 constant a9 = 128402541668774148407; // eˆ(x9)
    int256 constant x10 = 12500000000000000000; // 2ˆ-3
    int256 constant a10 = 113314845306682631683; // eˆ(x10)
    int256 constant x11 = 6250000000000000000; // 2ˆ-4
    int256 constant a11 = 106449445891785942956; // eˆ(x11)

    /**
     * @dev Exponentiation (x^y) with unsigned 18 decimal fixed point base and exponent.
     *
     * Reverts if ln(x) * y is smaller than `MIN_NATURAL_EXPONENT`, or larger than `MAX_NATURAL_EXPONENT`.
     */
    function pow(uint256 x, uint256 y) internal pure returns (uint256) {
        if (y == 0) {
            // We solve the 0^0 indetermination by making it equal one.
            return uint256(ONE_18);
        }

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

        // Instead of computing x^y directly, we instead rely on the properties of logarithms and exponentiation to
        // arrive at that result. In particular, exp(ln(x)) = x, and ln(x^y) = y * ln(x). This means
        // x^y = exp(y * ln(x)).

        // The ln function takes a signed value, so we need to make sure x fits in the signed 256 bit range.
        if (x >> 255 != 0) {
            revert BaseOutOfBounds();
        }
        int256 x_int256 = int256(x);

        // We will compute y * ln(x) in a single step. Depending on the value of x, we can either use ln or ln_36. In
        // both cases, we leave the division by ONE_18 (due to fixed point multiplication) to the end.

        // This prevents y * ln(x) from overflowing, and at the same time guarantees y fits in the signed 256 bit range.
        if (y >= MILD_EXPONENT_BOUND) {
            revert ExponentOutOfBounds();
        }
        int256 y_int256 = int256(y);

        int256 logx_times_y;
        unchecked {
            if (LN_36_LOWER_BOUND < x_int256 && x_int256 < LN_36_UPPER_BOUND) {
                int256 ln_36_x = _ln_36(x_int256);

                // ln_36_x has 36 decimal places, so multiplying by y_int256 isn't as straightforward, since we can't just
                // bring y_int256 to 36 decimal places, as it might overflow. Instead, we perform two 18 decimal
                // multiplications and add the results: one with the first 18 decimals of ln_36_x, and one with the
                // (downscaled) last 18 decimals.
                logx_times_y = ((ln_36_x / ONE_18) * y_int256 + ((ln_36_x % ONE_18) * y_int256) / ONE_18);
            } else {
                logx_times_y = _ln(x_int256) * y_int256;
            }
            logx_times_y /= ONE_18;
        }

        // Finally, we compute exp(y * ln(x)) to arrive at x^y
        if (!(MIN_NATURAL_EXPONENT <= logx_times_y && logx_times_y <= MAX_NATURAL_EXPONENT)) {
            revert ProductOutOfBounds();
        }

        return uint256(exp(logx_times_y));
    }

    /**
     * @dev Natural exponentiation (e^x) with signed 18 decimal fixed point exponent.
     *
     * Reverts if `x` is smaller than MIN_NATURAL_EXPONENT, or larger than `MAX_NATURAL_EXPONENT`.
     */
    function exp(int256 x) internal pure returns (int256) {
        if (!(x >= MIN_NATURAL_EXPONENT && x <= MAX_NATURAL_EXPONENT)) {
            revert InvalidExponent();
        }

        // We avoid using recursion here because zkSync doesn't support it.
        bool negativeExponent = false;

        if (x < 0) {
            // We only handle positive exponents: e^(-x) is computed as 1 / e^x. We can safely make x positive since it
            // fits in the signed 256 bit range (as it is larger than MIN_NATURAL_EXPONENT). In the negative
            // exponent case, compute e^x, then return 1 / result.
            unchecked {
                x = -x;
            }
            negativeExponent = true;
        }

        // First, we use the fact that e^(x+y) = e^x * e^y to decompose x into a sum of powers of two, which we call x_n,
        // where x_n == 2^(7 - n), and e^x_n = a_n has been precomputed. We choose the first x_n, x0, to equal 2^7
        // because all larger powers are larger than MAX_NATURAL_EXPONENT, and therefore not present in the
        // decomposition.
        // At the end of this process we will have the product of all e^x_n = a_n that apply, and the remainder of this
        // decomposition, which will be lower than the smallest x_n.
        // exp(x) = k_0 * a_0 * k_1 * a_1 * ... + k_n * a_n * exp(remainder), where each k_n equals either 0 or 1.
        // We mutate x by subtracting x_n, making it the remainder of the decomposition.

        // The first two a_n (e^(2^7) and e^(2^6)) are too large if stored as 18 decimal numbers, and could cause
        // intermediate overflows. Instead we store them as plain integers, with 0 decimals.
        // Additionally, x0 + x1 is larger than MAX_NATURAL_EXPONENT, which means they will not both be present in the
        // decomposition.

        // For each x_n, we test if that term is present in the decomposition (if x is larger than it), and if so deduct
        // it and compute the accumulated product.

        int256 firstAN;
        unchecked {
            if (x >= x0) {
                x -= x0;
                firstAN = a0;
            } else if (x >= x1) {
                x -= x1;
                firstAN = a1;
            } else {
                firstAN = 1; // One with no decimal places
            }

            // We now transform x into a 20 decimal fixed point number, to have enhanced precision when computing the
            // smaller terms.
            x *= 100;
        }

        // `product` is the accumulated product of all a_n (except a0 and a1), which starts at 20 decimal fixed point
        // one. Recall that fixed point multiplication requires dividing by ONE_20.
        int256 product = ONE_20;

        unchecked {
            if (x >= x2) {
                x -= x2;
                product = (product * a2) / ONE_20;
            }
            if (x >= x3) {
                x -= x3;
                product = (product * a3) / ONE_20;
            }
            if (x >= x4) {
                x -= x4;
                product = (product * a4) / ONE_20;
            }
            if (x >= x5) {
                x -= x5;
                product = (product * a5) / ONE_20;
            }
            if (x >= x6) {
                x -= x6;
                product = (product * a6) / ONE_20;
            }
            if (x >= x7) {
                x -= x7;
                product = (product * a7) / ONE_20;
            }
            if (x >= x8) {
                x -= x8;
                product = (product * a8) / ONE_20;
            }
            if (x >= x9) {
                x -= x9;
                product = (product * a9) / ONE_20;
            }
        }

        // x10 and x11 are unnecessary here since we have high enough precision already.

        // Now we need to compute e^x, where x is small (in particular, it is smaller than x9). We use the Taylor series
        // expansion for e^x: 1 + x + (x^2 / 2!) + (x^3 / 3!) + ... + (x^n / n!).

        int256 seriesSum = ONE_20; // The initial one in the sum, with 20 decimal places.
        int256 term; // Each term in the sum, where the nth term is (x^n / n!).

        // The first term is simply x.
        term = x;
        unchecked {
            seriesSum += term;

            // Each term (x^n / n!) equals the previous one times x, divided by n. Since x is a fixed point number,
            // multiplying by it requires dividing by ONE_20, but dividing by the non-fixed point n values does not.

            term = ((term * x) / ONE_20) / 2;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 3;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 4;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 5;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 6;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 7;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 8;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 9;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 10;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 11;
            seriesSum += term;

            term = ((term * x) / ONE_20) / 12;
            seriesSum += term;

            // 12 Taylor terms are sufficient for 18 decimal precision.

            // We now have the first a_n (with no decimals), and the product of all other a_n present, and the Taylor
            // approximation of the exponentiation of the remainder (both with 20 decimals). All that remains is to multiply
            // all three (one 20 decimal fixed point multiplication, dividing by ONE_20, and one integer multiplication),
            // and then drop two digits to return an 18 decimal value.

            int256 result = (((product * seriesSum) / ONE_20) * firstAN) / 100;

            // We avoid using recursion here because zkSync doesn't support it.
            return negativeExponent ? (ONE_18 * ONE_18) / result : result;
        }
    }

    /// @dev Logarithm (log(arg, base), with signed 18 decimal fixed point base and argument.
    function log(int256 arg, int256 base) internal pure returns (int256) {
        // This performs a simple base change: log(arg, base) = ln(arg) / ln(base).

        // Both logBase and logArg are computed as 36 decimal fixed point numbers, either by using ln_36, or by
        // upscaling.

        int256 logBase;
        unchecked {
            if (LN_36_LOWER_BOUND < base && base < LN_36_UPPER_BOUND) {
                logBase = _ln_36(base);
            } else {
                logBase = _ln(base) * ONE_18;
            }
        }

        int256 logArg;
        unchecked {
            if (LN_36_LOWER_BOUND < arg && arg < LN_36_UPPER_BOUND) {
                logArg = _ln_36(arg);
            } else {
                logArg = _ln(arg) * ONE_18;
            }

            // When dividing, we multiply by ONE_18 to arrive at a result with 18 decimal places
            return (logArg * ONE_18) / logBase;
        }
    }

    /// @dev Natural logarithm (ln(a)) with signed 18 decimal fixed point argument.
    function ln(int256 a) internal pure returns (int256) {
        // The real natural logarithm is not defined for negative numbers or zero.
        if (a <= 0) {
            revert OutOfBounds();
        }
        if (LN_36_LOWER_BOUND < a && a < LN_36_UPPER_BOUND) {
            unchecked {
                return _ln_36(a) / ONE_18;
            }
        } else {
            return _ln(a);
        }
    }

    /// @dev Internal natural logarithm (ln(a)) with signed 18 decimal fixed point argument.
    function _ln(int256 a) private pure returns (int256) {
        // We avoid using recursion here because zkSync doesn't support it.
        bool negativeExponent = false;

        if (a < ONE_18) {
            // Since ln(a^k) = k * ln(a), we can compute ln(a) as ln(a) = ln((1/a)^(-1)) = - ln((1/a)). If a is less
            // than one, 1/a will be greater than one, so in this case we compute ln(1/a) and negate the final result.
            unchecked {
                a = (ONE_18 * ONE_18) / a;
            }
            negativeExponent = true;
        }

        // First, we use the fact that ln^(a * b) = ln(a) + ln(b) to decompose ln(a) into a sum of powers of two, which
        // we call x_n, where x_n == 2^(7 - n), which are the natural logarithm of precomputed quantities a_n (that is,
        // ln(a_n) = x_n). We choose the first x_n, x0, to equal 2^7 because the exponential of all larger powers cannot
        // be represented as 18 fixed point decimal numbers in 256 bits, and are therefore larger than a.
        // At the end of this process we will have the sum of all x_n = ln(a_n) that apply, and the remainder of this
        // decomposition, which will be lower than the smallest a_n.
        // ln(a) = k_0 * x_0 + k_1 * x_1 + ... + k_n * x_n + ln(remainder), where each k_n equals either 0 or 1.
        // We mutate a by subtracting a_n, making it the remainder of the decomposition.

        // For reasons related to how `exp` works, the first two a_n (e^(2^7) and e^(2^6)) are not stored as fixed point
        // numbers with 18 decimals, but instead as plain integers with 0 decimals, so we need to multiply them by
        // ONE_18 to convert them to fixed point.
        // For each a_n, we test if that term is present in the decomposition (if a is larger than it), and if so divide
        // by it and compute the accumulated sum.

        int256 sum = 0;
        unchecked {
            if (a >= a0 * ONE_18) {
                a /= a0; // Integer, not fixed point division
                sum += x0;
            }

            if (a >= a1 * ONE_18) {
                a /= a1; // Integer, not fixed point division
                sum += x1;
            }

            // All other a_n and x_n are stored as 20 digit fixed point numbers, so we convert the sum and a to this format.
            sum *= 100;
            a *= 100;

            // Because further a_n are  20 digit fixed point numbers, we multiply by ONE_20 when dividing by them.

            if (a >= a2) {
                a = (a * ONE_20) / a2;
                sum += x2;
            }

            if (a >= a3) {
                a = (a * ONE_20) / a3;
                sum += x3;
            }

            if (a >= a4) {
                a = (a * ONE_20) / a4;
                sum += x4;
            }

            if (a >= a5) {
                a = (a * ONE_20) / a5;
                sum += x5;
            }

            if (a >= a6) {
                a = (a * ONE_20) / a6;
                sum += x6;
            }

            if (a >= a7) {
                a = (a * ONE_20) / a7;
                sum += x7;
            }

            if (a >= a8) {
                a = (a * ONE_20) / a8;
                sum += x8;
            }

            if (a >= a9) {
                a = (a * ONE_20) / a9;
                sum += x9;
            }

            if (a >= a10) {
                a = (a * ONE_20) / a10;
                sum += x10;
            }

            if (a >= a11) {
                a = (a * ONE_20) / a11;
                sum += x11;
            }
        }

        // a is now a small number (smaller than a_11, which roughly equals 1.06). This means we can use a Taylor series
        // that converges rapidly for values of `a` close to one - the same one used in ln_36.
        // Let z = (a - 1) / (a + 1).
        // ln(a) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))

        // Recall that 20 digit fixed point division requires multiplying by ONE_20, and multiplication requires
        // division by ONE_20.
        unchecked {
            int256 z = ((a - ONE_20) * ONE_20) / (a + ONE_20);
            int256 z_squared = (z * z) / ONE_20;

            // num is the numerator of the series: the z^(2 * n + 1) term
            int256 num = z;

            // seriesSum holds the accumulated sum of each term in the series, starting with the initial z
            int256 seriesSum = num;

            // In each step, the numerator is multiplied by z^2
            num = (num * z_squared) / ONE_20;
            seriesSum += num / 3;

            num = (num * z_squared) / ONE_20;
            seriesSum += num / 5;

            num = (num * z_squared) / ONE_20;
            seriesSum += num / 7;

            num = (num * z_squared) / ONE_20;
            seriesSum += num / 9;

            num = (num * z_squared) / ONE_20;
            seriesSum += num / 11;

            // 6 Taylor terms are sufficient for 36 decimal precision.

            // Finally, we multiply by 2 (non fixed point) to compute ln(remainder)
            seriesSum *= 2;

            // We now have the sum of all x_n present, and the Taylor approximation of the logarithm of the remainder (both
            // with 20 decimals). All that remains is to sum these two, and then drop two digits to return a 18 decimal
            // value.

            int256 result = (sum + seriesSum) / 100;

            // We avoid using recursion here because zkSync doesn't support it.
            return negativeExponent ? -result : result;
        }
    }

    /**
     * @dev Internal high precision (36 decimal places) natural logarithm (ln(x)) with signed 18 decimal fixed point argument,
     * for x close to one.
     *
     * Should only be used if x is between LN_36_LOWER_BOUND and LN_36_UPPER_BOUND.
     */
    function _ln_36(int256 x) private pure returns (int256) {
        // Since ln(1) = 0, a value of x close to one will yield a very small result, which makes using 36 digits
        // worthwhile.

        // First, we transform x to a 36 digit fixed point value.
        unchecked {
            x *= ONE_18;

            // We will use the following Taylor expansion, which converges very rapidly. Let z = (x - 1) / (x + 1).
            // ln(x) = 2 * (z + z^3 / 3 + z^5 / 5 + z^7 / 7 + ... + z^(2 * n + 1) / (2 * n + 1))

            // Recall that 36 digit fixed point division requires multiplying by ONE_36, and multiplication requires
            // division by ONE_36.
            int256 z = ((x - ONE_36) * ONE_36) / (x + ONE_36);
            int256 z_squared = (z * z) / ONE_36;

            // num is the numerator of the series: the z^(2 * n + 1) term
            int256 num = z;

            // seriesSum holds the accumulated sum of each term in the series, starting with the initial z
            int256 seriesSum = num;

            // In each step, the numerator is multiplied by z^2
            num = (num * z_squared) / ONE_36;
            seriesSum += num / 3;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 5;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 7;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 9;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 11;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 13;

            num = (num * z_squared) / ONE_36;
            seriesSum += num / 15;

            // 8 Taylor terms are sufficient for 36 decimal precision.

            // All that remains is multiplying by 2 (non fixed point).
            return seriesSum * 2;
        }
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

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

/**
 * @notice Implementation of Balancer Weighted Math, essentially unchanged since v1.
 * @dev It is a generalization of the x * y = k constant product formula, accounting for cases with more than two
 * tokens, and weights that are not 50/50. See https://docs.qr68.com/tech-implementations/weighted-math.
 *
 * For security reasons, to help ensure that for all possible "round trip" paths the caller always receives the same
 * or fewer tokens than supplied, we have chosen the rounding direction to favor the protocol in all cases.
 */
library WeightedMath {
    using FixedPoint for uint256;

    /// @notice User attempted to extract a disproportionate amountOut of tokens from a pool.
    error MaxOutRatio();

    /// @notice User attempted to add a disproportionate amountIn of tokens to a pool.
    error MaxInRatio();

    /**
     * @notice Error thrown when the calculated invariant is zero, indicating an issue with the invariant calculation.
     * @dev Most commonly, this happens when a token balance is zero.
     */
    error ZeroInvariant();

    // Pool limits that arise from limitations in the fixed point power function. When computing x^y, the valid range
    // of `x` is -41 (ExpMin) to 130 (ExpMax). See `LogExpMath.sol` for the derivation of these values.
    //
    // Invariant calculation:
    // In computing `balance^normalizedWeight`, `log(balance) * normalizedWeight` must fall within the `pow` function
    // bounds described above. Since 0.01 <= normalizedWeight <= 0.99, the balance is constrained to the range between
    // e^(ExpMin) and e^(ExpMax).
    //
    // This corresponds to 10^(-18) < balance < 2^(188.56). Since the maximum balance is 2^(128) - 1, the invariant
    // calculation is unconstrained by the `pow` function limits.
    //
    // It's a different story with `computeBalanceOutGivenInvariant` (inverse invariant):
    // This uses the power function to raise the invariant ratio to the power of 1/weight. Similar to the computation
    // for the invariant, this means the following expression must hold:
    // ExpMin < log(invariantRatio) * 1/weight < ExpMax
    //
    // Given the valid range of weights (i.e., 1 < 1/weight < 100), we have:
    // ExpMin/100 < log(invariantRatio) < ExpMax/100, or e^(-0.41) < invariantRatio < e^(1.3). Numerically, this
    // constrains the invariantRatio to between 0.661 and 3.695. For an added safety margin, we set the limits to
    // 0.7 < invariantRatio < 3.

    // Swap limits: amounts swapped may not be larger than this percentage of the total balance.
    uint256 internal constant _MAX_IN_RATIO = 30e16; // 30%
    uint256 internal constant _MAX_OUT_RATIO = 30e16; // 30%

    // Invariant growth limit: non-proportional add cannot cause the invariant to increase by more than this ratio.
    uint256 internal constant _MAX_INVARIANT_RATIO = 300e16; // 300%
    // Invariant shrink limit: non-proportional remove cannot cause the invariant to decrease by less than this ratio.
    uint256 internal constant _MIN_INVARIANT_RATIO = 70e16; // 70%

    /**
     * @notice Compute the invariant, rounding down.
     * @dev The invariant functions are called by the Vault during various liquidity operations, and require a specific
     * rounding direction in order to ensure safety (i.e., that the final result is always rounded in favor of the
     * protocol. The invariant (i.e., all token balances) must always be greater than 0, or it will revert.
     *
     * @param normalizedWeights The pool token weights, sorted in token registration order
     * @param balances The pool token balances, sorted in token registration order
     * @return invariant The invariant, rounded down
     */
    function computeInvariantDown(
        uint256[] memory normalizedWeights,
        uint256[] memory balances
    ) internal pure returns (uint256 invariant) {
        /**********************************************************************************************
        // invariant               _____                                                             //
        // wi = weight index i      | |      wi                                                      //
        // bi = balance index i     | |  bi ^   = i                                                  //
        // i = invariant                                                                             //
        **********************************************************************************************/

        invariant = FixedPoint.ONE;
        for (uint256 i = 0; i < normalizedWeights.length; ++i) {
            invariant = invariant.mulDown(balances[i].powDown(normalizedWeights[i]));
        }

        if (invariant == 0) {
            revert ZeroInvariant();
        }
    }

    /**
     * @notice Compute the invariant, rounding up.
     * @dev The invariant functions are called by the Vault during various liquidity operations, and require a specific
     * rounding direction in order to ensure safety (i.e., that the final result is always rounded in favor of the
     * protocol. The invariant (i.e., all token balances) must always be greater than 0, or it will revert.
     *
     * @param normalizedWeights The pool token weights, sorted in token registration order
     * @param balances The pool token balances, sorted in token registration order
     * @return invariant The invariant, rounded up
     */
    function computeInvariantUp(
        uint256[] memory normalizedWeights,
        uint256[] memory balances
    ) internal pure returns (uint256 invariant) {
        /**********************************************************************************************
        // invariant               _____                                                             //
        // wi = weight index i      | |      wi                                                      //
        // bi = balance index i     | |  bi ^   = i                                                  //
        // i = invariant                                                                             //
        **********************************************************************************************/

        invariant = FixedPoint.ONE;
        for (uint256 i = 0; i < normalizedWeights.length; ++i) {
            invariant = invariant.mulUp(balances[i].powUp(normalizedWeights[i]));
        }

        if (invariant == 0) {
            revert ZeroInvariant();
        }
    }

    /**
     * @notice Compute a token balance after a liquidity operation, given the current balance and invariant ratio.
     * @dev This is called as part of the "inverse invariant" `computeBalance` calculation.
     * @param currentBalance The current balance of the token
     * @param weight The weight of the token
     * @param invariantRatio The invariant ratio (i.e., new/old; will be > 1 for add; < 1 for remove)
     * @return newBalance The adjusted token balance after the operation
     */
    function computeBalanceOutGivenInvariant(
        uint256 currentBalance,
        uint256 weight,
        uint256 invariantRatio
    ) internal pure returns (uint256 newBalance) {
        /******************************************************************************************
        // calculateBalanceGivenInvariant                                                        //
        // o = balanceOut                                                                        //
        // b = balanceIn                      (1 / w)                                            //
        // w = weight              o = b * i ^                                                   //
        // i = invariantRatio                                                                    //
        ******************************************************************************************/

        // Rounds result up overall, rounding up the two individual steps:
        // - balanceRatio = invariantRatio ^ (1 / weight)
        // - newBalance = balance * balanceRatio
        //
        // Regarding `balanceRatio`, the exponent is always > FP(1), but the invariant ratio can be either greater or
        // lower than FP(1) depending on whether this is solving an `add` or a `remove` operation.
        // - For i > 1, we need to round the exponent up, as i^x is monotonically increasing for i > 1.
        // - For i < 1, we need to round the exponent down, as as i^x is monotonically decreasing for i < 1.

        function(uint256, uint256) internal pure returns (uint256) divUpOrDown = invariantRatio > 1
            ? FixedPoint.divUp
            : FixedPoint.divDown;

        // Calculate by how much the token balance has to increase to match the invariantRatio.
        uint256 balanceRatio = invariantRatio.powUp(divUpOrDown(FixedPoint.ONE, weight));

        return currentBalance.mulUp(balanceRatio);
    }

    /**
     * @notice Compute the `amountOut` of tokenOut in a swap, given the current balances and weights.
     * @param balanceIn The current balance of `tokenIn`
     * @param weightIn  The weight of `tokenIn`
     * @param balanceOut The current balance of `tokenOut`
     * @param weightOut The weight of `tokenOut`
     * @param amountIn The exact amount of `tokenIn` (i.e., the amount given in an ExactIn swap)
     * @return amountOut The calculated amount of `tokenOut` returned in an ExactIn swap
     */
    function computeOutGivenExactIn(
        uint256 balanceIn,
        uint256 weightIn,
        uint256 balanceOut,
        uint256 weightOut,
        uint256 amountIn
    ) internal pure returns (uint256 amountOut) {
        /**********************************************************************************************
        // outGivenExactIn                                                                           //
        // aO = amountOut                                                                            //
        // bO = balanceOut                                                                           //
        // bI = balanceIn              /      /            bI             \    (wI / wO) \           //
        // aI = amountIn    aO = bO * |  1 - | --------------------------  | ^            |          //
        // wI = weightIn               \      \       ( bI + aI )         /              /           //
        // wO = weightOut                                                                            //
        **********************************************************************************************/

        // Amount out, so we round down overall.

        // The multiplication rounds down, and the subtrahend (power) rounds up (so the base rounds up too).
        // Because bI / (bI + aI) <= 1, the exponent rounds down.

        // Cannot exceed maximum in ratio.
        if (amountIn > balanceIn.mulDown(_MAX_IN_RATIO)) {
            revert MaxInRatio();
        }

        uint256 denominator = balanceIn + amountIn;
        uint256 base = balanceIn.divUp(denominator);
        uint256 exponent = weightIn.divDown(weightOut);
        uint256 power = base.powUp(exponent);

        // Because of rounding up, power can be greater than one. Using complement prevents reverts.
        return balanceOut.mulDown(power.complement());
    }

    /**
     * @notice Compute the `amountIn` of tokenIn in a swap, given the current balances and weights.
     * @param balanceIn The current balance of `tokenIn`
     * @param weightIn  The weight of `tokenIn`
     * @param balanceOut The current balance of `tokenOut`
     * @param weightOut The weight of `tokenOut`
     * @param amountOut The exact amount of `tokenOut` (i.e., the amount given in an ExactOut swap)
     * @return amountIn The calculated amount of `tokenIn` returned in an ExactOut swap
     */
    function computeInGivenExactOut(
        uint256 balanceIn,
        uint256 weightIn,
        uint256 balanceOut,
        uint256 weightOut,
        uint256 amountOut
    ) internal pure returns (uint256 amountIn) {
        /**********************************************************************************************
        // inGivenExactOut                                                                           //
        // aO = amountOut                                                                            //
        // bO = balanceOut                                                                           //
        // bI = balanceIn              /  /            bO             \    (wO / wI)      \          //
        // aI = amountIn    aI = bI * |  | --------------------------  | ^            - 1  |         //
        // wI = weightIn               \  \       ( bO - aO )         /                   /          //
        // wO = weightOut                                                                            //
        **********************************************************************************************/

        // Amount in, so we round up overall.

        // The multiplication rounds up, and the power rounds up (so the base rounds up too).
        // Because b0 / (b0 - a0) >= 1, the exponent rounds up.

        // Cannot exceed maximum out ratio.
        if (amountOut > balanceOut.mulDown(_MAX_OUT_RATIO)) {
            revert MaxOutRatio();
        }

        uint256 base = balanceOut.divUp(balanceOut - amountOut);
        uint256 exponent = weightOut.divUp(weightIn);
        uint256 power = base.powUp(exponent);

        // Because the base is larger than one (and the power rounds up), the power should always be larger than one, so
        // the following subtraction should never revert.
        uint256 ratio = power - FixedPoint.ONE;

        return balanceIn.mulUp(ratio);
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

import { IERC20Metadata } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Metadata.sol";
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import { ERC165 } from "@openzeppelin/contracts/utils/introspection/ERC165.sol";
import { EIP712 } from "@openzeppelin/contracts/utils/cryptography/EIP712.sol";
import { ECDSA } from "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { Nonces } from "@openzeppelin/contracts/utils/Nonces.sol";

import { IRateProvider } from "@balancer-labs/v3-interfaces/contracts/solidity-utils/helpers/IRateProvider.sol";
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";

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

/**
 * @notice `BalancerPoolToken` is a fully ERC20-compatible token to be used as the base contract for Balancer Pools,
 * with all the data and implementation delegated to the ERC20Multitoken contract.

 * @dev Implementation of the ERC-20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[ERC-2612].
 */
contract BalancerPoolToken is IERC20, IERC20Metadata, IERC20Permit, IRateProvider, EIP712, Nonces, ERC165, VaultGuard {
    bytes32 public constant PERMIT_TYPEHASH =
        keccak256("Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)");

    /**
     * @notice Operation failed due to an expired permit signature.
     * @param deadline The permit deadline that expired
     */
    error ERC2612ExpiredSignature(uint256 deadline);

    /**
     * @notice Operation failed due to a non-matching signature.
     * @param signer The address corresponding to the signature provider
     * @param owner The address of the owner (expected value of the signature provider)
     */
    error ERC2612InvalidSigner(address signer, address owner);

    // EIP712 also defines _name.
    string private _bptName;
    string private _bptSymbol;

    constructor(IVault vault_, string memory bptName, string memory bptSymbol) EIP712(bptName, "1") VaultGuard(vault_) {
        _bptName = bptName;
        _bptSymbol = bptSymbol;
    }

    /// @inheritdoc IERC20Metadata
    function name() external view returns (string memory) {
        return _bptName;
    }

    /// @inheritdoc IERC20Metadata
    function symbol() external view returns (string memory) {
        return _bptSymbol;
    }

    /// @inheritdoc IERC20Metadata
    function decimals() external pure returns (uint8) {
        // Always 18 decimals for BPT.
        return 18;
    }

    /// @inheritdoc IERC20
    function totalSupply() public view returns (uint256) {
        return _vault.totalSupply(address(this));
    }

    function getVault() public view returns (IVault) {
        return _vault;
    }

    /// @inheritdoc IERC20
    function balanceOf(address account) external view returns (uint256) {
        return _vault.balanceOf(address(this), account);
    }

    /// @inheritdoc IERC20
    function transfer(address to, uint256 amount) external returns (bool) {
        // Vault will perform the transfer and call emitTransfer to emit the event from this contract.
        _vault.transfer(msg.sender, to, amount);
        return true;
    }

    /// @inheritdoc IERC20
    function allowance(address owner, address spender) external view returns (uint256) {
        return _vault.allowance(address(this), owner, spender);
    }

    /// @inheritdoc IERC20
    function approve(address spender, uint256 amount) external returns (bool) {
        // Vault will perform the approval and call emitApproval to emit the event from this contract.
        _vault.approve(msg.sender, spender, amount);
        return true;
    }

    /// @inheritdoc IERC20
    function transferFrom(address from, address to, uint256 amount) external returns (bool) {
        // Vault will perform the transfer and call emitTransfer to emit the event from this contract.
        _vault.transferFrom(msg.sender, from, to, amount);
        return true;
    }

    /**
     * Accounting is centralized in the MultiToken contract, and the actual transfers and approvals are done there.
     * Operations can be initiated from either the token contract or the MultiToken.
     *
     * To maintain compliance with the ERC-20 standard, and conform to the expectations of off-chain processes,
     * the MultiToken calls `emitTransfer` and `emitApproval` during those operations, so that the event is emitted
     * only from the token contract. These events are NOT defined in the MultiToken contract.
     */

    /// @dev Emit the Transfer event. This function can only be called by the MultiToken.
    function emitTransfer(address from, address to, uint256 amount) external onlyVault {
        emit Transfer(from, to, amount);
    }

    /// @dev Emit the Approval event. This function can only be called by the MultiToken.
    function emitApproval(address owner, address spender, uint256 amount) external onlyVault {
        emit Approval(owner, spender, amount);
    }

    // @inheritdoc IERC20Permit
    function permit(
        address owner,
        address spender,
        uint256 amount,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) public virtual {
        // solhint-disable-next-line not-rely-on-time
        if (block.timestamp > deadline) {
            revert ERC2612ExpiredSignature(deadline);
        }

        bytes32 structHash = keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, amount, _useNonce(owner), deadline));

        bytes32 hash = _hashTypedDataV4(structHash);

        address signer = ECDSA.recover(hash, v, r, s);
        if (signer != owner) {
            revert ERC2612InvalidSigner(signer, owner);
        }

        _vault.approve(owner, spender, amount);
    }

    // @inheritdoc IERC20Permit
    function nonces(address owner) public view virtual override(IERC20Permit, Nonces) returns (uint256) {
        return super.nonces(owner);
    }

    /// @notice Increment the sender's nonce to revoke any currently granted (but not yet executed) `permit`.
    function incrementNonce() external {
        _useNonce(msg.sender);
    }

    // @inheritdoc IERC20Permit
    // solhint-disable-next-line func-name-mixedcase
    function DOMAIN_SEPARATOR() external view virtual returns (bytes32) {
        return _domainSeparatorV4();
    }

    /**
     * @notice Get the BPT rate, which is defined as: pool invariant/total supply.
     * @dev The VaultExtension contract defines a default implementation (`getBptRate`) to calculate the rate
     * of any given pool, which should be sufficient in nearly all cases.
     *
     * @return rate Rate of the pool's BPT
     */
    function getRate() public view virtual returns (uint256) {
        return getVault().getBptRate(address(this));
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

import { IAuthorizer } from "@balancer-labs/v3-interfaces/contracts/vault/IAuthorizer.sol";
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";

import { Authentication } from "@balancer-labs/v3-solidity-utils/contracts/helpers/Authentication.sol";

/**
 * @notice Base contract suitable for Singleton contracts (e.g., pool factories) that have permissioned functions.
 * @dev The disambiguator is the contract's own address. This is used in the construction of actionIds for permissioned
 * functions, to avoid conflicts when multiple contracts (or multiple versions of the same contract) use the same
 * function name.
 */
abstract contract SingletonAuthentication is Authentication {
    IVault private immutable _vault;

    // Use the contract's own address to disambiguate action identifiers.
    constructor(IVault vault) Authentication(bytes32(uint256(uint160(address(this))))) {
        _vault = vault;
    }

    /**
     * @notice Get the address of the Balancer Vault.
     * @return vault An interface pointer to the Vault
     */
    function getVault() public view returns (IVault) {
        return _vault;
    }

    /**
     * @notice Get the address of the Authorizer.
     * @return authorizer An interface pointer to the Authorizer
     */
    function getAuthorizer() public view returns (IAuthorizer) {
        return getVault().getAuthorizer();
    }

    function _canPerform(bytes32 actionId, address account) internal view override returns (bool) {
        return getAuthorizer().canPerform(actionId, account, address(this));
    }

    function _canPerform(bytes32 actionId, address account, address where) internal view returns (bool) {
        return getAuthorizer().canPerform(actionId, account, where);
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity ^0.8.24;

import { IVaultErrors } from "@balancer-labs/v3-interfaces/contracts/vault/IVaultErrors.sol";
import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";

/// @notice Contract that shares the modifier `onlyVault`.
contract VaultGuard {
    IVault internal immutable _vault;

    constructor(IVault vault) {
        _vault = vault;
    }

    modifier onlyVault() {
        _ensureOnlyVault();
        _;
    }

    function _ensureOnlyVault() private view {
        if (msg.sender != address(_vault)) {
            revert IVaultErrors.SenderIsNotVault(msg.sender);
        }
    }
}

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

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which provides a basic access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is set to the address provided by the deployer. This can
 * later be changed with {transferOwnership}.
 *
 * This module is used through inheritance. It will make available the modifier
 * `onlyOwner`, which can be applied to your functions to restrict their use to
 * the owner.
 */
abstract contract Ownable is Context {
    address private _owner;

    /**
     * @dev The caller account is not authorized to perform an operation.
     */
    error OwnableUnauthorizedAccount(address account);

    /**
     * @dev The owner is not a valid owner account. (eg. `address(0)`)
     */
    error OwnableInvalidOwner(address owner);

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

    /**
     * @dev Initializes the contract setting the address provided by the deployer as the initial owner.
     */
    constructor(address initialOwner) {
        if (initialOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(initialOwner);
    }

    /**
     * @dev Throws if called by any account other than the owner.
     */
    modifier onlyOwner() {
        _checkOwner();
        _;
    }

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

    /**
     * @dev Throws if the sender is not the owner.
     */
    function _checkOwner() internal view virtual {
        if (owner() != _msgSender()) {
            revert OwnableUnauthorizedAccount(_msgSender());
        }
    }

    /**
     * @dev Leaves the contract without owner. It will not be possible to call
     * `onlyOwner` functions. Can only be called by the current owner.
     *
     * NOTE: Renouncing ownership will leave the contract without an owner,
     * thereby disabling any functionality that is only available to the owner.
     */
    function renounceOwnership() public virtual onlyOwner {
        _transferOwnership(address(0));
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Can only be called by the current owner.
     */
    function transferOwnership(address newOwner) public virtual onlyOwner {
        if (newOwner == address(0)) {
            revert OwnableInvalidOwner(address(0));
        }
        _transferOwnership(newOwner);
    }

    /**
     * @dev Transfers ownership of the contract to a new account (`newOwner`).
     * Internal function without access restriction.
     */
    function _transferOwnership(address newOwner) internal virtual {
        address oldOwner = _owner;
        _owner = newOwner;
        emit OwnershipTransferred(oldOwner, newOwner);
    }
}

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

pragma solidity ^0.8.20;

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

/**
 * @dev Contract module which provides access control mechanism, where
 * there is an account (an owner) that can be granted exclusive access to
 * specific functions.
 *
 * The initial owner is specified at deployment time in the constructor for `Ownable`. This
 * can later be changed with {transferOwnership} and {acceptOwnership}.
 *
 * This module is used through inheritance. It will make available all functions
 * from parent (Ownable).
 */
abstract contract Ownable2Step is Ownable {
    address private _pendingOwner;

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

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

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

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

    /**
     * @dev The new owner accepts the ownership transfer.
     */
    function acceptOwnership() public virtual {
        address sender = _msgSender();
        if (pendingOwner() != sender) {
            revert OwnableUnauthorizedAccount(sender);
        }
        _transferOwnership(sender);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC4626.sol)

pragma solidity ^0.8.20;

import {IERC20} from "../token/ERC20/IERC20.sol";
import {IERC20Metadata} from "../token/ERC20/extensions/IERC20Metadata.sol";

/**
 * @dev Interface of the ERC4626 "Tokenized Vault Standard", as defined in
 * https://eips.ethereum.org/EIPS/eip-4626[ERC-4626].
 */
interface IERC4626 is IERC20, IERC20Metadata {
    event Deposit(address indexed sender, address indexed owner, uint256 assets, uint256 shares);

    event Withdraw(
        address indexed sender,
        address indexed receiver,
        address indexed owner,
        uint256 assets,
        uint256 shares
    );

    /**
     * @dev Returns the address of the underlying token used for the Vault for accounting, depositing, and withdrawing.
     *
     * - MUST be an ERC-20 token contract.
     * - MUST NOT revert.
     */
    function asset() external view returns (address assetTokenAddress);

    /**
     * @dev Returns the total amount of the underlying asset that is “managed” by Vault.
     *
     * - SHOULD include any compounding that occurs from yield.
     * - MUST be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT revert.
     */
    function totalAssets() external view returns (uint256 totalManagedAssets);

    /**
     * @dev Returns the amount of shares that the Vault would exchange for the amount of assets provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToShares(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Returns the amount of assets that the Vault would exchange for the amount of shares provided, in an ideal
     * scenario where all the conditions are met.
     *
     * - MUST NOT be inclusive of any fees that are charged against assets in the Vault.
     * - MUST NOT show any variations depending on the caller.
     * - MUST NOT reflect slippage or other on-chain conditions, when performing the actual exchange.
     * - MUST NOT revert.
     *
     * NOTE: This calculation MAY NOT reflect the “per-user” price-per-share, and instead should reflect the
     * “average-user’s” price-per-share, meaning what the average user should expect to see when exchanging to and
     * from.
     */
    function convertToAssets(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be deposited into the Vault for the receiver,
     * through a deposit call.
     *
     * - MUST return a limited value if receiver is subject to some deposit limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of assets that may be deposited.
     * - MUST NOT revert.
     */
    function maxDeposit(address receiver) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their deposit at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of Vault shares that would be minted in a deposit
     *   call in the same transaction. I.e. deposit should return the same or more shares as previewDeposit if called
     *   in the same transaction.
     * - MUST NOT account for deposit limits like those returned from maxDeposit and should always act as though the
     *   deposit would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewDeposit SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewDeposit(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Mints shares Vault shares to receiver by depositing exactly amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   deposit execution, and are accounted for during deposit.
     * - MUST revert if all of assets cannot be deposited (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function deposit(uint256 assets, address receiver) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of the Vault shares that can be minted for the receiver, through a mint call.
     * - MUST return a limited value if receiver is subject to some mint limit.
     * - MUST return 2 ** 256 - 1 if there is no limit on the maximum amount of shares that may be minted.
     * - MUST NOT revert.
     */
    function maxMint(address receiver) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their mint at the current block, given
     * current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of assets that would be deposited in a mint call
     *   in the same transaction. I.e. mint should return the same or fewer assets as previewMint if called in the
     *   same transaction.
     * - MUST NOT account for mint limits like those returned from maxMint and should always act as though the mint
     *   would be accepted, regardless if the user has enough tokens approved, etc.
     * - MUST be inclusive of deposit fees. Integrators should be aware of the existence of deposit fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewMint SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by minting.
     */
    function previewMint(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Mints exactly shares Vault shares to receiver by depositing amount of underlying tokens.
     *
     * - MUST emit the Deposit event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the mint
     *   execution, and are accounted for during mint.
     * - MUST revert if all of shares cannot be minted (due to deposit limit being reached, slippage, the user not
     *   approving enough underlying tokens to the Vault contract, etc).
     *
     * NOTE: most implementations will require pre-approval of the Vault with the Vault’s underlying asset token.
     */
    function mint(uint256 shares, address receiver) external returns (uint256 assets);

    /**
     * @dev Returns the maximum amount of the underlying asset that can be withdrawn from the owner balance in the
     * Vault, through a withdraw call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxWithdraw(address owner) external view returns (uint256 maxAssets);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their withdrawal at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no fewer than the exact amount of Vault shares that would be burned in a withdraw
     *   call in the same transaction. I.e. withdraw should return the same or fewer shares as previewWithdraw if
     *   called
     *   in the same transaction.
     * - MUST NOT account for withdrawal limits like those returned from maxWithdraw and should always act as though
     *   the withdrawal would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToShares and previewWithdraw SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by depositing.
     */
    function previewWithdraw(uint256 assets) external view returns (uint256 shares);

    /**
     * @dev Burns shares from owner and sends exactly assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   withdraw execution, and are accounted for during withdraw.
     * - MUST revert if all of assets cannot be withdrawn (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * Note that some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function withdraw(uint256 assets, address receiver, address owner) external returns (uint256 shares);

    /**
     * @dev Returns the maximum amount of Vault shares that can be redeemed from the owner balance in the Vault,
     * through a redeem call.
     *
     * - MUST return a limited value if owner is subject to some withdrawal limit or timelock.
     * - MUST return balanceOf(owner) if owner is not subject to any withdrawal limit or timelock.
     * - MUST NOT revert.
     */
    function maxRedeem(address owner) external view returns (uint256 maxShares);

    /**
     * @dev Allows an on-chain or off-chain user to simulate the effects of their redeemption at the current block,
     * given current on-chain conditions.
     *
     * - MUST return as close to and no more than the exact amount of assets that would be withdrawn in a redeem call
     *   in the same transaction. I.e. redeem should return the same or more assets as previewRedeem if called in the
     *   same transaction.
     * - MUST NOT account for redemption limits like those returned from maxRedeem and should always act as though the
     *   redemption would be accepted, regardless if the user has enough shares, etc.
     * - MUST be inclusive of withdrawal fees. Integrators should be aware of the existence of withdrawal fees.
     * - MUST NOT revert.
     *
     * NOTE: any unfavorable discrepancy between convertToAssets and previewRedeem SHOULD be considered slippage in
     * share price or some other type of condition, meaning the depositor will lose assets by redeeming.
     */
    function previewRedeem(uint256 shares) external view returns (uint256 assets);

    /**
     * @dev Burns exactly shares from owner and sends assets of underlying tokens to receiver.
     *
     * - MUST emit the Withdraw event.
     * - MAY support an additional flow in which the underlying tokens are owned by the Vault contract before the
     *   redeem execution, and are accounted for during redeem.
     * - MUST revert if all of shares cannot be redeemed (due to withdrawal limit being reached, slippage, the owner
     *   not having enough shares, etc).
     *
     * NOTE: some implementations will require pre-requesting to the Vault before a withdrawal may be performed.
     * Those methods should be performed separately.
     */
    function redeem(uint256 shares, address receiver, address owner) external returns (uint256 assets);
}

File 44 of 72 : IERC5267.sol
// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (interfaces/IERC5267.sol)

pragma solidity ^0.8.20;

interface IERC5267 {
    /**
     * @dev MAY be emitted to signal that the domain could have changed.
     */
    event EIP712DomainChanged();

    /**
     * @dev returns the fields and values that describe the domain separator used by this contract for EIP-712
     * signature.
     */
    function eip712Domain()
        external
        view
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        );
}

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

pragma solidity ^0.8.20;

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

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

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

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

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

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reininitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

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

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

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        assembly {
            $.slot := INITIALIZABLE_STORAGE
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Metadata.sol)

pragma solidity ^0.8.20;

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (token/ERC20/extensions/IERC20Permit.sol)

pragma solidity ^0.8.20;

/**
 * @dev Interface of the ERC20 Permit extension allowing approvals to be made via signatures, as defined in
 * https://eips.ethereum.org/EIPS/eip-2612[EIP-2612].
 *
 * Adds the {permit} method, which can be used to change an account's ERC20 allowance (see {IERC20-allowance}) by
 * presenting a message signed by the account. By not relying on {IERC20-approve}, the token holder account doesn't
 * need to send a transaction, and thus is not required to hold Ether at all.
 *
 * ==== Security Considerations
 *
 * There are two important considerations concerning the use of `permit`. The first is that a valid permit signature
 * expresses an allowance, and it should not be assumed to convey additional meaning. In particular, it should not be
 * considered as an intention to spend the allowance in any specific way. The second is that because permits have
 * built-in replay protection and can be submitted by anyone, they can be frontrun. A protocol that uses permits should
 * take this into consideration and allow a `permit` call to fail. Combining these two aspects, a pattern that may be
 * generally recommended is:
 *
 * ```solidity
 * function doThingWithPermit(..., uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) public {
 *     try token.permit(msg.sender, address(this), value, deadline, v, r, s) {} catch {}
 *     doThing(..., value);
 * }
 *
 * function doThing(..., uint256 value) public {
 *     token.safeTransferFrom(msg.sender, address(this), value);
 *     ...
 * }
 * ```
 *
 * Observe that: 1) `msg.sender` is used as the owner, leaving no ambiguity as to the signer intent, and 2) the use of
 * `try/catch` allows the permit to fail and makes the code tolerant to frontrunning. (See also
 * {SafeERC20-safeTransferFrom}).
 *
 * Additionally, note that smart contract wallets (such as Argent or Safe) are not able to produce permit signatures, so
 * contracts should have entry points that don't rely on permit.
 */
interface IERC20Permit {
    /**
     * @dev Sets `value` as the allowance of `spender` over ``owner``'s tokens,
     * given ``owner``'s signed approval.
     *
     * IMPORTANT: The same issues {IERC20-approve} has related to transaction
     * ordering also apply here.
     *
     * Emits an {Approval} event.
     *
     * Requirements:
     *
     * - `spender` cannot be the zero address.
     * - `deadline` must be a timestamp in the future.
     * - `v`, `r` and `s` must be a valid `secp256k1` signature from `owner`
     * over the EIP712-formatted function arguments.
     * - the signature must use ``owner``'s current nonce (see {nonces}).
     *
     * For more information on the signature format, see the
     * https://eips.ethereum.org/EIPS/eip-2612#specification[relevant EIP
     * section].
     *
     * CAUTION: See Security Considerations above.
     */
    function permit(
        address owner,
        address spender,
        uint256 value,
        uint256 deadline,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) external;

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

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

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

pragma solidity ^0.8.20;

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

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

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

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

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

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

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

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

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

pragma solidity ^0.8.20;

import {IERC20} from "../IERC20.sol";
import {IERC20Permit} from "../extensions/IERC20Permit.sol";
import {Address} from "../../../utils/Address.sol";

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

    /**
     * @dev An operation with an ERC20 token failed.
     */
    error SafeERC20FailedOperation(address token);

    /**
     * @dev Indicates a failed `decreaseAllowance` request.
     */
    error SafeERC20FailedDecreaseAllowance(address spender, uint256 currentAllowance, uint256 requestedDecrease);

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

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

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

    /**
     * @dev Decrease the calling contract's allowance toward `spender` by `requestedDecrease`. If `token` returns no
     * value, non-reverting calls are assumed to be successful.
     */
    function safeDecreaseAllowance(IERC20 token, address spender, uint256 requestedDecrease) internal {
        unchecked {
            uint256 currentAllowance = token.allowance(address(this), spender);
            if (currentAllowance < requestedDecrease) {
                revert SafeERC20FailedDecreaseAllowance(spender, currentAllowance, requestedDecrease);
            }
            forceApprove(token, spender, currentAllowance - requestedDecrease);
        }
    }

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

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

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

        bytes memory returndata = address(token).functionCall(data);
        if (returndata.length != 0 && !abi.decode(returndata, (bool))) {
            revert SafeERC20FailedOperation(address(token));
        }
    }

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

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

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

pragma solidity ^0.8.20;

/**
 * @dev Collection of functions related to the address type
 */
library Address {
    /**
     * @dev The ETH balance of the account is not enough to perform the operation.
     */
    error AddressInsufficientBalance(address account);

    /**
     * @dev There's no code at `target` (it is not a contract).
     */
    error AddressEmptyCode(address target);

    /**
     * @dev A call to an address target failed. The target may have reverted.
     */
    error FailedInnerCall();

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

        (bool success, ) = recipient.call{value: amount}("");
        if (!success) {
            revert FailedInnerCall();
        }
    }

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

    /**
     * @dev Same as {xref-Address-functionCall-address-bytes-}[`functionCall`],
     * but also transferring `value` wei to `target`.
     *
     * Requirements:
     *
     * - the calling contract must have an ETH balance of at least `value`.
     * - the called Solidity function must be `payable`.
     */
    function functionCallWithValue(address target, bytes memory data, uint256 value) internal returns (bytes memory) {
        if (address(this).balance < value) {
            revert AddressInsufficientBalance(address(this));
        }
        (bool success, bytes memory returndata) = target.call{value: value}(data);
        return verifyCallResultFromTarget(target, success, returndata);
    }

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

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

    /**
     * @dev Tool to verify that a low level call to smart-contract was successful, and reverts if the target
     * was not a contract or bubbling up the revert reason (falling back to {FailedInnerCall}) in case of an
     * unsuccessful call.
     */
    function verifyCallResultFromTarget(
        address target,
        bool success,
        bytes memory returndata
    ) internal view returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            // only check if target is a contract if the call was successful and the return data is empty
            // otherwise we already know that it was a contract
            if (returndata.length == 0 && target.code.length == 0) {
                revert AddressEmptyCode(target);
            }
            return returndata;
        }
    }

    /**
     * @dev Tool to verify that a low level call was successful, and reverts if it wasn't, either by bubbling the
     * revert reason or with a default {FailedInnerCall} error.
     */
    function verifyCallResult(bool success, bytes memory returndata) internal pure returns (bytes memory) {
        if (!success) {
            _revert(returndata);
        } else {
            return returndata;
        }
    }

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Context.sol)

pragma solidity ^0.8.20;

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Create2.sol)

pragma solidity ^0.8.20;

/**
 * @dev Helper to make usage of the `CREATE2` EVM opcode easier and safer.
 * `CREATE2` can be used to compute in advance the address where a smart
 * contract will be deployed, which allows for interesting new mechanisms known
 * as 'counterfactual interactions'.
 *
 * See the https://eips.ethereum.org/EIPS/eip-1014#motivation[EIP] for more
 * information.
 */
library Create2 {
    /**
     * @dev Not enough balance for performing a CREATE2 deploy.
     */
    error Create2InsufficientBalance(uint256 balance, uint256 needed);

    /**
     * @dev There's no code to deploy.
     */
    error Create2EmptyBytecode();

    /**
     * @dev The deployment failed.
     */
    error Create2FailedDeployment();

    /**
     * @dev Deploys a contract using `CREATE2`. The address where the contract
     * will be deployed can be known in advance via {computeAddress}.
     *
     * The bytecode for a contract can be obtained from Solidity with
     * `type(contractName).creationCode`.
     *
     * Requirements:
     *
     * - `bytecode` must not be empty.
     * - `salt` must have not been used for `bytecode` already.
     * - the factory must have a balance of at least `amount`.
     * - if `amount` is non-zero, `bytecode` must have a `payable` constructor.
     */
    function deploy(uint256 amount, bytes32 salt, bytes memory bytecode) internal returns (address addr) {
        if (address(this).balance < amount) {
            revert Create2InsufficientBalance(address(this).balance, amount);
        }
        if (bytecode.length == 0) {
            revert Create2EmptyBytecode();
        }
        /// @solidity memory-safe-assembly
        assembly {
            addr := create2(amount, add(bytecode, 0x20), mload(bytecode), salt)
        }
        if (addr == address(0)) {
            revert Create2FailedDeployment();
        }
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy}. Any change in the
     * `bytecodeHash` or `salt` will result in a new destination address.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash) internal view returns (address) {
        return computeAddress(salt, bytecodeHash, address(this));
    }

    /**
     * @dev Returns the address where a contract will be stored if deployed via {deploy} from a contract located at
     * `deployer`. If `deployer` is this contract's address, returns the same value as {computeAddress}.
     */
    function computeAddress(bytes32 salt, bytes32 bytecodeHash, address deployer) internal pure returns (address addr) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40) // Get free memory pointer

            // |                   | ↓ ptr ...  ↓ ptr + 0x0B (start) ...  ↓ ptr + 0x20 ...  ↓ ptr + 0x40 ...   |
            // |-------------------|---------------------------------------------------------------------------|
            // | bytecodeHash      |                                                        CCCCCCCCCCCCC...CC |
            // | salt              |                                      BBBBBBBBBBBBB...BB                   |
            // | deployer          | 000000...0000AAAAAAAAAAAAAAAAAAA...AA                                     |
            // | 0xFF              |            FF                                                             |
            // |-------------------|---------------------------------------------------------------------------|
            // | memory            | 000000...00FFAAAAAAAAAAAAAAAAAAA...AABBBBBBBBBBBBB...BBCCCCCCCCCCCCC...CC |
            // | keccak(start, 85) |            ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑ |

            mstore(add(ptr, 0x40), bytecodeHash)
            mstore(add(ptr, 0x20), salt)
            mstore(ptr, deployer) // Right-aligned with 12 preceding garbage bytes
            let start := add(ptr, 0x0b) // The hashed data starts at the final garbage byte which we will set to 0xff
            mstore8(start, 0xff)
            addr := keccak256(start, 85)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/ECDSA.sol)

pragma solidity ^0.8.20;

/**
 * @dev Elliptic Curve Digital Signature Algorithm (ECDSA) operations.
 *
 * These functions can be used to verify that a message was signed by the holder
 * of the private keys of a given address.
 */
library ECDSA {
    enum RecoverError {
        NoError,
        InvalidSignature,
        InvalidSignatureLength,
        InvalidSignatureS
    }

    /**
     * @dev The signature derives the `address(0)`.
     */
    error ECDSAInvalidSignature();

    /**
     * @dev The signature has an invalid length.
     */
    error ECDSAInvalidSignatureLength(uint256 length);

    /**
     * @dev The signature has an S value that is in the upper half order.
     */
    error ECDSAInvalidSignatureS(bytes32 s);

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with `signature` or an error. This will not
     * return address(0) without also returning an error description. Errors are documented using an enum (error type)
     * and a bytes32 providing additional information about the error.
     *
     * If no error is returned, then the address can be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     *
     * Documentation for signature generation:
     * - with https://web3js.readthedocs.io/en/v1.3.4/web3-eth-accounts.html#sign[Web3.js]
     * - with https://docs.ethers.io/v5/api/signer/#Signer-signMessage[ethers]
     */
    function tryRecover(bytes32 hash, bytes memory signature) internal pure returns (address, RecoverError, bytes32) {
        if (signature.length == 65) {
            bytes32 r;
            bytes32 s;
            uint8 v;
            // ecrecover takes the signature parameters, and the only way to get them
            // currently is to use assembly.
            /// @solidity memory-safe-assembly
            assembly {
                r := mload(add(signature, 0x20))
                s := mload(add(signature, 0x40))
                v := byte(0, mload(add(signature, 0x60)))
            }
            return tryRecover(hash, v, r, s);
        } else {
            return (address(0), RecoverError.InvalidSignatureLength, bytes32(signature.length));
        }
    }

    /**
     * @dev Returns the address that signed a hashed message (`hash`) with
     * `signature`. This address can then be used for verification purposes.
     *
     * The `ecrecover` EVM precompile allows for malleable (non-unique) signatures:
     * this function rejects them by requiring the `s` value to be in the lower
     * half order, and the `v` value to be either 27 or 28.
     *
     * IMPORTANT: `hash` _must_ be the result of a hash operation for the
     * verification to be secure: it is possible to craft signatures that
     * recover to arbitrary addresses for non-hashed data. A safe way to ensure
     * this is by receiving a hash of the original message (which may otherwise
     * be too long), and then calling {MessageHashUtils-toEthSignedMessageHash} on it.
     */
    function recover(bytes32 hash, bytes memory signature) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, signature);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `r` and `vs` short-signature fields separately.
     *
     * See https://eips.ethereum.org/EIPS/eip-2098[EIP-2098 short signatures]
     */
    function tryRecover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address, RecoverError, bytes32) {
        unchecked {
            bytes32 s = vs & bytes32(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff);
            // We do not check for an overflow here since the shift operation results in 0 or 1.
            uint8 v = uint8((uint256(vs) >> 255) + 27);
            return tryRecover(hash, v, r, s);
        }
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `r and `vs` short-signature fields separately.
     */
    function recover(bytes32 hash, bytes32 r, bytes32 vs) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, r, vs);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Overload of {ECDSA-tryRecover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function tryRecover(
        bytes32 hash,
        uint8 v,
        bytes32 r,
        bytes32 s
    ) internal pure returns (address, RecoverError, bytes32) {
        // EIP-2 still allows signature malleability for ecrecover(). Remove this possibility and make the signature
        // unique. Appendix F in the Ethereum Yellow paper (https://ethereum.github.io/yellowpaper/paper.pdf), defines
        // the valid range for s in (301): 0 < s < secp256k1n ÷ 2 + 1, and for v in (302): v ∈ {27, 28}. Most
        // signatures from current libraries generate a unique signature with an s-value in the lower half order.
        //
        // If your library generates malleable signatures, such as s-values in the upper range, calculate a new s-value
        // with 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141 - s1 and flip v from 27 to 28 or
        // vice versa. If your library also generates signatures with 0/1 for v instead 27/28, add 27 to v to accept
        // these malleable signatures as well.
        if (uint256(s) > 0x7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF5D576E7357A4501DDFE92F46681B20A0) {
            return (address(0), RecoverError.InvalidSignatureS, s);
        }

        // If the signature is valid (and not malleable), return the signer address
        address signer = ecrecover(hash, v, r, s);
        if (signer == address(0)) {
            return (address(0), RecoverError.InvalidSignature, bytes32(0));
        }

        return (signer, RecoverError.NoError, bytes32(0));
    }

    /**
     * @dev Overload of {ECDSA-recover} that receives the `v`,
     * `r` and `s` signature fields separately.
     */
    function recover(bytes32 hash, uint8 v, bytes32 r, bytes32 s) internal pure returns (address) {
        (address recovered, RecoverError error, bytes32 errorArg) = tryRecover(hash, v, r, s);
        _throwError(error, errorArg);
        return recovered;
    }

    /**
     * @dev Optionally reverts with the corresponding custom error according to the `error` argument provided.
     */
    function _throwError(RecoverError error, bytes32 errorArg) private pure {
        if (error == RecoverError.NoError) {
            return; // no error: do nothing
        } else if (error == RecoverError.InvalidSignature) {
            revert ECDSAInvalidSignature();
        } else if (error == RecoverError.InvalidSignatureLength) {
            revert ECDSAInvalidSignatureLength(uint256(errorArg));
        } else if (error == RecoverError.InvalidSignatureS) {
            revert ECDSAInvalidSignatureS(errorArg);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/EIP712.sol)

pragma solidity ^0.8.20;

import {MessageHashUtils} from "./MessageHashUtils.sol";
import {ShortStrings, ShortString} from "../ShortStrings.sol";
import {IERC5267} from "../../interfaces/IERC5267.sol";

/**
 * @dev https://eips.ethereum.org/EIPS/eip-712[EIP 712] is a standard for hashing and signing of typed structured data.
 *
 * The encoding scheme specified in the EIP requires a domain separator and a hash of the typed structured data, whose
 * encoding is very generic and therefore its implementation in Solidity is not feasible, thus this contract
 * does not implement the encoding itself. Protocols need to implement the type-specific encoding they need in order to
 * produce the hash of their typed data using a combination of `abi.encode` and `keccak256`.
 *
 * This contract implements the EIP 712 domain separator ({_domainSeparatorV4}) that is used as part of the encoding
 * scheme, and the final step of the encoding to obtain the message digest that is then signed via ECDSA
 * ({_hashTypedDataV4}).
 *
 * The implementation of the domain separator was designed to be as efficient as possible while still properly updating
 * the chain id to protect against replay attacks on an eventual fork of the chain.
 *
 * NOTE: This contract implements the version of the encoding known as "v4", as implemented by the JSON RPC method
 * https://docs.metamask.io/guide/signing-data.html[`eth_signTypedDataV4` in MetaMask].
 *
 * NOTE: In the upgradeable version of this contract, the cached values will correspond to the address, and the domain
 * separator of the implementation contract. This will cause the {_domainSeparatorV4} function to always rebuild the
 * separator from the immutable values, which is cheaper than accessing a cached version in cold storage.
 *
 * @custom:oz-upgrades-unsafe-allow state-variable-immutable
 */
abstract contract EIP712 is IERC5267 {
    using ShortStrings for *;

    bytes32 private constant TYPE_HASH =
        keccak256("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)");

    // Cache the domain separator as an immutable value, but also store the chain id that it corresponds to, in order to
    // invalidate the cached domain separator if the chain id changes.
    bytes32 private immutable _cachedDomainSeparator;
    uint256 private immutable _cachedChainId;
    address private immutable _cachedThis;

    bytes32 private immutable _hashedName;
    bytes32 private immutable _hashedVersion;

    ShortString private immutable _name;
    ShortString private immutable _version;
    string private _nameFallback;
    string private _versionFallback;

    /**
     * @dev Initializes the domain separator and parameter caches.
     *
     * The meaning of `name` and `version` is specified in
     * https://eips.ethereum.org/EIPS/eip-712#definition-of-domainseparator[EIP 712]:
     *
     * - `name`: the user readable name of the signing domain, i.e. the name of the DApp or the protocol.
     * - `version`: the current major version of the signing domain.
     *
     * NOTE: These parameters cannot be changed except through a xref:learn::upgrading-smart-contracts.adoc[smart
     * contract upgrade].
     */
    constructor(string memory name, string memory version) {
        _name = name.toShortStringWithFallback(_nameFallback);
        _version = version.toShortStringWithFallback(_versionFallback);
        _hashedName = keccak256(bytes(name));
        _hashedVersion = keccak256(bytes(version));

        _cachedChainId = block.chainid;
        _cachedDomainSeparator = _buildDomainSeparator();
        _cachedThis = address(this);
    }

    /**
     * @dev Returns the domain separator for the current chain.
     */
    function _domainSeparatorV4() internal view returns (bytes32) {
        if (address(this) == _cachedThis && block.chainid == _cachedChainId) {
            return _cachedDomainSeparator;
        } else {
            return _buildDomainSeparator();
        }
    }

    function _buildDomainSeparator() private view returns (bytes32) {
        return keccak256(abi.encode(TYPE_HASH, _hashedName, _hashedVersion, block.chainid, address(this)));
    }

    /**
     * @dev Given an already https://eips.ethereum.org/EIPS/eip-712#definition-of-hashstruct[hashed struct], this
     * function returns the hash of the fully encoded EIP712 message for this domain.
     *
     * This hash can be used together with {ECDSA-recover} to obtain the signer of a message. For example:
     *
     * ```solidity
     * bytes32 digest = _hashTypedDataV4(keccak256(abi.encode(
     *     keccak256("Mail(address to,string contents)"),
     *     mailTo,
     *     keccak256(bytes(mailContents))
     * )));
     * address signer = ECDSA.recover(digest, signature);
     * ```
     */
    function _hashTypedDataV4(bytes32 structHash) internal view virtual returns (bytes32) {
        return MessageHashUtils.toTypedDataHash(_domainSeparatorV4(), structHash);
    }

    /**
     * @dev See {IERC-5267}.
     */
    function eip712Domain()
        public
        view
        virtual
        returns (
            bytes1 fields,
            string memory name,
            string memory version,
            uint256 chainId,
            address verifyingContract,
            bytes32 salt,
            uint256[] memory extensions
        )
    {
        return (
            hex"0f", // 01111
            _EIP712Name(),
            _EIP712Version(),
            block.chainid,
            address(this),
            bytes32(0),
            new uint256[](0)
        );
    }

    /**
     * @dev The name parameter for the EIP712 domain.
     *
     * NOTE: By default this function reads _name which is an immutable value.
     * It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
     */
    // solhint-disable-next-line func-name-mixedcase
    function _EIP712Name() internal view returns (string memory) {
        return _name.toStringWithFallback(_nameFallback);
    }

    /**
     * @dev The version parameter for the EIP712 domain.
     *
     * NOTE: By default this function reads _version which is an immutable value.
     * It only reads from storage if necessary (in case the value is too large to fit in a ShortString).
     */
    // solhint-disable-next-line func-name-mixedcase
    function _EIP712Version() internal view returns (string memory) {
        return _version.toStringWithFallback(_versionFallback);
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/cryptography/MessageHashUtils.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Signature message hash utilities for producing digests to be consumed by {ECDSA} recovery or signing.
 *
 * The library provides methods for generating a hash of a message that conforms to the
 * https://eips.ethereum.org/EIPS/eip-191[EIP 191] and https://eips.ethereum.org/EIPS/eip-712[EIP 712]
 * specifications.
 */
library MessageHashUtils {
    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing a bytes32 `messageHash` with
     * `"\x19Ethereum Signed Message:\n32"` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * NOTE: The `messageHash` parameter is intended to be the result of hashing a raw message with
     * keccak256, although any bytes32 value can be safely used because the final digest will
     * be re-hashed.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes32 messageHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            mstore(0x00, "\x19Ethereum Signed Message:\n32") // 32 is the bytes-length of messageHash
            mstore(0x1c, messageHash) // 0x1c (28) is the length of the prefix
            digest := keccak256(0x00, 0x3c) // 0x3c is the length of the prefix (0x1c) + messageHash (0x20)
        }
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x45` (`personal_sign` messages).
     *
     * The digest is calculated by prefixing an arbitrary `message` with
     * `"\x19Ethereum Signed Message:\n" + len(message)` and hashing the result. It corresponds with the
     * hash signed when using the https://eth.wiki/json-rpc/API#eth_sign[`eth_sign`] JSON-RPC method.
     *
     * See {ECDSA-recover}.
     */
    function toEthSignedMessageHash(bytes memory message) internal pure returns (bytes32) {
        return
            keccak256(bytes.concat("\x19Ethereum Signed Message:\n", bytes(Strings.toString(message.length)), message));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-191 signed data with version
     * `0x00` (data with intended validator).
     *
     * The digest is calculated by prefixing an arbitrary `data` with `"\x19\x00"` and the intended
     * `validator` address. Then hashing the result.
     *
     * See {ECDSA-recover}.
     */
    function toDataWithIntendedValidatorHash(address validator, bytes memory data) internal pure returns (bytes32) {
        return keccak256(abi.encodePacked(hex"19_00", validator, data));
    }

    /**
     * @dev Returns the keccak256 digest of an EIP-712 typed data (EIP-191 version `0x01`).
     *
     * The digest is calculated from a `domainSeparator` and a `structHash`, by prefixing them with
     * `\x19\x01` and hashing the result. It corresponds to the hash signed by the
     * https://eips.ethereum.org/EIPS/eip-712[`eth_signTypedData`] JSON-RPC method as part of EIP-712.
     *
     * See {ECDSA-recover}.
     */
    function toTypedDataHash(bytes32 domainSeparator, bytes32 structHash) internal pure returns (bytes32 digest) {
        /// @solidity memory-safe-assembly
        assembly {
            let ptr := mload(0x40)
            mstore(ptr, hex"19_01")
            mstore(add(ptr, 0x02), domainSeparator)
            mstore(add(ptr, 0x22), structHash)
            digest := keccak256(ptr, 0x42)
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/ERC165.sol)

pragma solidity ^0.8.20;

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

/**
 * @dev Implementation of the {IERC165} interface.
 *
 * Contracts that want to implement ERC165 should inherit from this contract and override {supportsInterface} to check
 * for the additional interface id that will be supported. For example:
 *
 * ```solidity
 * function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
 *     return interfaceId == type(MyInterface).interfaceId || super.supportsInterface(interfaceId);
 * }
 * ```
 */
abstract contract ERC165 is IERC165 {
    /**
     * @dev See {IERC165-supportsInterface}.
     */
    function supportsInterface(bytes4 interfaceId) public view virtual returns (bool) {
        return interfaceId == type(IERC165).interfaceId;
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/introspection/IERC165.sol)

pragma solidity ^0.8.20;

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

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

pragma solidity ^0.8.20;

/**
 * @dev Standard math utilities missing in the Solidity language.
 */
library Math {
    /**
     * @dev Muldiv operation overflow.
     */
    error MathOverflowedMulDiv();

    enum Rounding {
        Floor, // Toward negative infinity
        Ceil, // Toward positive infinity
        Trunc, // Toward zero
        Expand // Away from zero
    }

    /**
     * @dev Returns the addition of two unsigned integers, with an overflow flag.
     */
    function tryAdd(uint256 a, uint256 b) internal pure returns (bool, uint256) {
        unchecked {
            uint256 c = a + b;
            if (c < a) return (false, 0);
            return (true, c);
        }
    }

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

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

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

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

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

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

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

    /**
     * @dev Returns the ceiling of the division of two numbers.
     *
     * This differs from standard division with `/` in that it rounds towards infinity instead
     * of rounding towards zero.
     */
    function ceilDiv(uint256 a, uint256 b) internal pure returns (uint256) {
        if (b == 0) {
            // Guarantee the same behavior as in a regular Solidity division.
            return a / b;
        }

        // (a + b - 1) / b can overflow on addition, so we distribute.
        return a == 0 ? 0 : (a - 1) / b + 1;
    }

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

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

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

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

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

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

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

            uint256 twos = denominator & (0 - denominator);
            assembly {
                // Divide denominator by twos.
                denominator := div(denominator, twos)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    /**
     * @dev Returns whether a provided rounding mode is considered rounding up for unsigned integers.
     */
    function unsignedRoundsUp(Rounding rounding) internal pure returns (bool) {
        return uint8(rounding) % 2 == 1;
    }
}

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

pragma solidity ^0.8.20;

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

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

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

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

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Nonces.sol)
pragma solidity ^0.8.20;

/**
 * @dev Provides tracking nonces for addresses. Nonces will only increment.
 */
abstract contract Nonces {
    /**
     * @dev The nonce used for an `account` is not the expected current nonce.
     */
    error InvalidAccountNonce(address account, uint256 currentNonce);

    mapping(address account => uint256) private _nonces;

    /**
     * @dev Returns the next unused nonce for an address.
     */
    function nonces(address owner) public view virtual returns (uint256) {
        return _nonces[owner];
    }

    /**
     * @dev Consumes a nonce.
     *
     * Returns the current value and increments nonce.
     */
    function _useNonce(address owner) internal virtual returns (uint256) {
        // For each account, the nonce has an initial value of 0, can only be incremented by one, and cannot be
        // decremented or reset. This guarantees that the nonce never overflows.
        unchecked {
            // It is important to do x++ and not ++x here.
            return _nonces[owner]++;
        }
    }

    /**
     * @dev Same as {_useNonce} but checking that `nonce` is the next valid for `owner`.
     */
    function _useCheckedNonce(address owner, uint256 nonce) internal virtual {
        uint256 current = _useNonce(owner);
        if (nonce != current) {
            revert InvalidAccountNonce(owner, current);
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ShortStrings.sol)

pragma solidity ^0.8.20;

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

// | string  | 0xAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   |
// | length  | 0x                                                              BB |
type ShortString is bytes32;

/**
 * @dev This library provides functions to convert short memory strings
 * into a `ShortString` type that can be used as an immutable variable.
 *
 * Strings of arbitrary length can be optimized using this library if
 * they are short enough (up to 31 bytes) by packing them with their
 * length (1 byte) in a single EVM word (32 bytes). Additionally, a
 * fallback mechanism can be used for every other case.
 *
 * Usage example:
 *
 * ```solidity
 * contract Named {
 *     using ShortStrings for *;
 *
 *     ShortString private immutable _name;
 *     string private _nameFallback;
 *
 *     constructor(string memory contractName) {
 *         _name = contractName.toShortStringWithFallback(_nameFallback);
 *     }
 *
 *     function name() external view returns (string memory) {
 *         return _name.toStringWithFallback(_nameFallback);
 *     }
 * }
 * ```
 */
library ShortStrings {
    // Used as an identifier for strings longer than 31 bytes.
    bytes32 private constant FALLBACK_SENTINEL = 0x00000000000000000000000000000000000000000000000000000000000000FF;

    error StringTooLong(string str);
    error InvalidShortString();

    /**
     * @dev Encode a string of at most 31 chars into a `ShortString`.
     *
     * This will trigger a `StringTooLong` error is the input string is too long.
     */
    function toShortString(string memory str) internal pure returns (ShortString) {
        bytes memory bstr = bytes(str);
        if (bstr.length > 31) {
            revert StringTooLong(str);
        }
        return ShortString.wrap(bytes32(uint256(bytes32(bstr)) | bstr.length));
    }

    /**
     * @dev Decode a `ShortString` back to a "normal" string.
     */
    function toString(ShortString sstr) internal pure returns (string memory) {
        uint256 len = byteLength(sstr);
        // using `new string(len)` would work locally but is not memory safe.
        string memory str = new string(32);
        /// @solidity memory-safe-assembly
        assembly {
            mstore(str, len)
            mstore(add(str, 0x20), sstr)
        }
        return str;
    }

    /**
     * @dev Return the length of a `ShortString`.
     */
    function byteLength(ShortString sstr) internal pure returns (uint256) {
        uint256 result = uint256(ShortString.unwrap(sstr)) & 0xFF;
        if (result > 31) {
            revert InvalidShortString();
        }
        return result;
    }

    /**
     * @dev Encode a string into a `ShortString`, or write it to storage if it is too long.
     */
    function toShortStringWithFallback(string memory value, string storage store) internal returns (ShortString) {
        if (bytes(value).length < 32) {
            return toShortString(value);
        } else {
            StorageSlot.getStringSlot(store).value = value;
            return ShortString.wrap(FALLBACK_SENTINEL);
        }
    }

    /**
     * @dev Decode a string that was encoded to `ShortString` or written to storage using {setWithFallback}.
     */
    function toStringWithFallback(ShortString value, string storage store) internal pure returns (string memory) {
        if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
            return toString(value);
        } else {
            return store;
        }
    }

    /**
     * @dev Return the length of a string that was encoded to `ShortString` or written to storage using
     * {setWithFallback}.
     *
     * WARNING: This will return the "byte length" of the string. This may not reflect the actual length in terms of
     * actual characters as the UTF-8 encoding of a single character can span over multiple bytes.
     */
    function byteLengthWithFallback(ShortString value, string storage store) internal view returns (uint256) {
        if (ShortString.unwrap(value) != FALLBACK_SENTINEL) {
            return byteLength(value);
        } else {
            return bytes(store).length;
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/StorageSlot.sol)
// This file was procedurally generated from scripts/generate/templates/StorageSlot.js.

pragma solidity ^0.8.20;

/**
 * @dev Library for reading and writing primitive types to specific storage slots.
 *
 * Storage slots are often used to avoid storage conflict when dealing with upgradeable contracts.
 * This library helps with reading and writing to such slots without the need for inline assembly.
 *
 * The functions in this library return Slot structs that contain a `value` member that can be used to read or write.
 *
 * Example usage to set ERC1967 implementation slot:
 * ```solidity
 * contract ERC1967 {
 *     bytes32 internal constant _IMPLEMENTATION_SLOT = 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc;
 *
 *     function _getImplementation() internal view returns (address) {
 *         return StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value;
 *     }
 *
 *     function _setImplementation(address newImplementation) internal {
 *         require(newImplementation.code.length > 0);
 *         StorageSlot.getAddressSlot(_IMPLEMENTATION_SLOT).value = newImplementation;
 *     }
 * }
 * ```
 */
library StorageSlot {
    struct AddressSlot {
        address value;
    }

    struct BooleanSlot {
        bool value;
    }

    struct Bytes32Slot {
        bytes32 value;
    }

    struct Uint256Slot {
        uint256 value;
    }

    struct StringSlot {
        string value;
    }

    struct BytesSlot {
        bytes value;
    }

    /**
     * @dev Returns an `AddressSlot` with member `value` located at `slot`.
     */
    function getAddressSlot(bytes32 slot) internal pure returns (AddressSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BooleanSlot` with member `value` located at `slot`.
     */
    function getBooleanSlot(bytes32 slot) internal pure returns (BooleanSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Bytes32Slot` with member `value` located at `slot`.
     */
    function getBytes32Slot(bytes32 slot) internal pure returns (Bytes32Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `Uint256Slot` with member `value` located at `slot`.
     */
    function getUint256Slot(bytes32 slot) internal pure returns (Uint256Slot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` with member `value` located at `slot`.
     */
    function getStringSlot(bytes32 slot) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `StringSlot` representation of the string storage pointer `store`.
     */
    function getStringSlot(string storage store) internal pure returns (StringSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` with member `value` located at `slot`.
     */
    function getBytesSlot(bytes32 slot) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := slot
        }
    }

    /**
     * @dev Returns an `BytesSlot` representation of the bytes storage pointer `store`.
     */
    function getBytesSlot(bytes storage store) internal pure returns (BytesSlot storage r) {
        /// @solidity memory-safe-assembly
        assembly {
            r.slot := store.slot
        }
    }
}

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/Strings.sol)

pragma solidity ^0.8.20;

import {Math} from "./math/Math.sol";
import {SignedMath} from "./math/SignedMath.sol";

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

    /**
     * @dev The `value` string doesn't fit in the specified `length`.
     */
    error StringsInsufficientHexLength(uint256 value, uint256 length);

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    struct SD59x18 {
        int256 value;
    }

    struct UD60x18 {
        uint256 value;
    }

    /// STORAGE ///

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

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

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

    /// FUNCTIONS ///

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

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

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

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

    /// @notice Calculates floor(x*y÷denominator) with full precision.
    ///
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv.
    ///
    /// Requirements:
    /// - The denominator cannot be zero.
    /// - The result must fit within uint256.
    ///
    /// Caveats:
    /// - This function does not work with fixed-point numbers.
    ///
    /// @param x The multiplicand as an uint256.
    /// @param y The multiplier as an uint256.
    /// @param denominator The divisor as an uint256.
    /// @return result The result as an uint256.
    function mulDiv(
        uint256 x,
        uint256 y,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        // 512-bit multiply [prod1 prod0] = x * y. Compute the product mod 2^256 and mod 2^256 - 1, then use
        // use the Chinese Remainder Theorem to reconstruct the 512 bit result. The result is stored in two 256
        // variables such that product = prod1 * 2^256 + prod0.
        uint256 prod0; // Least significant 256 bits of the product
        uint256 prod1; // Most significant 256 bits of the product
        assembly {
            let mm := mulmod(x, y, not(0))
            prod0 := mul(x, y)
            prod1 := sub(sub(mm, prod0), lt(mm, prod0))
        }

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

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

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

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

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

        // Factor powers of two out of denominator and compute largest power of two divisor of denominator. Always >= 1.
        // See https://cs.stackexchange.com/q/138556/92363.
        unchecked {
            // Does not overflow because the denominator cannot be zero at this stage in the function.
            uint256 lpotdod = denominator & (~denominator + 1);
            assembly {
                // Divide denominator by lpotdod.
                denominator := div(denominator, lpotdod)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

import "./PRBMath.sol";

/// @title PRBMathSD59x18
/// @author Paul Razvan Berg
/// @notice Smart contract library for advanced fixed-point math that works with int256 numbers considered to have 18
/// trailing decimals. We call this number representation signed 59.18-decimal fixed-point, since the numbers can have
/// a sign and there can be up to 59 digits in the integer part and up to 18 decimals in the fractional part. The numbers
/// are bound by the minimum and the maximum values permitted by the Solidity type int256.
library PRBMathSD59x18 {
    /// @dev log2(e) as a signed 59.18-decimal fixed-point number.
    int256 internal constant LOG2_E = 1_442695040888963407;

    /// @dev Half the SCALE number.
    int256 internal constant HALF_SCALE = 5e17;

    /// @dev The maximum value a signed 59.18-decimal fixed-point number can have.
    int256 internal constant MAX_SD59x18 =
        57896044618658097711785492504343953926634992332820282019728_792003956564819967;

    /// @dev The maximum whole value a signed 59.18-decimal fixed-point number can have.
    int256 internal constant MAX_WHOLE_SD59x18 =
        57896044618658097711785492504343953926634992332820282019728_000000000000000000;

    /// @dev The minimum value a signed 59.18-decimal fixed-point number can have.
    int256 internal constant MIN_SD59x18 =
        -57896044618658097711785492504343953926634992332820282019728_792003956564819968;

    /// @dev The minimum whole value a signed 59.18-decimal fixed-point number can have.
    int256 internal constant MIN_WHOLE_SD59x18 =
        -57896044618658097711785492504343953926634992332820282019728_000000000000000000;

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

    /// INTERNAL FUNCTIONS ///

    /// @notice Calculate the absolute value of x.
    ///
    /// @dev Requirements:
    /// - x must be greater than MIN_SD59x18.
    ///
    /// @param x The number to calculate the absolute value for.
    /// @param result The absolute value of x.
    function abs(int256 x) internal pure returns (int256 result) {
        unchecked {
            if (x == MIN_SD59x18) {
                revert PRBMathSD59x18__AbsInputTooSmall();
            }
            result = x < 0 ? -x : x;
        }
    }

    /// @notice Calculates the arithmetic average of x and y, rounding down.
    /// @param x The first operand as a signed 59.18-decimal fixed-point number.
    /// @param y The second operand as a signed 59.18-decimal fixed-point number.
    /// @return result The arithmetic average as a signed 59.18-decimal fixed-point number.
    function avg(int256 x, int256 y) internal pure returns (int256 result) {
        // The operations can never overflow.
        unchecked {
            int256 sum = (x >> 1) + (y >> 1);
            if (sum < 0) {
                // If at least one of x and y is odd, we add 1 to the result. This is because shifting negative numbers to the
                // right rounds down to infinity.
                assembly {
                    result := add(sum, and(or(x, y), 1))
                }
            } else {
                // If both x and y are odd, we add 1 to the result. This is because if both numbers are odd, the 0.5
                // remainder gets truncated twice.
                result = sum + (x & y & 1);
            }
        }
    }

    /// @notice Yields the least greatest signed 59.18 decimal fixed-point number greater than or equal to x.
    ///
    /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
    /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
    ///
    /// Requirements:
    /// - x must be less than or equal to MAX_WHOLE_SD59x18.
    ///
    /// @param x The signed 59.18-decimal fixed-point number to ceil.
    /// @param result The least integer greater than or equal to x, as a signed 58.18-decimal fixed-point number.
    function ceil(int256 x) internal pure returns (int256 result) {
        if (x > MAX_WHOLE_SD59x18) {
            revert PRBMathSD59x18__CeilOverflow(x);
        }
        unchecked {
            int256 remainder = x % SCALE;
            if (remainder == 0) {
                result = x;
            } else {
                // Solidity uses C fmod style, which returns a modulus with the same sign as x.
                result = x - remainder;
                if (x > 0) {
                    result += SCALE;
                }
            }
        }
    }

    /// @notice Divides two signed 59.18-decimal fixed-point numbers, returning a new signed 59.18-decimal fixed-point number.
    ///
    /// @dev Variant of "mulDiv" that works with signed numbers. Works by computing the signs and the absolute values separately.
    ///
    /// Requirements:
    /// - All from "PRBMath.mulDiv".
    /// - None of the inputs can be MIN_SD59x18.
    /// - The denominator cannot be zero.
    /// - The result must fit within int256.
    ///
    /// Caveats:
    /// - All from "PRBMath.mulDiv".
    ///
    /// @param x The numerator as a signed 59.18-decimal fixed-point number.
    /// @param y The denominator as a signed 59.18-decimal fixed-point number.
    /// @param result The quotient as a signed 59.18-decimal fixed-point number.
    function div(int256 x, int256 y) internal pure returns (int256 result) {
        if (x == MIN_SD59x18 || y == MIN_SD59x18) {
            revert PRBMathSD59x18__DivInputTooSmall();
        }

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

        // Compute the absolute value of (x*SCALE)÷y. The result must fit within int256.
        uint256 rAbs = PRBMath.mulDiv(ax, uint256(SCALE), ay);
        if (rAbs > uint256(MAX_SD59x18)) {
            revert PRBMathSD59x18__DivOverflow(rAbs);
        }

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

        // XOR over sx and sy. This is basically checking whether the inputs have the same sign. If yes, the result
        // should be positive. Otherwise, it should be negative.
        result = sx ^ sy == 1 ? -int256(rAbs) : int256(rAbs);
    }

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

    /// @notice Calculates the natural exponent of x.
    ///
    /// @dev Based on the insight that e^x = 2^(x * log2(e)).
    ///
    /// Requirements:
    /// - All from "log2".
    /// - x must be less than 133.084258667509499441.
    ///
    /// Caveats:
    /// - All from "exp2".
    /// - For any x less than -41.446531673892822322, the result is zero.
    ///
    /// @param x The exponent as a signed 59.18-decimal fixed-point number.
    /// @return result The result as a signed 59.18-decimal fixed-point number.
    function exp(int256 x) internal pure returns (int256 result) {
        // Without this check, the value passed to "exp2" would be less than -59.794705707972522261.
        if (x < -41_446531673892822322) {
            return 0;
        }

        // Without this check, the value passed to "exp2" would be greater than 192.
        if (x >= 133_084258667509499441) {
            revert PRBMathSD59x18__ExpInputTooBig(x);
        }

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

    /// @notice Calculates the binary exponent of x using the binary fraction method.
    ///
    /// @dev See https://ethereum.stackexchange.com/q/79903/24693.
    ///
    /// Requirements:
    /// - x must be 192 or less.
    /// - The result must fit within MAX_SD59x18.
    ///
    /// Caveats:
    /// - For any x less than -59.794705707972522261, the result is zero.
    ///
    /// @param x The exponent as a signed 59.18-decimal fixed-point number.
    /// @return result The result as a signed 59.18-decimal fixed-point number.
    function exp2(int256 x) internal pure returns (int256 result) {
        // This works because 2^(-x) = 1/2^x.
        if (x < 0) {
            // 2^59.794705707972522262 is the maximum number whose inverse does not truncate down to zero.
            if (x < -59_794705707972522261) {
                return 0;
            }

            // Do the fixed-point inversion inline to save gas. The numerator is SCALE * SCALE.
            unchecked {
                result = 1e36 / exp2(-x);
            }
        } else {
            // 2^192 doesn't fit within the 192.64-bit format used internally in this function.
            if (x >= 192e18) {
                revert PRBMathSD59x18__Exp2InputTooBig(x);
            }

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

                // Safe to convert the result to int256 directly because the maximum input allowed is 192.
                result = int256(PRBMath.exp2(x192x64));
            }
        }
    }

    /// @notice Yields the greatest signed 59.18 decimal fixed-point number less than or equal to x.
    ///
    /// @dev Optimized for fractional value inputs, because for every whole value there are (1e18 - 1) fractional counterparts.
    /// See https://en.wikipedia.org/wiki/Floor_and_ceiling_functions.
    ///
    /// Requirements:
    /// - x must be greater than or equal to MIN_WHOLE_SD59x18.
    ///
    /// @param x The signed 59.18-decimal fixed-point number to floor.
    /// @param result The greatest integer less than or equal to x, as a signed 58.18-decimal fixed-point number.
    function floor(int256 x) internal pure returns (int256 result) {
        if (x < MIN_WHOLE_SD59x18) {
            revert PRBMathSD59x18__FloorUnderflow(x);
        }
        unchecked {
            int256 remainder = x % SCALE;
            if (remainder == 0) {
                result = x;
            } else {
                // Solidity uses C fmod style, which returns a modulus with the same sign as x.
                result = x - remainder;
                if (x < 0) {
                    result -= SCALE;
                }
            }
        }
    }

    /// @notice Yields the excess beyond the floor of x for positive numbers and the part of the number to the right
    /// of the radix point for negative numbers.
    /// @dev Based on the odd function definition. https://en.wikipedia.org/wiki/Fractional_part
    /// @param x The signed 59.18-decimal fixed-point number to get the fractional part of.
    /// @param result The fractional part of x as a signed 59.18-decimal fixed-point number.
    function frac(int256 x) internal pure returns (int256 result) {
        unchecked {
            result = x % SCALE;
        }
    }

    /// @notice Converts a number from basic integer form to signed 59.18-decimal fixed-point representation.
    ///
    /// @dev Requirements:
    /// - x must be greater than or equal to MIN_SD59x18 divided by SCALE.
    /// - x must be less than or equal to MAX_SD59x18 divided by SCALE.
    ///
    /// @param x The basic integer to convert.
    /// @param result The same number in signed 59.18-decimal fixed-point representation.
    function fromInt(int256 x) internal pure returns (int256 result) {
        unchecked {
            if (x < MIN_SD59x18 / SCALE) {
                revert PRBMathSD59x18__FromIntUnderflow(x);
            }
            if (x > MAX_SD59x18 / SCALE) {
                revert PRBMathSD59x18__FromIntOverflow(x);
            }
            result = x * SCALE;
        }
    }

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

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

            // The product cannot be negative.
            if (xy < 0) {
                revert PRBMathSD59x18__GmNegativeProduct(x, y);
            }

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

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

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

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

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

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

    /// @notice Calculates the binary logarithm of x.
    ///
    /// @dev Based on the iterative approximation algorithm.
    /// https://en.wikipedia.org/wiki/Binary_logarithm#Iterative_approximation
    ///
    /// Requirements:
    /// - x must be greater than zero.
    ///
    /// Caveats:
    /// - The results are not perfectly accurate to the last decimal, due to the lossy precision of the iterative approximation.
    ///
    /// @param x The signed 59.18-decimal fixed-point number for which to calculate the binary logarithm.
    /// @return result The binary logarithm as a signed 59.18-decimal fixed-point number.
    function log2(int256 x) internal pure returns (int256 result) {
        if (x <= 0) {
            revert PRBMathSD59x18__LogInputTooSmall(x);
        }
        unchecked {
            // This works because log2(x) = -log2(1/x).
            int256 sign;
            if (x >= SCALE) {
                sign = 1;
            } else {
                sign = -1;
                // Do the fixed-point inversion inline to save gas. The numerator is SCALE * SCALE.
                assembly {
                    x := div(1000000000000000000000000000000000000, x)
                }
            }

            // Calculate the integer part of the logarithm and add it to the result and finally calculate y = x * 2^(-n).
            uint256 n = PRBMath.mostSignificantBit(uint256(x / SCALE));

            // The integer part of the logarithm as a signed 59.18-decimal fixed-point number. The operation can't overflow
            // because n is maximum 255, SCALE is 1e18 and sign is either 1 or -1.
            result = int256(n) * SCALE;

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

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

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

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

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

    /// @notice Multiplies two signed 59.18-decimal fixed-point numbers together, returning a new signed 59.18-decimal
    /// fixed-point number.
    ///
    /// @dev Variant of "mulDiv" that works with signed numbers and employs constant folding, i.e. the denominator is
    /// always 1e18.
    ///
    /// Requirements:
    /// - All from "PRBMath.mulDivFixedPoint".
    /// - None of the inputs can be MIN_SD59x18
    /// - The result must fit within MAX_SD59x18.
    ///
    /// Caveats:
    /// - The body is purposely left uncommented; see the NatSpec comments in "PRBMath.mulDiv" to understand how this works.
    ///
    /// @param x The multiplicand as a signed 59.18-decimal fixed-point number.
    /// @param y The multiplier as a signed 59.18-decimal fixed-point number.
    /// @return result The product as a signed 59.18-decimal fixed-point number.
    function mul(int256 x, int256 y) internal pure returns (int256 result) {
        if (x == MIN_SD59x18 || y == MIN_SD59x18) {
            revert PRBMathSD59x18__MulInputTooSmall();
        }

        unchecked {
            uint256 ax;
            uint256 ay;
            ax = x < 0 ? uint256(-x) : uint256(x);
            ay = y < 0 ? uint256(-y) : uint256(y);

            uint256 rAbs = PRBMath.mulDivFixedPoint(ax, ay);
            if (rAbs > uint256(MAX_SD59x18)) {
                revert PRBMathSD59x18__MulOverflow(rAbs);
            }

            uint256 sx;
            uint256 sy;
            assembly {
                sx := sgt(x, sub(0, 1))
                sy := sgt(y, sub(0, 1))
            }
            result = sx ^ sy == 1 ? -int256(rAbs) : int256(rAbs);
        }
    }

    /// @notice Returns PI as a signed 59.18-decimal fixed-point number.
    function pi() internal pure returns (int256 result) {
        result = 3_141592653589793238;
    }

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

    /// @notice Raises x (signed 59.18-decimal fixed-point number) to the power of y (basic unsigned integer) using the
    /// famous algorithm "exponentiation by squaring".
    ///
    /// @dev See https://en.wikipedia.org/wiki/Exponentiation_by_squaring
    ///
    /// Requirements:
    /// - All from "abs" and "PRBMath.mulDivFixedPoint".
    /// - The result must fit within MAX_SD59x18.
    ///
    /// Caveats:
    /// - All from "PRBMath.mulDivFixedPoint".
    /// - Assumes 0^0 is 1.
    ///
    /// @param x The base as a signed 59.18-decimal fixed-point number.
    /// @param y The exponent as an uint256.
    /// @return result The result as a signed 59.18-decimal fixed-point number.
    function powu(int256 x, uint256 y) internal pure returns (int256 result) {
        uint256 xAbs = uint256(abs(x));

        // Calculate the first iteration of the loop in advance.
        uint256 rAbs = y & 1 > 0 ? xAbs : uint256(SCALE);

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

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

        // The result must fit within the 59.18-decimal fixed-point representation.
        if (rAbs > uint256(MAX_SD59x18)) {
            revert PRBMathSD59x18__PowuOverflow(rAbs);
        }

        // Is the base negative and the exponent an odd number?
        bool isNegative = x < 0 && y & 1 == 1;
        result = isNegative ? -int256(rAbs) : int256(rAbs);
    }

    /// @notice Returns 1 as a signed 59.18-decimal fixed-point number.
    function scale() internal pure returns (int256 result) {
        result = SCALE;
    }

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

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

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

import "@prb/math/contracts/PRBMathSD59x18.sol";

/*
ARCHITECTURE DESIGN NOTES

The storage is a generalised AMM that can be used for any asset type, including scalars, vectors and matrices.
The storage is designed to be as gas efficient as possible, and to be as flexible as possible.
The storage is designed to be used with the QuantAMM contract, but can be used with any contract that implements the QuantAMM interface.
A couple of assumptions underpin the design: 

- 1 that you can pack only the same type of int.
- 2 that with the matrices calculations are only done on square matrices.
- 3 All checks regarding array lengths are done on registration of the pool and are fixed.

 */

// On casting to uint first, Solidity does not revert when casting negative values
//it just interprets the bitstring as a uint.
//Normally this is unintended behaviour, but here it is actually useful

/// @title QuantAMMStorage contract for QuantAMM storage slot packing and unpacking
/// @notice Contains the logic for packing and unpacking storage slots with 128 bit integers
abstract contract QuantAMMStorage {
    //for gas efficiency, likely compiler does this anyway
    int256 private constant MAX128 = int256(type(int128).max);
    int256 private constant MIN128 = int256(type(int128).min);

    /// @notice Packs two 128 bit integers into one 256 bit integer
    /// @param _leftInt the left integer to pack
    /// @param _rightInt the right integer to pack
    function _quantAMMPackTwo128(int256 _leftInt, int256 _rightInt) internal pure returns (int256 packed) {
        require((_leftInt <= MAX128) && (_rightInt <= MAX128), "Overflow");
        require((_leftInt >= MIN128) && (_rightInt >= MIN128), "Underflow");
        packed = (_leftInt << 128) | int256(uint256(_rightInt << 128) >> 128);
    }
}

/// @title QuantAMMStorage contract for QuantAMM storage slot packing and unpacking scalar quantAMM Base weights
/// @notice Contains the logic for packing and unpacking storage slots with 32 bit integers
abstract contract ScalarQuantAMMBaseStorage {
    //for gas efficiency, likely compiler does this anyway
    int256 private constant MAX32 = int256(type(int32).max);
    int256 private constant MIN32 = int256(type(int32).min);

    /// @notice Packs eight 32 bit integers into one 256 bit integer
    /// @param _firstInt the first integer to pack
    /// @param _secondInt the second integer to pack
    /// @param _thirdInt the third integer to pack
    /// @param _fourthInt the fourth integer to pack
    /// @param _fifthInt the fifth integer to pack
    /// @param _sixthInt the sixth integer to pack
    /// @param _seventhInt the seventh integer to pack
    /// @param _eighthInt the eighth integer to pack
    function quantAMMPackEight32(
        int256 _firstInt,
        int256 _secondInt,
        int256 _thirdInt,
        int256 _fourthInt,
        int256 _fifthInt,
        int256 _sixthInt,
        int256 _seventhInt,
        int256 _eighthInt
    ) internal pure returns (int256 packed) {
        require(
            _firstInt <= MAX32 &&
                _firstInt >= MIN32 &&
                _secondInt <= MAX32 &&
                _secondInt >= MIN32 &&
                _thirdInt <= MAX32 &&
                _thirdInt >= MIN32 &&
                _fourthInt <= MAX32 &&
                _fourthInt >= MIN32 &&
                _fifthInt <= MAX32 &&
                _fifthInt >= MIN32 &&
                _sixthInt <= MAX32 &&
                _sixthInt >= MIN32 &&
                _seventhInt <= MAX32 &&
                _seventhInt >= MIN32 &&
                _eighthInt <= MAX32 &&
                _eighthInt >= MIN32,
            //CODEHAWKS INFO /s/505 /s/706
            "Overflow/Underflow"
        );

        int256 firstPacked = int256(uint256(_firstInt << 224) >> 224) << 224;
        int256 secondPacked = int256(uint256(_secondInt << 224) >> 224) << 192;
        int256 thirdPacked = int256(uint256(_thirdInt << 224) >> 224) << 160;
        int256 fourthPacked = int256(uint256(_fourthInt << 224) >> 224) << 128;
        int256 fifthPacked = int256(uint256(_fifthInt << 224) >> 224) << 96;
        int256 sixthPacked = int256(uint256(_sixthInt << 224) >> 224) << 64;
        int256 seventhPacked = int256(uint256(_seventhInt << 224) >> 224) << 32;
        int256 eighthPacked = int256(uint256(_eighthInt << 224) >> 224);

        packed =
            firstPacked |
            secondPacked |
            thirdPacked |
            fourthPacked |
            fifthPacked |
            sixthPacked |
            seventhPacked |
            eighthPacked;
    }

    /// @notice Unpacks a 256 bit integer into 8 32 bit integers
    /// @param sourceElem the integer to unpack
    function quantAMMUnpack32(int256 sourceElem) internal pure returns (int256[] memory targetArray) {
        targetArray = new int256[](8);
        targetArray[0] = (sourceElem >> 224) * 1e9;
        targetArray[1] = int256(int32(sourceElem >> 192)) * 1e9;
        targetArray[2] = int256(int32(sourceElem >> 160)) * 1e9;
        targetArray[3] = int256(int32(sourceElem >> 128)) * 1e9;
        targetArray[4] = int256(int32(sourceElem >> 96)) * 1e9;
        targetArray[5] = int256(int32(sourceElem >> 64)) * 1e9;
        targetArray[6] = int256(int32(sourceElem >> 32)) * 1e9;
        targetArray[7] = int256(int32(sourceElem)) * 1e9;

        return targetArray;
    }

    /// @notice Unpacks a 256 bit integer into n 32 bit integers
    /// @param _sourceArray the array to unpack
    /// @param _targetArrayLength the number of 32 bit integers to unpack
    function quantAMMUnpack32Array(
        int256[] memory _sourceArray,
        uint _targetArrayLength
    ) internal pure returns (int256[] memory targetArray) {
        require(_sourceArray.length * 8 >= _targetArrayLength, "SRC!=TGT");
        targetArray = new int256[](_targetArrayLength);
        uint targetIndex;
        uint sourceArrayLengthMinusOne = _sourceArray.length - 1;
        bool divisibleByEight = _targetArrayLength % 8 == 0;
        uint stickyEndSourceElem;

        //more than the first slot so need to loop
        if (_targetArrayLength > 8) {
            for (uint i; i < _sourceArray.length; ) {
                if (divisibleByEight || i < sourceArrayLengthMinusOne) {
                    unchecked {
                        int256 sourceElem = _sourceArray[i];
                        targetArray[targetIndex] = (sourceElem >> 224) * 1e9;
                        ++targetIndex;

                        targetArray[targetIndex] = int256(int32(sourceElem >> 192)) * 1e9;
                        ++targetIndex;

                        targetArray[targetIndex] = int256(int32(sourceElem >> 160)) * 1e9;
                        ++targetIndex;

                        targetArray[targetIndex] = int256(int32(sourceElem >> 128)) * 1e9;
                        ++targetIndex;

                        targetArray[targetIndex] = int256(int32(sourceElem >> 96)) * 1e9;
                        ++targetIndex;

                        targetArray[targetIndex] = int256(int32(sourceElem >> 64)) * 1e9;
                        ++targetIndex;

                        targetArray[targetIndex] = int256(int32(sourceElem >> 32)) * 1e9;
                        ++targetIndex;

                        targetArray[targetIndex] = int256(int32(sourceElem)) * 1e9;
                        ++targetIndex;
                    }
                }
                unchecked {
                    ++i;
                }
            }
            //get sticky end slot
            if (!divisibleByEight) {
                unchecked {
                    stickyEndSourceElem = _sourceArray.length - 1;
                }
            }
        } else if (_targetArrayLength == 8) {
            //effiency at the price of increased function length works out cheaper
            //hardcoded index access is cheaper than a loop
            int256 sourceElem = _sourceArray[0];
            targetArray[0] = (sourceElem >> 224) * 1e9;
            targetArray[1] = int256(int32(sourceElem >> 192)) * 1e9;
            targetArray[2] = int256(int32(sourceElem >> 160)) * 1e9;
            targetArray[3] = int256(int32(sourceElem >> 128)) * 1e9;
            targetArray[4] = int256(int32(sourceElem >> 96)) * 1e9;
            targetArray[5] = int256(int32(sourceElem >> 64)) * 1e9;
            targetArray[6] = int256(int32(sourceElem >> 32)) * 1e9;
            targetArray[7] = int256(int32(sourceElem)) * 1e9;
        }

        // deal with up to 7 sticky end elements
        if (!divisibleByEight) {
            unchecked {
                uint offset = 224;
                for (uint i = targetIndex; i < targetArray.length; ) {
                    targetArray[i] = int256(int32(_sourceArray[stickyEndSourceElem] >> offset)) * 1e9;
                    offset -= 32;
                    ++i;
                }
            }
        }
    }

    /// @notice Packs an array of 32 bit integers into an array of 256 bit integers
    /// @param _sourceArray the array to pack
    function quantAMMPack32Array(int256[] memory _sourceArray) internal pure returns (int256[] memory targetArray) {
        uint targetArrayLength;
        uint storageIndex;
        uint nonStickySourceLength;

        //logic if more than 1 slot is required to store the array
        if (_sourceArray.length >= 8) {
            for (uint i = _sourceArray.length; i >= 8; ) {
                unchecked {
                    if (i % 8 == 0) {
                        nonStickySourceLength = i;
                        break;
                    }
                    --i;
                }
            }

            //add one for the sticky end to be dealt with later
            if (_sourceArray.length != nonStickySourceLength) {
                unchecked {
                    targetArrayLength = (nonStickySourceLength / 8) + 1;
                }
            } else {
                unchecked {
                    targetArrayLength = (nonStickySourceLength) / 8;
                }
            }

            targetArray = new int256[](targetArrayLength);

            for (uint i; i < nonStickySourceLength; ) {
                unchecked {
                    targetArray[storageIndex] = quantAMMPackEight32(
                        int256(_sourceArray[i] / 1e9),
                        int256(_sourceArray[i + 1] / 1e9),
                        int256(_sourceArray[i + 2] / 1e9),
                        int256(_sourceArray[i + 3] / 1e9),
                        int256(_sourceArray[i + 4] / 1e9),
                        int256(_sourceArray[i + 5] / 1e9),
                        int256(_sourceArray[i + 6] / 1e9),
                        int256(_sourceArray[i + 7] / 1e9)
                    );

                    i += 8;
                    ++storageIndex;
                }
            }
        }

        if (targetArrayLength == 0) {
            unchecked {
                //CODEHAWKS INFO /s/8
                targetArrayLength = (nonStickySourceLength / 8) + 1;
                targetArray = new int256[](targetArrayLength);
            }
        }
        //pack up to 7 sticky ends
        uint stickyEndElems = _sourceArray.length - nonStickySourceLength;
        if (stickyEndElems > 0) {
            uint offset = 224;
            int256 packed;
            for (uint i = nonStickySourceLength; i < _sourceArray.length; ) {
                unchecked {
                    int256 elem = _sourceArray[i] / 1e9;

                    //CODEHAWKS INFO /s/505 /s/706
                    require(elem <= MAX32 && elem >= MIN32, "Overflow/Underflow");
                    packed |= int256(uint256(elem << 224) >> 224) << offset;
                    offset -= 32;
                    ++i;
                }
            }
            targetArray[storageIndex] = packed;
        }
    }
}

/// @title QuantAMMStorage contract for QuantAMM storage slot packing and unpacking scalar rule weights
/// @notice Contains the logic for packing and unpacking storage slots with 128 bit integers for rule weights
abstract contract ScalarRuleQuantAMMStorage is QuantAMMStorage {
    /// @notice Packs n 128 bit integers into n/2 256 bit integers
    /// @param _sourceArray the array to pack
    function _quantAMMPack128Array(int256[] memory _sourceArray) internal pure returns (int256[] memory targetArray) {
        uint sourceArrayLength = _sourceArray.length;
        uint targetArrayLength = sourceArrayLength;
        uint storageIndex;

        require(_sourceArray.length != 0, "LEN0");

        if (_sourceArray.length % 2 == 0) {
            unchecked {
                targetArrayLength = (targetArrayLength) / 2;
            }
            targetArray = new int256[](targetArrayLength);
            for (uint i; i < sourceArrayLength - 1; ) {
                targetArray[storageIndex] = _quantAMMPackTwo128(_sourceArray[i], _sourceArray[i + 1]);
                unchecked {
                    i += 2;
                    ++storageIndex;
                }
            }
        } else {
            int256 lastArrayItem = _sourceArray[_sourceArray.length - 1];
            require(
                (lastArrayItem >= int256(type(int128).min)) && (lastArrayItem <= int256(type(int128).max)),
                "Last array element overflow"
            );
            unchecked {
                targetArrayLength = ((targetArrayLength - 1) / 2) + 1;
            }
            targetArray = new int256[](targetArrayLength);
            uint sourceArrayLengthMinusTwo = sourceArrayLength - 2;
            for (uint i; i < sourceArrayLengthMinusTwo; ) {
                targetArray[storageIndex] = _quantAMMPackTwo128(_sourceArray[i], _sourceArray[i + 1]);
                unchecked {
                    i += 2;
                    ++storageIndex;
                }
            }
            targetArray[storageIndex] = int256(int128(_sourceArray[sourceArrayLength - 1]));
        }
    }

    /// @notice Unpacks n/2 256 bit integers into n 128 bit integers
    /// @param _sourceArray the array to unpack
    /// @param _targetArrayLength the number of 128 bit integers to unpack
    function _quantAMMUnpack128Array(
        int256[] memory _sourceArray,
        uint _targetArrayLength
    ) internal pure returns (int256[] memory targetArray) {
        require(_sourceArray.length * 2 >= _targetArrayLength, "SRC!=TGT");
        targetArray = new int256[](_targetArrayLength);
        uint targetIndex;
        uint sourceArrayLengthMinusOne = _sourceArray.length - 1;
        bool divisibleByTwo = _targetArrayLength % 2 == 0;
        for (uint i; i < _sourceArray.length; ) {
            targetArray[targetIndex] = _sourceArray[i] >> 128;
            unchecked {
                ++targetIndex;
            }
            if ((!divisibleByTwo && i < sourceArrayLengthMinusOne) || divisibleByTwo) {
                targetArray[targetIndex] = int256(int128(_sourceArray[i]));
            }
            unchecked {
                ++i;
                ++targetIndex;
            }
        }

        if (!divisibleByTwo) {
            targetArray[_targetArrayLength - 1] = int256(int128(_sourceArray[sourceArrayLengthMinusOne]));
        }
    }
}

// On casting to uint first, Solidity does not revert when casting negative values
//it just interprets the bitstring as a uint.
//Normally this is unintended behaviour, but here it is actually useful
/// @title QuantAMMStorage contract for QuantAMM storage slot packing and unpacking vector rule weights
/// @notice This logic to pack and unpack vectors is hardcoded for square matrices only as that is the usecase for QuantAMM
abstract contract VectorRuleQuantAMMStorage is QuantAMMStorage {
    /// @notice Packs n 128 bit integers into n/2 256 bit integers
    /// @param _sourceMatrix the matrix to pack
    /// @param _targetArray the array to pack into
    function _quantAMMPack128Matrix(int256[][] memory _sourceMatrix, int256[] storage _targetArray) internal {
        // 2d array of 3 elements each with 3 elements

        // | |1|, |2|, |3|, |
        // | |4|, |5|, |6|, |
        // | |7|, |8|, |9|  |

        // becomes array of 5 elements, the last being half filled

        // | 1 2 | 3 4 | 5 6 | 7 8 | 9 _ |

        // this saves 3 length SSTORES and SLOADS, as well as reducing the slots by 3

        uint targetArrayLength = _targetArray.length;
        require(targetArrayLength * 2 >= _sourceMatrix.length * _sourceMatrix.length, "Matrix doesnt fit storage");
        uint targetArrayIndex;
        int256 leftInt;
        uint right;
        unchecked {
            for (uint i; i < _sourceMatrix.length; ) {
                for (uint j; j < _sourceMatrix[i].length; ) {
                    if (right == 1) {
                        right = 0;
                        //SSTORE done inline to avoid length SSTORE as length doesnt ever change
                        _targetArray[targetArrayIndex] = _quantAMMPackTwo128(leftInt, _sourceMatrix[i][j]);
                        ++targetArrayIndex;
                    } else {
                        leftInt = _sourceMatrix[i][j];
                        right = 1;
                    }
                    ++j;
                }
                ++i;
            }
            if (((_sourceMatrix.length * _sourceMatrix.length) % 2) != 0) {                
                //CODEHAWKS INFO /s/755
                _targetArray[targetArrayLength - 1] = _quantAMMPackTwo128(0, _sourceMatrix[_sourceMatrix.length - 1][_sourceMatrix.length - 1]);
            }
        }
    }

    /// @notice Unpacks packed array into a 2d array of 128 bit integers
    /// @param _sourceArray the array to unpack
    /// @param _numberOfAssets the number of 128 bit integers to unpack
    function _quantAMMUnpack128Matrix(
        int256[] memory _sourceArray,
        uint _numberOfAssets
    ) internal pure returns (int256[][] memory targetArray) {
        // | 1 2 | 3 4 | 5 6 | 7 8 | 9 _ |

        // becomes 2d array of 3 elements each with 3 elements

        // | |1|, |2|, |3|, |
        // | |4|, |5|, |6|, |
        // | |7|, |8|, |9|  |
        require(_sourceArray.length * 2 >= _numberOfAssets * _numberOfAssets, "Source cannot provide target");
        targetArray = new int256[][](_numberOfAssets);
        for (uint i; i < _numberOfAssets; ) {
            targetArray[i] = new int256[](_numberOfAssets);
            unchecked {
                ++i;
            }
        }

        uint targetIndex;
        uint targetRow;
        for (uint i; i < _sourceArray.length; ) {
            if (targetIndex < _numberOfAssets) {
                targetArray[targetRow][targetIndex] = int256(int128(_sourceArray[i] >> 128));
                unchecked {
                    ++targetIndex;
                }

                if (targetIndex < _numberOfAssets) {
                    targetArray[targetRow][targetIndex] = int256(int128(_sourceArray[i]));
                    unchecked {
                        ++targetIndex;
                    }
                } else {
                    unchecked {
                        ++targetRow;
                        targetIndex = 0;
                    }
                    if (targetRow < _numberOfAssets) {
                        //CODEHAWKS INFO /s/922 remove double initialisation
                        if (targetIndex < _numberOfAssets) {
                            targetArray[targetRow][targetIndex] = int256(int128(_sourceArray[i]));
                            unchecked {
                                ++targetIndex;
                            }
                        }
                    }
                }
            } else {
                unchecked {
                    ++targetRow;
                    targetIndex = 0;
                }
                if (targetRow < _numberOfAssets) {
                    //CODEHAWKS INFO /s/922 remove double initialisation
                    targetArray[targetRow][targetIndex] = int256(int128(_sourceArray[i] >> 128));
                    unchecked {
                        ++targetIndex;
                    }

                    if (targetIndex < _numberOfAssets) {
                        //CODEHAWKS INFO /s/922 remove double initialisation
                        targetArray[targetRow][targetIndex] = int256(int128(_sourceArray[i]));
                        unchecked {
                            ++targetIndex;
                        }
                    } else {
                        unchecked {
                            ++targetRow;
                            targetIndex = 0;
                        }
                    }
                }
            }

            unchecked {
                ++i;
            }
        }

        if ((_numberOfAssets * _numberOfAssets) % 2 != 0) {
            targetArray[_numberOfAssets - 1][_numberOfAssets - 1] = int256(
                int128(_sourceArray[_sourceArray.length - 1])
            );
        }
    }
}

// SPDX-License-Identifier: GPL-3.0-or-later

pragma solidity >=0.8.24;

import { IVault } from "@balancer-labs/v3-interfaces/contracts/vault/IVault.sol";
import { IPoolVersion } from "@balancer-labs/v3-interfaces/contracts/solidity-utils/helpers/IPoolVersion.sol";
import {
    TokenConfig,
    PoolRoleAccounts,
    LiquidityManagement
} from "@balancer-labs/v3-interfaces/contracts/vault/VaultTypes.sol";

import { BasePoolFactory } from "@balancer-labs/v3-pool-utils/contracts/BasePoolFactory.sol";
import { Version } from "@balancer-labs/v3-solidity-utils/contracts/helpers/Version.sol";

import { IQuantAMMWeightedPool } from "@balancer-labs/v3-interfaces/contracts/pool-quantamm/IQuantAMMWeightedPool.sol";
import { QuantAMMWeightedPool } from "./QuantAMMWeightedPool.sol";
import { IHooks } from "@balancer-labs/v3-interfaces/contracts/vault/IHooks.sol";

import { InputHelpers } from "@balancer-labs/v3-solidity-utils/contracts/helpers/InputHelpers.sol";
import { FixedPoint } from "@balancer-labs/v3-solidity-utils/contracts/math/FixedPoint.sol";

import "@prb/math/contracts/PRBMathSD59x18.sol";

/**
 * @param name The name of the pool
* @param symbol The symbol of the pool
* @param tokens An array of descriptors for the tokens the pool will manage
* @param normalizedWeights The pool weights (must add to FixedPoint.ONE)
* @param roleAccounts Addresses the Vault will allow to change certain pool settings
* @param swapFeePercentage Initial swap fee percentage
* @param poolHooksContract Contract that implements the hooks for the pool
* @param enableDonation If true, the pool will support the donation add liquidity mechanism
* @param disableUnbalancedLiquidity If true, only proportional add and remove liquidity are accepted
* @param salt The salt value that will be passed to create3 deployment

 */

/**
 * @notice General Weighted Pool factory
 * @dev This is the most general factory, which allows up to eight tokens and arbitrary weights.
 */
contract QuantAMMWeightedPoolFactory is IPoolVersion, BasePoolFactory, Version {
    // solhint-disable not-rely-on-time

    /// @dev Indicates that the sum of the pool tokens' weights is not FP 1.
    error NormalizedWeightInvariant();

    /// @dev Indicates that one of the pool tokens' weight is below the minimum allowed.
    error MinWeight();

    /// @notice Unsafe or bad configuration for routers and liquidity management
    error ImcompatibleRouterConfiguration();

    struct CreationNewPoolParams {
        string name;
        string symbol;
        TokenConfig[] tokens;
        uint256[] normalizedWeights;
        PoolRoleAccounts roleAccounts;
        uint256 swapFeePercentage;
        address poolHooksContract;
        bool enableDonation;
        bool disableUnbalancedLiquidity;
        bytes32 salt;
        int256[] _initialWeights;
        IQuantAMMWeightedPool.PoolSettings _poolSettings;
        int256[] _initialMovingAverages;
        int256[] _initialIntermediateValues;
        uint256 _oracleStalenessThreshold;
        uint256 poolRegistry;
        string[][] poolDetails;
    }

    string private _poolVersion;
    address private immutable _updateWeightRunner;

    /// @param vault the balancer v3 valt
    /// @param pauseWindowDuration the pause duration
    /// @param factoryVersion factory version
    /// @param poolVersion pool version
    /// @param updateWeightRunner singleton update weight runner
    constructor(
        IVault vault,
        uint32 pauseWindowDuration,
        string memory factoryVersion,
        string memory poolVersion,
        address updateWeightRunner
    ) BasePoolFactory(vault, pauseWindowDuration, type(QuantAMMWeightedPool).creationCode) Version(factoryVersion) {
        require(updateWeightRunner != address(0), "update weight runner cannot be default address");
        _poolVersion = poolVersion;
        _updateWeightRunner = updateWeightRunner;
    }

    /// @inheritdoc IPoolVersion
    function getPoolVersion() external view returns (string memory) {
        return _poolVersion;
    }

    function _constructionChecks(CreationNewPoolParams memory params) internal pure {     
        //from update weight runner
        uint256 MASK_POOL_PERFORM_UPDATE = 1;
        uint256 MASK_POOL_GET_DATA = 2;
        uint256 MASK_POOL_OWNER_UPDATES = 8;
        uint256 MASK_POOL_QUANTAMM_ADMIN_UPDATES = 16;
        uint256 MASK_POOL_RULE_DIRECT_SET_WEIGHT = 32;

        //CODEHAWKS INFO /s/314
        require(
            (params.poolRegistry & MASK_POOL_PERFORM_UPDATE > 0) ||
            (params.poolRegistry & MASK_POOL_GET_DATA > 0) ||
            (params.poolRegistry & MASK_POOL_OWNER_UPDATES > 0) ||
            (params.poolRegistry & MASK_POOL_QUANTAMM_ADMIN_UPDATES > 0) ||
            (params.poolRegistry & MASK_POOL_RULE_DIRECT_SET_WEIGHT > 0),
            "Invalid pool registry"
        );

        require(params.poolDetails.length <= 50, "Limit exceeds array length");
        for(uint i; i < params.poolDetails.length; i++){
            require(params.poolDetails[i].length == 4, "detail needs all 4 [category, name, type, detail]");
        }
    }

    function _initialisationCheck(CreationNewPoolParams memory params) internal view {
        //checks copied from initialise

        //CODEHAWKS INFO /s/696
        require(params._poolSettings.assets.length > 0 
        && params._poolSettings.assets.length == params._initialWeights.length 
        && params._initialWeights.length == params.normalizedWeights.length/*_totalTokens*/, "INVASSWEIG"); //Invalid assets / weights array

        //CODEHAWKS INFO /s/157
        require(params._oracleStalenessThreshold > 0, "INVORCSTAL"); //Invalid oracle staleness threshold
        

        //checks coped from _setRule

        require(address(params._poolSettings.rule) != address(0), "Invalid rule");

        for (uint i; i < params._poolSettings.lambda.length; ++i) {
            int256 currentLambda = int256(uint256(params._poolSettings.lambda[i]));
            require(currentLambda > PRBMathSD59x18.fromInt(0) && currentLambda < PRBMathSD59x18.fromInt(1), "INVLAM"); //Invalid lambda value
        }

        require(
            params._poolSettings.lambda.length == 1 || params._poolSettings.lambda.length == params._initialWeights.length,
            "Either scalar or vector"
        );
        int256 currentEpsilonMax = int256(uint256(params._poolSettings.epsilonMax));
        require(
            currentEpsilonMax > PRBMathSD59x18.fromInt(0) && currentEpsilonMax <= PRBMathSD59x18.fromInt(1),
            "INV_EPMX"
        ); //Invalid epsilonMax value

        //applied both as a max (1 - x) and a min, so it cant be more than 0.49 or less than 0.01
        //all pool logic assumes that absolute guard rail is already stored as an 18dp int256
        require(
            int256(uint256(params._poolSettings.absoluteWeightGuardRail)) <
                PRBMathSD59x18.fromInt(1) / int256(uint256((params._initialWeights.length))) &&
                int256(uint256(params._poolSettings.absoluteWeightGuardRail)) >= 0.01e18,
            "INV_ABSWGT"
        ); //Invalid absoluteWeightGuardRail value

        require(params._poolSettings.oracles.length > 0, "NOPROVORC"); //No oracle indices provided"

        //CODEHAWKS INFO /s/154
        require(params._poolSettings.oracles.length == params._initialWeights.length, "OLNWEIG"); //Oracle length not equal to weights length
        require(params._poolSettings.rule.validParameters(params._poolSettings.ruleParameters), "INVRLEPRM"); //Invalid rule parameters

        //0 is hodl, 1 is trade whole pool which invariant doesnt let you do anyway
        require(params._poolSettings.maxTradeSizeRatio > 0 && params._poolSettings.maxTradeSizeRatio <= 0.3e18, "INVMAXTRADE"); //Invalid max trade size

        //checked copied from _setInitialWeights

        require(params.tokens.length > 1, "At least two tokens are required");
        
        InputHelpers.ensureInputLengthMatch(params.normalizedWeights.length /*_totalTokens */, params._initialWeights.length);
        int256 normalizedSum;

        int256[] memory _weightsAndBlockMultiplier = new int256[](params._initialWeights.length * 2);
        for (uint i; i < params._initialWeights.length; ) {
            if (params._initialWeights[i] < int256(uint256(params._poolSettings.absoluteWeightGuardRail))) {
                revert MinWeight();
            }

            _weightsAndBlockMultiplier[i] = params._initialWeights[i];
            normalizedSum += params._initialWeights[i];
            //Initially register pool with no movement, first update will come and set block multiplier.
            _weightsAndBlockMultiplier[i + params._initialWeights.length] = int256(0);
            unchecked {
                ++i;
            }
        }

        // Ensure that the normalized weights sum to ONE
        if (uint256(normalizedSum) != FixedPoint.ONE) {
            revert NormalizedWeightInvariant();
        }     
    }

    function createWithoutArgs(CreationNewPoolParams memory params) external returns (address pool) {
        if (params.roleAccounts.poolCreator != address(0)) {
            revert StandardPoolWithCreator();
        }

        if(params.poolHooksContract != address(0) 
            && IHooks(params.poolHooksContract).getHookFlags().enableHookAdjustedAmounts != params.disableUnbalancedLiquidity){
            revert ImcompatibleRouterConfiguration();
        }
        
        LiquidityManagement memory liquidityManagement = getDefaultLiquidityManagement();
        liquidityManagement.enableDonation = params.enableDonation;
        // disableUnbalancedLiquidity must be set to true if a hook has the flag enableHookAdjustedAmounts = true.
        liquidityManagement.disableUnbalancedLiquidity = params.disableUnbalancedLiquidity;
        require(params.tokens.length == params.normalizedWeights.length, "Token and weight counts must match");
        
        _constructionChecks(params);

        pool = _create(abi.encode(
                QuantAMMWeightedPool.NewPoolParams({
                    name: params.name,
                    symbol: params.symbol,
                    numTokens: params.normalizedWeights.length,
                    //CODEHAWKS INFO /s/26 /s/31 /s/190 /s/468
                    version: _poolVersion,
                    updateWeightRunner: _updateWeightRunner,
                    poolRegistry: params.poolRegistry,
                    poolDetails: params.poolDetails
                }),
                getVault()
            ), params.salt);

        _initialisationCheck(params);

        QuantAMMWeightedPool(pool).initialize(params);

        _registerPoolWithVault(
            pool,
            params.tokens,
            params.swapFeePercentage,
            false, // not exempt from protocol fees
            params.roleAccounts,
            params.poolHooksContract,
            liquidityManagement
        );
    }

    /**
     * @notice Deploys a new `WeightedPool`.
     * @dev Tokens must be sorted for pool registration.
     */
    function create(CreationNewPoolParams memory params) external returns (address pool, bytes memory poolArgs) {
        if (params.roleAccounts.poolCreator != address(0)) {
            revert StandardPoolWithCreator();
        }
        
        LiquidityManagement memory liquidityManagement = getDefaultLiquidityManagement();
        liquidityManagement.enableDonation = params.enableDonation;
        // disableUnbalancedLiquidity must be set to true if a hook has the flag enableHookAdjustedAmounts = true.
        liquidityManagement.disableUnbalancedLiquidity = params.disableUnbalancedLiquidity;
        
        
        poolArgs = abi.encode(
                QuantAMMWeightedPool.NewPoolParams({
                    name: params.name,
                    symbol: params.symbol,
                    numTokens: params.normalizedWeights.length,
                    //CODEHAWKS INFO /s/26 /s/31 /s/190 /s/468
                    version: _poolVersion,
                    updateWeightRunner: _updateWeightRunner,
                    poolRegistry: params.poolRegistry,
                    poolDetails: params.poolDetails
                }),
                getVault()
            );
        
        //CODEHAWKS INFO /s/_586 /s/860 /s/962
        require(params.tokens.length == params.normalizedWeights.length, "Token and weight counts must match");
        
        _constructionChecks(params);

        pool = _create(poolArgs, params.salt);

        _initialisationCheck(params);
        
        QuantAMMWeightedPool(pool).initialize(params);

        _registerPoolWithVault(
            pool,
            params.tokens,
            params.swapFeePercentage,
            false, // not exempt from protocol fees
            params.roleAccounts,
            params.poolHooksContract,
            liquidityManagement
        );
    }
}

File 68 of 72 : QuantammBasedRuleHelpers.sol
// SPDX-License-Identifier: MIT
pragma solidity >=0.8.24;

/// @param _movingAverage p̅(t)
/// @param _lambda λ
/// @param _numberOfAssets number of assets in the pool
/// @param _pool the target pool address
struct QuantAMMPoolParameters {
    address pool;
    uint numberOfAssets;
    int128[] lambda;
    int256[] movingAverage;
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.24;

import "@prb/math/contracts/PRBMathSD59x18.sol";

/// @title QuantAMMMathGuard contract to implement guard rails for QuantAMM weights updates
/// @notice This contract implements the guard rails for QuantAMM weights updates as described in the QuantAMM whitepaper.
abstract contract QuantAMMMathGuard {
    using PRBMathSD59x18 for int256;

    int256 private constant ONE = 1 * 1e18; // Result of PRBMathSD59x18.fromInt(1), store as constant to avoid recalculation every time
    int256 private constant TWO = 2 * 1e18;

    /// @notice Guards QuantAMM weights updates
    /// @param _weights Raw weights to be guarded and normalized
    /// @param _prevWeights Previous weights to be used for normalization
    /// @param _epsilonMax  Maximum allowed change in weights per update step (epsilon) in the QuantAMM whitepaper
    /// @param _absoluteWeightGuardRail Minimum allowed weight in the QuantAMM whitepaper
    function _guardQuantAMMWeights(
        int256[] memory _weights,
        int256[] calldata _prevWeights,
        int256 _epsilonMax,
        int256 _absoluteWeightGuardRail
    ) internal pure returns (int256[] memory guardedNewWeights) {
        // first see if the weights go beyond the maximum/minimum weights
        _weights = _clampWeights(_weights, _absoluteWeightGuardRail);

        //then reduce even further if the weight change is beyond the allowed "speed limit" that protects the changes from front running
        guardedNewWeights = _normalizeWeightUpdates(_prevWeights, _weights, _epsilonMax);
    }

    /// @dev there are some edge cases where the clamping might result to break the guard rail. This is known and the last interpolation block logic in the update weight runner is an ultimate guard against this.
    /// @notice Applies guard rails (min value, max value) to weights and returns the normalized weights
    /// @param _weights Raw weights
    /// @return Clamped weights
    function _clampWeights(
        int256[] memory _weights,
        int256 _absoluteWeightGuardRail
    ) internal pure returns (int256[] memory) {
        unchecked {
            uint weightLength = _weights.length;
            if (weightLength == 1) {
                return _weights;
            }
            int256 absoluteMin = _absoluteWeightGuardRail;
            int256 absoluteMax = ONE -
                (PRBMathSD59x18.fromInt(int256(_weights.length - 1)).mul(_absoluteWeightGuardRail));
            int256 sumRemainerWeight = ONE;
            int256 sumOtherWeights;

            for (uint i; i < weightLength; ++i) {
                if (_weights[i] < absoluteMin) {
                    _weights[i] = absoluteMin;
                    sumRemainerWeight -= absoluteMin;
                } else if (_weights[i] > absoluteMax) {
                    _weights[i] = absoluteMax;
                    sumOtherWeights += absoluteMax;
                }
                else{
                    //CODEHAWKS M-14
                    sumOtherWeights += _weights[i];
                }
            }
            if (sumOtherWeights != 0) {
                int256 proportionalRemainder = sumRemainerWeight.div(sumOtherWeights);
                for (uint i; i < weightLength; ++i) {
                    if (_weights[i] != absoluteMin) {
                        _weights[i] = _weights[i].mul(proportionalRemainder);
                    }
                }
            }
        }
        return _weights;
    }

    ///@notice Normalizes the weights to ensure that the sum of the weights is equal to 1
    ///@param _prevWeights Previous weights
    ///@param _newWeights New weights
    ///@param _epsilonMax Maximum allowed change in weights per update step (epsilon) in the QuantAMM whitepaper
    function _normalizeWeightUpdates(
        int256[] memory _prevWeights,
        int256[] memory _newWeights,
        int256 _epsilonMax
    ) internal pure returns (int256[] memory) {
        unchecked {
            int256 maxAbsChange = _epsilonMax;
            for (uint i; i < _prevWeights.length; ++i) {
                int256 absChange;
                if (_prevWeights[i] > _newWeights[i]) {
                    absChange = _prevWeights[i] - _newWeights[i];
                } else {
                    absChange = _newWeights[i] - _prevWeights[i];
                }
                if (absChange > maxAbsChange) {
                    maxAbsChange = absChange;
                }
            }
            int256 newWeightsSum;
            uint maxWeightIndex;
            int256 maxWeight;
            uint secondMaxWeightIndex;
            int256 secondMaxWeight;
            if (maxAbsChange > _epsilonMax) {
                int256 rescaleFactor = _epsilonMax.div(maxAbsChange);
                for (uint i; i < _newWeights.length; ++i) {
                    int256 newDelta = (_newWeights[i] - _prevWeights[i]).mul(rescaleFactor);
                    _newWeights[i] = _prevWeights[i] + newDelta;
                    newWeightsSum += _newWeights[i];
                    if (_newWeights[i] > maxWeight) {
                        secondMaxWeight = maxWeight;
                        secondMaxWeightIndex = maxWeightIndex;
                        maxWeight = _newWeights[i];
                        maxWeightIndex = i;
                    } else if (_newWeights[i] > secondMaxWeight) {
                        secondMaxWeight = _newWeights[i];
                        secondMaxWeightIndex = i;
                    }
                }
            } else {
                for (uint i; i < _newWeights.length; ++i) {
                    newWeightsSum += _newWeights[i];
                    if (_newWeights[i] > maxWeight) {
                        secondMaxWeight = maxWeight;
                        secondMaxWeightIndex = maxWeightIndex;
                        maxWeight = _newWeights[i];
                        maxWeightIndex = i;
                    } else if (_newWeights[i] > secondMaxWeight) {
                        secondMaxWeight = _newWeights[i];
                        secondMaxWeightIndex = i;
                    }
                }
            }

            // There might a very small (1e-18) rounding error, add this to the first element.
            // Given how the block multiplier guard stops when the multiplier is not 0, and 
            // a first weight hits the guard rail, if applied solely to the max weight, the 
            // updates will stop at T0.
            // we have to make sure that where the rounding error is added to will not be on 
            // a weight that is already at the max weight. if the rounding error is negative
            // it can be added to the max weight, if it is positive add to the second as only 
            // one weight can be at max, guarenteeing the second is not at abs max weight
            //CODEHAWKS M-17
            if(newWeightsSum != ONE){
                if(newWeightsSum > ONE){
                    _newWeights[maxWeightIndex] = _newWeights[maxWeightIndex] - (newWeightsSum - ONE);
                }else{
                    _newWeights[secondMaxWeightIndex] = _newWeights[secondMaxWeightIndex] + (ONE - newWeightsSum);
                }
            }
        }
        return _newWeights;
    }

    /// @notice Raises SD59x18 number x to an arbitrary SD59x18 number y
    /// @dev Calculates (2^(log2(x)))^y == x^y == 2^(log2(x) * y)
    /// @param _x Base
    /// @param _y Exponent
    /// @return result x^y
    function _pow(int256 _x, int256 _y) internal pure returns (int256 result) {
        if (_y == 0 || (_x == 0 && _y == 0)) {
            return 1 * 1e18;
        }
        if (_x == 0) {
            return 0;
        }

        //Noticed effect of this reorg- 2^(log2(x) * y) - the variable multiplied by exp2() can be a large negative and lib can return 0
        return _y.mul(_x.log2()).exp2();
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.24;

import "@prb/math/contracts/PRBMathSD59x18.sol";
import "../../QuantAMMStorage.sol";

/// @title QuantAMMMathMovingAverage contract for QuantAMM moving average calculations and storage of moving averages for QuantAMM pools
/// @notice Contains the logic for calculating the moving average of the pool price and storing the moving averages
abstract contract QuantAMMMathMovingAverage is ScalarRuleQuantAMMStorage {
    using PRBMathSD59x18 for int256;

    int256 private constant ONE = 1 * 1e18; // Result of PRBMathSD59x18.fromInt(1), store as constant to avoid recalculation every time

    // this can be just the moving averages per token, or if prev moving average is true then it is [...moving averages, ...prev moving averages]
    mapping(address => int256[]) public movingAverages;

    /// @notice View function to get the moving averages for a given pool
    /// @param poolAddress The address of the pool
    /// @param numberOfAssets The number of assets in the pool
    /// @return The unpacked moving averages as an array of int256
    function getMovingAverages(address poolAddress, uint numberOfAssets) external view returns (int256[] memory) {
        return _quantAMMUnpack128Array(movingAverages[poolAddress], numberOfAssets);
    }

    /// @notice Calculates the new moving average value, i.e. p̅(t) = p̅(t - 1) + (1 - λ)(p(t) - p̅(t - 1))
    /// @param _prevMovingAverage p̅(t - 1)
    /// @param _newData p(t)
    /// @param _lambda λ
    /// @param _numberOfAssets number of assets in the pool
    /// @return p̅(t) avertage price of the pool
    function _calculateQuantAMMMovingAverage(
        int256[] memory _prevMovingAverage,
        int256[] memory _newData,
        int128[] memory _lambda,
        uint _numberOfAssets
    ) internal pure returns (int256[] memory) {
        int256[] memory newMovingAverage = new int256[](_numberOfAssets);
        int256 convertedLambda = int256(_lambda[0]);
        int256 oneMinusLambda = ONE - convertedLambda;
        if (_lambda.length == 1) {
            for (uint i; i < _numberOfAssets; ) {
                // p̅(t) = p̅(t - 1) + (1 - λ)(p(t) - p̅(t - 1)) - see whitepaper
                int256 movingAverageI = _prevMovingAverage[i];
                //CODEHAWKS INFO /s/200 reordering to avoid precision loss
                newMovingAverage[i] = (movingAverageI.mul(convertedLambda) + _newData[i].mul(oneMinusLambda)).div(ONE);
                unchecked {
                    ++i;
                }
            }
        } else {
            for (uint i; i < _numberOfAssets; ) {
                unchecked {
                    convertedLambda = int256(_lambda[i]);
                    oneMinusLambda = ONE - convertedLambda;
                }
                int256 movingAverageI = _prevMovingAverage[i];
                // p̅(t) = p̅(t - 1) + (1 - λ)(p(t) - p̅(t - 1))
                //CODEHAWKS INFO /s/200 reordering to avoid precision loss
                newMovingAverage[i] = (movingAverageI.mul(convertedLambda) + _newData[i].mul(oneMinusLambda)).div(ONE);
                unchecked {
                    ++i;
                }
            }
        }

        return newMovingAverage;
    }

    /// @param _poolAddress address of pool being initialised
    /// @param _initialMovingAverages array of initial moving averages
    /// @param _numberOfAssets number of assets in the pool
    function _setInitialMovingAverages(
        address _poolAddress,
        int256[] memory _initialMovingAverages,
        uint _numberOfAssets
    ) internal {
        //CODEHAWKS H-04 no longer storing prev, also /s/767
        if (_initialMovingAverages.length == _numberOfAssets) {
            //should be during create pool
            movingAverages[_poolAddress] = _quantAMMPack128Array(_initialMovingAverages);
        } else {
            revert("Invalid set moving avg");
        }
    }
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.24;

import "@prb/math/contracts/PRBMathSD59x18.sol";
import "./base/QuantammMathGuard.sol";
import "./base/QuantammMathMovingAverage.sol";
import "../UpdateWeightRunner.sol";
import "./base/QuantammBasedRuleHelpers.sol";
import "@balancer-labs/v3-interfaces/contracts/pool-quantamm/IUpdateRule.sol";

/*
ARCHITECTURE DESIGN NOTES

TFMM specified oracle estimators are specified in base contracts inherited by update rule contracts.
A single deployment is required for multiple pools as intermediate state is keyed against the individual
pool address.

The update rule selection and its tuning is registered against a new pool when the pool is created. Use
of base contracts and benefits over library implementation is discussed in the technical appendix of the wp.

Initial versions created a math library with the oracle estimator functions and a separate library for the update rules, however external library calls and detached intermediate calculation value retrieval increased gas costs to an unsustainable level. 

An inherited base contract approach created a good domain driven design where both intermediate values and QuantAmm specific logic are stored in a single contract. 

*/
/// @title QuantAMMUpdateRule base contract for QuantAMM update rules
/// @notice Contains the logic for calculating the new weights of a QuantAMM pool and protections, must be implemented by all rules used in quantAMM
abstract contract UpdateRule is QuantAMMMathGuard, QuantAMMMathMovingAverage, IUpdateRule {
    uint16 private constant REQ_PREV_MAVG_VAL = 1;
    address private immutable updateWeightRunner;
    
    string public name;
    string[] public parameterDescriptions;

    /// @dev struct to avoid stack too deep issues
    /// @notice Struct to store local variables for the update rule
    /// @param i index for looping
    /// @param nMinusOne number of assets minus one
    /// @param numberOfAssets number of assets in the pool
    /// @param requiresPrevAverage boolean to determine if the rule requires the previous moving average
    /// @param intermediateMovingAverageStateLength length of the intermediate moving average state
    /// @param currMovingAverage current moving average
    /// @param updatedMovingAverage updated moving average
    /// @param calculationMovingAverage moving average used in the calculation
    /// @param intermediateGradientState intermediate gradient state
    /// @param unGuardedUpdatedWeights unguarded updated weights
    /// @param lambda lambda values
    /// @param secondIndex second index for looping
    /// @param storageIndex storage index for moving averages
    /// @param lastAssetIndex last asset index
    struct QuantAMMUpdateRuleLocals {
        uint i;
        uint nMinusOne;
        uint numberOfAssets;
        bool requiresPrevAverage;
        uint intermediateMovingAverageStateLength;
        int256[] currMovingAverage;
        int256[] updatedMovingAverage;
        int256[] calculationMovingAverage;
        int256[] intermediateGradientState;
        int256[] unGuardedUpdatedWeights;
        int128[] lambda;
        uint secondIndex;
        uint storageIndex;
        uint lastAssetIndex;
    }

    constructor(address _updateWeightRunner){
        updateWeightRunner = _updateWeightRunner;
    }

    /// @param _prevWeights the previous weights retrieved from the vault
    /// @param _data the latest data from the signal, usually price
    /// @param _pool the target pool address
    /// @param _parameters the parameters of the rule that are not lambda
    /// @param _lambdaStore either vector or scalar lambda
    /// @param _epsilonMax the maximum weights can change in a given update interval
    /// @param _absoluteWeightGuardRail the minimum weight a token can have
    function CalculateNewWeights(
        int256[] calldata _prevWeights,
        int256[] calldata _data,
        address _pool,
        int256[][] calldata _parameters,
        uint64[] calldata _lambdaStore,
        uint64 _epsilonMax,
        uint64 _absoluteWeightGuardRail
    ) external returns (int256[] memory updatedWeights) {
        require(msg.sender == updateWeightRunner, "UNAUTH_CALC");

        QuantAMMUpdateRuleLocals memory locals;

        locals.numberOfAssets = _prevWeights.length;
        locals.nMinusOne = locals.numberOfAssets - 1;
        locals.lambda = new int128[](_lambdaStore.length);

        for (locals.i; locals.i < locals.lambda.length; ) {
            locals.lambda[locals.i] = int128(uint128(_lambdaStore[locals.i]));
            unchecked {
                ++locals.i;
            }
        }

        locals.requiresPrevAverage = _requiresPrevMovingAverage() == REQ_PREV_MAVG_VAL;
        locals.intermediateMovingAverageStateLength = locals.numberOfAssets;

        if (locals.requiresPrevAverage) {
            unchecked {
                locals.intermediateMovingAverageStateLength *= 2;
            }
        }

        locals.currMovingAverage = new int256[](locals.numberOfAssets);
        locals.updatedMovingAverage = new int256[](locals.numberOfAssets);
        locals.calculationMovingAverage = new int256[](locals.intermediateMovingAverageStateLength);

        locals.currMovingAverage = _quantAMMUnpack128Array(movingAverages[_pool], locals.numberOfAssets);

        //All rules require the use of moving averages so the logic is executed in the base
        locals.updatedMovingAverage = _calculateQuantAMMMovingAverage(
            locals.currMovingAverage,
            _data,
            locals.lambda,
            locals.numberOfAssets
        );

        if (locals.numberOfAssets % 2 != 0) {
            unchecked {
                --locals.nMinusOne;
            }
        }

        locals.secondIndex;
        locals.storageIndex;

        // The packing and storing of moving averages is done per slot to save on SSTORES
        // The potential sticky end if there is an odd number if constituents is dealt at the end
        // The saving of one SSTORE is more than the extra logic gas
        locals.i = 0;
        for (; locals.i < locals.nMinusOne; ) {
            if (locals.requiresPrevAverage) {
                locals.calculationMovingAverage[locals.i + locals.numberOfAssets] = locals.currMovingAverage[locals.i];
            }
            locals.calculationMovingAverage[locals.i] = locals.updatedMovingAverage[locals.i];

            unchecked {
                locals.secondIndex = locals.i + 1;
            }
            if (locals.requiresPrevAverage) {
                locals.calculationMovingAverage[locals.secondIndex + locals.numberOfAssets] = locals.currMovingAverage[
                    locals.secondIndex
                ];
            }
            locals.calculationMovingAverage[locals.secondIndex] = locals.updatedMovingAverage[locals.secondIndex];

            if (!locals.requiresPrevAverage) {
                movingAverages[_pool][locals.storageIndex] = _quantAMMPackTwo128(
                    locals.updatedMovingAverage[locals.i],
                    locals.updatedMovingAverage[locals.secondIndex]
                );
            }

            unchecked {
                ++locals.storageIndex;
                locals.i += 2;
            }
        }

        if (locals.numberOfAssets % 2 != 0) {
            locals.lastAssetIndex = locals.numberOfAssets - 1;
            unchecked {
                locals.nMinusOne = ((locals.lastAssetIndex) / 2);
            }
            if (locals.requiresPrevAverage) {
                locals.calculationMovingAverage[locals.lastAssetIndex + locals.numberOfAssets] = locals
                    .currMovingAverage[locals.lastAssetIndex];
            }
            locals.calculationMovingAverage[locals.lastAssetIndex] = locals.updatedMovingAverage[locals.lastAssetIndex];
            if (!locals.requiresPrevAverage) {
                //CODEHAWKS INFO /s/755
                movingAverages[_pool][locals.nMinusOne] = _quantAMMPackTwo128(
                    int256(0),
                    locals.updatedMovingAverage[locals.lastAssetIndex]
                );
            }
        }

        //because of mixing of prev and current if the numassets is odd it is makes normal code unreadable to do inline
        //this means for rules requiring prev moving average there is an addition SSTORE and local packed array
        if (locals.requiresPrevAverage) {
            //CODEHAWKS H-04 no need to store prev averages anymore. 
            movingAverages[_pool] = _quantAMMPack128Array(locals.updatedMovingAverage);
        }

        QuantAMMPoolParameters memory poolParameters;
        poolParameters.lambda = locals.lambda;
        poolParameters.movingAverage = locals.calculationMovingAverage;
        poolParameters.pool = _pool;

        //calling the function in the derived contract specific to the specific rule
        locals.unGuardedUpdatedWeights = _getWeights(_prevWeights, _data, _parameters, poolParameters);

        //Guard weights is done in the base contract so regardless of the rule the logic will always be executed
        updatedWeights = _guardQuantAMMWeights(
            locals.unGuardedUpdatedWeights,
            _prevWeights,
            int128(uint128(_epsilonMax)),
            int128(uint128(_absoluteWeightGuardRail))
        );
    }

    /// @notice Function that has to be implemented by update rules. Given previous weights, current data, and current gradient of the data, calculate the new weights.
    /// @param _prevWeights w(t - 1), the weights at the previous timestamp
    /// @param _data p(t), the data at the current timestamp, usually referring to prices (but could also be other values that are returned by an oracle)
    /// @param _parameters Arbitrary values that parametrize the rule, interpretation depends on rule
    /// @param _poolParameters PoolParameters
    /// @return newWeights w(t), the updated weights
    function _getWeights(
        int256[] calldata _prevWeights,
        int256[]  memory _data,
        int256[][] calldata _parameters,
        QuantAMMPoolParameters memory _poolParameters
    ) internal virtual returns (int256[] memory newWeights);

    function _requiresPrevMovingAverage() internal pure virtual returns (uint16);

    /// @param _poolAddress address of pool being initialised
    /// @param _initialValues the initial intermediate values to be saved
    /// @param _numberOfAssets the number of assets in the pool
    function _setInitialIntermediateValues(
        address _poolAddress,
        int256[] memory _initialValues,
        uint _numberOfAssets
    ) internal virtual;

    /// @param _poolAddress address of pool being initialised
    /// @param _newMovingAverages the initial moving averages to be saved
    /// @param _newInitialValues the initial intermediate values to be saved
    /// @param _numberOfAssets the number of assets in the pool
    /// @notice top level initialisation function to be called during pool registration
    function initialisePoolRuleIntermediateValues(
        address _poolAddress,
        int256[] memory _newMovingAverages,
        int256[] memory _newInitialValues,
        uint _numberOfAssets
    ) external {
        //initialisation is controlled during the registration process
        //this is to make sure no external actor can call this function
        require(msg.sender == _poolAddress || msg.sender == updateWeightRunner, "UNAUTH");
        _setInitialMovingAverages(_poolAddress, _newMovingAverages, _numberOfAssets);
        _setInitialIntermediateValues(_poolAddress, _newInitialValues, _numberOfAssets);
    }

    /// @notice Check if the given parameters are valid for the rule
    function validParameters(int256[][] calldata parameters) external view virtual returns (bool);
}

// SPDX-License-Identifier: BUSL-1.1
pragma solidity >=0.8.24;

import "@openzeppelin/contracts/access/Ownable2Step.sol";
import "@prb/math/contracts/PRBMathSD59x18.sol";
import "@balancer-labs/v3-interfaces/contracts/pool-quantamm/OracleWrapper.sol";
import "@balancer-labs/v3-interfaces/contracts/pool-quantamm/IQuantAMMWeightedPool.sol";
import "@balancer-labs/v3-interfaces/contracts/pool-quantamm/IUpdateRule.sol";
import "@balancer-labs/v3-interfaces/contracts/pool-quantamm/IUpdateWeightRunner.sol";
import "./rules/UpdateRule.sol";

import "./QuantAMMWeightedPool.sol";

import { IWeightedPool } from "@balancer-labs/v3-interfaces/contracts/pool-weighted/IWeightedPool.sol";

/*
ARCHITECTURE DESIGN NOTES

The update weight runner is a singleton contract that is responsible for running all weight updates. It is a singleton contract as it is responsible for managing the update rule state of all pools.

The update weight runner is responsible for:
- Managing the state of all update rules
- Managing the state of all pools related to update rules
- Managing the state of all oracles related to update rules
- Managing the state of all quantAMM weight runners
- Managing the state of the ETH/USD oracle - important for exit fee calculations
- Managing the state of all approved oracles
- Managing the state of all oracle staleness thresholds
- Managing the state of all pool last run times
- Managing the state of all pool rule settings
- Managing the state of all pool primary oracles
- Managing the state of all pool backup oracles
- Managing the state of all pool rules

As all QuantAMM pools are based on the TFMM approach, core aspects of running a periodic strategy
update are shared. This allows for appropriate centralisation of the process in a single update weight
runner.
What benefits are achieved by such centralisation? Efficiency of external contract calls is a great benefit
however security should always come before efficiency. A single runner allows for a gated approach
where pool contracts can be built, however only when registered with the quantammAdmin and update weight
runner can they be considered to be ”approved” and running within the QuantAMM umbrella.
Centralised common logic also allows for ease of protecting the interaction between quantammAdmin and update
weight runner, while reducing the pool specific code that will require n number of audits per n pools
designs. Such logic includes a single heavily tested implementation for oracle fall backs, triggering of
updates and guard rails.


 */

/// @title UpdateWeightRunner singleton contract that is responsible for running all weight updates

contract UpdateWeightRunner is IUpdateWeightRunner {
    //CODEHAWKS INFO /s/336 remove Ownable2Step as it is not used anymore
    event OracleAdded(address indexed oracleAddress);
    event OracleRemved(address indexed oracleAddress);
    event SetWeightManual(
        address indexed caller,
        address indexed pool,
        int256[] weights,
        uint40 lastInterpolationTimePossible,
        uint40 lastUpdateTime
    );
    event SetIntermediateValuesManually(
        address indexed caller,
        address indexed pool,
        int256[] newMovingAverages,
        int256[] newParameters,
        uint numberOfAssets
    );
    event SwapFeeTakeSet(uint256 oldSwapFee, uint256 newSwapFee);
    event UpliftFeeTakeSet(uint256 oldSwapFee, uint256 newSwapFee);
    event UpdatePerformed(address indexed caller, address indexed pool);
    event UpdatePerformedQuantAMM(address indexed caller, address indexed pool);
    event SetApprovedActionsForPool(address indexed caller, address indexed pool, uint256 actions);
    event ETHUSDOracleSet(address ethUsdOracle);
    event PoolLastRunSet(address poolAddress, uint40 time);
    event PoolRuleSetAdminOverride(address admin, address poolAddress, address ruleAddress);
    event CalculateWeightsRequest(
        int256[] currentWeights,
        int256[] data,
        address pool,
        int256[][] ruleParameters,
        uint64[] lambda,
        uint64 epsilonMax,
        uint64 absoluteWeightGuardRail
    );

    event CalculateWeightsResponse(
        int256[] updatedWeights
    );

    ///@dev Emitted when the weights of the pool are updated
    event WeightsUpdated(
        address indexed poolAddress,
        address updateOwner,
        int256[] weights,
        uint40 lastInterpolationTimePossible,
        uint40 lastUpdateTime
    );

    /// @notice main eth oracle that could be used to determine value of pools and assets.
    /// @dev this could be used for things like uplift only withdrawal fee hooks
    OracleWrapper public ethOracle;

    /// @notice Mask to check if a pool is allowed to perform an update, some might only want to get data
    uint256 private constant MASK_POOL_PERFORM_UPDATE = 1;

    /// @notice Mask to check if a pool is allowed to get data
    uint256 private constant MASK_POOL_GET_DATA = 2;

    /// @notice Mask to check if a pool owner can update weights
    uint256 private constant MASK_POOL_OWNER_UPDATES = 8;

    /// @notice Mask to check if a pool is allowed to perform admin updates
    uint256 private constant MASK_POOL_QUANTAMM_ADMIN_UPDATES = 16;

    /// @notice Mask to check if a pool is allowed to perform direct weight update from a rule
    uint256 private constant MASK_POOL_RULE_DIRECT_SET_WEIGHT = 32;

    constructor(address _quantammAdmin, address _ethOracle) {
        require(_quantammAdmin != address(0), "Admin cannot be default address");
        require(_ethOracle != address(0), "eth oracle cannot be default address");

        quantammAdmin = _quantammAdmin;
        ethOracle = OracleWrapper(_ethOracle);
    }

    address public immutable quantammAdmin;

    /// @notice key is pool address, value is rule settings for running the pool
    mapping(address => PoolRuleSettings) public poolRuleSettings;

    /// @notice Mapping of pool primary oracles keyed by pool address. Happy path oracles in the same order as the constituent assets
    mapping(address => address[]) public poolOracles;

    /// @notice Mapping of pool backup oracles keyed by pool address for each asset in the pool (in order of priority)
    mapping(address => address[][]) public poolBackupOracles;

    /// @notice The % of the total swap fee that is allocated to the protocol for running costs.
    uint256 public quantAMMSwapFeeTake = 0.5e18;

    function setQuantAMMSwapFeeTake(uint256 _quantAMMSwapFeeTake) external override {
        require(msg.sender == quantammAdmin, "ONLYADMIN");
        require(_quantAMMSwapFeeTake <= 1e18, "Swap fee must be less than 100%");
        uint256 oldSwapFee = quantAMMSwapFeeTake;
        quantAMMSwapFeeTake = _quantAMMSwapFeeTake;

        emit SwapFeeTakeSet(oldSwapFee, _quantAMMSwapFeeTake);
    }

    function getQuantAMMSwapFeeTake() external view override returns (uint256) {
        return quantAMMSwapFeeTake;
    }

    /// @notice Set the quantAMM uplift fee % amount allocated to the protocol for running costs
    /// @param _quantAMMUpliftFeeTake The new uplift fee % amount allocated to the protocol for running costs
    function setQuantAMMUpliftFeeTake(uint256 _quantAMMUpliftFeeTake) external {
        require(msg.sender == quantammAdmin, "ONLYADMIN");
        require(_quantAMMUpliftFeeTake <= 1e18, "Uplift fee must be less than 100%");
        uint256 oldSwapFee = quantAMMSwapFeeTake;
        quantAMMSwapFeeTake = _quantAMMUpliftFeeTake;

        emit UpliftFeeTakeSet(oldSwapFee, _quantAMMUpliftFeeTake);
    }

    /// @notice Get the quantAMM uplift fee % amount allocated to the protocol for running costs
    function getQuantAMMUpliftFeeTake() external view returns (uint256) {
        return quantAMMSwapFeeTake;
    }

    function getQuantAMMAdmin() external view override returns (address) {
        return quantammAdmin;
    }

    /// @notice Get the happy path primary oracles for the constituents of a pool
    /// @param _poolAddress Address of the pool
    function getOptimisedPoolOracle(address _poolAddress) public view returns (address[] memory oracles) {
        return poolOracles[_poolAddress];
    }

    /// @notice Get the backup oracles for the constituents of a pool
    /// @param _poolAddress Address of the pool
    function getPoolOracleAndBackups(address _poolAddress) public view returns (address[][] memory oracles) {
        return poolBackupOracles[_poolAddress];
    }

    /// @notice Get the rule settings for a pool
    /// @param _poolAddress Address of the pool
    function getPoolRuleSettings(address _poolAddress) public view returns (PoolRuleSettings memory oracles) {
        return poolRuleSettings[_poolAddress];
    }

    /// @notice Get the actions a pool has been approved for
    /// @param _poolAddress Address of the pool
    function getPoolApprovedActions(address _poolAddress) public view returns (uint256) {
        return approvedPoolActions[_poolAddress];
    }

    /// @notice List of approved oracles that can be used for updating weights.
    mapping(address => bool) public approvedOracles;

    /// @notice Mapping of actions approved for a pool by the QuantAMM protocol team.
    mapping(address => uint256) public approvedPoolActions;

    /// @notice mapping keyed of oracle address to staleness threshold in seconds. Created for gas efficincy.
    mapping(address => uint) public ruleOracleStalenessThreshold;

    /// @notice Mapping of pools to rules
    mapping(address => IUpdateRule) public rules;

    /// @notice Get the rule for a pool
    /// @param _poolAddress Address of the pool
    function getPoolRule(address _poolAddress) public view returns (IUpdateRule rule) {
        return rules[_poolAddress];
    }

    /// @notice Add a new oracle to the available oracles
    /// @param _oracle Oracle to add
    function addOracle(OracleWrapper _oracle) external {
        address oracleAddress = address(_oracle);
        require(oracleAddress != address(0), "Invalid oracle address");
        require(msg.sender == quantammAdmin, "ONLYADMIN");

        if (!approvedOracles[oracleAddress]) {
            approvedOracles[oracleAddress] = true;
        } else {
            revert("Oracle already added");
        }
        emit OracleAdded(oracleAddress);
    }

    /// @notice Removes an existing oracle from the approved oracles
    /// @param _oracleToRemove The oracle to remove
    function removeOracle(OracleWrapper _oracleToRemove) external {
        //CODEHAWKS INFO /s/491 /s/492 requires ordering
        require(msg.sender == quantammAdmin, "ONLYADMIN");
        approvedOracles[address(_oracleToRemove)] = false;
        emit OracleRemved(address(_oracleToRemove));
    }

    /// @notice Set the actions a pool is approved for
    /// @param _pool Pool to set actions for
    function setApprovedActionsForPool(address _pool, uint256 _actions) external {
        require(msg.sender == quantammAdmin, "ONLYADMIN");
        require(_actions != approvedPoolActions[_pool], "DUPEACTION");
        approvedPoolActions[_pool] = _actions;
        emit SetApprovedActionsForPool(msg.sender, _pool, _actions);
    }

    /// @notice Set the rule for a pool, called by the pool creator
    /// @param _poolSettings Settings for the pool
    /// @param _pool Pool to set the rule for
    /// @dev CODEHAWKS M-02
    function setRuleForPoolAdminInitialise(
        IQuantAMMWeightedPool.PoolSettings memory _poolSettings,
        address _pool
    ) external {
        require(msg.sender == quantammAdmin, "ONLYADMIN");
        require(_pool != address(0), "Invalid pool address");
        require(IQuantAMMWeightedPool(_pool).getWithinFixWindow(), "Pool now immutable");

        require(address(rules[_pool]) == address(0), "Rule already set");
        require(_poolSettings.oracles.length > 0, "Empty oracles array");
        require(poolOracles[_pool].length == 0, "pool rule already set");
        //needed to prevent 2 step amend

        //CODEHAWKS INFO /s/700
        require(_poolSettings.updateInterval > 0, "Update interval must be greater than 0");

        _setRuleForPool(_poolSettings, _pool);

        emit PoolRuleSetAdminOverride(msg.sender, _pool, address(_poolSettings.rule));
    }

    /// @notice Set a rule for a pool, called by the pool
    /// @param _poolSettings Settings for the pool
    function setRuleForPool(IQuantAMMWeightedPool.PoolSettings memory _poolSettings) external {
        require(address(rules[msg.sender]) == address(0), "Rule already set");
        require(_poolSettings.oracles.length > 0, "Empty oracles array");
        require(poolOracles[msg.sender].length == 0, "pool rule already set");

        //CODEHAWKS INFO /s/700
        require(_poolSettings.updateInterval > 0, "Update interval must be greater than 0");
        _setRuleForPool(_poolSettings, msg.sender);
    }

    function _setRuleForPool(IQuantAMMWeightedPool.PoolSettings memory _poolSettings, address pool) internal {
        for (uint i; i < _poolSettings.oracles.length; ++i) {
            require(_poolSettings.oracles[i].length > 0, "Empty oracles array");
            for (uint j; j < _poolSettings.oracles[i].length; ++j) {
                if (!approvedOracles[_poolSettings.oracles[i][j]]) {
                    revert("Not approved oracled used");
                }
            }
        }

        address[] memory optimisedHappyPathOracles = new address[](_poolSettings.oracles.length);
        for (uint i; i < _poolSettings.oracles.length; ++i) {
            optimisedHappyPathOracles[i] = _poolSettings.oracles[i][0];
        }
        poolOracles[pool] = optimisedHappyPathOracles;
        poolBackupOracles[pool] = _poolSettings.oracles;
        rules[pool] = _poolSettings.rule;
        poolRuleSettings[pool] = PoolRuleSettings({
            lambda: _poolSettings.lambda,
            epsilonMax: _poolSettings.epsilonMax,
            absoluteWeightGuardRail: _poolSettings.absoluteWeightGuardRail,
            ruleParameters: _poolSettings.ruleParameters,
            timingSettings: PoolTimingSettings({ updateInterval: _poolSettings.updateInterval, lastPoolUpdateRun: 0 }),
            poolManager: _poolSettings.poolManager
        });
    }

    /// @notice Run the update for the provided rule. Last update must be performed more than or equal (CODEHAWKS INFO /2/228) to updateInterval seconds ago.
    function performUpdate(address _pool) public {
        //Main external access point to trigger an update
        address rule = address(rules[_pool]);
        require(rule != address(0), "Pool not registered");

        PoolRuleSettings memory settings = poolRuleSettings[_pool];

        require(
            block.timestamp - settings.timingSettings.lastPoolUpdateRun >= settings.timingSettings.updateInterval,
            "Update not allowed"
        );

        uint256 poolRegistryEntry = approvedPoolActions[_pool];
        if (poolRegistryEntry & MASK_POOL_PERFORM_UPDATE > 0) {
            _performUpdateAndGetData(_pool, settings);

            // emit event for easier tracking of updates and to allow for easier querying of updates
            emit UpdatePerformed(msg.sender, _pool);
        } else {
            revert("Pool not approved to perform update");
        }
    }

    /// @notice Change the ETH/USD oracle
    /// @param _ethUsdOracle The new oracle address to use for ETH/USD
    function setETHUSDOracle(address _ethUsdOracle) public {
        require(msg.sender == quantammAdmin, "ONLYADMIN");
        //CODEHAWKS INFO /s/158
        require(_ethUsdOracle != address(0), "INVETHUSDORACLE");
        ethOracle = OracleWrapper(_ethUsdOracle);
        emit ETHUSDOracleSet(_ethUsdOracle);
    }

    /// @notice Sets the timestamp of when an update was last run for a pool. Can by used as a breakgrass measure to retrigger an update.
    /// @param _poolAddress the target pool address
    /// @param _time the time to initialise the last update run to
    function InitialisePoolLastRunTime(address _poolAddress, uint40 _time) external {
        uint256 poolRegistryEntry = approvedPoolActions[_poolAddress];

        //current breakglass settings allow pool creator trigger. This is subject to review
        if (poolRegistryEntry & MASK_POOL_OWNER_UPDATES > 0) {
            require(msg.sender == poolRuleSettings[_poolAddress].poolManager, "ONLYMANAGER");
        } else if (poolRegistryEntry & MASK_POOL_QUANTAMM_ADMIN_UPDATES > 0) {
            require(msg.sender == quantammAdmin, "ONLYADMIN");
        } else {
            revert("No permission to set last run time");
        }

        poolRuleSettings[_poolAddress].timingSettings.lastPoolUpdateRun = _time;
        emit PoolLastRunSet(_poolAddress, _time);
    }

    /// @notice Wrapper for if someone wants to get the oracle data the rule is using from an external source
    /// @param _pool Pool to get data for
    function getData(address _pool) public view virtual returns (int256[] memory outputData) {
        return _getData(_pool, false);
    }

    /// @notice Get the data for a pool from the oracles and return it in the same order as the assets in the pool
    /// @param _pool Pool to get data for
    /// @param internalCall Internal call flag to detect if the function was called internally for emission and permissions
    function _getData(address _pool, bool internalCall) private view returns (int256[] memory outputData) {
        require(internalCall || (approvedPoolActions[_pool] & MASK_POOL_GET_DATA > 0), "Not allowed to get data");
        //optimised == happy path, optimised into a different array to save gas
        address[] memory optimisedOracles = poolOracles[_pool];
        uint oracleLength = optimisedOracles.length;
        uint numAssetOracles;
        outputData = new int256[](oracleLength);
        uint oracleStalenessThreshold = IQuantAMMWeightedPool(_pool).getOracleStalenessThreshold();

        for (uint i; i < oracleLength; ) {
            // Asset is base asset
            OracleData memory oracleResult;

            require(approvedOracles[address(optimisedOracles[i])], "Oracle not approved");

            try OracleWrapper(optimisedOracles[i]).getData() returns (int216 data, uint40 timestamp) {
                oracleResult.data = data;
                oracleResult.timestamp = timestamp;
            } catch {
                oracleResult.data = 0;
                oracleResult.timestamp = 0;
            }

            if (oracleResult.timestamp > block.timestamp - oracleStalenessThreshold) {
                outputData[i] = oracleResult.data;
            } else {
                unchecked {
                    numAssetOracles = poolBackupOracles[_pool][i].length;
                }

                //CODEHAWKS M-11 throw if no backup
                if (numAssetOracles == 1) {
                    revert("No fresh oracle values available");
                }

                for (uint j = 1 /*0 already done via optimised poolOracles*/; j < numAssetOracles; ) {
                    require(approvedOracles[address(poolBackupOracles[_pool][i][j])], "Oracle not approved");

                    try OracleWrapper(poolBackupOracles[_pool][i][j]).getData() returns (
                        int216 data,
                        uint40 timestamp
                    ) {
                        oracleResult.data = data;
                        oracleResult.timestamp = timestamp;
                    } catch {
                        oracleResult.data = 0;
                        oracleResult.timestamp = 0;
                    }

                    if (oracleResult.timestamp > block.timestamp - oracleStalenessThreshold) {
                        // Oracle has fresh values
                        break;
                    } else if (j == numAssetOracles - 1) {
                        // All oracle results for this data point are stale. Should rarely happen in practice with proper backup oracles.

                        revert("No fresh oracle values available");
                    }
                    unchecked {
                        ++j;
                    }
                }
                outputData[i] = oracleResult.data;
            }

            unchecked {
                ++i;
            }
        }
    }

    function _getUpdatedWeightsAndOracleData(
        address _pool,
        int256[] memory _currentWeights,
        PoolRuleSettings memory _ruleSettings
    ) private returns (int256[] memory updatedWeights, int256[] memory data) {
        data = _getData(_pool, true);

        emit CalculateWeightsRequest(
            _currentWeights,
            data,
            _pool,
            _ruleSettings.ruleParameters,
            _ruleSettings.lambda,
            _ruleSettings.epsilonMax,
            _ruleSettings.absoluteWeightGuardRail
        );

        updatedWeights = rules[_pool].CalculateNewWeights(
            _currentWeights,
            data,
            _pool,
            _ruleSettings.ruleParameters,
            _ruleSettings.lambda,
            _ruleSettings.epsilonMax,
            _ruleSettings.absoluteWeightGuardRail
        );

        emit CalculateWeightsResponse(updatedWeights);

        poolRuleSettings[_pool].timingSettings.lastPoolUpdateRun = uint40(block.timestamp);
    }

    /// @notice Perform the update for a pool and get the new data
    /// @param _poolAddress Pool to update
    /// @param _ruleSettings Settings for the rule to use for the update (lambda, epsilonMax, absolute guard rails, ruleParameters)
    function _performUpdateAndGetData(address _poolAddress, PoolRuleSettings memory _ruleSettings) private {
        //CODEHAWKS INFO /s/405
        uint256[] memory currentWeightsUnsigned = IWeightedPool(_poolAddress).getNormalizedWeights();
        int256[] memory currentWeights = new int256[](currentWeightsUnsigned.length);

        for (uint i; i < currentWeights.length; ) {
            currentWeights[i] = int256(currentWeightsUnsigned[i]);

            unchecked {
                i++;
            }
        }

        (int256[] memory updatedWeights, ) = _getUpdatedWeightsAndOracleData(
            _poolAddress,
            currentWeights,
            _ruleSettings
        );

        _calculateMultiplerAndSetWeights(
            CalculateMuliplierAndSetWeightsLocal({
                currentWeights: currentWeights,
                updatedWeights: updatedWeights,
                updateInterval: int256(int40(_ruleSettings.timingSettings.updateInterval)),
                absoluteWeightGuardRail18: int256(int64(_ruleSettings.absoluteWeightGuardRail)),
                poolAddress: _poolAddress
            })
        );
    }

    struct CalculateMuliplierAndSetWeightsLocal {
        int256[] currentWeights;
        int256[] updatedWeights;
        int256 updateInterval;
        int256 absoluteWeightGuardRail18;
        address poolAddress;
    }

    /// @notice Flatten the weights and multipliers into a single array
    /// @param firstFourWeightsAndMultipliers The first four weights and multipliers w,w,w,w,m,m,m,m
    /// @param secondFourWeightsAndMultipliers The second four weights and multipliers w,w,w,w,m,m,m,m
    /// @return The flattened weights and multipliers w,w,w,w,w,w,w,w,m,m,m,m,m,m,m,m
    function flattenDynamicDataWeightAndMutlipliers(
        int256[] memory firstFourWeightsAndMultipliers,
        int256[] memory secondFourWeightsAndMultipliers
    ) internal pure returns (int256[] memory) {
        int256[] memory flattenedWeightsAndMultipliers = new int256[](16);
        
        //as dynamic data always returns 2, 8 elem arrays (populated with 0s if it is not an 8 token pool)
        //we can make the assumptions made below about array lengths.
        for (uint i = 0; i < 4; i++) {
            flattenedWeightsAndMultipliers[i] = firstFourWeightsAndMultipliers[i];
            flattenedWeightsAndMultipliers[i + 4] = secondFourWeightsAndMultipliers[i];
            flattenedWeightsAndMultipliers[i + 8] = firstFourWeightsAndMultipliers[i + 4];
            flattenedWeightsAndMultipliers[i + 12] = secondFourWeightsAndMultipliers[i + 4];
        }

            return flattenedWeightsAndMultipliers;
        }

    /// @dev The multipler is the amount per block to add/remove from the last successful weight update.
    /// @notice Calculate the multiplier and set the weights for a pool.
    /// @param local Local data for the function
    function _calculateMultiplerAndSetWeights(CalculateMuliplierAndSetWeightsLocal memory local) internal {
        uint weightAndMultiplierLength = local.currentWeights.length * 2;
        // the base pool needs both the target weights and the per block multipler per asset
        int256[] memory targetWeightsAndBlockMultiplier = new int256[](weightAndMultiplierLength);

        int256 currentLastInterpolationPossible = type(int256).max;

        for (uint i; i < local.currentWeights.length; ) {
            targetWeightsAndBlockMultiplier[i] = local.currentWeights[i];

            // this would be the simple scenario if we did not have to worry about guard rails
            int256 blockMultiplier = (local.updatedWeights[i] - local.currentWeights[i]) / local.updateInterval;

            targetWeightsAndBlockMultiplier[i + local.currentWeights.length] = blockMultiplier;

            int256 upperGuardRail = (PRBMathSD59x18.fromInt(1) -
                (
                    PRBMathSD59x18.mul(
                        PRBMathSD59x18.fromInt(int256(local.currentWeights.length - 1)),
                        local.absoluteWeightGuardRail18
                    )
                ));

            unchecked {
                //This is your worst case scenario, usually you expect (and have DR) that at your next interval you
                //get another update. However what if you don't, you can carry on interpolating until you hit a rail
                //This calculates the first blocktime which one of your constituents hits the rail and that is your max
                //interpolation weight
                //There are also economic reasons for this detailed in the whitepaper design notes.
                //In an event of a chain halt, the pool will still be able to interpolate weights,
                //there are reasons for or against this being better than stopping at the update interval blocktime.
                int256 weightBetweenTargetAndMax;
                int256 blockTimeUntilGuardRailHit;
                if (blockMultiplier > int256(0)) {
                    weightBetweenTargetAndMax = upperGuardRail - local.currentWeights[i];
                    //the updated weight should never be above guard rail. final check as block multiplier
                    //will be even worse if
                    //not using .div so that the 18dp is removed
                    blockTimeUntilGuardRailHit = weightBetweenTargetAndMax / blockMultiplier;
                } else if (blockMultiplier == int256(0)) {
                    blockTimeUntilGuardRailHit = type(int256).max;
                } else {
                    weightBetweenTargetAndMax = local.currentWeights[i] - local.absoluteWeightGuardRail18;

                    //not using .div so that the 18dp is removed
                    //abs block multiplier
                    blockTimeUntilGuardRailHit = weightBetweenTargetAndMax / int256(uint256(-1 * blockMultiplier));
                }

                if (blockTimeUntilGuardRailHit < currentLastInterpolationPossible) {
                    //-1 to avoid any round issues at boundry. Cheaper than seeing if there will be and then doing -1
                    currentLastInterpolationPossible = blockTimeUntilGuardRailHit;
                }

                ++i;
            }
        }

        uint40 lastTimestampThatInterpolationWorks = uint40(type(uint40).max);

        //L01 possible if multiplier is 0
        if (currentLastInterpolationPossible < int256(type(int40).max) - int256(int40(uint40(block.timestamp)))) {
            //next expected update + time beyond that
            lastTimestampThatInterpolationWorks = uint40(
                int40(currentLastInterpolationPossible + int40(uint40(block.timestamp)))
            );
        }

        //the main point of interaction between the update weight runner and the quantammAdmin is here
        IQuantAMMWeightedPool(local.poolAddress).setWeights(
            targetWeightsAndBlockMultiplier,
            local.poolAddress,
            lastTimestampThatInterpolationWorks
        );

        IQuantAMMWeightedPool.QuantAMMWeightedPoolDynamicData memory dynamicData = IQuantAMMWeightedPool(local.poolAddress).getQuantAMMWeightedPoolDynamicData();

        //L-04 similar possibility with set weights as set rule for pool.
        emit WeightsUpdated(
            local.poolAddress,
            msg.sender,
            flattenDynamicDataWeightAndMutlipliers(dynamicData.firstFourWeightsAndMultipliers, dynamicData.secondFourWeightsAndMultipliers),
            lastTimestampThatInterpolationWorks,
            uint40(block.timestamp)
        );
    }

    /// @notice Ability to set weights from a rule without calculating new weights being triggered for approved configured pools
    /// @param params Local data for the function
    /// @dev requested for use in zk rules where weights are calculated with circuit and this is only called post verifier call
    function calculateMultiplierAndSetWeightsFromRule(CalculateMuliplierAndSetWeightsLocal memory params) external {
        //some level of protocol oversight required here that no rule is approved where this function is not called inapproriately
        require(msg.sender == address(rules[params.poolAddress]), "ONLYRULECANSETWEIGHTS");

        //CODEHAWKS M-02 redeployment of update weight runners means really it is a better
        //design to have pool creator trusted managed pools as a separate factory and weight runner
        uint256 poolRegistryEntry = approvedPoolActions[params.poolAddress];
        require(poolRegistryEntry & MASK_POOL_RULE_DIRECT_SET_WEIGHT > 0, "FUNCTIONNOTAPPROVEDFORPOOL");

        //why do we still need to calculate the multiplier and why not just set the weights like in the manual override?
        //the reason is we enforce clamp weights for all base rules however that still requires the catch all
        //pre update interval guardrail reach check. This is the only place where this is enforced
        //it also centralises logic for weight vectors, just like normal rules, zk rules do not to duplicate logic somewhere else.
        _calculateMultiplerAndSetWeights(params);
    }

    /// @notice Breakglass function to allow the admin or the pool manager to set the quantammAdmins weights manually
    /// @param _weights the new weights
    /// @param _poolAddress the target pool
    /// @param _lastInterpolationTimePossible the last time that the interpolation will work
    /// @param _numberOfAssets the number of assets in the pool
    function setWeightsManually(
        int256[] calldata _weights,
        address _poolAddress,
        uint40 _lastInterpolationTimePossible,
        uint _numberOfAssets
    ) external {
        //CODEHAWKS M-02 redeployment of update weight runners means really it is a better
        //design to have pool creator trusted managed pools as a separate factory and weight runner
        uint256 poolRegistryEntry = approvedPoolActions[_poolAddress];
        if (poolRegistryEntry & MASK_POOL_OWNER_UPDATES > 0) {
            require(msg.sender == poolRuleSettings[_poolAddress].poolManager, "ONLYMANAGER");
        } else if (poolRegistryEntry & MASK_POOL_QUANTAMM_ADMIN_UPDATES > 0) {
            require(msg.sender == quantammAdmin, "ONLYADMIN");
        } else {
            revert("No permission to set weight values");
        }

        //though we try to keep manual overrides as open as possible for unknown unknows
        //given how the math library works weights it is easiest to define weights as 18dp
        //even though technically G3M works of the ratio between them so it is not strictly necessary
        //CYFRIN L-02
        for (uint i; i < _weights.length; i++) {
            if (i < _numberOfAssets) {
                //CODEHAWKS M-08 change to weighted math underlying limits
                require(_weights[i] >= 0.01e18, "Below min allowed weight");
                require(_weights[i] <= 0.99e18, "Above max allowed weight");
            }
        }

        IQuantAMMWeightedPool(_poolAddress).setWeights(_weights, _poolAddress, _lastInterpolationTimePossible);
        IQuantAMMWeightedPool.QuantAMMWeightedPoolDynamicData memory dynamicData = IQuantAMMWeightedPool(_poolAddress).getQuantAMMWeightedPoolDynamicData();
        
        emit SetWeightManual(
            msg.sender,
            _poolAddress,
            flattenDynamicDataWeightAndMutlipliers(dynamicData.firstFourWeightsAndMultipliers, dynamicData.secondFourWeightsAndMultipliers),
            _lastInterpolationTimePossible,
            uint40(block.timestamp)
        );
    }

    /// @notice Breakglass function to allow the admin or the pool manager to set the intermediate values of the rule manually
    /// @param _poolAddress the target pool
    /// @param _newMovingAverages manual new moving averages
    /// @param _newParameters manual new parameters
    /// @param _numberOfAssets number of assets in the pool
    function setIntermediateValuesManually(
        address _poolAddress,
        int256[] memory _newMovingAverages,
        int256[] memory _newParameters,
        uint _numberOfAssets
    ) external {
        uint256 poolRegistryEntry = approvedPoolActions[_poolAddress];

        //Who can trigger these very powerful breakglass features is under review
        if (poolRegistryEntry & MASK_POOL_OWNER_UPDATES > 0) {
            require(msg.sender == poolRuleSettings[_poolAddress].poolManager, "ONLYMANAGER");
        } else if (poolRegistryEntry & MASK_POOL_QUANTAMM_ADMIN_UPDATES > 0) {
            require(msg.sender == quantammAdmin, "ONLYADMIN");
        } else {
            revert("No permission to set intermediate values");
        }

        IUpdateRule rule = rules[_poolAddress];

        // utilises the base function so that manual updates go through the standard process
        rule.initialisePoolRuleIntermediateValues(_poolAddress, _newMovingAverages, _newParameters, _numberOfAssets);

        emit SetIntermediateValuesManually(
            msg.sender,
            _poolAddress,
            _newMovingAverages,
            _newParameters,
            _numberOfAssets
        );
    }
}

Settings
{
  "viaIR": true,
  "evmVersion": "cancun",
  "optimizer": {
    "enabled": true,
    "runs": 850,
    "details": {
      "yulDetails": {
        "optimizerSteps": "dhfoDgvulfnTUtnIf [ xa[r]EscLM cCTUtTOntnfDIul Lcul Vcul [j] Tpeul xa[rul] xa[r]cL gvif CTUca[r]LSsTFOtfDnca[r]Iulc ] jmul[jul] VcTOcul jmul : fDnTOcmu"
      }
    }
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "libraries": {}
}

Contract Security Audit

Contract ABI

API
[{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"internalType":"uint256","name":"numTokens","type":"uint256"},{"internalType":"string","name":"version","type":"string"},{"internalType":"address","name":"updateWeightRunner","type":"address"},{"internalType":"uint256","name":"poolRegistry","type":"uint256"},{"internalType":"string[][]","name":"poolDetails","type":"string[][]"}],"internalType":"struct QuantAMMWeightedPool.NewPoolParams","name":"params","type":"tuple"},{"internalType":"contract IVault","name":"vault","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"BaseOutOfBounds","type":"error"},{"inputs":[],"name":"ECDSAInvalidSignature","type":"error"},{"inputs":[{"internalType":"uint256","name":"length","type":"uint256"}],"name":"ECDSAInvalidSignatureLength","type":"error"},{"inputs":[{"internalType":"bytes32","name":"s","type":"bytes32"}],"name":"ECDSAInvalidSignatureS","type":"error"},{"inputs":[{"internalType":"uint256","name":"deadline","type":"uint256"}],"name":"ERC2612ExpiredSignature","type":"error"},{"inputs":[{"internalType":"address","name":"signer","type":"address"},{"internalType":"address","name":"owner","type":"address"}],"name":"ERC2612InvalidSigner","type":"error"},{"inputs":[],"name":"ExponentOutOfBounds","type":"error"},{"inputs":[{"internalType":"address","name":"account","type":"address"},{"internalType":"uint256","name":"currentNonce","type":"uint256"}],"name":"InvalidAccountNonce","type":"error"},{"inputs":[],"name":"InvalidExponent","type":"error"},{"inputs":[],"name":"InvalidInitialization","type":"error"},{"inputs":[],"name":"InvalidShortString","type":"error"},{"inputs":[],"name":"MaxInRatio","type":"error"},{"inputs":[],"name":"MaxOutRatio","type":"error"},{"inputs":[],"name":"NotInitializing","type":"error"},{"inputs":[],"name":"ProductOutOfBounds","type":"error"},{"inputs":[{"internalType":"address","name":"sender","type":"address"}],"name":"SenderIsNotVault","type":"error"},{"inputs":[{"internalType":"string","name":"str","type":"string"}],"name":"StringTooLong","type":"error"},{"inputs":[],"name":"WeightedPoolBptRateUnsupported","type":"error"},{"inputs":[],"name":"ZeroDivision","type":"error"},{"inputs":[],"name":"ZeroInvariant","type":"error"},{"inputs":[],"name":"maxTradeSizeRatioExceeded","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":"value","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[],"name":"EIP712DomainChanged","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"uint64","name":"version","type":"uint64"}],"name":"Initialized","type":"event"},{"anonymous":false,"inputs":[{"indexed":false,"internalType":"address","name":"rule","type":"address"},{"indexed":false,"internalType":"address[][]","name":"poolOracles","type":"address[][]"},{"indexed":false,"internalType":"uint64[]","name":"lambda","type":"uint64[]"},{"indexed":false,"internalType":"int256[][]","name":"ruleParameters","type":"int256[][]"},{"indexed":false,"internalType":"uint64","name":"epsilonMax","type":"uint64"},{"indexed":false,"internalType":"uint64","name":"absoluteWeightGuardRail","type":"uint64"},{"indexed":false,"internalType":"uint40","name":"updateInterval","type":"uint40"},{"indexed":false,"internalType":"address","name":"poolManager","type":"address"},{"indexed":false,"internalType":"address","name":"creatorAddress","type":"address"}],"name":"PoolRuleSet","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"from","type":"address"},{"indexed":true,"internalType":"address","name":"to","type":"address"},{"indexed":false,"internalType":"uint256","name":"value","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"oldAddress","type":"address"},{"indexed":true,"internalType":"address","name":"newAddress","type":"address"}],"name":"UpdateWeightRunnerAddressUpdated","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"address","name":"poolAddress","type":"address"},{"indexed":false,"internalType":"int256[]","name":"calculatedWeightsAndMultipliers","type":"int256[]"},{"indexed":false,"internalType":"uint40","name":"lastInterpolationTimePossible","type":"uint40"},{"indexed":false,"internalType":"uint40","name":"lastUpdateTime","type":"uint40"}],"name":"WeightsUpdated","type":"event"},{"inputs":[],"name":"DOMAIN_SEPARATOR","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PERMIT_TYPEHASH","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"absoluteWeightGuardRail","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"}],"name":"allowance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"approve","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"account","type":"address"}],"name":"balanceOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"balancesLiveScaled18","type":"uint256[]"},{"internalType":"uint256","name":"tokenInIndex","type":"uint256"},{"internalType":"uint256","name":"invariantRatio","type":"uint256"}],"name":"computeBalance","outputs":[{"internalType":"uint256","name":"newBalance","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256[]","name":"balancesLiveScaled18","type":"uint256[]"},{"internalType":"enum Rounding","name":"rounding","type":"uint8"}],"name":"computeInvariant","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"decimals","outputs":[{"internalType":"uint8","name":"","type":"uint8"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"deploymentTime","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"eip712Domain","outputs":[{"internalType":"bytes1","name":"fields","type":"bytes1"},{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"version","type":"string"},{"internalType":"uint256","name":"chainId","type":"uint256"},{"internalType":"address","name":"verifyingContract","type":"address"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"uint256[]","name":"extensions","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"emitApproval","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"emitTransfer","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"epsilonMax","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getAggregateFeePercentages","outputs":[{"internalType":"uint256","name":"aggregateSwapFeePercentage","type":"uint256"},{"internalType":"uint256","name":"aggregateYieldFeePercentage","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getCurrentLiveBalances","outputs":[{"internalType":"uint256[]","name":"balancesLiveScaled18","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getMaximumInvariantRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getMaximumSwapFeePercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getMinimumInvariantRatio","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getMinimumSwapFeePercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getNormalizedWeights","outputs":[{"internalType":"uint256[]","name":"","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOracleStalenessThreshold","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"string","name":"category","type":"string"},{"internalType":"string","name":"name","type":"string"}],"name":"getPoolDetail","outputs":[{"internalType":"string","name":"","type":"string"},{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getQuantAMMWeightedPoolDynamicData","outputs":[{"components":[{"internalType":"uint256[]","name":"balancesLiveScaled18","type":"uint256[]"},{"internalType":"uint256[]","name":"tokenRates","type":"uint256[]"},{"internalType":"uint256","name":"totalSupply","type":"uint256"},{"internalType":"bool","name":"isPoolInitialized","type":"bool"},{"internalType":"bool","name":"isPoolPaused","type":"bool"},{"internalType":"bool","name":"isPoolInRecoveryMode","type":"bool"},{"internalType":"int256[]","name":"firstFourWeightsAndMultipliers","type":"int256[]"},{"internalType":"int256[]","name":"secondFourWeightsAndMultipliers","type":"int256[]"},{"internalType":"uint40","name":"lastUpdateTime","type":"uint40"},{"internalType":"uint40","name":"lastInteropTime","type":"uint40"}],"internalType":"struct IQuantAMMWeightedPool.QuantAMMWeightedPoolDynamicData","name":"data","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getQuantAMMWeightedPoolImmutableData","outputs":[{"components":[{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"internalType":"uint256","name":"oracleStalenessThreshold","type":"uint256"},{"internalType":"uint256","name":"poolRegistry","type":"uint256"},{"internalType":"int256[][]","name":"ruleParameters","type":"int256[][]"},{"internalType":"uint64[]","name":"lambda","type":"uint64[]"},{"internalType":"uint64","name":"epsilonMax","type":"uint64"},{"internalType":"uint64","name":"absoluteWeightGuardRail","type":"uint64"},{"internalType":"uint64","name":"updateInterval","type":"uint64"},{"internalType":"uint256","name":"maxTradeSizeRatio","type":"uint256"}],"internalType":"struct IQuantAMMWeightedPool.QuantAMMWeightedPoolImmutableData","name":"data","type":"tuple"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getRate","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"pure","type":"function"},{"inputs":[],"name":"getStaticSwapFeePercentage","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokenInfo","outputs":[{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"},{"components":[{"internalType":"enum TokenType","name":"tokenType","type":"uint8"},{"internalType":"contract IRateProvider","name":"rateProvider","type":"address"},{"internalType":"bool","name":"paysYieldFees","type":"bool"}],"internalType":"struct TokenInfo[]","name":"tokenInfo","type":"tuple[]"},{"internalType":"uint256[]","name":"balancesRaw","type":"uint256[]"},{"internalType":"uint256[]","name":"lastBalancesLiveScaled18","type":"uint256[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getTokens","outputs":[{"internalType":"contract IERC20[]","name":"tokens","type":"address[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getVault","outputs":[{"internalType":"contract IVault","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getWithinFixWindow","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"incrementNonce","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"components":[{"internalType":"string","name":"name","type":"string"},{"internalType":"string","name":"symbol","type":"string"},{"components":[{"internalType":"contract IERC20","name":"token","type":"address"},{"internalType":"enum TokenType","name":"tokenType","type":"uint8"},{"internalType":"contract IRateProvider","name":"rateProvider","type":"address"},{"internalType":"bool","name":"paysYieldFees","type":"bool"}],"internalType":"struct TokenConfig[]","name":"tokens","type":"tuple[]"},{"internalType":"uint256[]","name":"normalizedWeights","type":"uint256[]"},{"components":[{"internalType":"address","name":"pauseManager","type":"address"},{"internalType":"address","name":"swapFeeManager","type":"address"},{"internalType":"address","name":"poolCreator","type":"address"}],"internalType":"struct PoolRoleAccounts","name":"roleAccounts","type":"tuple"},{"internalType":"uint256","name":"swapFeePercentage","type":"uint256"},{"internalType":"address","name":"poolHooksContract","type":"address"},{"internalType":"bool","name":"enableDonation","type":"bool"},{"internalType":"bool","name":"disableUnbalancedLiquidity","type":"bool"},{"internalType":"bytes32","name":"salt","type":"bytes32"},{"internalType":"int256[]","name":"_initialWeights","type":"int256[]"},{"components":[{"internalType":"contract IERC20[]","name":"assets","type":"address[]"},{"internalType":"contract IUpdateRule","name":"rule","type":"address"},{"internalType":"address[][]","name":"oracles","type":"address[][]"},{"internalType":"uint40","name":"updateInterval","type":"uint40"},{"internalType":"uint64[]","name":"lambda","type":"uint64[]"},{"internalType":"uint64","name":"epsilonMax","type":"uint64"},{"internalType":"uint64","name":"absoluteWeightGuardRail","type":"uint64"},{"internalType":"uint64","name":"maxTradeSizeRatio","type":"uint64"},{"internalType":"int256[][]","name":"ruleParameters","type":"int256[][]"},{"internalType":"address","name":"poolManager","type":"address"}],"internalType":"struct IQuantAMMWeightedPool.PoolSettings","name":"_poolSettings","type":"tuple"},{"internalType":"int256[]","name":"_initialMovingAverages","type":"int256[]"},{"internalType":"int256[]","name":"_initialIntermediateValues","type":"int256[]"},{"internalType":"uint256","name":"_oracleStalenessThreshold","type":"uint256"},{"internalType":"uint256","name":"poolRegistry","type":"uint256"},{"internalType":"string[][]","name":"poolDetails","type":"string[][]"}],"internalType":"struct QuantAMMWeightedPoolFactory.CreationNewPoolParams","name":"params","type":"tuple"}],"name":"initialize","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"}],"name":"lambda","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","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":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"enum SwapKind","name":"kind","type":"uint8"},{"internalType":"uint256","name":"amountGivenScaled18","type":"uint256"},{"internalType":"uint256[]","name":"balancesScaled18","type":"uint256[]"},{"internalType":"uint256","name":"indexIn","type":"uint256"},{"internalType":"uint256","name":"indexOut","type":"uint256"},{"internalType":"address","name":"router","type":"address"},{"internalType":"bytes","name":"userData","type":"bytes"}],"internalType":"struct PoolSwapParams","name":"request","type":"tuple"}],"name":"onSwap","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"owner","type":"address"},{"internalType":"address","name":"spender","type":"address"},{"internalType":"uint256","name":"amount","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":"poolRegistry","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"","type":"uint256"},{"internalType":"uint256","name":"","type":"uint256"}],"name":"ruleParameters","outputs":[{"internalType":"int256","name":"","type":"int256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"_updateWeightRunner","type":"address"}],"name":"setUpdateWeightRunnerAddress","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"int256[]","name":"_weights","type":"int256[]"},{"internalType":"address","name":"_address","type":"address"},{"internalType":"uint40","name":"_lastInteropTime","type":"uint40"}],"name":"setWeights","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"bytes4","name":"interfaceId","type":"bytes4"}],"name":"supportsInterface","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"symbol","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"totalSupply","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transfer","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"address","name":"from","type":"address"},{"internalType":"address","name":"to","type":"address"},{"internalType":"uint256","name":"amount","type":"uint256"}],"name":"transferFrom","outputs":[{"internalType":"bool","name":"","type":"bool"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"updateInterval","outputs":[{"internalType":"uint64","name":"","type":"uint64"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"updateWeightRunner","outputs":[{"internalType":"contract UpdateWeightRunner","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"version","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"view","type":"function"}]

6102206040523461082257616e94803803809161001e82610220610b47565b610220396040811261082257610220516001600160401b03811161082257610220019060e08282610220010312610822576040519160e083016001600160401b038111848210176106b15760405280516001600160401b0381116108225761008d908361022001908301610b6a565b835260208101516001600160401b038111610822576100b3908361022001908301610b6a565b60208401526040818101519084015260608101516001600160401b038111610822576100e6908361022001908301610b6a565b60608401526100f760808201610bbf565b608084015260a0818101519084015260c08101516001600160401b038111610822578261022001601f82840101121561082257808201519061013882610bd3565b936101466040519586610b47565b8285526020850193816102200160208560051b85840101011161082257602083820101945b60208560051b85840101018610610a7f5760c0880187905261024051886001600160a01b03821682036108225760608101519080519260208201519360405180604081011060018060401b036040830111176106b1576040810160405260018152603160f81b60208201526101df82610c38565b610120526101ec81610d9d565b6101405281516020830120908160e0526020815191012080610100524660a052604051917f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f6020840152604083015260608201524660808201523060a082015260a081528060c081011060018060401b0360c0830111176106b15760c08101604052602081519101206080523060c052816101605280519060018060401b0382116106b15761029c600354610bea565b601f8111610a31575b50602090601f83116001146109aa576102d592915f918361099f575b50508160011b915f199060031b1c19161790565b6003555b83516001600160401b0381116106b1576102f4600454610bea565b601f8111610951575b506020601f82116001146108ea57819061032e939495965f926108df5750508160011b915f199060031b1c19161790565b6004555b610180528151916001600160401b0383116106b157610352600554610bea565b601f81116108ae575b50602090601f841160011461084457928061038d926004955f926108395750508160011b915f199060031b1c19161790565b6005555b6040818101516101a0526080820151600980546001600160a01b0319166001600160a01b0392909216918217905590516360bcf9b160e11b81529260209184919082905afa801561082e575f906107f0575b6101e052426101c05260a08101516102005260c00151805191506801000000000000000082116106b15760065482600655808310610735575b506020019060065f5260205f205f925b82841061054557604051615fe79081610ead823960805181614997015260a05181614a62015260c05181614968015260e051816149e601526101005181614a0c015261012051816129ed01526101405181612a1901526101605181818161075b0152818161085b01528181610d2401528181612661015281816129b101528181612cf3015281816130630152818161337401528181613f5c015261490a015261018051818181610e370152818161226701528181612357015281816125820152612ad201526101a0518181816103870152818161312b015281816142050152614aa101526101c0518181816102ba01528181610611015261374301526101e051816105e101526102005181818161089801526122e80152f35b80518051906801000000000000000082116106b15783548285558083106106c5575b5060200190835f5260205f205f925b82841061059357505050506001602081920192019301929061042c565b80518051906001600160401b0382116106b1576105b08454610bea565b601f8111610676575b50602090601f831160011461060a57926105f0836001959460209487965f926105ff5750508160011b915f199060031b1c19161790565b85555b01920193019290610576565b015190508e806102c1565b90845f5260205f20915f5b601f198516811061065e575083602093600196938796938794601f19811610610646575b505050811b0185556105f3565b01515f1960f88460031b161c191690558d8080610639565b91926020600181928685015181550194019201610615565b6106a190855f5260205f20601f850160051c810191602086106106a7575b601f0160051c0190610c22565b8a6105b9565b9091508190610694565b634e487b7160e01b5f52604160045260245ffd5b845f528260205f2091820191015b8181106106e05750610567565b806106ed60019254610bea565b806106fa575b50016106d3565b601f8111831461070f57505f81555b896106f3565b5f9082825261072c601f60208420920160051c8201858301610c22565b81835555610709565b60065f527ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f9081019083015b81811061076e575061041c565b80545f825580610782575b50600101610761565b815f5260205f20908101905b81811061079b5750610779565b806107a860019254610bea565b806107b5575b500161078e565b601f811183146107ca57505f81555b876107ae565b5f908282526107e7601f60208420920160051c8201858301610c22565b818355556107c4565b506020823d602011610826575b8161080a60209383610b47565b810103126108225761081d60c092610bbf565b6103e3565b5f80fd5b3d91506107fd565b6040513d5f823e3d90fd5b0151905085806102c1565b9060055f5260205f20915f5b601f19861681106108965750918491600193600496601f1981161061087e575b505050811b01600555610391565b01515f1960f88460031b161c19169055848080610870565b91926020600181928685015181550194019201610850565b6108d99060055f5260205f20601f860160051c810191602087106106a757601f0160051c0190610c22565b8361035b565b0151905086806102c1565b60045f5260205f20905f5b601f1984168110610939575060019394959683601f19811610610921575b505050811b01600455610332565b01515f1960f88460031b161c19169055858080610913565b9091602060018192858b0151815501930191016108f5565b60045f52610999907f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b601f840160051c810191602085106106a757601f0160051c0190610c22565b856102fd565b0151905087806102c1565b60035f90815292917fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b915b601f1984168510610a16576001945083601f198116106109fe575b505050811b016003556102d9565b01515f1960f88460031b161c191690558680806109f0565b818101518355602094850194600190930192909101906109d5565b60035f52610a79907fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b601f850160051c810191602086106106a757601f0160051c0190610c22565b866102a5565b85516001600160401b038111610822578361022001603f828786010101121561082257602081868501010151610ab481610bd3565b91610ac26040519384610b47565b81835260208301866102200160408460051b848b8a0101010111610822576040828988010101905b60408460051b848b8a010101018210610b1057505050908252506020958601950161016b565b81516001600160401b03811161082257602091610b3c8960408594888f8f610220019401010101610b6a565b815201910190610aea565b601f909101601f19168101906001600160401b038211908210176106b157604052565b81601f82011215610822578051906001600160401b0382116106b15760405192610b9e601f8401601f191660200185610b47565b8284526020838301011161082257815f9260208093018386015e8301015290565b51906001600160a01b038216820361082257565b6001600160401b0381116106b15760051b60200190565b90600182811c92168015610c18575b6020831014610c0457565b634e487b7160e01b5f52602260045260245ffd5b91607f1691610bf9565b818110610c2d575050565b5f8155600101610c22565b805160209081811015610cae5750601f825111610c705780825192015190808310610c6257501790565b825f19910360031b1b161790565b60448260405192839163305a27a960e01b83528160048401528051918291826024860152018484015e5f828201840152601f01601f19168101030190fd5b919291906001600160401b0382116106b157610cca5f54610bea565b601f8111610d76575b5080601f8311600114610d1457508190610d0293945f92610d095750508160011b915f199060031b1c19161790565b5f5560ff90565b015190505f806102c1565b90601f198316945f8052825f20925f905b878210610d5e575050836001959610610d46575b505050811b015f5560ff90565b01515f1960f88460031b161c191690555f8080610d39565b80600185968294968601518155019501930190610d25565b610d97905f8052601f835f20910160051c810190601f850160051c01610c22565b5f610cd3565b805160209081811015610dc75750601f825111610c705780825192015190808310610c6257501790565b9192916001600160401b0381116106b157600191610de58354610bea565b601f8111610e85575b5080601f8311600114610e2557508190610e1e9394955f92610d095750508160011b915f199060031b1c19161790565b905560ff90565b90601f19831695845f52825f20925f905b888210610e6e5750508385969710610e56575b505050811b01905560ff90565b01515f1960f88460031b161c191690555f8080610e49565b808785968294968601518155019501930190610e36565b610ea690845f52601f835f20910160051c810190601f850160051c01610c22565b5f610dee56fe610160806040526004361015610013575f80fd5b5f905f3560e01c90816301ffc9a71461377157508063021eb7cd1461372757806306fdde031461367e578063095ea7b3146136275780630f6480df1461360a578063132f42641461330f57806316a0b3e0146130b457806318160ddd1461309a57806323b872dd14612fff57806323de665114612fce578063273c1adf14612fac57806330adf81f14612f72578063313ce56714612f575780633644e51514612f3d578063451a10aa14612ebf57806354fd4d5014612e165780635687f2b814612dc2578063627cdcb914612d99578063654cf15d14612d77578063679aefce14612d5957806370a0823114612c9357806372c9818614612ba75780637445e37f14612b815780637ecebe0014612b4957806381fa807c14612a9c57806384b0196e146129d55780638d928af8146129925780638ddf01c01461296957806395d89b411461288b578063984de9e8146126d0578063a9059cbb146125e4578063aa6ca80814612550578063abb1dc441461230b578063afcff50f146122d1578063b156aa0a14612235578063b677fa5614612213578063c0f38e74146121b3578063ce20ece714612193578063ce288eb514610e70578063d335b0cf14610de9578063d505accf14610bc9578063d5787898146107d4578063dd62ed3e146106e9578063dddd104a146105bc578063e313fe3814610554578063e4e3b53514610304578063eb296de1146102dd578063ecda10f5146102a2578063f89f27ed1461026e5763fd2c80ae14610245575f80fd5b3461026b578060031936011261026b5760206001600160401b03600f5416604051908152f35b80fd5b503461026b578060031936011261026b5761029e61028a614a9f565b604051918291602083526020830190613a8a565b0390f35b503461026b578060031936011261026b5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b503461026b578060031936011261026b5760206001600160401b03600d5416604051908152f35b503461026b57606036600319011261026b576004356001600160401b038082116105505736602383011215610550578160040135908111610550576024820191602436918360051b0101116105505761035b613813565b906044359164ffffffffff9182841680850361054c576001600160a01b039384600954163303610521577f0000000000000000000000000000000000000000000000000000000000000000906001918060011b908082046002149015171561050d5784036104e35760088411156104c05761040b6103fd6103f76103e86103e336898e613b24565b61513d565b6104026103fd6103f783613e59565b51614d32565b613e59565b51600755613e7a565b51600855959092955b4216926040519161042483613908565b848352836020809401528469ffffffffff0000000000600a549260281b169169ffffffffffffffffffff19161717600a5560405196806060890160608a52526080880198928a5b8281106104ad578b8b7f3f678b7d550c0f8dfb5dce7c8568f6e139b01f5582a5613d669ebe77729a49fc8c808d8d8d8d6020850152604084015216930390a280f35b84358b529981019993810193830161046b565b6104d66103fd6104d136878c613b24565b614d32565b5160075595909295610414565b60405162461bcd60e51b815260206004820152600260248201526115d160f21b6044820152606490fd5b60248a634e487b7160e01b81526011600452fd5b60405162461bcd60e51b81526020600482015260036024820152622c2aa960e91b6044820152606490fd5b5f80fd5b8280fd5b503461026b57602036600319011261026b57600435600c548110156105b857600c602092526001600160401b038160021c7fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7015460c06040519360061b161c168152f35b5080fd5b503461026b57602036600319011261026b576105d66137fd565b6001600160a01b03807f00000000000000000000000000000000000000000000000000000000000000001633036106be576305a39a806106367f000000000000000000000000000000000000000000000000000000000000000042613c39565b10156106935780600954921691827fffffffffffffffffffffffff0000000000000000000000000000000000000000821617600955167fc51fb6cc8e37fb27ff77886c5b33be204bb0a91dc410398c3a9bef63760c0b9d8380a380f35b60405162461bcd60e51b815260206004820152600360248201526258494d60e81b6044820152606490fd5b60405162461bcd60e51b815260206004820152600360248201526216105160ea1b6044820152606490fd5b503461026b57604036600319011261026b576107036137fd565b90602061070e613813565b9260646001600160a01b0391828060405197889586947f927da1050000000000000000000000000000000000000000000000000000000086523060048701521660248501521660448301527f0000000000000000000000000000000000000000000000000000000000000000165afa9081156107c85790610795575b602090604051908152f35b506020813d6020116107c0575b816107af6020938361395a565b8101031261054c576020905161078a565b3d91506107a2565b604051903d90823e3d90fd5b503461026b578060031936011261026b576040519061012082018281106001600160401b03821117610bb55760405260608252806020830152806040830152606080830152606060808301528060a08301528060c08301528060e08301528061010083015260405163ca4f280360e01b815230600482015281816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa908115610baa578291610b88575b50825260105460208301527f00000000000000000000000000000000000000000000000000000000000000006040830152600b546108c88161397b565b906108d6604051928361395a565b808252827f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9602084015b838310610b2e57505050506060830152604051908180926020600c54918281520190600c84527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c793905b806003830110610adc57610982945490808310610ac0575b808310610aa1575b808310610a82575b8210610a6e575b5050038261395a565b60808201526001600160401b03600d5481811660a084015260401c1660c0820152600e546101008201526001600160401b03600f541660e0820152604051809160208252610100610a296109e483516101206020870152610140860190613abd565b6020840151604086015260408401516060860152610a14606085015191601f199283888303016080890152613ba1565b906080850151908683030160a0870152613bfd565b916001600160401b0360a08201511660c08501526001600160401b0360c08201511660e08501526001600160401b0360e0820151168285015201516101208301520390f35b60c01c82526020909101906001015f610979565b919260206001916001600160401b038460801c16815201930191610972565b919260206001916001600160401b038460401c1681520193019161096a565b919260206001916001600160401b038416815201930191610962565b916004919350608060019186546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192018492939161094a565b604051825480825283885260208089209083019189905b808210610b705750505060019282610b628594602094038261395a565b815201920192019190610900565b90919260016020819286548152019401920190610b45565b610ba491503d8084833e610b9c818361395a565b8101906146a5565b5f61088b565b6040513d84823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b503461026b5760e036600319011261026b57610be36137fd565b610beb613813565b90604435916064359160843560ff81168103610de557834211610db957610c2c826001600160a01b03165f52600260205260405f2080549060018201905590565b9060405160208101917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c983526001600160a01b039687861694856040850152888816606085015289608085015260a084015260c083015260c0825260e08201908282106001600160401b03831117610bb5578793610ce693610cdd93604052519020610cb661495e565b906040519161190160f01b83526002830152602282015260c43591604260a435922061564e565b909291926156d0565b16818103610d8b5760405163e1f21c6760e01b81526001600160a01b03848116600483015285166024820152604481018790528790602081606481857f00000000000000000000000000000000000000000000000000000000000000008c165af18015610baa57610d55575080f35b6020813d602011610d83575b81610d6e6020938361395a565b810103126105b857610d7f90613c7e565b5080f35b3d9150610d61565b7f4b800e46000000000000000000000000000000000000000000000000000000008752600452602452604485fd5b602486857f62791302000000000000000000000000000000000000000000000000000000008252600452fd5b8580fd5b503461026b578060031936011261026b57604051907fb45090f90000000000000000000000000000000000000000000000000000000082523060048301526020826024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa9081156107c8579061079557602090604051908152f35b503461054c57602036600319011261054c576001600160401b036004351161054c576102606004353603600319011261054c5760405161022081018181106001600160401b03821117610bb557604052600435600401356001600160401b03811161054c57610ee6906004369181350101613a6c565b8152602460043501356001600160401b03811161054c57610f0e906004369181350101613a6c565b6020820152604460043501356001600160401b03811161054c57600435013660238201121561054c57600481013590610f468261397b565b91610f54604051938461395a565b80835260208301913660248360071b8301011161054c579160248301925b60248360071b820101841061211557505050506040820152606460043501356001600160401b03811161054c57610fb0906004369181350101613992565b606082015260606083196004353603011261054c57604051610fd1816138bf565b610fdf608460043501613829565b8152610fef60a460043501613829565b602082015261100260c460043501613829565b6040820152608082015260e4600435013560a082015261102761010460043501613829565b60c082015261103b61012460043501613b17565b60e082015261104f61014460043501613b17565b61010082015260043561016481013561012083015261018401356001600160401b03811161054c57611088906004369181350101613b72565b6101408201526101a460043501356001600160401b03811161054c5761014060043582013603600319011261054c57604051906110c4826138a3565b60048181350101356001600160401b03811161054c573660238284600435010101121561054c576110fd6004828482350101013561397b565b9061110b604051928361395a565b60048035840182019081013580845260208401913660059290921b016024011161054c5760248285600435010101905b60048035860184019081013560051b0160240182106120f95750505082526004358101602401356001600160a01b038116900361054c57600435810160248101356020840152604401356001600160401b03811161054c573660238284600435010101121561054c5760048183823501010135906111b88261397b565b916111c6604051938461395a565b808352602083013660248360051b8588600435010101011161054c5760248386600435010101905b60248360051b85886004350101010182106120415750505050604083015260043581016064013564ffffffffff8116900361054c57600435810160648101356060840152608401356001600160401b03811161054c573660238284600435010101121561054c57600481838235010101356112688161397b565b91611276604051938461395a565b81835260208301903660248460051b8388600435010101011161054c579060248286600435010101915b60248460051b8288600435010101018310612025575050505060808301526112ce60a4826004350101613b8d565b60a08301526112e360c4826004350101613b8d565b60c08301526112f860e4826004350101613b8d565b60e0830152610104816004350101356001600160401b03811161054c573660238284600435010101121561054c57600481838235010101356113398161397b565b91611347604051938461395a565b818352602083013660248460051b8488600435010101011161054c5760248286600435010101905b60248460051b8488600435010101018210611fec575050505061010083015261139f906004350161012401613829565b6101208201526101608201526101c460043501356001600160401b03811161054c576113d2906004369181350101613b72565b6101808201526001600160401b036101e460043501351161054c5761140236600480356101e48101350101613b72565b6101a08201526004356102048101356101c08301526102248101356101e083015261024401356001600160401b03811161054c57366023826004350101121561054c576004818135010135906114578261397b565b91611465604051938461395a565b80835260208301913660248360051b836004350101011161054c576024816004350101925b60248360051b836004350101018410611f2457505050506102008201527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054916001600160401b0383161580611f16575b60016001600160401b038516149081611f0c575b159081611f03575b50611edb5760016001600160401b03198416177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005560ff8360401c1615611e9c575b6101c0820151601055608061016083015164ffffffffff6060820151166001600160401b0319600f541617600f5501518051906001600160401b038211610bb557680100000000000000008211610bb557600c5482600c55808310611de4575b5060200190600c5f525f5b8160021c8110611d7f57506003198116808203611d02575b5050506101006101608301516001600160401b0360a082015116600d54906fffffffffffffffff000000000000000060c084015160401b16916fffffffffffffffffffffffffffffffff19161717600d556001600160401b0360e082015116600e550151805190680100000000000000008211610bb557600b5482600b55808310611c93575b5060200190600b5f527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9915f905b828210611bfe57505050506001600160a01b036020610160840151015116610180830151906101a08401516101408501515191803b1561054c576116f85f61170a958195604051978896879586947f44a01eb3000000000000000000000000000000000000000000000000000000008652306004870152608060248701526084860190614c8f565b84810360031901604486015290614c8f565b90606483015203925af18015611bf357611be2575b506001600160a01b036009541661016083015190803b15610550578291829160405180948180947faab9033c000000000000000000000000000000000000000000000000000000008252602060048301526001600160a01b036101206118376117e56117be61179c865161014060248a0152610164890190613abd565b60208701518616604489015260408701518882036023190160648a01526146ca565b606086015164ffffffffff16608488015260808601518782036023190160a4890152613bfd565b6001600160401b0360a08601511660c48701526001600160401b0360c08601511660e48701526001600160401b0360e08601511661010487015261010085015160231987830301610124880152613ba1565b9201511661014483015203925af18015610baa57908291611bce575b5050610140820151600754611b8a578051808060011b0460021481151715611b76576118819060011b614602565b93825b82518110156118c2578061189a60019285613f0c565b516118a58289613f0c565b52846118bb6118b5865184614a92565b89613f0c565b5201611884565b5082610160858760088151115f14611b61576118e66103fd6103f76103e88461513d565b516008555b7f3f678b7d550c0f8dfb5dce7c8568f6e139b01f5582a5613d669ebe77729a49fc64ffffffffff42169182602060405161192481613908565b8281520152600a548369ffffffffff00000000004260281b169169ffffffffffffffffffff19161717600a5561196560405191606083526060830190614c8f565b9280602083015260408201528030930390a20151916001600160a01b03602084015116604084015193608081015194610100820151916001600160401b0360a082015116966001600160401b0360c083015116906119f16001600160a01b0361012064ffffffffff606087015116950151169460405197885261012060208901526101208801906146ca565b868103604088015260208083519283815201920190895b818110611b42575050508581036060870152845180825260208201906020808260051b8501019701918a905b828210611b10578b60ff8c8f8d808e8e8e8e7f0fac2c21f556c18c8ccc516e0c773f53ac94c19685206bc8dabe8a189cb1f43a96608086015260a085015260c084015260e0830152336101008301520390a160401c1615611a925780f35b68ff0000000000000000197ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054167ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a180f35b90919297602080611b31879f9793600194601f199082030186528c51613870565b9a01920192019092919b939b611a34565b82516001600160401b0316845260209384019390920191600101611a08565b611b6d6103fd82614d32565b516007556118eb565b602483634e487b7160e01b81526011600452fd5b606460405162461bcd60e51b815260206004820152600460248201527f696e6974000000000000000000000000000000000000000000000000000000006044820152fd5b611bd7906138f5565b61026b57805f611853565b611bec91506138f5565b5f8061171f565b6040513d5f823e3d90fd5b80518051906001600160401b038211610bb557680100000000000000008211610bb5578554828755808310611c6c575b50602001855f5260205f205f5b838110611c58575050505060016020819201940191019092611670565b600190602084519401938184015501611c3b565b865f5260205f20908382015b8183018110611c88575050611c2e565b5f8155600101611c78565b7f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9908382015b8183018110611cc9575050611642565b80545f825580611cdd575b50600101611cb9565b815f5260205f2090815b8183018110611cf7575050611cd4565b5f8155600101611ce7565b915f925f5b8184038110611d415750505060021c7fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c701555f80806115bc565b9091936020611d756001926001600160401b03885116908560031b60031b916001600160401b03809116831b921b19161790565b9501929101611d07565b5f805b60048110611db757507fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c78201556001016115a4565b84519094916001916020916001600160401b03600689901b81811b199092169216901b1792019401611d82565b60188360031b1680611e5c575b506003830160021c7fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7015b6003820160021c7fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7018110611e515750611599565b5f8155600101611e1c565b7fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c66003850160021c01908154905f199060200360031b1c1690555f611df1565b68ffffffffffffffffff19831668010000000000000001177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055611539565b7ff92ee8a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b9050155f6114f7565b303b1591506114ef565b5060ff8360401c16156114db565b83356001600160401b03811161054c573660438285600435010101121561054c576024818460043501010135611f598161397b565b91611f67604051938461395a565b818352602083013660448460051b8489600435010101011161054c5760448287600435010101905b60448460051b8489600435010101018210611fb757505050908252506020938401930161148a565b81356001600160401b03811161054c57602091611fe1839260443691888d60043501010101613a6c565b815201910190611f8f565b8135906001600160401b03821161054c5760208091612018602494853691898d60043501010101613b72565b815201920191905061136f565b60208060249361203486613b8d565b81520193019291506112a0565b81356001600160401b03811161054c5736604382878a60043501010101121561054c576024818689600435010101013561207a8161397b565b91612088604051938461395a565b81835260208301903660448b838b8760051b9260043501010101011161054c57604481898c60043501010101915b60448b838b8760051b92600435010101010183106120e15750505090825250602091820191016111ee565b602080916120ee85613829565b8152019201916120b6565b60208060249261210885613829565b815201920191905061113b565b60808436031261054c576040519161212c836138da565b61213585613829565b835260026020860135101561054c576020858101359084015260408501356001600160a01b038116900361054c576020608091602494604088013560408201526060612182818a01613b17565b908201528152019401939150610f72565b3461054c575f36600319011261054c5760206040516509184e72a0008152f35b3461054c57604036600319011261054c5760043560243590600b5481101561054c577f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90190815481101561054c576020915f52815f200154604051908152f35b3461054c575f36600319011261054c5760206040516709b6e64a8ec600008152f35b3461054c575f36600319011261054c576040516329ae7ec560e11b81523060048201525f816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa8015611bf35761029e915f916122af575b50604051918291602083526020830190613a8a565b6122cb91503d805f833e6122c3818361395a565b810190613cec565b8261229a565b3461054c575f36600319011261054c5760206040517f00000000000000000000000000000000000000000000000000000000000000008152f35b3461054c575f36600319011261054c576001600160a01b036040517f67e0e0760000000000000000000000000000000000000000000000000000000081523060048201525f81602481857f0000000000000000000000000000000000000000000000000000000000000000165afa8015611bf3575f915f935f915f93612423575b506123a260405194608086526080860190613abd565b6020858203818701528080885193848152019701925f905b8382106123e757878061029e896123d98d8b8582036040870152613a8a565b908382036060850152613a8a565b909192939783606060019260408c51805161240181613af9565b83528085015187168584015201511515604082015201990194939201906123ba565b9450925050503d805f843e612438818461395a565b82019060808383031261054c578251926001600160401b039384811161054c5783612464918301614634565b906020938482015186811161054c57820181601f8201121561054c5780519061248c8261397b565b9661249a604051988961395a565b8288528088018160608095028401019285841161054c578201905b8382106124fb575050505050604082015186811161054c57816124d9918401613c8b565b95606083015190811161054c576124f09201613c8b565b90929390918561238c565b848287031261054c5760405190612511826138bf565b8251600281101561054c57825283830151908a8216820361054c57828592838995015261254060408601613c7e565b60408201528152019101906124b5565b3461054c575f36600319011261054c5760405163ca4f280360e01b81523060048201525f816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa8015611bf35761029e915f916125ca575b50604051918291602083526020830190613abd565b6125de91503d805f833e610b9c818361395a565b826125b5565b3461054c57604036600319011261054c5761265460206126026137fd565b6040517fbeabacc80000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b0390911660248083019190915235604482015291829081906064820190565b03815f6001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165af18015611bf357612699575b602060405160018152f35b6020813d6020116126c8575b816126b26020938361395a565b8101031261054c576126c390613c7e565b61268e565b3d91506126a5565b3461054c57604036600319011261054c576004356001600160401b03811161054c57612700903690600401613992565b602435600281101561054c5761271581613af9565b6128845760045b612724614a9f565b908060031461283057806004146127ac578060011461276c5760021461275857634e487b7160e01b5f52605160045260245ffd5b6020916127649161577b565b604051908152f35b50670de0b6b3a7640000908181029181830414901517156127985760209161279391614a88565b612764565b634e487b7160e01b5f52601160045260245ffd5b505f9190670de0b6b3a76400005b81518410156127f6576127ee6001916127e86127d68787613f0c565b516127e18887613f0c565b51906152db565b90615366565b9301926127ba565b92505050801561280857602090612764565b7f26543689000000000000000000000000000000000000000000000000000000005f5260045ffd5b505f9190670de0b6b3a76400005b81518410156127f657670de0b6b3a764000061287b6001926128756128638888613f0c565b5161286e8988613f0c565b5190615559565b9061478e565b0493019261283e565b600361271c565b3461054c575f36600319011261054c576040516004545f826128ac83613c46565b91828252602093600190856001821691825f146129495750506001146128ee575b506128da9250038361395a565b61029e6040519282849384528301906137d9565b84915060045f527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b905f915b8583106129315750506128da9350820101856128cd565b8054838901850152879450869390920191810161291a565b60ff1916858201526128da95151560051b85010192508791506128cd9050565b3461054c575f36600319011261054c5760206001600160401b03600d5460401c16604051908152f35b3461054c575f36600319011261054c5760206040516001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000168152f35b3461054c575f36600319011261054c57612a6e612a117f0000000000000000000000000000000000000000000000000000000000000000615388565b61029e612a3d7f000000000000000000000000000000000000000000000000000000000000000061549b565b612a7c60405191612a4d83613923565b5f8352604051958695600f60f81b875260e0602088015260e08701906137d9565b9085820360408701526137d9565b904660608501523060808501525f60a085015283820360c0850152613a8a565b3461054c575f36600319011261054c5760405163f29486a160e01b81523060048201526101a09081816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa908115611bf3576040925f92612b1c575b505060608282015191015182519182526020820152f35b612b3b9250803d10612b42575b612b33818361395a565b810190613d11565b8280612b05565b503d612b29565b3461054c57602036600319011261054c576001600160a01b03612b6a6137fd565b165f526002602052602060405f2054604051908152f35b3461054c575f36600319011261054c5760206001600160a01b0360095416604051908152f35b3461054c5760031960203682011261054c57600435906001600160401b039081831161054c5760e090833603011261054c576040519160e0830183811083821117610bb5576040528060040135600281101561054c57835260248101356020840152604481013582811161054c57612c259060043691840101613992565b60408401526064810135606084015260848101356080840152612c4a60a48201613829565b60a084015260c481013591821161054c57013660238201121561054c57602091612c81612764923690602460048201359101613a27565b60c0820152612c8e614900565b6141fb565b3461054c5760208060031936011261054c57604481612cb06137fd565b6040517ff7888aec0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03918216602482015292839182907f0000000000000000000000000000000000000000000000000000000000000000165afa908115611bf3575f91612d2c575b50604051908152f35b90508181813d8311612d52575b612d43818361395a565b8101031261054c575182612d23565b503d612d39565b3461054c575f36600319011261054c5762c73cd160e51b5f5260045ffd5b3461054c575f36600319011261054c57602060405167016345785d8a00008152f35b3461054c575f36600319011261054c57335f908152600260205260409020805460018101909155005b3461054c5760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925612df3366139f2565b92919390612dff614900565b6001600160a01b03809160405195865216941692a3005b3461054c575f36600319011261054c576040516005545f82612e3783613c46565b91828252602093600190856001821691825f14612949575050600114612e6457506128da9250038361395a565b84915060055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0905f915b858310612ea75750506128da9350820101856128cd565b80548389018501528794508693909201918101612e90565b3461054c57604036600319011261054c576001600160401b0360043581811161054c57612ef0903690600401613a6c565b60243591821161054c57612f1861029e91612f12612f2f943690600401613a6c565b90613fbe565b6040929192519384936040855260408501906137d9565b9083820360208501526137d9565b3461054c575f36600319011261054c57602061276461495e565b3461054c575f36600319011261054c57602060405160128152f35b3461054c575f36600319011261054c5760206040517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98152f35b3461054c575f36600319011261054c5760206040516729a2241af62c00008152f35b3461054c5760207fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef612df3366139f2565b3461054c5760846020613011366139f2565b6040517f15dacbea0000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b0393841660248201529183166044830152606482015292839182905f907f0000000000000000000000000000000000000000000000000000000000000000165af18015611bf35761269957602060405160018152f35b3461054c575f36600319011261054c576020612764613f20565b3461054c57606036600319011261054c576004356001600160401b03811161054c576130e4903690600401613992565b604435906131506024356131278164ffffffffff61312042821682613107613e1a565b8160208201511680421015613307575b50511690613e42565b1694613f0c565b51927f000000000000000000000000000000000000000000000000000000000000000091614892565b60019190828411156133015760025b80600314613290578060041461322257806001146131e35760021461319257634e487b7160e01b5f52605160045260245ffd5b80156131bb57602093612764936127e8926ec097ce7bc90715b34b9f0fffffffff0401906152db565b7f0a0c22c7000000000000000000000000000000000000000000000000000000005f5260045ffd5b50809392501561320e576127e8612764926020946ec097ce7bc90715b34b9f100000000004906152db565b634e487b7160e01b5f52601260045260245ffd5b50670de0b6b3a764000093925f91815b613250575b5050508215612808576127e861276492602094906152db565b909194670de0b6b3a76400005186101561328a57908261328281936127e86132788a86613f0c565b516127e18b613eeb565b960192613232565b94613237565b50670de0b6b3a764000093925f91815b6132bd575050508215612808576127e861276492602094906152db565b909194670de0b6b3a76400005186101561328a579082670de0b6b3a76400006132f882946128756132ee8b87613f0c565b5161286e8c613eeb565b049601926132a0565b8261315f565b92508b613117565b3461054c575f36600319011261054c5760405161332b816138a3565b60608152606060208201525f60408201525f60608201525f60808201525f60a0820152606060c0820152606060e08201525f6101008201525f6101208201526001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000166040516329ae7ec560e11b81523060048201525f81602481855afa908115611bf3575f916135f0575b5082526040517f7e361bde0000000000000000000000000000000000000000000000000000000081523060048201525f81602481855afa908115611bf3575f9161358b575b506024916101a0916020850152613417613f20565b60408501526040519283809263f29486a160e01b82523060048301525afa8015611bf357610120915f9161356a575b5060e081015115156060840152610100810151151560808401520151151560a08201526134746007546147ba565b60c08201526134846008546147ba565b60e082015264ffffffffff6020613499613e1a565b8281511661010085015201511661012082015260405180916020825264ffffffffff6101206135506134d98451610140602088015261016087019061383d565b61353a6134f8602087015192601f1993848a83030160408b015261383d565b604087015160608901526060870151151560808901526080870151151560a089015260a0870151151560c089015260c0870151838983030160e08a0152613870565b9060e08601519087830301610100880152613870565b928261010082015116828601520151166101408301520390f35b61358591506101a03d6101a011612b4257612b33818361395a565b83613446565b90503d805f833e61359c818361395a565b81019160408284031261054c578151916001600160401b039283811161054c57846135c8918301613c8b565b90602081015193841161054c576024946101a0946135e69201613c8b565b9050915091613402565b61360491503d805f833e6122c3818361395a565b836133bd565b3461054c575f36600319011261054c576020601054604051908152f35b3461054c57604036600319011261054c5761265460206136456137fd565b60405163e1f21c6760e01b81523360048201526001600160a01b0390911660248083019190915235604482015291829081906064820190565b3461054c575f36600319011261054c576040516003545f8261369f83613c46565b91828252602093600190856001821691825f146129495750506001146136cc57506128da9250038361395a565b84915060035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b905f915b85831061370f5750506128da9350820101856128cd565b805483890185015287945086939092019181016136f8565b3461054c575f36600319011261054c5760206305a39a806137687f000000000000000000000000000000000000000000000000000000000000000042613c39565b10604051908152f35b3461054c57602036600319011261054c57600435907fffffffff00000000000000000000000000000000000000000000000000000000821680920361054c577f01ffc9a700000000000000000000000000000000000000000000000000000000602092148152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b600435906001600160a01b038216820361054c57565b602435906001600160a01b038216820361054c57565b35906001600160a01b038216820361054c57565b9081518082526020808093019301915f5b82811061385c575050505090565b83518552938101939281019260010161384e565b9081518082526020808093019301915f5b82811061388f575050505090565b835185529381019392810192600101613881565b61014081019081106001600160401b03821117610bb557604052565b606081019081106001600160401b03821117610bb557604052565b608081019081106001600160401b03821117610bb557604052565b6001600160401b038111610bb557604052565b604081019081106001600160401b03821117610bb557604052565b602081019081106001600160401b03821117610bb557604052565b61012081019081106001600160401b03821117610bb557604052565b90601f801991011681019081106001600160401b03821117610bb557604052565b6001600160401b038111610bb55760051b60200190565b9080601f8301121561054c5760209082356139ac8161397b565b936139ba604051958661395a565b81855260208086019260051b82010192831161054c57602001905b8282106139e3575050505090565b813581529083019083016139d5565b606090600319011261054c576001600160a01b0390600435828116810361054c5791602435908116810361054c579060443590565b9291926001600160401b038211610bb55760405191613a50601f8201601f19166020018461395a565b82948184528183011161054c578281602093845f960137010152565b9080601f8301121561054c57816020613a8793359101613a27565b90565b9081518082526020808093019301915f5b828110613aa9575050505090565b835185529381019392810192600101613a9b565b9081518082526020808093019301915f5b828110613adc575050505090565b83516001600160a01b031685529381019392810192600101613ace565b60021115613b0357565b634e487b7160e01b5f52602160045260245ffd5b3590811515820361054c57565b9291613b2f8261397b565b91613b3d604051938461395a565b829481845260208094019160051b810192831161054c57905b828210613b635750505050565b81358152908301908301613b56565b9080601f8301121561054c57816020613a8793359101613b24565b35906001600160401b038216820361054c57565b90808251908181526020809101926020808460051b8301019501935f915b848310613bcf5750505050505090565b9091929394958480613bed600193601f198682030187528a51613870565b9801930193019194939290613bbf565b9081518082526020808093019301915f5b828110613c1c575050505090565b83516001600160401b031685529381019392810192600101613c0e565b9190820391821161279857565b90600182811c92168015613c74575b6020831014613c6057565b634e487b7160e01b5f52602260045260245ffd5b91607f1691613c55565b5190811515820361054c57565b9080601f8301121561054c57815190602091613ca68161397b565b93613cb4604051958661395a565b81855260208086019260051b82010192831161054c57602001905b828210613cdd575050505090565b81518152908301908301613ccf565b9060208282031261054c5781516001600160401b03811161054c57613a879201613c8b565b809103906101a0821261054c57608060405192613d2d846138a3565b1261054c57604051613d3e816138da565b613d4782613c7e565b8152613d5560208301613c7e565b6020820152613d6660408301613c7e565b6040820152613d7760608301613c7e565b606082015282526080810151602083015260a0810151604083015260c0810151606083015260e081015164ffffffffff8116810361054c576080830152610100908181015163ffffffff8116810361054c57613e13916101809160a086015261012093613de5858301613c7e565b60c0870152613df76101408301613c7e565b60e0870152613e096101608301613c7e565b9086015201613c7e565b9082015290565b60405190613e2782613908565b816020600a5464ffffffffff90818116845260281c16910152565b64ffffffffff918216908216039190821161279857565b805115613e665760200190565b634e487b7160e01b5f52603260045260245ffd5b805160011015613e665760400190565b805160021015613e665760600190565b805160031015613e665760800190565b805160041015613e665760a00190565b805160051015613e665760c00190565b805160061015613e665760e00190565b805160071015613e66576101000190565b670de0b6b3a764000051811015613e665760051b670de0b6b3a76400200190565b8051821015613e665760209160051b010190565b6040517fe4dc2aa40000000000000000000000000000000000000000000000000000000081523060048201526020816024816001600160a01b037f0000000000000000000000000000000000000000000000000000000000000000165afa908115611bf3575f91613f8f575090565b90506020813d602011613fb6575b81613faa6020938361395a565b8101031261054c575190565b3d9150613f9d565b905f9060068054925b838110613ff5575050505050604051613fdf81613923565b5f815290604051613fef81613923565b5f815290565b815f52807ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f018054906140278261397b565b916040906140378251948561395a565b808452602092838501905f52835f205f915b83831061414b575050505061405d83613e59565b51815161408484828180820195805191829101875e81015f8382015203808452018261395a565b5190208882516140ae85828180820195805191829101875e81015f8382015203808452018261395a565b5190201491826140e6575b50506140c85750600101613fc7565b959450505050506140e26140db83613e8a565b5192613e9a565b5190565b9091506140f283613e7a565b51825161411983828180820195805191829101875e81015f8382015203808452018261395a565b51902091516141408282019282895180838c01875e81015f8382015203808452018261395a565b519020145f806140b9565b8451825491905f61415b84613c46565b8083526001948a86821691825f146141de575050600114614198575b50918161418a8a9360019695038261395a565b815201920192019190614049565b9050989e9d9c9b9a9998845f52885f20905f915b8183106141cb57509e9f9d9e509b9c9a9b999a98998101880181614177565b80548484018c0152918a019185016141ac565b60ff19168582015291151560051b84019091019150829050614177565b614203613e1a565b7f00000000000000000000000000000000000000000000000000000000000000009061424c64ffffffffff91828042169181602082015116804210156145fa5750511690613e42565b16906060830180519260049384811080806145eb575b81156145ce575b50156145ad57508151926080860151905f602060405161428881613908565b82815201528185878381841061457b57505050506003199485810190811161456857918581019081116145685794810190811161452e57906142f36143099392956143026142f9856142db6008546147ba565b956142f36142eb8c830189613f0c565b519188613f0c565b516155f6565b97820184613f0c565b5192613f0c565b9182602060405161431981613908565b838152015291935b805161432c81613af9565b61433581613af9565b61446d576020810180519360408301966143528851865190613f0c565b5194600e5496879661436e670de0b6b3a7640000998a9261478e565b041061445f578851905161438191613f0c565b5161439460808a51960195865190613f0c565b519351876143a18361475a565b04811161443857906143b66143bc9282614a92565b9061577b565b918681029080820488149015171561442557614407936143ef61440d9796946143e98a956143fc95614a88565b906152db565b838181039110029061478e565b049651905190613f0c565b5161478e565b048211614418575090565b6327e92f0f60e01b5f525ffd5b601188634e487b7160e01b5f525260245ffd5b887f340a4533000000000000000000000000000000000000000000000000000000005f525ffd5b876327e92f0f60e01b5f525ffd5b906020829593950191825193604082019661449060808951940193845190613f0c565b5194600e549687966144ac670de0b6b3a7640000998a9261478e565b041061445f576144ce6144c28a51875190613f0c565b51948a51905190613f0c565b519051876144db8361475a565b04811161454157916144f8826143b66144fe96956143e995613c39565b9261577b565b670de0b6b3a763ffff1981019190821161452e57916145246144079261440d9594615366565b9651905190613f0c565b601186634e487b7160e01b5f525260245ffd5b887f64590b9f000000000000000000000000000000000000000000000000000000005f525ffd5b601187634e487b7160e01b5f525260245ffd5b919396959450919650116145a5575b6142f36143099394956143026142f9856142db6007546147ba565b85935061458a565b926145bd81836145c89496614892565b936080870151614892565b93614321565b905015806145dd575b5f614269565b5084608087015110156145d7565b90508560808801511090614262565b92505f613117565b9061460c8261397b565b614619604051918261395a565b828152809261462a601f199161397b565b0190602036910137565b9080601f8301121561054c5781519060209161464f8161397b565b9361465d604051958661395a565b81855260208086019260051b82010192831161054c57602001905b828210614686575050505090565b81516001600160a01b038116810361054c578152908301908301614678565b9060208282031261054c5781516001600160401b03811161054c57613a879201614634565b908082519081815260208091019281808460051b8301019501935f915b8483106146f75750505050505090565b90919280949596601f198382030184528751908280835192838152019201905f905b80821061473857505050908060019298019301930191949392906146e7565b919380600192946001600160a01b038751168152019401920187939291614719565b90670429d069189e00009182810292818404149015171561279857565b906127109182810292818404149015171561279857565b8181029291811591840414171561279857565b90633b9aca009182810292818405149015171561279857565b614885604051916147ca8361393e565b600883526101003660208501376147e38160e01d6147a1565b6147ec84613e59565b5260036147fd8260c01d820b6147a1565b61480685613e7a565b526148158260a01d820b6147a1565b61481e85613e8a565b5261482d8260801d820b6147a1565b61483685613e9a565b526148458260601d820b6147a1565b61484e85613eaa565b5261485d8260401d820b6147a1565b61486685613eba565b526148758260201d820b6147a1565b61487e85613eca565b520b6147a1565b61488e82613eda565b5290565b8083600482106148d757506003199180830191508111612798579260085491810190811161279857613a87936143026148ce6142f393946147ba565b93820184613f0c565b613a879492506004106148f7575b6142f3906143026148ce6007546147ba565b600491506148e5565b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016330361493257565b7f089676d5000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b6001600160a01b037f000000000000000000000000000000000000000000000000000000000000000016301480614a5f575b156149b9577f000000000000000000000000000000000000000000000000000000000000000090565b60405160208101907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527f000000000000000000000000000000000000000000000000000000000000000060408201527f000000000000000000000000000000000000000000000000000000000000000060608201524660808201523060a082015260a0815260c081018181106001600160401b03821117610bb55760405251902090565b507f00000000000000000000000000000000000000000000000000000000000000004614614990565b811561320e570490565b9190820180921161279857565b7f0000000000000000000000000000000000000000000000000000000000000000614ac981614602565b9064ffffffffff80421681600a54818160281c1680421015614c87575b5016900316614af66007546147ba565b9180600482119384614c7e575b614b2284614b1083613e59565b51614b1b8585613f0c565b51906155f6565b614b2b87613e59565b52614b4784614b3983613e7a565b51614b1b6001860185613f0c565b614b5087613e7a565b526002831115614c7657614b7584614b6783613e8a565b51614b1b6002860185613f0c565b614b7e87613e8a565b526003831115614c7657614ba591614b1b826003614b9c8895613e9a565b51930190613f0c565b614bae85613e9a565b5260048114614c7057614bc26008546147ba565b9215614c7057614be482614bd585613e59565b51614b1b600319850187613f0c565b614bed85613eaa565b526005811115614c7057614c1382614c0485613e7a565b51614b1b600219850187613f0c565b614c1c85613eba565b526006811115614c7057614c4282614c3385613e8a565b51614b1b600119850187613f0c565b614c4b85613eca565b526007811115614c7057614b1b83614c6561488595613e9a565b51925f190190613f0c565b50505090565b505050505090565b60049150614b03565b92505f614ae6565b9081518082526020808093019301915f5b828110614cae575050505090565b835185529381019392810192600101614ca0565b801561320e576ec097ce7bc90715b34b9f10000000000590565b811561320e570590565b15614ced57565b60405162461bcd60e51b815260206004820152601260248201527f4f766572666c6f772f556e646572666c6f7700000000000000000000000000006044820152606490fd5b906060915f805f9083516008906008811015614df4575b505015614ddd575b614d5c818451613c39565b614d6557505050565b919360e0939193915f5b8651851015614dc95760019063ffffffff633b9aca00614d8f888b613f0c565b5105637fffffff81131580614dba575b614da890614ce6565b16851b1793601f190194019392614d6f565b50637fffffff19811215614d9f565b935093909450614dda915084613f0c565b52565b9350614dee60018560031c01614602565b93614d51565b809197508392505b8781101561511c575b50146151135760018160031c015b614e1c81614602565b955f5b83811015614d495781633b9aca009182614e39828a613f0c565b51056101405263ffffffff60019380614e548685018c613f0c565b51056101205280614e68600285018c613f0c565b51056101005280614e7c600385018c613f0c565b510560e05280614e8f600485018c613f0c565b510560c05280614ea2600585018c613f0c565b510560a05280614eb5600685018c613f0c565b5105608052614ec7600784018b613f0c565b5105637fffffff61014051131580615101575b806150f0575b806150de575b806150cd575b806150bb575b806150ab575b8061509a575b8061508a575b80615079575b80615069575b80615058575b80615048575b80615037575b80615029575b8061501a575b614f3790614ce6565b1667ffffffff0000000060805160201b166bffffffff000000000000000060a05160401b166fffffffff00000000000000000000000060c05160601b1673ffffffff0000000000000000000000000000000060e05160801b1677ffffffff00000000000000000000000000000000000000006101005160a01b167bffffffff0000000000000000000000000000000000000000000000006101205160c01b167fffffffff000000000000000000000000000000000000000000000000000000006101405160e01b1617171717171717615010888c613f0c565b5201940193614e1f565b50637fffffff19811215614f2e565b50637fffffff811315614f28565b50637fffffff196080511215614f22565b50637fffffff6080511315614f1c565b50637fffffff1960a0511215614f16565b50637fffffff60a0511315614f10565b50637fffffff1960c0511215614f0a565b50637fffffff60c0511315614f04565b50637fffffff1960e0511215614efe565b50637fffffff60e0511315614ef8565b50637fffffff19610100511215614ef2565b50637fffffff610100511315614eec565b50637fffffff19610120511215614ee6565b50637fffffff610120511315614ee0565b50637fffffff19610140511215614eda565b8060031c614e13565b9091506007811615615133578291905f1901614dfc565b915081905f614e05565b90815160019060011c6040805190615154826138bf565b600282525f5b8181106152ca57505080946040516151718161393e565b60088152610100908136602083013761518984613e59565b5261519383613e59565b505f855b615260575b50604051906151aa8261393e565b600882523660208301376151bd83613e7a565b526151c782613e7a565b50600319830190838211612798575f5b8281106151e657505050505050565b60048082019081831161524d57509061522861522287846152088b9688613f0c565b5161521c866152168c613e7a565b51613f0c565b52614a92565b84613f0c565b5161524661523587613e7a565b516152408785614a92565b90613f0c565b52016151d7565b601190634e487b7160e01b5f525260245ffd5b6004808210156152c4576152748285613f0c565b516152828361521688613e59565b526152966152908784614a92565b85613f0c565b51906152a186613e59565b518184019182851161524d5750916152bc8994928594613f0c565b520190615197565b5061519c565b80606060208093860101520161515a565b90670de0b6b3a7640000908181036152f257505090565b671bc16d674ec80000810361530d57505080613a8791615366565b673782dace9d900000810361533157505061532b81613a8792615366565b80615366565b61533b91926157ac565b90600161534783614777565b915f1983010401901515026001810180911161279857613a8791614a92565b906153709161478e565b6001670de0b6b3a76400005f19830104019015150290565b60ff81146153dc5760ff811690601f82116153b457604051916153aa83613908565b8252602082015290565b7fb3512b0c000000000000000000000000000000000000000000000000000000005f5260045ffd5b506040515f815f54916153ee83613c46565b808352926020906001908181169081156154775750600114615419575b5050613a879250038261395a565b9150925f80527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563935f925b82841061545f5750613a879450505081016020015f8061540b565b85548785018301529485019486945092810192615444565b91505060209250613a8794915060ff191682840152151560051b8201015f8061540b565b60ff81146154bd5760ff811690601f82116153b457604051916153aa83613908565b506040515f816001916001546154d281613c46565b808452936020916001811690811561547757506001146154fa575050613a879250038261395a565b91509260015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6935f925b8284106155415750613a879450505081016020015f8061540b565b85548785018301529485019486945092810192615526565b670de0b6b3a7640000918083036155705750905090565b8290671bc16d674ec8000081036155915750508061558d9161478e565b0490565b673782dace9d90000081036155b557506155ae8261558d9361478e565b048061478e565b90506155c0916157ac565b6155c981614777565b60015f19938483010401901515029060018201808311612798578110156155f1575050505f90565b030190565b90670de0b6b3a76400009081810290808205831481151715612798575f121561562d57613a87936156269161478e565b0490614a92565b600160ff1b811461279857613a8793615647915f0361478e565b0490613c39565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116156c5579160209360809260ff5f9560405194855216868401526040830152606082015282805260015afa15611bf3575f516001600160a01b038116156156bb57905f905f90565b505f906001905f90565b5050505f9160039190565b6004811015613b0357806156e2575050565b60018103615712577ff645eedf000000000000000000000000000000000000000000000000000000005f5260045ffd5b6002810361574657507ffce698f7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6003146157505750565b7fd78bce0c000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b9080156131bb57670de0b6b3a764000091828102928184041490151715612798576001905f19830104019015150290565b908015615fa3578115615f9d578160ff1c615f8f57770bce5086492111aea88f4bb1ca6bcf584181ea8059f76532811015615f675781670c7d713b49da00001280615f56575b15615c0957670de0b6b3a7640000916ec097ce7bc90715b34b9f100000000090615835908402828101906ec097ce7bc90715b34b9f0fffffffff19018302614cdc565b9080828002059181838202058284820205838582020591848684020593858786020595808888020597880205600f900596600d900595600b900594600990059360079005926005900591600390050101010101010160011b918082818507020592050201670de0b6b3a7640000905b05680238fd42c5cf03ffff198181131580615bf6575b15615bce57819082121580615bbb575b15615b93575f915f8112615b84575b506064906806f05b59d3b20000008112615b37576806f05b59d3b1ffffff190168056bc75e2d6310000082770195e54c5dd42177f53a27172fa9ec630262827000000000925b02819068ad78ebc5ac62000000811215615b14575b6856bc75e2d631000000811215615af0575b682b5e3af16b18800000811215615ace575b6815af1d78b58c400000811215615aac575b680ad78ebc5ac6200000811215615a8b575b82811215615a6a575b6802b5e3af16b1880000811215615a49575b68015af1d78b58c40000811215615a28575b60028382800205056003848383020505600485848302050585600581868402050560068287830205056007838883020505906008848984020505926009858a8602050595600a868b8902050597600b878c8b02050599600c888d8d0205059b01010101010101010101010102050205905f14613a8757613a8790614cc2565b6806f5f1775788937937839168015af1d78b58c3ffff1901920205906159a9565b6808f00f760a4b2db55d83916802b5e3af16b187ffff190192020590615997565b680ebc5fb41746121110839168056bc75e2d630fffff190192020590615985565b68280e60114edb805d038391680ad78ebc5ac61fffff19019202059061597c565b690127fa27722cc06cc5e283916815af1d78b58c3fffff19019202059061596a565b693f1fce3da636ea5cf8508391682b5e3af16b187fffff190192020590615958565b6b02df0ab5a80a22c61ab5a70083916856bc75e2d630ffffff190192020590615946565b6e01855144814a7ff805980ff0084000915068ad78ebc5ac61ffffff1901615934565b6803782dace9d90000008112615b71576803782dace9d8ffffff190168056bc75e2d63100000826b1425982cf597cd205cef73809261591f565b68056bc75e2d631000008260019261591f565b600192505f03905060646158d9565b7fd4794efd000000000000000000000000000000000000000000000000000000005f5260045ffd5b5068070c1cc73b00c800008213156158ca565b7fa2f9f7e3000000000000000000000000000000000000000000000000000000005f5260045ffd5b5068070c1cc73b00c800008213156158ba565b81670de0b6b3a7640000925f91848112615f40575b506064905f7e1600ef3172e58d2e933ec884fde10064c63b5372d805e203c0000000000000821215615f15575b73011798004d755d3c8bc8e03204cf44619e000000821215615ef4575b820290808302906e01855144814a7ff805980ff00840009081831215615ed1575b50506b02df0ab5a80a22c61ab5a70080821215615eb1575b50693f1fce3da636ea5cf85080821215615e91575b50690127fa27722cc06cc5e280821215615e71575b5068280e60114edb805d0380821215615e51575b50680ebc5fb4174612111080821215615e3a575b506808f00f760a4b2db55d80821215615e1a575b506806f5f177578893793780821215615dfa575b506806248f33704b28660380821215615ddb575b506805c548670b9510e7ac80821215615dbc575b50615d6968056bc75e2d6310000091828082019168056bc75e2d630fffff190102614cdc565b9080828002059181838202058284820205916003600560076009600b888a89020598808b8b02059a8b0205059805960594059205010101010160011b0105905f14615db7575f035b026158a4565b615db1565b68056bc75e2d631000006756bc75e2d63100009202059101905f615d43565b68056bc75e2d6310000067ad78ebc5ac6200009202059101905f615d2f565b68056bc75e2d6310000068015af1d78b58c400009202059101905f615d1b565b68056bc75e2d631000006802b5e3af16b18800009202059101905f615d07565b68056bc75e2d63100000809202059101905f615cf3565b68056bc75e2d63100000680ad78ebc5ac62000009202059101905f615cdf565b68056bc75e2d631000006815af1d78b58c4000009202059101905f615ccb565b68056bc75e2d63100000682b5e3af16b188000009202059101905f615cb6565b68056bc75e2d631000006856bc75e2d6310000009202059101905f615ca1565b68ad78ebc5ac62000000925069021e19e0c9bab240000002059101905f80615c89565b906b1425982cf597cd205cef73806803782dace9d900000091059101615c68565b50770195e54c5dd42177f53a27172fa9ec63026282700000000090056806f05b59d3b2000000615c4b565b9050615f4c9150614cc2565b6001906064615c1e565b50670f43fc2c04ee000082126157f2565b7fd8317311000000000000000000000000000000000000000000000000000000005f5260045ffd5b6211380f60e51b5f5260045ffd5b50505f90565b5050670de0b6b3a76400009056fea26469706673582212201275773d2fb2990e16f9d3cab6b7df7b03b6f6f9d9e9413bbbd86bf0673a507864736f6c634300081a00330000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba900000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000016000000000000000000000000021ae9576a393413d6d91dfe2543dcb548dbb8748000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000185361666520486176656e2d4254433a504158473a55534443000000000000000000000000000000000000000000000000000000000000000000000000000000054254465348000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004f7b226e616d65223a225175616e74414d4d5765696768746564506f6f6c222c2276657273696f6e223a312c226465706c6f796d656e74223a2232303235303431392d76332d7175616e74616d6d227d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000086f76657276696577000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001161646170746162696c69747953636f726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066e756d6265720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000135000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000b72756c6544657461696c73000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e75706461746552756c654e616d650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d506f776572204368616e6e656c00000000000000000000000000000000000000

Deployed Bytecode

0x610160806040526004361015610013575f80fd5b5f905f3560e01c90816301ffc9a71461377157508063021eb7cd1461372757806306fdde031461367e578063095ea7b3146136275780630f6480df1461360a578063132f42641461330f57806316a0b3e0146130b457806318160ddd1461309a57806323b872dd14612fff57806323de665114612fce578063273c1adf14612fac57806330adf81f14612f72578063313ce56714612f575780633644e51514612f3d578063451a10aa14612ebf57806354fd4d5014612e165780635687f2b814612dc2578063627cdcb914612d99578063654cf15d14612d77578063679aefce14612d5957806370a0823114612c9357806372c9818614612ba75780637445e37f14612b815780637ecebe0014612b4957806381fa807c14612a9c57806384b0196e146129d55780638d928af8146129925780638ddf01c01461296957806395d89b411461288b578063984de9e8146126d0578063a9059cbb146125e4578063aa6ca80814612550578063abb1dc441461230b578063afcff50f146122d1578063b156aa0a14612235578063b677fa5614612213578063c0f38e74146121b3578063ce20ece714612193578063ce288eb514610e70578063d335b0cf14610de9578063d505accf14610bc9578063d5787898146107d4578063dd62ed3e146106e9578063dddd104a146105bc578063e313fe3814610554578063e4e3b53514610304578063eb296de1146102dd578063ecda10f5146102a2578063f89f27ed1461026e5763fd2c80ae14610245575f80fd5b3461026b578060031936011261026b5760206001600160401b03600f5416604051908152f35b80fd5b503461026b578060031936011261026b5761029e61028a614a9f565b604051918291602083526020830190613a8a565b0390f35b503461026b578060031936011261026b5760206040517f00000000000000000000000000000000000000000000000000000000681aadfb8152f35b503461026b578060031936011261026b5760206001600160401b03600d5416604051908152f35b503461026b57606036600319011261026b576004356001600160401b038082116105505736602383011215610550578160040135908111610550576024820191602436918360051b0101116105505761035b613813565b906044359164ffffffffff9182841680850361054c576001600160a01b039384600954163303610521577f0000000000000000000000000000000000000000000000000000000000000003906001918060011b908082046002149015171561050d5784036104e35760088411156104c05761040b6103fd6103f76103e86103e336898e613b24565b61513d565b6104026103fd6103f783613e59565b51614d32565b613e59565b51600755613e7a565b51600855959092955b4216926040519161042483613908565b848352836020809401528469ffffffffff0000000000600a549260281b169169ffffffffffffffffffff19161717600a5560405196806060890160608a52526080880198928a5b8281106104ad578b8b7f3f678b7d550c0f8dfb5dce7c8568f6e139b01f5582a5613d669ebe77729a49fc8c808d8d8d8d6020850152604084015216930390a280f35b84358b529981019993810193830161046b565b6104d66103fd6104d136878c613b24565b614d32565b5160075595909295610414565b60405162461bcd60e51b815260206004820152600260248201526115d160f21b6044820152606490fd5b60248a634e487b7160e01b81526011600452fd5b60405162461bcd60e51b81526020600482015260036024820152622c2aa960e91b6044820152606490fd5b5f80fd5b8280fd5b503461026b57602036600319011261026b57600435600c548110156105b857600c602092526001600160401b038160021c7fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7015460c06040519360061b161c168152f35b5080fd5b503461026b57602036600319011261026b576105d66137fd565b6001600160a01b03807f000000000000000000000000d785201fd2d9be7602f6682296bb415530c027ef1633036106be576305a39a806106367f00000000000000000000000000000000000000000000000000000000681aadfb42613c39565b10156106935780600954921691827fffffffffffffffffffffffff0000000000000000000000000000000000000000821617600955167fc51fb6cc8e37fb27ff77886c5b33be204bb0a91dc410398c3a9bef63760c0b9d8380a380f35b60405162461bcd60e51b815260206004820152600360248201526258494d60e81b6044820152606490fd5b60405162461bcd60e51b815260206004820152600360248201526216105160ea1b6044820152606490fd5b503461026b57604036600319011261026b576107036137fd565b90602061070e613813565b9260646001600160a01b0391828060405197889586947f927da1050000000000000000000000000000000000000000000000000000000086523060048701521660248501521660448301527f000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba9165afa9081156107c85790610795575b602090604051908152f35b506020813d6020116107c0575b816107af6020938361395a565b8101031261054c576020905161078a565b3d91506107a2565b604051903d90823e3d90fd5b503461026b578060031936011261026b576040519061012082018281106001600160401b03821117610bb55760405260608252806020830152806040830152606080830152606060808301528060a08301528060c08301528060e08301528061010083015260405163ca4f280360e01b815230600482015281816024816001600160a01b037f000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba9165afa908115610baa578291610b88575b50825260105460208301527f00000000000000000000000000000000000000000000000000000000000000116040830152600b546108c88161397b565b906108d6604051928361395a565b808252827f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9602084015b838310610b2e57505050506060830152604051908180926020600c54918281520190600c84527fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c793905b806003830110610adc57610982945490808310610ac0575b808310610aa1575b808310610a82575b8210610a6e575b5050038261395a565b60808201526001600160401b03600d5481811660a084015260401c1660c0820152600e546101008201526001600160401b03600f541660e0820152604051809160208252610100610a296109e483516101206020870152610140860190613abd565b6020840151604086015260408401516060860152610a14606085015191601f199283888303016080890152613ba1565b906080850151908683030160a0870152613bfd565b916001600160401b0360a08201511660c08501526001600160401b0360c08201511660e08501526001600160401b0360e0820151168285015201516101208301520390f35b60c01c82526020909101906001015f610979565b919260206001916001600160401b038460801c16815201930191610972565b919260206001916001600160401b038460401c1681520193019161096a565b919260206001916001600160401b038416815201930191610962565b916004919350608060019186546001600160401b03811682526001600160401b038160401c1660208301526001600160401b0381841c16604083015260c01c606082015201940192018492939161094a565b604051825480825283885260208089209083019189905b808210610b705750505060019282610b628594602094038261395a565b815201920192019190610900565b90919260016020819286548152019401920190610b45565b610ba491503d8084833e610b9c818361395a565b8101906146a5565b5f61088b565b6040513d84823e3d90fd5b634e487b7160e01b5f52604160045260245ffd5b503461026b5760e036600319011261026b57610be36137fd565b610beb613813565b90604435916064359160843560ff81168103610de557834211610db957610c2c826001600160a01b03165f52600260205260405f2080549060018201905590565b9060405160208101917f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c983526001600160a01b039687861694856040850152888816606085015289608085015260a084015260c083015260c0825260e08201908282106001600160401b03831117610bb5578793610ce693610cdd93604052519020610cb661495e565b906040519161190160f01b83526002830152602282015260c43591604260a435922061564e565b909291926156d0565b16818103610d8b5760405163e1f21c6760e01b81526001600160a01b03848116600483015285166024820152604481018790528790602081606481857f000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba98c165af18015610baa57610d55575080f35b6020813d602011610d83575b81610d6e6020938361395a565b810103126105b857610d7f90613c7e565b5080f35b3d9150610d61565b7f4b800e46000000000000000000000000000000000000000000000000000000008752600452602452604485fd5b602486857f62791302000000000000000000000000000000000000000000000000000000008252600452fd5b8580fd5b503461026b578060031936011261026b57604051907fb45090f90000000000000000000000000000000000000000000000000000000082523060048301526020826024816001600160a01b037f000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba9165afa9081156107c8579061079557602090604051908152f35b503461054c57602036600319011261054c576001600160401b036004351161054c576102606004353603600319011261054c5760405161022081018181106001600160401b03821117610bb557604052600435600401356001600160401b03811161054c57610ee6906004369181350101613a6c565b8152602460043501356001600160401b03811161054c57610f0e906004369181350101613a6c565b6020820152604460043501356001600160401b03811161054c57600435013660238201121561054c57600481013590610f468261397b565b91610f54604051938461395a565b80835260208301913660248360071b8301011161054c579160248301925b60248360071b820101841061211557505050506040820152606460043501356001600160401b03811161054c57610fb0906004369181350101613992565b606082015260606083196004353603011261054c57604051610fd1816138bf565b610fdf608460043501613829565b8152610fef60a460043501613829565b602082015261100260c460043501613829565b6040820152608082015260e4600435013560a082015261102761010460043501613829565b60c082015261103b61012460043501613b17565b60e082015261104f61014460043501613b17565b61010082015260043561016481013561012083015261018401356001600160401b03811161054c57611088906004369181350101613b72565b6101408201526101a460043501356001600160401b03811161054c5761014060043582013603600319011261054c57604051906110c4826138a3565b60048181350101356001600160401b03811161054c573660238284600435010101121561054c576110fd6004828482350101013561397b565b9061110b604051928361395a565b60048035840182019081013580845260208401913660059290921b016024011161054c5760248285600435010101905b60048035860184019081013560051b0160240182106120f95750505082526004358101602401356001600160a01b038116900361054c57600435810160248101356020840152604401356001600160401b03811161054c573660238284600435010101121561054c5760048183823501010135906111b88261397b565b916111c6604051938461395a565b808352602083013660248360051b8588600435010101011161054c5760248386600435010101905b60248360051b85886004350101010182106120415750505050604083015260043581016064013564ffffffffff8116900361054c57600435810160648101356060840152608401356001600160401b03811161054c573660238284600435010101121561054c57600481838235010101356112688161397b565b91611276604051938461395a565b81835260208301903660248460051b8388600435010101011161054c579060248286600435010101915b60248460051b8288600435010101018310612025575050505060808301526112ce60a4826004350101613b8d565b60a08301526112e360c4826004350101613b8d565b60c08301526112f860e4826004350101613b8d565b60e0830152610104816004350101356001600160401b03811161054c573660238284600435010101121561054c57600481838235010101356113398161397b565b91611347604051938461395a565b818352602083013660248460051b8488600435010101011161054c5760248286600435010101905b60248460051b8488600435010101018210611fec575050505061010083015261139f906004350161012401613829565b6101208201526101608201526101c460043501356001600160401b03811161054c576113d2906004369181350101613b72565b6101808201526001600160401b036101e460043501351161054c5761140236600480356101e48101350101613b72565b6101a08201526004356102048101356101c08301526102248101356101e083015261024401356001600160401b03811161054c57366023826004350101121561054c576004818135010135906114578261397b565b91611465604051938461395a565b80835260208301913660248360051b836004350101011161054c576024816004350101925b60248360051b836004350101018410611f2457505050506102008201527ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054916001600160401b0383161580611f16575b60016001600160401b038516149081611f0c575b159081611f03575b50611edb5760016001600160401b03198416177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a005560ff8360401c1615611e9c575b6101c0820151601055608061016083015164ffffffffff6060820151166001600160401b0319600f541617600f5501518051906001600160401b038211610bb557680100000000000000008211610bb557600c5482600c55808310611de4575b5060200190600c5f525f5b8160021c8110611d7f57506003198116808203611d02575b5050506101006101608301516001600160401b0360a082015116600d54906fffffffffffffffff000000000000000060c084015160401b16916fffffffffffffffffffffffffffffffff19161717600d556001600160401b0360e082015116600e550151805190680100000000000000008211610bb557600b5482600b55808310611c93575b5060200190600b5f527f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9915f905b828210611bfe57505050506001600160a01b036020610160840151015116610180830151906101a08401516101408501515191803b1561054c576116f85f61170a958195604051978896879586947f44a01eb3000000000000000000000000000000000000000000000000000000008652306004870152608060248701526084860190614c8f565b84810360031901604486015290614c8f565b90606483015203925af18015611bf357611be2575b506001600160a01b036009541661016083015190803b15610550578291829160405180948180947faab9033c000000000000000000000000000000000000000000000000000000008252602060048301526001600160a01b036101206118376117e56117be61179c865161014060248a0152610164890190613abd565b60208701518616604489015260408701518882036023190160648a01526146ca565b606086015164ffffffffff16608488015260808601518782036023190160a4890152613bfd565b6001600160401b0360a08601511660c48701526001600160401b0360c08601511660e48701526001600160401b0360e08601511661010487015261010085015160231987830301610124880152613ba1565b9201511661014483015203925af18015610baa57908291611bce575b5050610140820151600754611b8a578051808060011b0460021481151715611b76576118819060011b614602565b93825b82518110156118c2578061189a60019285613f0c565b516118a58289613f0c565b52846118bb6118b5865184614a92565b89613f0c565b5201611884565b5082610160858760088151115f14611b61576118e66103fd6103f76103e88461513d565b516008555b7f3f678b7d550c0f8dfb5dce7c8568f6e139b01f5582a5613d669ebe77729a49fc64ffffffffff42169182602060405161192481613908565b8281520152600a548369ffffffffff00000000004260281b169169ffffffffffffffffffff19161717600a5561196560405191606083526060830190614c8f565b9280602083015260408201528030930390a20151916001600160a01b03602084015116604084015193608081015194610100820151916001600160401b0360a082015116966001600160401b0360c083015116906119f16001600160a01b0361012064ffffffffff606087015116950151169460405197885261012060208901526101208801906146ca565b868103604088015260208083519283815201920190895b818110611b42575050508581036060870152845180825260208201906020808260051b8501019701918a905b828210611b10578b60ff8c8f8d808e8e8e8e7f0fac2c21f556c18c8ccc516e0c773f53ac94c19685206bc8dabe8a189cb1f43a96608086015260a085015260c084015260e0830152336101008301520390a160401c1615611a925780f35b68ff0000000000000000197ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0054167ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00557fc7f505b2f371ae2175ee4913f4499e1f2633a7b5936321eed1cdaeb6115181d2602060405160018152a180f35b90919297602080611b31879f9793600194601f199082030186528c51613870565b9a01920192019092919b939b611a34565b82516001600160401b0316845260209384019390920191600101611a08565b611b6d6103fd82614d32565b516007556118eb565b602483634e487b7160e01b81526011600452fd5b606460405162461bcd60e51b815260206004820152600460248201527f696e6974000000000000000000000000000000000000000000000000000000006044820152fd5b611bd7906138f5565b61026b57805f611853565b611bec91506138f5565b5f8061171f565b6040513d5f823e3d90fd5b80518051906001600160401b038211610bb557680100000000000000008211610bb5578554828755808310611c6c575b50602001855f5260205f205f5b838110611c58575050505060016020819201940191019092611670565b600190602084519401938184015501611c3b565b865f5260205f20908382015b8183018110611c88575050611c2e565b5f8155600101611c78565b7f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db9908382015b8183018110611cc9575050611642565b80545f825580611cdd575b50600101611cb9565b815f5260205f2090815b8183018110611cf7575050611cd4565b5f8155600101611ce7565b915f925f5b8184038110611d415750505060021c7fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c701555f80806115bc565b9091936020611d756001926001600160401b03885116908560031b60031b916001600160401b03809116831b921b19161790565b9501929101611d07565b5f805b60048110611db757507fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c78201556001016115a4565b84519094916001916020916001600160401b03600689901b81811b199092169216901b1792019401611d82565b60188360031b1680611e5c575b506003830160021c7fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7015b6003820160021c7fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c7018110611e515750611599565b5f8155600101611e1c565b7fdf6966c971051c3d54ec59162606531493a51404a002842f56009d7e5cf4a8c66003850160021c01908154905f199060200360031b1c1690555f611df1565b68ffffffffffffffffff19831668010000000000000001177ff0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a0055611539565b7ff92ee8a9000000000000000000000000000000000000000000000000000000005f5260045ffd5b9050155f6114f7565b303b1591506114ef565b5060ff8360401c16156114db565b83356001600160401b03811161054c573660438285600435010101121561054c576024818460043501010135611f598161397b565b91611f67604051938461395a565b818352602083013660448460051b8489600435010101011161054c5760448287600435010101905b60448460051b8489600435010101018210611fb757505050908252506020938401930161148a565b81356001600160401b03811161054c57602091611fe1839260443691888d60043501010101613a6c565b815201910190611f8f565b8135906001600160401b03821161054c5760208091612018602494853691898d60043501010101613b72565b815201920191905061136f565b60208060249361203486613b8d565b81520193019291506112a0565b81356001600160401b03811161054c5736604382878a60043501010101121561054c576024818689600435010101013561207a8161397b565b91612088604051938461395a565b81835260208301903660448b838b8760051b9260043501010101011161054c57604481898c60043501010101915b60448b838b8760051b92600435010101010183106120e15750505090825250602091820191016111ee565b602080916120ee85613829565b8152019201916120b6565b60208060249261210885613829565b815201920191905061113b565b60808436031261054c576040519161212c836138da565b61213585613829565b835260026020860135101561054c576020858101359084015260408501356001600160a01b038116900361054c576020608091602494604088013560408201526060612182818a01613b17565b908201528152019401939150610f72565b3461054c575f36600319011261054c5760206040516509184e72a0008152f35b3461054c57604036600319011261054c5760043560243590600b5481101561054c577f0175b7a638427703f0dbe7bb9bbf987a2551717b34e79f33b5b1008d1fa01db90190815481101561054c576020915f52815f200154604051908152f35b3461054c575f36600319011261054c5760206040516709b6e64a8ec600008152f35b3461054c575f36600319011261054c576040516329ae7ec560e11b81523060048201525f816024816001600160a01b037f000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba9165afa8015611bf35761029e915f916122af575b50604051918291602083526020830190613a8a565b6122cb91503d805f833e6122c3818361395a565b810190613cec565b8261229a565b3461054c575f36600319011261054c5760206040517f00000000000000000000000000000000000000000000000000000000000000118152f35b3461054c575f36600319011261054c576001600160a01b036040517f67e0e0760000000000000000000000000000000000000000000000000000000081523060048201525f81602481857f000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba9165afa8015611bf3575f915f935f915f93612423575b506123a260405194608086526080860190613abd565b6020858203818701528080885193848152019701925f905b8382106123e757878061029e896123d98d8b8582036040870152613a8a565b908382036060850152613a8a565b909192939783606060019260408c51805161240181613af9565b83528085015187168584015201511515604082015201990194939201906123ba565b9450925050503d805f843e612438818461395a565b82019060808383031261054c578251926001600160401b039384811161054c5783612464918301614634565b906020938482015186811161054c57820181601f8201121561054c5780519061248c8261397b565b9661249a604051988961395a565b8288528088018160608095028401019285841161054c578201905b8382106124fb575050505050604082015186811161054c57816124d9918401613c8b565b95606083015190811161054c576124f09201613c8b565b90929390918561238c565b848287031261054c5760405190612511826138bf565b8251600281101561054c57825283830151908a8216820361054c57828592838995015261254060408601613c7e565b60408201528152019101906124b5565b3461054c575f36600319011261054c5760405163ca4f280360e01b81523060048201525f816024816001600160a01b037f000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba9165afa8015611bf35761029e915f916125ca575b50604051918291602083526020830190613abd565b6125de91503d805f833e610b9c818361395a565b826125b5565b3461054c57604036600319011261054c5761265460206126026137fd565b6040517fbeabacc80000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b0390911660248083019190915235604482015291829081906064820190565b03815f6001600160a01b037f000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba9165af18015611bf357612699575b602060405160018152f35b6020813d6020116126c8575b816126b26020938361395a565b8101031261054c576126c390613c7e565b61268e565b3d91506126a5565b3461054c57604036600319011261054c576004356001600160401b03811161054c57612700903690600401613992565b602435600281101561054c5761271581613af9565b6128845760045b612724614a9f565b908060031461283057806004146127ac578060011461276c5760021461275857634e487b7160e01b5f52605160045260245ffd5b6020916127649161577b565b604051908152f35b50670de0b6b3a7640000908181029181830414901517156127985760209161279391614a88565b612764565b634e487b7160e01b5f52601160045260245ffd5b505f9190670de0b6b3a76400005b81518410156127f6576127ee6001916127e86127d68787613f0c565b516127e18887613f0c565b51906152db565b90615366565b9301926127ba565b92505050801561280857602090612764565b7f26543689000000000000000000000000000000000000000000000000000000005f5260045ffd5b505f9190670de0b6b3a76400005b81518410156127f657670de0b6b3a764000061287b6001926128756128638888613f0c565b5161286e8988613f0c565b5190615559565b9061478e565b0493019261283e565b600361271c565b3461054c575f36600319011261054c576040516004545f826128ac83613c46565b91828252602093600190856001821691825f146129495750506001146128ee575b506128da9250038361395a565b61029e6040519282849384528301906137d9565b84915060045f527f8a35acfbc15ff81a39ae7d344fd709f28e8600b4aa8c65c6b64bfe7fe36bd19b905f915b8583106129315750506128da9350820101856128cd565b8054838901850152879450869390920191810161291a565b60ff1916858201526128da95151560051b85010192508791506128cd9050565b3461054c575f36600319011261054c5760206001600160401b03600d5460401c16604051908152f35b3461054c575f36600319011261054c5760206040516001600160a01b037f000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba9168152f35b3461054c575f36600319011261054c57612a6e612a117f5361666520486176656e2d4254433a504158473a555344430000000000000018615388565b61029e612a3d7f310000000000000000000000000000000000000000000000000000000000000161549b565b612a7c60405191612a4d83613923565b5f8352604051958695600f60f81b875260e0602088015260e08701906137d9565b9085820360408701526137d9565b904660608501523060808501525f60a085015283820360c0850152613a8a565b3461054c575f36600319011261054c5760405163f29486a160e01b81523060048201526101a09081816024816001600160a01b037f000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba9165afa908115611bf3576040925f92612b1c575b505060608282015191015182519182526020820152f35b612b3b9250803d10612b42575b612b33818361395a565b810190613d11565b8280612b05565b503d612b29565b3461054c57602036600319011261054c576001600160a01b03612b6a6137fd565b165f526002602052602060405f2054604051908152f35b3461054c575f36600319011261054c5760206001600160a01b0360095416604051908152f35b3461054c5760031960203682011261054c57600435906001600160401b039081831161054c5760e090833603011261054c576040519160e0830183811083821117610bb5576040528060040135600281101561054c57835260248101356020840152604481013582811161054c57612c259060043691840101613992565b60408401526064810135606084015260848101356080840152612c4a60a48201613829565b60a084015260c481013591821161054c57013660238201121561054c57602091612c81612764923690602460048201359101613a27565b60c0820152612c8e614900565b6141fb565b3461054c5760208060031936011261054c57604481612cb06137fd565b6040517ff7888aec0000000000000000000000000000000000000000000000000000000081523060048201526001600160a01b03918216602482015292839182907f000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba9165afa908115611bf3575f91612d2c575b50604051908152f35b90508181813d8311612d52575b612d43818361395a565b8101031261054c575182612d23565b503d612d39565b3461054c575f36600319011261054c5762c73cd160e51b5f5260045ffd5b3461054c575f36600319011261054c57602060405167016345785d8a00008152f35b3461054c575f36600319011261054c57335f908152600260205260409020805460018101909155005b3461054c5760207f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925612df3366139f2565b92919390612dff614900565b6001600160a01b03809160405195865216941692a3005b3461054c575f36600319011261054c576040516005545f82612e3783613c46565b91828252602093600190856001821691825f14612949575050600114612e6457506128da9250038361395a565b84915060055f527f036b6384b5eca791c62761152d0c79bb0604c104a5fb6f4eb0703f3154bb3db0905f915b858310612ea75750506128da9350820101856128cd565b80548389018501528794508693909201918101612e90565b3461054c57604036600319011261054c576001600160401b0360043581811161054c57612ef0903690600401613a6c565b60243591821161054c57612f1861029e91612f12612f2f943690600401613a6c565b90613fbe565b6040929192519384936040855260408501906137d9565b9083820360208501526137d9565b3461054c575f36600319011261054c57602061276461495e565b3461054c575f36600319011261054c57602060405160128152f35b3461054c575f36600319011261054c5760206040517f6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c98152f35b3461054c575f36600319011261054c5760206040516729a2241af62c00008152f35b3461054c5760207fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef612df3366139f2565b3461054c5760846020613011366139f2565b6040517f15dacbea0000000000000000000000000000000000000000000000000000000081523360048201526001600160a01b0393841660248201529183166044830152606482015292839182905f907f000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba9165af18015611bf35761269957602060405160018152f35b3461054c575f36600319011261054c576020612764613f20565b3461054c57606036600319011261054c576004356001600160401b03811161054c576130e4903690600401613992565b604435906131506024356131278164ffffffffff61312042821682613107613e1a565b8160208201511680421015613307575b50511690613e42565b1694613f0c565b51927f000000000000000000000000000000000000000000000000000000000000000391614892565b60019190828411156133015760025b80600314613290578060041461322257806001146131e35760021461319257634e487b7160e01b5f52605160045260245ffd5b80156131bb57602093612764936127e8926ec097ce7bc90715b34b9f0fffffffff0401906152db565b7f0a0c22c7000000000000000000000000000000000000000000000000000000005f5260045ffd5b50809392501561320e576127e8612764926020946ec097ce7bc90715b34b9f100000000004906152db565b634e487b7160e01b5f52601260045260245ffd5b50670de0b6b3a764000093925f91815b613250575b5050508215612808576127e861276492602094906152db565b909194670de0b6b3a76400005186101561328a57908261328281936127e86132788a86613f0c565b516127e18b613eeb565b960192613232565b94613237565b50670de0b6b3a764000093925f91815b6132bd575050508215612808576127e861276492602094906152db565b909194670de0b6b3a76400005186101561328a579082670de0b6b3a76400006132f882946128756132ee8b87613f0c565b5161286e8c613eeb565b049601926132a0565b8261315f565b92508b613117565b3461054c575f36600319011261054c5760405161332b816138a3565b60608152606060208201525f60408201525f60608201525f60808201525f60a0820152606060c0820152606060e08201525f6101008201525f6101208201526001600160a01b037f000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba9166040516329ae7ec560e11b81523060048201525f81602481855afa908115611bf3575f916135f0575b5082526040517f7e361bde0000000000000000000000000000000000000000000000000000000081523060048201525f81602481855afa908115611bf3575f9161358b575b506024916101a0916020850152613417613f20565b60408501526040519283809263f29486a160e01b82523060048301525afa8015611bf357610120915f9161356a575b5060e081015115156060840152610100810151151560808401520151151560a08201526134746007546147ba565b60c08201526134846008546147ba565b60e082015264ffffffffff6020613499613e1a565b8281511661010085015201511661012082015260405180916020825264ffffffffff6101206135506134d98451610140602088015261016087019061383d565b61353a6134f8602087015192601f1993848a83030160408b015261383d565b604087015160608901526060870151151560808901526080870151151560a089015260a0870151151560c089015260c0870151838983030160e08a0152613870565b9060e08601519087830301610100880152613870565b928261010082015116828601520151166101408301520390f35b61358591506101a03d6101a011612b4257612b33818361395a565b83613446565b90503d805f833e61359c818361395a565b81019160408284031261054c578151916001600160401b039283811161054c57846135c8918301613c8b565b90602081015193841161054c576024946101a0946135e69201613c8b565b9050915091613402565b61360491503d805f833e6122c3818361395a565b836133bd565b3461054c575f36600319011261054c576020601054604051908152f35b3461054c57604036600319011261054c5761265460206136456137fd565b60405163e1f21c6760e01b81523360048201526001600160a01b0390911660248083019190915235604482015291829081906064820190565b3461054c575f36600319011261054c576040516003545f8261369f83613c46565b91828252602093600190856001821691825f146129495750506001146136cc57506128da9250038361395a565b84915060035f527fc2575a0e9e593c00f959f8c92f12db2869c3395a3b0502d05e2516446f71f85b905f915b85831061370f5750506128da9350820101856128cd565b805483890185015287945086939092019181016136f8565b3461054c575f36600319011261054c5760206305a39a806137687f00000000000000000000000000000000000000000000000000000000681aadfb42613c39565b10604051908152f35b3461054c57602036600319011261054c57600435907fffffffff00000000000000000000000000000000000000000000000000000000821680920361054c577f01ffc9a700000000000000000000000000000000000000000000000000000000602092148152f35b805180835260209291819084018484015e5f828201840152601f01601f1916010190565b600435906001600160a01b038216820361054c57565b602435906001600160a01b038216820361054c57565b35906001600160a01b038216820361054c57565b9081518082526020808093019301915f5b82811061385c575050505090565b83518552938101939281019260010161384e565b9081518082526020808093019301915f5b82811061388f575050505090565b835185529381019392810192600101613881565b61014081019081106001600160401b03821117610bb557604052565b606081019081106001600160401b03821117610bb557604052565b608081019081106001600160401b03821117610bb557604052565b6001600160401b038111610bb557604052565b604081019081106001600160401b03821117610bb557604052565b602081019081106001600160401b03821117610bb557604052565b61012081019081106001600160401b03821117610bb557604052565b90601f801991011681019081106001600160401b03821117610bb557604052565b6001600160401b038111610bb55760051b60200190565b9080601f8301121561054c5760209082356139ac8161397b565b936139ba604051958661395a565b81855260208086019260051b82010192831161054c57602001905b8282106139e3575050505090565b813581529083019083016139d5565b606090600319011261054c576001600160a01b0390600435828116810361054c5791602435908116810361054c579060443590565b9291926001600160401b038211610bb55760405191613a50601f8201601f19166020018461395a565b82948184528183011161054c578281602093845f960137010152565b9080601f8301121561054c57816020613a8793359101613a27565b90565b9081518082526020808093019301915f5b828110613aa9575050505090565b835185529381019392810192600101613a9b565b9081518082526020808093019301915f5b828110613adc575050505090565b83516001600160a01b031685529381019392810192600101613ace565b60021115613b0357565b634e487b7160e01b5f52602160045260245ffd5b3590811515820361054c57565b9291613b2f8261397b565b91613b3d604051938461395a565b829481845260208094019160051b810192831161054c57905b828210613b635750505050565b81358152908301908301613b56565b9080601f8301121561054c57816020613a8793359101613b24565b35906001600160401b038216820361054c57565b90808251908181526020809101926020808460051b8301019501935f915b848310613bcf5750505050505090565b9091929394958480613bed600193601f198682030187528a51613870565b9801930193019194939290613bbf565b9081518082526020808093019301915f5b828110613c1c575050505090565b83516001600160401b031685529381019392810192600101613c0e565b9190820391821161279857565b90600182811c92168015613c74575b6020831014613c6057565b634e487b7160e01b5f52602260045260245ffd5b91607f1691613c55565b5190811515820361054c57565b9080601f8301121561054c57815190602091613ca68161397b565b93613cb4604051958661395a565b81855260208086019260051b82010192831161054c57602001905b828210613cdd575050505090565b81518152908301908301613ccf565b9060208282031261054c5781516001600160401b03811161054c57613a879201613c8b565b809103906101a0821261054c57608060405192613d2d846138a3565b1261054c57604051613d3e816138da565b613d4782613c7e565b8152613d5560208301613c7e565b6020820152613d6660408301613c7e565b6040820152613d7760608301613c7e565b606082015282526080810151602083015260a0810151604083015260c0810151606083015260e081015164ffffffffff8116810361054c576080830152610100908181015163ffffffff8116810361054c57613e13916101809160a086015261012093613de5858301613c7e565b60c0870152613df76101408301613c7e565b60e0870152613e096101608301613c7e565b9086015201613c7e565b9082015290565b60405190613e2782613908565b816020600a5464ffffffffff90818116845260281c16910152565b64ffffffffff918216908216039190821161279857565b805115613e665760200190565b634e487b7160e01b5f52603260045260245ffd5b805160011015613e665760400190565b805160021015613e665760600190565b805160031015613e665760800190565b805160041015613e665760a00190565b805160051015613e665760c00190565b805160061015613e665760e00190565b805160071015613e66576101000190565b670de0b6b3a764000051811015613e665760051b670de0b6b3a76400200190565b8051821015613e665760209160051b010190565b6040517fe4dc2aa40000000000000000000000000000000000000000000000000000000081523060048201526020816024816001600160a01b037f000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba9165afa908115611bf3575f91613f8f575090565b90506020813d602011613fb6575b81613faa6020938361395a565b8101031261054c575190565b3d9150613f9d565b905f9060068054925b838110613ff5575050505050604051613fdf81613923565b5f815290604051613fef81613923565b5f815290565b815f52807ff652222313e28459528d920b65115c16c04f3efc82aaedc97be59f3f377c0d3f018054906140278261397b565b916040906140378251948561395a565b808452602092838501905f52835f205f915b83831061414b575050505061405d83613e59565b51815161408484828180820195805191829101875e81015f8382015203808452018261395a565b5190208882516140ae85828180820195805191829101875e81015f8382015203808452018261395a565b5190201491826140e6575b50506140c85750600101613fc7565b959450505050506140e26140db83613e8a565b5192613e9a565b5190565b9091506140f283613e7a565b51825161411983828180820195805191829101875e81015f8382015203808452018261395a565b51902091516141408282019282895180838c01875e81015f8382015203808452018261395a565b519020145f806140b9565b8451825491905f61415b84613c46565b8083526001948a86821691825f146141de575050600114614198575b50918161418a8a9360019695038261395a565b815201920192019190614049565b9050989e9d9c9b9a9998845f52885f20905f915b8183106141cb57509e9f9d9e509b9c9a9b999a98998101880181614177565b80548484018c0152918a019185016141ac565b60ff19168582015291151560051b84019091019150829050614177565b614203613e1a565b7f00000000000000000000000000000000000000000000000000000000000000039061424c64ffffffffff91828042169181602082015116804210156145fa5750511690613e42565b16906060830180519260049384811080806145eb575b81156145ce575b50156145ad57508151926080860151905f602060405161428881613908565b82815201528185878381841061457b57505050506003199485810190811161456857918581019081116145685794810190811161452e57906142f36143099392956143026142f9856142db6008546147ba565b956142f36142eb8c830189613f0c565b519188613f0c565b516155f6565b97820184613f0c565b5192613f0c565b9182602060405161431981613908565b838152015291935b805161432c81613af9565b61433581613af9565b61446d576020810180519360408301966143528851865190613f0c565b5194600e5496879661436e670de0b6b3a7640000998a9261478e565b041061445f578851905161438191613f0c565b5161439460808a51960195865190613f0c565b519351876143a18361475a565b04811161443857906143b66143bc9282614a92565b9061577b565b918681029080820488149015171561442557614407936143ef61440d9796946143e98a956143fc95614a88565b906152db565b838181039110029061478e565b049651905190613f0c565b5161478e565b048211614418575090565b6327e92f0f60e01b5f525ffd5b601188634e487b7160e01b5f525260245ffd5b887f340a4533000000000000000000000000000000000000000000000000000000005f525ffd5b876327e92f0f60e01b5f525ffd5b906020829593950191825193604082019661449060808951940193845190613f0c565b5194600e549687966144ac670de0b6b3a7640000998a9261478e565b041061445f576144ce6144c28a51875190613f0c565b51948a51905190613f0c565b519051876144db8361475a565b04811161454157916144f8826143b66144fe96956143e995613c39565b9261577b565b670de0b6b3a763ffff1981019190821161452e57916145246144079261440d9594615366565b9651905190613f0c565b601186634e487b7160e01b5f525260245ffd5b887f64590b9f000000000000000000000000000000000000000000000000000000005f525ffd5b601187634e487b7160e01b5f525260245ffd5b919396959450919650116145a5575b6142f36143099394956143026142f9856142db6007546147ba565b85935061458a565b926145bd81836145c89496614892565b936080870151614892565b93614321565b905015806145dd575b5f614269565b5084608087015110156145d7565b90508560808801511090614262565b92505f613117565b9061460c8261397b565b614619604051918261395a565b828152809261462a601f199161397b565b0190602036910137565b9080601f8301121561054c5781519060209161464f8161397b565b9361465d604051958661395a565b81855260208086019260051b82010192831161054c57602001905b828210614686575050505090565b81516001600160a01b038116810361054c578152908301908301614678565b9060208282031261054c5781516001600160401b03811161054c57613a879201614634565b908082519081815260208091019281808460051b8301019501935f915b8483106146f75750505050505090565b90919280949596601f198382030184528751908280835192838152019201905f905b80821061473857505050908060019298019301930191949392906146e7565b919380600192946001600160a01b038751168152019401920187939291614719565b90670429d069189e00009182810292818404149015171561279857565b906127109182810292818404149015171561279857565b8181029291811591840414171561279857565b90633b9aca009182810292818405149015171561279857565b614885604051916147ca8361393e565b600883526101003660208501376147e38160e01d6147a1565b6147ec84613e59565b5260036147fd8260c01d820b6147a1565b61480685613e7a565b526148158260a01d820b6147a1565b61481e85613e8a565b5261482d8260801d820b6147a1565b61483685613e9a565b526148458260601d820b6147a1565b61484e85613eaa565b5261485d8260401d820b6147a1565b61486685613eba565b526148758260201d820b6147a1565b61487e85613eca565b520b6147a1565b61488e82613eda565b5290565b8083600482106148d757506003199180830191508111612798579260085491810190811161279857613a87936143026148ce6142f393946147ba565b93820184613f0c565b613a879492506004106148f7575b6142f3906143026148ce6007546147ba565b600491506148e5565b6001600160a01b037f000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba916330361493257565b7f089676d5000000000000000000000000000000000000000000000000000000005f523360045260245ffd5b6001600160a01b037f0000000000000000000000006b61d8680c4f9e560c8306807908553f95c749c516301480614a5f575b156149b9577f3498693e9e4baf8310ad55edd20c3fc4b1e1521f72b2666c9c0dcc82df63ccda90565b60405160208101907f8b73c3c69bb8fe3d512ecc4cf759cc79239f7b179b0ffacaa9a75d522b39400f82527f602adc57d0a51eaf011568596e80a672f9c49ce424aecead8ca58a146872979860408201527fc89efdaa54c0f20c7adf612882df0950f5a951637e0307cdcb4c672f298b8bc660608201524660808201523060a082015260a0815260c081018181106001600160401b03821117610bb55760405251902090565b507f00000000000000000000000000000000000000000000000000000000000000014614614990565b811561320e570490565b9190820180921161279857565b7f0000000000000000000000000000000000000000000000000000000000000003614ac981614602565b9064ffffffffff80421681600a54818160281c1680421015614c87575b5016900316614af66007546147ba565b9180600482119384614c7e575b614b2284614b1083613e59565b51614b1b8585613f0c565b51906155f6565b614b2b87613e59565b52614b4784614b3983613e7a565b51614b1b6001860185613f0c565b614b5087613e7a565b526002831115614c7657614b7584614b6783613e8a565b51614b1b6002860185613f0c565b614b7e87613e8a565b526003831115614c7657614ba591614b1b826003614b9c8895613e9a565b51930190613f0c565b614bae85613e9a565b5260048114614c7057614bc26008546147ba565b9215614c7057614be482614bd585613e59565b51614b1b600319850187613f0c565b614bed85613eaa565b526005811115614c7057614c1382614c0485613e7a565b51614b1b600219850187613f0c565b614c1c85613eba565b526006811115614c7057614c4282614c3385613e8a565b51614b1b600119850187613f0c565b614c4b85613eca565b526007811115614c7057614b1b83614c6561488595613e9a565b51925f190190613f0c565b50505090565b505050505090565b60049150614b03565b92505f614ae6565b9081518082526020808093019301915f5b828110614cae575050505090565b835185529381019392810192600101614ca0565b801561320e576ec097ce7bc90715b34b9f10000000000590565b811561320e570590565b15614ced57565b60405162461bcd60e51b815260206004820152601260248201527f4f766572666c6f772f556e646572666c6f7700000000000000000000000000006044820152606490fd5b906060915f805f9083516008906008811015614df4575b505015614ddd575b614d5c818451613c39565b614d6557505050565b919360e0939193915f5b8651851015614dc95760019063ffffffff633b9aca00614d8f888b613f0c565b5105637fffffff81131580614dba575b614da890614ce6565b16851b1793601f190194019392614d6f565b50637fffffff19811215614d9f565b935093909450614dda915084613f0c565b52565b9350614dee60018560031c01614602565b93614d51565b809197508392505b8781101561511c575b50146151135760018160031c015b614e1c81614602565b955f5b83811015614d495781633b9aca009182614e39828a613f0c565b51056101405263ffffffff60019380614e548685018c613f0c565b51056101205280614e68600285018c613f0c565b51056101005280614e7c600385018c613f0c565b510560e05280614e8f600485018c613f0c565b510560c05280614ea2600585018c613f0c565b510560a05280614eb5600685018c613f0c565b5105608052614ec7600784018b613f0c565b5105637fffffff61014051131580615101575b806150f0575b806150de575b806150cd575b806150bb575b806150ab575b8061509a575b8061508a575b80615079575b80615069575b80615058575b80615048575b80615037575b80615029575b8061501a575b614f3790614ce6565b1667ffffffff0000000060805160201b166bffffffff000000000000000060a05160401b166fffffffff00000000000000000000000060c05160601b1673ffffffff0000000000000000000000000000000060e05160801b1677ffffffff00000000000000000000000000000000000000006101005160a01b167bffffffff0000000000000000000000000000000000000000000000006101205160c01b167fffffffff000000000000000000000000000000000000000000000000000000006101405160e01b1617171717171717615010888c613f0c565b5201940193614e1f565b50637fffffff19811215614f2e565b50637fffffff811315614f28565b50637fffffff196080511215614f22565b50637fffffff6080511315614f1c565b50637fffffff1960a0511215614f16565b50637fffffff60a0511315614f10565b50637fffffff1960c0511215614f0a565b50637fffffff60c0511315614f04565b50637fffffff1960e0511215614efe565b50637fffffff60e0511315614ef8565b50637fffffff19610100511215614ef2565b50637fffffff610100511315614eec565b50637fffffff19610120511215614ee6565b50637fffffff610120511315614ee0565b50637fffffff19610140511215614eda565b8060031c614e13565b9091506007811615615133578291905f1901614dfc565b915081905f614e05565b90815160019060011c6040805190615154826138bf565b600282525f5b8181106152ca57505080946040516151718161393e565b60088152610100908136602083013761518984613e59565b5261519383613e59565b505f855b615260575b50604051906151aa8261393e565b600882523660208301376151bd83613e7a565b526151c782613e7a565b50600319830190838211612798575f5b8281106151e657505050505050565b60048082019081831161524d57509061522861522287846152088b9688613f0c565b5161521c866152168c613e7a565b51613f0c565b52614a92565b84613f0c565b5161524661523587613e7a565b516152408785614a92565b90613f0c565b52016151d7565b601190634e487b7160e01b5f525260245ffd5b6004808210156152c4576152748285613f0c565b516152828361521688613e59565b526152966152908784614a92565b85613f0c565b51906152a186613e59565b518184019182851161524d5750916152bc8994928594613f0c565b520190615197565b5061519c565b80606060208093860101520161515a565b90670de0b6b3a7640000908181036152f257505090565b671bc16d674ec80000810361530d57505080613a8791615366565b673782dace9d900000810361533157505061532b81613a8792615366565b80615366565b61533b91926157ac565b90600161534783614777565b915f1983010401901515026001810180911161279857613a8791614a92565b906153709161478e565b6001670de0b6b3a76400005f19830104019015150290565b60ff81146153dc5760ff811690601f82116153b457604051916153aa83613908565b8252602082015290565b7fb3512b0c000000000000000000000000000000000000000000000000000000005f5260045ffd5b506040515f815f54916153ee83613c46565b808352926020906001908181169081156154775750600114615419575b5050613a879250038261395a565b9150925f80527f290decd9548b62a8d60345a988386fc84ba6bc95484008f6362f93160ef3e563935f925b82841061545f5750613a879450505081016020015f8061540b565b85548785018301529485019486945092810192615444565b91505060209250613a8794915060ff191682840152151560051b8201015f8061540b565b60ff81146154bd5760ff811690601f82116153b457604051916153aa83613908565b506040515f816001916001546154d281613c46565b808452936020916001811690811561547757506001146154fa575050613a879250038261395a565b91509260015f527fb10e2d527612073b26eecdfd717e6a320cf44b4afac2b0732d9fcbe2b7fa0cf6935f925b8284106155415750613a879450505081016020015f8061540b565b85548785018301529485019486945092810192615526565b670de0b6b3a7640000918083036155705750905090565b8290671bc16d674ec8000081036155915750508061558d9161478e565b0490565b673782dace9d90000081036155b557506155ae8261558d9361478e565b048061478e565b90506155c0916157ac565b6155c981614777565b60015f19938483010401901515029060018201808311612798578110156155f1575050505f90565b030190565b90670de0b6b3a76400009081810290808205831481151715612798575f121561562d57613a87936156269161478e565b0490614a92565b600160ff1b811461279857613a8793615647915f0361478e565b0490613c39565b91907f7fffffffffffffffffffffffffffffff5d576e7357a4501ddfe92f46681b20a084116156c5579160209360809260ff5f9560405194855216868401526040830152606082015282805260015afa15611bf3575f516001600160a01b038116156156bb57905f905f90565b505f906001905f90565b5050505f9160039190565b6004811015613b0357806156e2575050565b60018103615712577ff645eedf000000000000000000000000000000000000000000000000000000005f5260045ffd5b6002810361574657507ffce698f7000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b6003146157505750565b7fd78bce0c000000000000000000000000000000000000000000000000000000005f5260045260245ffd5b9080156131bb57670de0b6b3a764000091828102928184041490151715612798576001905f19830104019015150290565b908015615fa3578115615f9d578160ff1c615f8f57770bce5086492111aea88f4bb1ca6bcf584181ea8059f76532811015615f675781670c7d713b49da00001280615f56575b15615c0957670de0b6b3a7640000916ec097ce7bc90715b34b9f100000000090615835908402828101906ec097ce7bc90715b34b9f0fffffffff19018302614cdc565b9080828002059181838202058284820205838582020591848684020593858786020595808888020597880205600f900596600d900595600b900594600990059360079005926005900591600390050101010101010160011b918082818507020592050201670de0b6b3a7640000905b05680238fd42c5cf03ffff198181131580615bf6575b15615bce57819082121580615bbb575b15615b93575f915f8112615b84575b506064906806f05b59d3b20000008112615b37576806f05b59d3b1ffffff190168056bc75e2d6310000082770195e54c5dd42177f53a27172fa9ec630262827000000000925b02819068ad78ebc5ac62000000811215615b14575b6856bc75e2d631000000811215615af0575b682b5e3af16b18800000811215615ace575b6815af1d78b58c400000811215615aac575b680ad78ebc5ac6200000811215615a8b575b82811215615a6a575b6802b5e3af16b1880000811215615a49575b68015af1d78b58c40000811215615a28575b60028382800205056003848383020505600485848302050585600581868402050560068287830205056007838883020505906008848984020505926009858a8602050595600a868b8902050597600b878c8b02050599600c888d8d0205059b01010101010101010101010102050205905f14613a8757613a8790614cc2565b6806f5f1775788937937839168015af1d78b58c3ffff1901920205906159a9565b6808f00f760a4b2db55d83916802b5e3af16b187ffff190192020590615997565b680ebc5fb41746121110839168056bc75e2d630fffff190192020590615985565b68280e60114edb805d038391680ad78ebc5ac61fffff19019202059061597c565b690127fa27722cc06cc5e283916815af1d78b58c3fffff19019202059061596a565b693f1fce3da636ea5cf8508391682b5e3af16b187fffff190192020590615958565b6b02df0ab5a80a22c61ab5a70083916856bc75e2d630ffffff190192020590615946565b6e01855144814a7ff805980ff0084000915068ad78ebc5ac61ffffff1901615934565b6803782dace9d90000008112615b71576803782dace9d8ffffff190168056bc75e2d63100000826b1425982cf597cd205cef73809261591f565b68056bc75e2d631000008260019261591f565b600192505f03905060646158d9565b7fd4794efd000000000000000000000000000000000000000000000000000000005f5260045ffd5b5068070c1cc73b00c800008213156158ca565b7fa2f9f7e3000000000000000000000000000000000000000000000000000000005f5260045ffd5b5068070c1cc73b00c800008213156158ba565b81670de0b6b3a7640000925f91848112615f40575b506064905f7e1600ef3172e58d2e933ec884fde10064c63b5372d805e203c0000000000000821215615f15575b73011798004d755d3c8bc8e03204cf44619e000000821215615ef4575b820290808302906e01855144814a7ff805980ff00840009081831215615ed1575b50506b02df0ab5a80a22c61ab5a70080821215615eb1575b50693f1fce3da636ea5cf85080821215615e91575b50690127fa27722cc06cc5e280821215615e71575b5068280e60114edb805d0380821215615e51575b50680ebc5fb4174612111080821215615e3a575b506808f00f760a4b2db55d80821215615e1a575b506806f5f177578893793780821215615dfa575b506806248f33704b28660380821215615ddb575b506805c548670b9510e7ac80821215615dbc575b50615d6968056bc75e2d6310000091828082019168056bc75e2d630fffff190102614cdc565b9080828002059181838202058284820205916003600560076009600b888a89020598808b8b02059a8b0205059805960594059205010101010160011b0105905f14615db7575f035b026158a4565b615db1565b68056bc75e2d631000006756bc75e2d63100009202059101905f615d43565b68056bc75e2d6310000067ad78ebc5ac6200009202059101905f615d2f565b68056bc75e2d6310000068015af1d78b58c400009202059101905f615d1b565b68056bc75e2d631000006802b5e3af16b18800009202059101905f615d07565b68056bc75e2d63100000809202059101905f615cf3565b68056bc75e2d63100000680ad78ebc5ac62000009202059101905f615cdf565b68056bc75e2d631000006815af1d78b58c4000009202059101905f615ccb565b68056bc75e2d63100000682b5e3af16b188000009202059101905f615cb6565b68056bc75e2d631000006856bc75e2d6310000009202059101905f615ca1565b68ad78ebc5ac62000000925069021e19e0c9bab240000002059101905f80615c89565b906b1425982cf597cd205cef73806803782dace9d900000091059101615c68565b50770195e54c5dd42177f53a27172fa9ec63026282700000000090056806f05b59d3b2000000615c4b565b9050615f4c9150614cc2565b6001906064615c1e565b50670f43fc2c04ee000082126157f2565b7fd8317311000000000000000000000000000000000000000000000000000000005f5260045ffd5b6211380f60e51b5f5260045ffd5b50505f90565b5050670de0b6b3a76400009056fea26469706673582212201275773d2fb2990e16f9d3cab6b7df7b03b6f6f9d9e9413bbbd86bf0673a507864736f6c634300081a0033

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

0000000000000000000000000000000000000000000000000000000000000040000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba900000000000000000000000000000000000000000000000000000000000000e000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000003000000000000000000000000000000000000000000000000000000000000016000000000000000000000000021ae9576a393413d6d91dfe2543dcb548dbb8748000000000000000000000000000000000000000000000000000000000000001100000000000000000000000000000000000000000000000000000000000001e000000000000000000000000000000000000000000000000000000000000000185361666520486176656e2d4254433a504158473a55534443000000000000000000000000000000000000000000000000000000000000000000000000000000054254465348000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004f7b226e616d65223a225175616e74414d4d5765696768746564506f6f6c222c2276657273696f6e223a312c226465706c6f796d656e74223a2232303235303431392d76332d7175616e74616d6d227d00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000001e00000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000100000000000000000000000000000000000000000000000000000000000000014000000000000000000000000000000000000000000000000000000000000000086f76657276696577000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001161646170746162696c69747953636f726500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000066e756d6265720000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000135000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000008000000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000000000000000000000000000000000000140000000000000000000000000000000000000000000000000000000000000000b72756c6544657461696c73000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000e75706461746552756c654e616d650000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000006737472696e670000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000d506f776572204368616e6e656c00000000000000000000000000000000000000

-----Decoded View---------------
Arg [0] : params (tuple):
Arg [1] : name (string): Safe Haven-BTC:PAXG:USDC
Arg [2] : symbol (string): BTFSH
Arg [3] : numTokens (uint256): 3
Arg [4] : version (string): {"name":"QuantAMMWeightedPool","version":1,"deployment":"20250419-v3-quantamm"}
Arg [5] : updateWeightRunner (address): 0x21Ae9576a393413D6d91dFE2543dCb548Dbb8748
Arg [6] : poolRegistry (uint256): 17
Arg [7] : poolDetails (string[][]): System.Collections.Generic.List`1[System.String],System.Collections.Generic.List`1[System.String]

Arg [1] : vault (address): 0xbA1333333333a1BA1108E8412f11850A5C319bA9

-----Encoded View---------------
46 Constructor Arguments found :
Arg [0] : 0000000000000000000000000000000000000000000000000000000000000040
Arg [1] : 000000000000000000000000ba1333333333a1ba1108e8412f11850a5c319ba9
Arg [2] : 00000000000000000000000000000000000000000000000000000000000000e0
Arg [3] : 0000000000000000000000000000000000000000000000000000000000000120
Arg [4] : 0000000000000000000000000000000000000000000000000000000000000003
Arg [5] : 0000000000000000000000000000000000000000000000000000000000000160
Arg [6] : 00000000000000000000000021ae9576a393413d6d91dfe2543dcb548dbb8748
Arg [7] : 0000000000000000000000000000000000000000000000000000000000000011
Arg [8] : 00000000000000000000000000000000000000000000000000000000000001e0
Arg [9] : 0000000000000000000000000000000000000000000000000000000000000018
Arg [10] : 5361666520486176656e2d4254433a504158473a555344430000000000000000
Arg [11] : 0000000000000000000000000000000000000000000000000000000000000005
Arg [12] : 4254465348000000000000000000000000000000000000000000000000000000
Arg [13] : 000000000000000000000000000000000000000000000000000000000000004f
Arg [14] : 7b226e616d65223a225175616e74414d4d5765696768746564506f6f6c222c22
Arg [15] : 76657273696f6e223a312c226465706c6f796d656e74223a2232303235303431
Arg [16] : 392d76332d7175616e74616d6d227d0000000000000000000000000000000000
Arg [17] : 0000000000000000000000000000000000000000000000000000000000000002
Arg [18] : 0000000000000000000000000000000000000000000000000000000000000040
Arg [19] : 00000000000000000000000000000000000000000000000000000000000001e0
Arg [20] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [21] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [22] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [23] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [24] : 0000000000000000000000000000000000000000000000000000000000000140
Arg [25] : 0000000000000000000000000000000000000000000000000000000000000008
Arg [26] : 6f76657276696577000000000000000000000000000000000000000000000000
Arg [27] : 0000000000000000000000000000000000000000000000000000000000000011
Arg [28] : 61646170746162696c69747953636f7265000000000000000000000000000000
Arg [29] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [30] : 6e756d6265720000000000000000000000000000000000000000000000000000
Arg [31] : 0000000000000000000000000000000000000000000000000000000000000001
Arg [32] : 3500000000000000000000000000000000000000000000000000000000000000
Arg [33] : 0000000000000000000000000000000000000000000000000000000000000004
Arg [34] : 0000000000000000000000000000000000000000000000000000000000000080
Arg [35] : 00000000000000000000000000000000000000000000000000000000000000c0
Arg [36] : 0000000000000000000000000000000000000000000000000000000000000100
Arg [37] : 0000000000000000000000000000000000000000000000000000000000000140
Arg [38] : 000000000000000000000000000000000000000000000000000000000000000b
Arg [39] : 72756c6544657461696c73000000000000000000000000000000000000000000
Arg [40] : 000000000000000000000000000000000000000000000000000000000000000e
Arg [41] : 75706461746552756c654e616d65000000000000000000000000000000000000
Arg [42] : 0000000000000000000000000000000000000000000000000000000000000006
Arg [43] : 737472696e670000000000000000000000000000000000000000000000000000
Arg [44] : 000000000000000000000000000000000000000000000000000000000000000d
Arg [45] : 506f776572204368616e6e656c00000000000000000000000000000000000000


Block Uncle Number Difficulty Gas Used Reward
View All Uncles
Loading...
Loading
Loading...
Loading
Loading...
Loading
[ Download: CSV Export  ]
[ Download: CSV Export  ]

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