S Price: $0.416685 (+5.43%)

Contract

0x2ED4d0Bbd7f16ED33269243Eb3Fe993Bd5561e93

Overview

S Balance

Sonic LogoSonic LogoSonic Logo0 S

S Value

$0.00

Multichain Info

No addresses found
Transaction Hash
Method
Block
From
To

There are no matching entries

1 Internal Transaction found.

Latest 1 internal transaction

Parent Transaction Hash Block From To
59497832025-01-30 22:39:398 days ago1738276779  Contract Creation0 S
Loading...
Loading

Contract Source Code Verified (Exact Match)

Contract Name:
ALMLib

Compiler Version
v0.8.23+commit.f704f362

Optimization Enabled:
Yes with 200 runs

Other Settings:
shanghai EvmVersion
File 1 of 17 : ALMLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IALM} from "../../interfaces/IALM.sol";
import {ILPStrategy} from "../../interfaces/ILPStrategy.sol";
import {IStrategy} from "../../interfaces/IStrategy.sol";
import {ICAmmAdapter} from "../../interfaces/ICAmmAdapter.sol";
import {AmmAdapterIdLib} from "../../adapters/libs/AmmAdapterIdLib.sol";
import {IUniswapV3Pool} from "../../integrations/uniswapv3/IUniswapV3Pool.sol";
import {UniswapV3MathLib} from "./UniswapV3MathLib.sol";

library ALMLib {
    uint public constant ALGO_FILL_UP = 0;

    uint public constant PRECISION = 1e36;

    /*function checkCompatibility(
        IALM.ALMStrategyBaseStorage storage $,
        ILPStrategy.LPStrategyBaseStorage storage _$_
    ) external view {
        ICAmmAdapter adapter = ICAmmAdapter(address(_$_.ammAdapter));
        if (keccak256(bytes(adapter.ammAdapterId())) != keccak256(bytes(AmmAdapterIdLib.UNISWAPV3))) {
            revert("Not supported CL AMM adapter");
        }
        if ($.algoId != ALGO_FILL_UP) {
            revert("Not supported ALM algo");
        }
    }*/

    function getAssetsProportions(
        IALM.ALMStrategyBaseStorage storage $,
        ILPStrategy.LPStrategyBaseStorage storage _$_,
        IStrategy.StrategyBaseStorage storage __$__
    ) external view returns (uint[] memory proportions) {
        uint[] memory amounts;
        if (__$__.total != 0) {
            (, amounts) = assetsAmounts($, _$_, __$__);
        } else {
            ICAmmAdapter adapter = ICAmmAdapter(address(_$_.ammAdapter));
            address pool = _$_.pool;
            int24[] memory ticks = new int24[](2);
            uint[] memory amountsMax = new uint[](2);
            amountsMax[0] = 1e20;
            amountsMax[1] = 1e20;
            int24 tick = getUniswapV3CurrentTick(pool);
            (ticks[0], ticks[1]) = calcFillUpBaseTicks(tick, $.params[0], getUniswapV3TickSpacing(pool));
            (, amounts) = adapter.getLiquidityForAmounts(pool, amountsMax, ticks);
        }

        uint price = getUniswapV3PoolPrice(_$_.pool);
        uint amount0PricedInAmount1 = amounts[0] * price / PRECISION;
        uint prop0 = 1e18 * amount0PricedInAmount1 / (amount0PricedInAmount1 + amounts[1]);
        proportions = new uint[](2);
        proportions[0] = prop0;
        proportions[1] = 1e18 - prop0;
    }

    function needRebalance(
        IALM.ALMStrategyBaseStorage storage $,
        ILPStrategy.LPStrategyBaseStorage storage _$_
    ) external view returns (bool need) {
        if ($.algoId == ALGO_FILL_UP) {
            uint len = $.positions.length;
            if (len == 0) {
                return false;
            }

            int24 halfRange = $.params[0] / 2;
            int24 halfTriggerRange = $.params[1] / 2;
            int24 oldMedianTick = $.positions[0].tickLower + halfRange;
            int24 currentTick = getUniswapV3CurrentTick(_$_.pool);
            return (currentTick > oldMedianTick + halfTriggerRange) || (currentTick < oldMedianTick - halfTriggerRange);
        }
    }

    function getAlgoNamyById(uint algoId) public pure returns (string memory) {
        if (algoId == ALGO_FILL_UP) {
            return "Fill-Up";
        }
        return "Unknown";
    }

    function getPresetNameByAlgoAndParams(uint algoId, int24[] memory params) public pure returns (string memory) {
        if (algoId == ALGO_FILL_UP) {
            if (params[0] >= 10000) {
                return "Stretched";
            }
            if (params[0] >= 4000) {
                return "Passive";
            }
            if (params[0] >= 2000) {
                return "Wide";
            }
            if (params[0] >= 1000) {
                return "Narrow";
            }
            if (params[0] >= 100) {
                return "Aggressive";
            }
            return "Insane";
        }
        return "Unknown";
    }

    function preset(IALM.ALMStrategyBaseStorage storage $)
        external
        view
        returns (uint algoId, string memory algoName, string memory presetName, int24[] memory params)
    {
        algoId = $.algoId;
        algoName = getAlgoNamyById(algoId);
        params = $.params;
        presetName = getPresetNameByAlgoAndParams(algoId, params);
    }

    function assetsAmounts(
        IALM.ALMStrategyBaseStorage storage $,
        ILPStrategy.LPStrategyBaseStorage storage _$_,
        IStrategy.StrategyBaseStorage storage __$__
    ) public view returns (address[] memory assets_, uint[] memory amounts_) {
        ICAmmAdapter adapter = ICAmmAdapter(address(_$_.ammAdapter));
        address _pool = _$_.pool;
        assets_ = __$__._assets;
        amounts_ = new uint[](2);
        uint len = $.positions.length;
        for (uint i; i < len; ++i) {
            IALM.Position memory position = $.positions[i];
            int24[] memory ticks = new int24[](2);
            ticks[0] = position.tickLower;
            ticks[1] = position.tickUpper;
            uint[] memory positionAmounts = adapter.getAmountsForLiquidity(_pool, ticks, position.liquidity);
            amounts_[0] += positionAmounts[0];
            amounts_[1] += positionAmounts[1];
        }
    }

    function previewDepositAssets(
        uint[] memory amountsMax,
        IALM.ALMStrategyBaseStorage storage $,
        ILPStrategy.LPStrategyBaseStorage storage _$_,
        IStrategy.StrategyBaseStorage storage __$__
    ) external view returns (uint[] memory amountsConsumed, uint value) {
        if ($.algoId == ALGO_FILL_UP) {
            ICAmmAdapter adapter = ICAmmAdapter(address(_$_.ammAdapter));
            address pool = _$_.pool;
            uint price = getUniswapV3PoolPrice(pool);

            if (__$__.total == 0) {
                int24 tick = getUniswapV3CurrentTick(pool);
                int24[] memory ticks = new int24[](2);
                (ticks[0], ticks[1]) = calcFillUpBaseTicks(tick, $.params[0], getUniswapV3TickSpacing(pool));
                (, amountsConsumed) = adapter.getLiquidityForAmounts(pool, amountsMax, ticks);
                value = amountsConsumed[1] + (amountsConsumed[0] * price / PRECISION);
            } else {
                uint positionsLength = $.positions.length;
                int24[] memory ticks = new int24[](2);
                IALM.Position memory position = $.positions[0];
                ticks[0] = position.tickLower;
                ticks[1] = position.tickUpper;
                (, amountsConsumed) = adapter.getLiquidityForAmounts(pool, amountsMax, ticks);

                if (positionsLength == 2) {
                    uint[] memory amountsRemaining = new uint[](2);
                    amountsRemaining[0] = amountsMax[0] - amountsConsumed[0];
                    amountsRemaining[1] = amountsMax[1] - amountsConsumed[1];
                    position = $.positions[1];
                    ticks[0] = position.tickLower;
                    ticks[1] = position.tickUpper;
                    (, uint[] memory amountsConsumedFillUp) =
                        adapter.getLiquidityForAmounts(pool, amountsRemaining, ticks);
                    amountsConsumed[0] += amountsConsumedFillUp[0];
                    amountsConsumed[1] += amountsConsumedFillUp[1];
                }

                value = amountsConsumed[1] + (amountsConsumed[0] * price / PRECISION);

                (, uint[] memory totalAmounts) = IStrategy(address(this)).assetsAmounts();
                uint totalAmount = totalAmounts[1] + totalAmounts[0] * price / PRECISION;
                value = value * __$__.total / totalAmount;
            }
        }
    }

    function calcFillUpBaseTicks(
        int24 tick,
        int24 tickRange,
        int24 tickSpacing
    ) public pure returns (int24 lowerTick, int24 upperTick) {
        int24 halfTickRange = tickRange / 2;
        if (tick < 0 && tick / tickSpacing * tickSpacing != tick) {
            lowerTick = ((tick - halfTickRange) / tickSpacing - 1) * tickSpacing;
        } else {
            lowerTick = (tick - halfTickRange) / tickSpacing * tickSpacing;
        }
        upperTick = lowerTick + halfTickRange * 2;
    }

    function getUniswapV3PoolPrice(address pool) public view returns (uint price) {
        (uint160 sqrtPrice,,,,,,) = IUniswapV3Pool(pool).slot0();
        price = UniswapV3MathLib.mulDiv(uint(sqrtPrice) * uint(sqrtPrice), PRECISION, 2 ** (96 * 2));
    }

    function getUniswapV3TickSpacing(address pool) public view returns (int24 tickSpacing) {
        tickSpacing = IUniswapV3Pool(pool).tickSpacing();
    }

    function getUniswapV3CurrentTick(address pool) public view returns (int24 tick) {
        //slither-disable-next-line unused-return
        (, tick,,,,,) = IUniswapV3Pool(pool).slot0();
    }

    /// @notice Check if the price change overflows or not based on given twap and threshold in pool
    /// @param pool UniswapV3Pool address
    /// @param twapInterval Time intervals
    /// @param priceThreshold Price Threshold
    function checkPriceChange(address pool, uint32 twapInterval, uint priceThreshold) external view {
        (uint160 sqrtPrice,,,,,,) = IUniswapV3Pool(pool).slot0();
        uint price = UniswapV3MathLib.mulDiv(uint(sqrtPrice) * uint(sqrtPrice), PRECISION, 2 ** (96 * 2));

        uint160 sqrtPriceBefore = _getUniswapV3SqrtTwapX96(pool, twapInterval);
        uint priceBefore =
            UniswapV3MathLib.mulDiv(uint(sqrtPriceBefore) * uint(sqrtPriceBefore), PRECISION, 2 ** (96 * 2));
        if (price * 10_000 / priceBefore > priceThreshold || priceBefore * 10_000 / price > priceThreshold) {
            revert IALM.PriceChangeProtection(price, priceBefore, priceThreshold, twapInterval);
        }
    }

    /// @notice Get the sqrt price before the given interval
    /// @param pool UniswapV3Pool address
    /// @param twapInterval Time intervals
    /// @return sqrtPriceX96 Sqrt price before interval
    function _getUniswapV3SqrtTwapX96(address pool, uint32 twapInterval) internal view returns (uint160 sqrtPriceX96) {
        if (twapInterval == 0) {
            /// return the current price if _twapInterval == 0
            (sqrtPriceX96,,,,,,) = IUniswapV3Pool(pool).slot0();
        } else {
            uint32[] memory secondsAgos = new uint32[](2);
            secondsAgos[0] = twapInterval;
            /// from (before)
            secondsAgos[1] = 0;
            /// to (now)

            (int56[] memory tickCumulatives,) = IUniswapV3Pool(pool).observe(secondsAgos);

            /// tick(imprecise as it's an integer) to price
            sqrtPriceX96 = UniswapV3MathLib.getSqrtRatioAtTick(
                int24((tickCumulatives[1] - tickCumulatives[0]) / int56(int32(twapInterval)))
            );
        }
    }

    function balance(address token) public view returns (uint) {
        return IERC20(token).balanceOf(address(this));
    }
}

File 2 of 17 : IERC20.sol
// 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);
}

File 3 of 17 : IALM.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

interface IALM {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                        CUSTOM ERRORS                       */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    error IncorrectRebalanceArgs();
    error NotNeedRebalance();
    error CantDoRebalance();
    error NotALM();
    error PriceChangeProtection(uint price, uint priceBefore, uint priceThreshold, uint32 twapInterval);

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

    event ALMParams(uint algoId, int24[] params);
    event PriceChangeProtectionParams(bool enabled, uint32 twapInterval, uint priceThreshold);
    event Rebalance(Position[] newPosition);

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         DATA TYPES                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @custom:storage-location erc7201:stability.ALMStrategyBase
    struct ALMStrategyBaseStorage {
        uint algoId;
        int24[] params;
        Position[] positions;
        address nft;
        bool priceChangeProtection;
        uint32 twapInterval;
        uint priceThreshold;
    }

    struct ALMStrategyBaseInitParams {
        uint algoId;
        int24[] params;
        address nft;
    }

    struct Position {
        uint tokenId;
        int24 tickLower;
        int24 tickUpper;
        uint128 liquidity;
    }

    struct NewPosition {
        int24 tickLower;
        int24 tickUpper;
        uint128 liquidity;
        uint minAmount0;
        uint minAmount1;
    }

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

    /// @notice ALM preset
    function preset()
        external
        view
        returns (uint algoId, string memory algoName, string memory presetName, int24[] memory params);

    /// @notice Show current ALM positions
    function positions() external view returns (Position[] memory);

    /// @notice Is a re-balance needed now
    function needRebalance() external view returns (bool);

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

    /// @notice Re-balance positions
    /// @param burnOldPositions Burn old position or keep. Burn all if length 0 passed than.
    /// @param mintNewPositions New positions params
    function rebalance(bool[] memory burnOldPositions, NewPosition[] memory mintNewPositions) external;

    /// @notice Setup price change protection params
    /// @param enabled Enable protection
    /// @param twapInterval TWAP interval in seconds
    /// @param priceThreshold Price threshold. Default is 10_000.
    function setupPriceChangeProtection(bool enabled, uint32 twapInterval, uint priceThreshold) external;

    /// @notice Change ALM re-balancing params
    /// @param algoId ID of ALM algorithm
    /// @param params Re-balancing params
    function setupALMParams(uint algoId, int24[] memory params) external;
}

File 4 of 17 : ILPStrategy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "../interfaces/IAmmAdapter.sol";

/// @title Liquidity providing strategy
/// @author Alien Deployer (https://github.com/a17)
interface ILPStrategy {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    event FeesClaimed(uint[] fees);

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

    error ZeroAmmAdapter();
    error IncorrectAssetsLength();
    error IncorrectAssets();
    error IncorrectAmountsLength();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         DATA TYPES                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @custom:storage-location erc7201:stability.LPStrategyBase
    struct LPStrategyBaseStorage {
        /// @inheritdoc ILPStrategy
        address pool;
        /// @inheritdoc ILPStrategy
        IAmmAdapter ammAdapter;
        uint[] _feesOnBalance;
    }

    struct LPStrategyBaseInitParams {
        string id;
        address platform;
        address vault;
        address pool;
        address underlying;
    }

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

    /// @dev AMM adapter string ID for interacting with pool
    function ammAdapterId() external view returns (string memory);

    /// @dev AMM adapter address for interacting with pool
    function ammAdapter() external view returns (IAmmAdapter);

    /// @dev AMM
    function pool() external view returns (address);
}

File 5 of 17 : IStrategy.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/// @dev Core interface of strategy logic
interface IStrategy is IERC165 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                           EVENTS                           */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    event HardWork(
        uint apr, uint compoundApr, uint earned, uint tvl, uint duration, uint sharePrice, uint[] assetPrices
    );
    event ExtractFees(
        uint vaultManagerReceiverFee,
        uint strategyLogicReceiverFee,
        uint ecosystemRevenueReceiverFee,
        uint multisigReceiverFee
    );

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

    error NotReadyForHardWork();

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         DATA TYPES                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    /// @custom:storage-location erc7201:stability.StrategyBase
    struct StrategyBaseStorage {
        /// @inheritdoc IStrategy
        address vault;
        /// @inheritdoc IStrategy
        uint total;
        /// @inheritdoc IStrategy
        uint lastHardWork;
        /// @inheritdoc IStrategy
        uint lastApr;
        /// @inheritdoc IStrategy
        uint lastAprCompound;
        /// @inheritdoc IStrategy
        address[] _assets;
        /// @inheritdoc IStrategy
        address _underlying;
        string _id;
        uint _exchangeAssetIndex;
        uint customPriceImpactTolerance;
    }

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

    /// @dev Strategy logic string ID
    function strategyLogicId() external view returns (string memory);

    /// @dev Extra data
    /// @return 0-2 bytes - strategy color
    ///         3-5 bytes - strategy background color
    ///         6-31 bytes - free
    function extra() external view returns (bytes32);

    /// @dev Types of vault that supported by strategy implementation
    /// @return types Vault type ID strings
    function supportedVaultTypes() external view returns (string[] memory types);

    /// @dev Linked vault address
    function vault() external view returns (address);

    /// @dev Final assets that strategy invests
    function assets() external view returns (address[] memory);

    /// @notice Final assets and amounts that strategy manages
    function assetsAmounts() external view returns (address[] memory assets_, uint[] memory amounts_);

    /// @notice Priced invested assets proportions
    /// @return proportions Proportions of assets with 18 decimals. Min is 0, max is 1e18.
    function getAssetsProportions() external view returns (uint[] memory proportions);

    /// @notice Underlying token address
    /// @dev Can be used for liquidity farming strategies where AMM has fungible liquidity token (Solidly forks, etc),
    ///      for concentrated liquidity tokenized vaults (Gamma, G-UNI etc) and for other needs.
    /// @return Address of underlying token or zero address if no underlying in strategy
    function underlying() external view returns (address);

    /// @dev Balance of liquidity token or liquidity value
    function total() external view returns (uint);

    /// @dev Last HardWork time
    /// @return Timestamp
    function lastHardWork() external view returns (uint);

    /// @dev Last APR of earned USD amount registered by HardWork
    ///      ONLY FOR OFF-CHAIN USE.
    ///      Not trusted asset price can be manipulated.
    /// @return APR with 18 decimals. 1e18 - 100%.
    function lastApr() external view returns (uint);

    /// @dev Last APR of compounded assets registered by HardWork.
    ///      Can be used on-chain.
    /// @return APR with 18 decimals. 1e18 - 100%.
    function lastAprCompound() external view returns (uint);

    /// @notice Calculation of consumed amounts and liquidity/underlying value for provided strategy assets and amounts.
    /// @param assets_ Strategy assets or part of them, if necessary
    /// @param amountsMax Amounts of specified assets available for investing
    /// @return amountsConsumed Cosumed amounts of assets when investing
    /// @return value Liquidity value or underlying token amount minted when investing
    function previewDepositAssets(
        address[] memory assets_,
        uint[] memory amountsMax
    ) external view returns (uint[] memory amountsConsumed, uint value);

    /// @notice Write version of previewDepositAssets
    /// @param assets_ Strategy assets or part of them, if necessary
    /// @param amountsMax Amounts of specified assets available for investing
    /// @return amountsConsumed Cosumed amounts of assets when investing
    /// @return value Liquidity value or underlying token amount minted when investing
    function previewDepositAssetsWrite(
        address[] memory assets_,
        uint[] memory amountsMax
    ) external returns (uint[] memory amountsConsumed, uint value);

    /// @notice All strategy revenue (pool fees, farm rewards etc) that not claimed by strategy yet
    /// @return assets_ Revenue assets
    /// @return amounts Amounts. Index of asset same as in previous array.
    function getRevenue() external view returns (address[] memory assets_, uint[] memory amounts);

    /// @notice Optional specific name of investing strategy, underyling type, setup variation etc
    /// @return name Empty string or specific name
    /// @return showInVaultSymbol Show specific in linked vault symbol
    function getSpecificName() external view returns (string memory name, bool showInVaultSymbol);

    /// @notice Variants pf strategy initializations with description of money making mechanic.
    /// As example, if strategy need farm, then number of variations is number of available farms.
    /// If CAMM strategy have set of available widths (tick ranges), then number of variations is number of available farms.
    /// If both example conditions are met then total number or variations = total farms * total widths.
    /// @param platform_ Need this param because method called when strategy implementation is not initialized
    /// @return variants Descriptions of the strategy for making money
    /// @return addresses Init strategy addresses. Indexes for each variants depends of copmpared arrays lengths.
    /// @return nums Init strategy numbers. Indexes for each variants depends of copmpared arrays lengths.
    /// @return ticks Init strategy ticks. Indexes for each variants depends of copmpared arrays lengths.
    function initVariants(address platform_)
        external
        view
        returns (string[] memory variants, address[] memory addresses, uint[] memory nums, int24[] memory ticks);

    /// @notice How does the strategy make money?
    /// @return Description in free form
    function description() external view returns (string memory);

    /// @notice Is HardWork on vault deposits can be enabled
    function isHardWorkOnDepositAllowed() external view returns (bool);

    /// @notice Is HardWork can be executed
    function isReadyForHardWork() external view returns (bool);

    /// @notice Strategy not need to process revenue on HardWorks
    function autoCompoundingByUnderlyingProtocol() external view returns (bool);

    /// @notice Custom price impact tolerance instead default need for specific cases where liquidity in pools is low
    function customPriceImpactTolerance() external view returns (uint);

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

    /// @dev A single universal initializer for all strategy implementations.
    /// @param addresses All addresses that strategy requires for initialization. Min array length is 2.
    ///        addresses[0]: platform (required)
    ///        addresses[1]: vault (required)
    ///        addresses[2]: initStrategyAddresses[0] (optional)
    ///        addresses[3]: initStrategyAddresses[1] (optional)
    ///        addresses[n]: initStrategyAddresses[n - 2] (optional)
    /// @param nums All uint values that strategy requires for initialization. Min array length is 0.
    /// @param ticks All int24 values that strategy requires for initialization. Min array length is 0.
    function initialize(address[] memory addresses, uint[] memory nums, int24[] memory ticks) external;

    /// @notice Invest strategy assets. Amounts of assets must be already on strategy contract balance.
    /// Only vault can call this.
    /// @param amounts Anounts of strategy assets
    /// @return value Liquidity value or underlying token amount
    function depositAssets(uint[] memory amounts) external returns (uint value);

    /// @notice Invest underlying asset. Asset must be already on strategy contract balance.
    /// Only vault can call this.
    /// @param amount Amount of underlying asset to invest
    /// @return amountsConsumed Cosumed amounts of invested assets
    function depositUnderlying(uint amount) external returns (uint[] memory amountsConsumed);

    /// @dev For specified amount of shares and assets_, withdraw strategy assets from farm/pool/staking and send to receiver if possible
    /// Only vault can call this.
    /// @param assets_ Here we give the user a choice of assets to withdraw if strategy support it
    /// @param value Part of strategy total value to withdraw
    /// @param receiver User address
    /// @return amountsOut Amounts of assets sent to user
    function withdrawAssets(
        address[] memory assets_,
        uint value,
        address receiver
    ) external returns (uint[] memory amountsOut);

    /// @notice Wothdraw underlying invested and send to receiver
    /// Only vault can call this.
    /// @param amount Ampunt of underlying asset to withdraw
    /// @param receiver User of vault which withdraw underlying from the vault
    function withdrawUnderlying(uint amount, address receiver) external;

    /// @dev For specified amount of shares, transfer strategy assets from contract balance and send to receiver if possible
    /// This method is called by vault w/o underlying on triggered fuse mode.
    /// Only vault can call this.
    /// @param amount Ampunt of liquidity value that user withdraw
    /// @param totalAmount Total amount of strategy liquidity
    /// @param receiver User of vault which withdraw assets
    /// @return amountsOut Amounts of strategy assets sent to user
    function transferAssets(
        uint amount,
        uint totalAmount,
        address receiver
    ) external returns (uint[] memory amountsOut);

    /// @notice Execute HardWork
    /// During HardWork strategy claiming revenue and processing it.
    /// Only vault can call this.
    function doHardWork() external;

    /// @notice Emergency stop investing by strategy, withdraw liquidity without rewards.
    /// This action triggers FUSE mode.
    /// Only governance or multisig can call this.
    function emergencyStopInvesting() external;

    /// @notice Custom price impact tolerance instead default need for specific cases where low liquidity in pools
    /// @param priceImpactTolerance Tolerance percent with 100_000 DENOMINATOR. 4_000 == 4%
    function setCustomPriceImpactTolerance(uint priceImpactTolerance) external;
}

File 6 of 17 : ICAmmAdapter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.22;

import "./IAmmAdapter.sol";

/// @dev Adapter for interacting with Concentrated Automated Market Maker
/// based on liquidity pool of 2 tokens.
/// @author Alien Deployer (https://github.com/a17)
interface ICAmmAdapter is IAmmAdapter {
    /// @notice Price in pool at specified tick
    /// @param pool Address of a pool supported by the adapter
    /// @param tokenIn Token for sell
    /// @return Output amount of swap 1.0 tokenIn in pool without price impact
    function getPriceAtTick(address pool, address tokenIn, int24 tick) external view returns (uint);

    /// @notice Priced proportions of pool assets in specified range
    /// @param pool Address of a pool supported by the adapter
    /// @param ticks Tick boundaries. Lower and upper ticks for UniswapV3-like AMM position.
    /// @return Proportions with 5 decimals precision. Max is 100_000, min is 0.
    function getProportions(address pool, int24[] memory ticks) external view returns (uint[] memory);

    /// @notice Computes the maximum amount of liquidity received for given amounts of pool assets and the current
    /// pool prices and the prices at the tick boundaries
    /// @param pool Address of a pool supported by the adapter
    /// @param amounts Amounts of pool assets
    /// @param ticks Tick boundaries. Lower and upper ticks for UniswapV3-like AMM position.
    /// @return liquidity Liquidity out value
    /// @return amountsConsumed Amounts of consumed assets of provided liquidity
    function getLiquidityForAmounts(
        address pool,
        uint[] memory amounts,
        int24[] memory ticks
    ) external view returns (uint liquidity, uint[] memory amountsConsumed);

    /// @notice Computes pool assets amounts for a given amount of liquidity, the current
    /// pool prices and the prices at the tick boundaries
    /// @param pool Address of a pool supported by the adapter
    /// @param ticks Tick boundaries. Lower and upper ticks for UniswapV3-like AMM position.
    /// @param liquidity Liquidity value
    /// @return amounts Amounts out of provided liquidity
    function getAmountsForLiquidity(
        address pool,
        int24[] memory ticks,
        uint128 liquidity
    ) external view returns (uint[] memory amounts);
}

File 7 of 17 : AmmAdapterIdLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.21;

library AmmAdapterIdLib {
    string public constant UNISWAPV3 = "UniswapV3";
    string public constant ALGEBRA = "Algebra";
    string public constant KYBER = "KyberSwap";
    string public constant CURVE = "Curve";
    string public constant SOLIDLY = "Solidly";
    string public constant BALANCER_COMPOSABLE_STABLE = "BalancerComposableStable";
    string public constant BALANCER_WEIGHTED = "BalancerWeighted";
    string public constant ALGEBRA_V4 = "AlgebraV4";
}

File 8 of 17 : IUniswapV3Pool.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.23;

import "./pool/IUniswapV3PoolImmutables.sol";
import "./pool/IUniswapV3PoolState.sol";
import "./pool/IUniswapV3PoolDerivedState.sol";
import "./pool/IUniswapV3PoolActions.sol";
import "./pool/IUniswapV3PoolOwnerActions.sol";
import "./pool/IUniswapV3PoolEvents.sol";

/// @title The interface for a Uniswap V3 Pool
/// @notice A Uniswap pool facilitates swapping and automated market making between any two assets that strictly conform
/// to the ERC20 specification
/// @dev The pool interface is broken up into many smaller pieces
interface IUniswapV3Pool is
    IUniswapV3PoolImmutables,
    IUniswapV3PoolState,
    IUniswapV3PoolDerivedState,
    IUniswapV3PoolActions,
    IUniswapV3PoolOwnerActions,
    IUniswapV3PoolEvents
{}

File 9 of 17 : UniswapV3MathLib.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

library UniswapV3MathLib {
    uint8 internal constant RESOLUTION = 96;
    uint internal constant Q96 = 0x1000000000000000000000000;
    uint internal constant TWO_96 = 2 ** 96;
    uint160 internal constant MIN_SQRT_RATIO = 4295128739 + 1;
    uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342 - 1;
    int24 internal constant MIN_TICK = -887272;
    int24 internal constant MAX_TICK = -MIN_TICK;

    struct ComputeFeesEarnedCommonParams {
        int24 tick;
        int24 lowerTick;
        int24 upperTick;
        uint128 liquidity;
    }

    function calcPriceOut(
        address tokenIn,
        address token0,
        uint160 sqrtPriceX96,
        uint tokenInDecimals,
        uint tokenOutDecimals,
        uint amount
    ) external pure returns (uint) {
        uint divider = tokenOutDecimals < 18 ? _max(10 ** tokenOutDecimals / 10 ** tokenInDecimals, 1) : 1;

        uint priceDigits = _countDigits(uint(sqrtPriceX96));
        uint purePrice;
        uint precision;
        if (tokenIn == token0) {
            precision = 10 ** ((priceDigits < 29 ? 29 - priceDigits : 0) + tokenInDecimals);
            uint part = uint(sqrtPriceX96) * precision / TWO_96;
            purePrice = part * part;
        } else {
            precision = 10 ** ((priceDigits > 29 ? priceDigits - 29 : 0) + tokenInDecimals);
            uint part = TWO_96 * precision / uint(sqrtPriceX96);
            purePrice = part * part;
        }
        uint price = purePrice / divider / precision / (precision > 1e18 ? (precision / 1e18) : 1);

        if (amount != 0) {
            return price * amount / (10 ** tokenInDecimals);
        } else {
            return price;
        }
    }

    /// @dev Working only for Uniswap V3 native fee calculations. Not usable for Kyber's auto compounding fees and other specific implementations.
    function computeFeesEarned(
        ComputeFeesEarnedCommonParams memory params,
        uint feeGrowthGlobal,
        uint feeGrowthOutsideLower,
        uint feeGrowthOutsideUpper,
        uint feeGrowthInsideLast
    ) external pure returns (uint fee) {
        unchecked {
            // calculate fee growth below
            uint feeGrowthBelow;
            if (params.tick >= params.lowerTick) {
                feeGrowthBelow = feeGrowthOutsideLower;
            } else {
                feeGrowthBelow = feeGrowthGlobal - feeGrowthOutsideLower;
            }
            // calculate fee growth above
            uint feeGrowthAbove;
            if (params.tick < params.upperTick) {
                feeGrowthAbove = feeGrowthOutsideUpper;
            } else {
                feeGrowthAbove = feeGrowthGlobal - feeGrowthOutsideUpper;
            }

            uint feeGrowthInside = feeGrowthGlobal - feeGrowthBelow - feeGrowthAbove;
            fee = mulDiv(params.liquidity, feeGrowthInside - feeGrowthInsideLast, 0x100000000000000000000000000000000);
        }
    }

    function getTicksInSpacing(
        int24 tick,
        int24 tickSpacing
    ) internal pure returns (int24 lowerTick, int24 upperTick) {
        // nosemgrep
        if (tick < 0 && tick / tickSpacing * tickSpacing != tick) {
            lowerTick = (tick / tickSpacing - 1) * tickSpacing;
        } else {
            lowerTick = tick / tickSpacing * tickSpacing;
        }
        upperTick = lowerTick + tickSpacing;
    }

    /// @notice Computes the maximum amount of liquidity received for a given amount of token0, token1, the current
    /// pool prices and the prices at the tick boundaries
    function getLiquidityForAmounts(
        uint160 sqrtRatioX96,
        int24 lowerTick,
        int24 upperTick,
        uint amount0,
        uint amount1
    ) internal pure returns (uint128 liquidity) {
        uint160 sqrtRatioAX96 = getSqrtRatioAtTick(lowerTick);
        uint160 sqrtRatioBX96 = getSqrtRatioAtTick(upperTick);
        if (sqrtRatioAX96 > sqrtRatioBX96) {
            (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
        }

        if (sqrtRatioX96 <= sqrtRatioAX96) {
            liquidity = _getLiquidityForAmount0(sqrtRatioAX96, sqrtRatioBX96, amount0);
        } else if (sqrtRatioX96 < sqrtRatioBX96) {
            uint128 liquidity0 = _getLiquidityForAmount0(sqrtRatioX96, sqrtRatioBX96, amount0);
            uint128 liquidity1 = _getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioX96, amount1);
            liquidity = liquidity0 < liquidity1 ? liquidity0 : liquidity1;
        } else {
            liquidity = _getLiquidityForAmount1(sqrtRatioAX96, sqrtRatioBX96, amount1);
        }
    }

    /// @notice Computes the token0 and token1 value for a given amount of liquidity, the current
    /// pool prices and the prices at the tick boundaries
    function getAmountsForLiquidity(
        uint160 sqrtRatioX96,
        int24 lowerTick,
        int24 upperTick,
        uint128 liquidity
    ) internal pure returns (uint amount0, uint amount1) {
        uint160 sqrtRatioAX96 = getSqrtRatioAtTick(lowerTick);
        uint160 sqrtRatioBX96 = getSqrtRatioAtTick(upperTick);

        if (sqrtRatioAX96 > sqrtRatioBX96) {
            (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
        }

        if (sqrtRatioX96 <= sqrtRatioAX96) {
            amount0 = _getAmount0ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
        } else if (sqrtRatioX96 < sqrtRatioBX96) {
            amount0 = _getAmount0ForLiquidity(sqrtRatioX96, sqrtRatioBX96, liquidity);
            amount1 = _getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioX96, liquidity);
        } else {
            amount1 = _getAmount1ForLiquidity(sqrtRatioAX96, sqrtRatioBX96, liquidity);
        }
    }

    /// @notice Computes the amount of liquidity received for a given amount of token0 and price range
    /// @dev Calculates amount0 * (sqrt(upper) * sqrt(lower)) / (sqrt(upper) - sqrt(lower)).
    /// @param sqrtRatioAX96 A sqrt price
    /// @param sqrtRatioBX96 Another sqrt price
    /// @param amount0 The amount0 being sent in
    /// @return liquidity The amount of returned liquidity
    function _getLiquidityForAmount0(
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint amount0
    ) internal pure returns (uint128 liquidity) {
        if (sqrtRatioAX96 > sqrtRatioBX96) {
            (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
        }
        uint intermediate = mulDiv(sqrtRatioAX96, sqrtRatioBX96, Q96);
        return _toUint128(mulDiv(amount0, intermediate, sqrtRatioBX96 - sqrtRatioAX96));
    }

    /// @notice Computes the amount of liquidity received for a given amount of token1 and price range
    /// @dev Calculates amount1 / (sqrt(upper) - sqrt(lower)).
    /// @param sqrtRatioAX96 A sqrt price
    /// @param sqrtRatioBX96 Another sqrt price
    /// @param amount1 The amount1 being sent in
    /// @return liquidity The amount of returned liquidity
    function _getLiquidityForAmount1(
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint amount1
    ) internal pure returns (uint128 liquidity) {
        if (sqrtRatioAX96 > sqrtRatioBX96) {
            (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
        }
        return _toUint128(mulDiv(amount1, Q96, sqrtRatioBX96 - sqrtRatioAX96));
    }

    /// @notice Computes the amount of token0 for a given amount of liquidity and a price range
    /// @param sqrtRatioAX96 A sqrt price
    /// @param sqrtRatioBX96 Another sqrt price
    /// @param liquidity The liquidity being valued
    /// @return amount0 The amount0
    function _getAmount0ForLiquidity(
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint128 liquidity
    ) internal pure returns (uint amount0) {
        if (sqrtRatioAX96 > sqrtRatioBX96) {
            (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
        }
        return _mulDivRoundingUp(
            1,
            _mulDivRoundingUp(uint(liquidity) << RESOLUTION, sqrtRatioBX96 - sqrtRatioAX96, sqrtRatioBX96),
            sqrtRatioAX96
        );
    }

    /// @notice Computes the amount of token1 for a given amount of liquidity and a price range
    /// @param sqrtRatioAX96 A sqrt price
    /// @param sqrtRatioBX96 Another sqrt price
    /// @param liquidity The liquidity being valued
    /// @return amount1 The amount1
    function _getAmount1ForLiquidity(
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint128 liquidity
    ) internal pure returns (uint amount1) {
        if (sqrtRatioAX96 > sqrtRatioBX96) {
            (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
        }
        return _mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, Q96);
    }

    /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    /// @dev Credit to Remco Bloemen under MIT license https://xn--2-umb.com/21/muldiv
    function mulDiv(uint a, uint b, uint denominator) public pure returns (uint result) {
        unchecked {
            // 512-bit multiply [prod1 prod0] = a * b
            // Compute the product mod 2**256 and mod 2**256 - 1
            // then use the Chinese Remainder Theorem to reconstruct
            // the 512 bit result. The result is stored in two 256
            // variables such that product = prod1 * 2**256 + prod0
            uint prod0;
            // Least significant 256 bits of the product
            uint prod1;
            // Most significant 256 bits of the product
            assembly {
                let mm := mulmod(a, b, not(0))
                prod0 := mul(a, b)
                prod1 := sub(sub(mm, prod0), lt(mm, prod0))
            }

            // Handle non-overflow cases, 256 by 256 division
            if (prod1 == 0) {
                require(denominator > 0);
                assembly {
                    result := div(prod0, denominator)
                }
                return result;
            }

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

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

            // Make division exact by subtracting the remainder from [prod1 prod0]
            // Compute remainder using mulmod
            uint remainder;
            assembly {
                remainder := mulmod(a, b, denominator)
            }
            // Subtract 256 bit number from 512 bit number
            assembly {
                prod1 := sub(prod1, gt(remainder, prod0))
                prod0 := sub(prod0, remainder)
            }

            // Factor powers of two out of denominator
            // Compute largest power of two divisor of denominator.
            // Always >= 1.
            // EDIT for 0.8 compatibility:
            // see: https://ethereum.stackexchange.com/questions/96642/unary-operator-cannot-be-applied-to-type-uint
            uint twos = denominator & (~denominator + 1);

            // Divide denominator by power of two
            assembly {
                denominator := div(denominator, twos)
            }

            // Divide [prod1 prod0] by the factors of two
            assembly {
                prod0 := div(prod0, twos)
            }
            // Shift in bits from prod1 into prod0. For this we need
            // to flip `twos` such that it is 2**256 / twos.
            // If twos is zero, then it becomes one
            assembly {
                twos := add(div(sub(0, twos), twos), 1)
            }
            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
            // correct for four bits. That is, denominator * inv = 1 mod 2**4
            uint inv = (3 * denominator) ^ 2;
            // Now use Newton-Raphson iteration to improve the precision.
            // Thanks to Hensel's lifting lemma, this also works in modular
            // arithmetic, doubling the correct bits in each step.
            inv *= 2 - denominator * inv;
            // inverse mod 2**8
            inv *= 2 - denominator * inv;
            // inverse mod 2**16
            inv *= 2 - denominator * inv;
            // inverse mod 2**32
            inv *= 2 - denominator * inv;
            // inverse mod 2**64
            inv *= 2 - denominator * inv;
            // inverse mod 2**128
            inv *= 2 - denominator * inv;
            // 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 precoditions 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 * inv;
            return result;
        }
    }

    /// @notice Calculates ceil(a×b÷denominator) with full precision. Throws if result overflows a uint or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    function _mulDivRoundingUp(uint a, uint b, uint denominator) internal pure returns (uint result) {
        result = mulDiv(a, b, denominator);
        if (mulmod(a, b, denominator) > 0) {
            require(result < type(uint).max);
            // nosemgrep
            result++;
        }
    }

    function _countDigits(uint n) internal pure returns (uint) {
        if (n == 0) {
            return 0;
        }
        uint count = 0;
        while (n != 0) {
            n = n / 10;
            ++count;
        }
        return count;
    }

    function _max(uint a, uint b) internal pure returns (uint) {
        return a > b ? a : b;
    }

    function _toUint128(uint x) private pure returns (uint128 y) {
        require((y = uint128(x)) == x);
    }

    /// @notice Calculates sqrt(1.0001^tick) * 2^96
    /// @dev Throws if |tick| > max tick
    /// @param tick The input tick for the above formula
    /// @return sqrtPriceX96 A Fixed point Q64.96 number representing the sqrt of the ratio of the two assets (token1/token0)
    /// at the given tick
    function getSqrtRatioAtTick(int24 tick) public pure returns (uint160 sqrtPriceX96) {
        uint absTick = tick < 0 ? uint(-int(tick)) : uint(int(tick));

        // EDIT: 0.8 compatibility
        // nosemgrep
        require(absTick <= uint(int(MAX_TICK)), "T");

        uint ratio = absTick & 0x1 != 0 ? 0xfffcb933bd6fad37aa2d162d1a594001 : 0x100000000000000000000000000000000;
        if (absTick & 0x2 != 0) {
            ratio = (ratio * 0xfff97272373d413259a46990580e213a) >> 128;
        }
        if (absTick & 0x4 != 0) {
            ratio = (ratio * 0xfff2e50f5f656932ef12357cf3c7fdcc) >> 128;
        }
        if (absTick & 0x8 != 0) {
            ratio = (ratio * 0xffe5caca7e10e4e61c3624eaa0941cd0) >> 128;
        }
        if (absTick & 0x10 != 0) {
            ratio = (ratio * 0xffcb9843d60f6159c9db58835c926644) >> 128;
        }
        if (absTick & 0x20 != 0) {
            ratio = (ratio * 0xff973b41fa98c081472e6896dfb254c0) >> 128;
        }
        if (absTick & 0x40 != 0) {
            ratio = (ratio * 0xff2ea16466c96a3843ec78b326b52861) >> 128;
        }
        if (absTick & 0x80 != 0) {
            ratio = (ratio * 0xfe5dee046a99a2a811c461f1969c3053) >> 128;
        }
        if (absTick & 0x100 != 0) {
            ratio = (ratio * 0xfcbe86c7900a88aedcffc83b479aa3a4) >> 128;
        }
        if (absTick & 0x200 != 0) {
            ratio = (ratio * 0xf987a7253ac413176f2b074cf7815e54) >> 128;
        }
        if (absTick & 0x400 != 0) {
            ratio = (ratio * 0xf3392b0822b70005940c7a398e4b70f3) >> 128;
        }
        if (absTick & 0x800 != 0) {
            ratio = (ratio * 0xe7159475a2c29b7443b29c7fa6e889d9) >> 128;
        }
        if (absTick & 0x1000 != 0) {
            ratio = (ratio * 0xd097f3bdfd2022b8845ad8f792aa5825) >> 128;
        }
        if (absTick & 0x2000 != 0) {
            ratio = (ratio * 0xa9f746462d870fdf8a65dc1f90e061e5) >> 128;
        }
        if (absTick & 0x4000 != 0) {
            ratio = (ratio * 0x70d869a156d2a1b890bb3df62baf32f7) >> 128;
        }
        if (absTick & 0x8000 != 0) {
            ratio = (ratio * 0x31be135f97d08fd981231505542fcfa6) >> 128;
        }
        if (absTick & 0x10000 != 0) {
            ratio = (ratio * 0x9aa508b5b7a84e1c677de54f3e99bc9) >> 128;
        }
        if (absTick & 0x20000 != 0) {
            ratio = (ratio * 0x5d6af8dedb81196699c329225ee604) >> 128;
        }
        if (absTick & 0x40000 != 0) {
            ratio = (ratio * 0x2216e584f5fa1ea926041bedfe98) >> 128;
        }
        if (absTick & 0x80000 != 0) {
            ratio = (ratio * 0x48a170391f7dc42444e8fa2) >> 128;
        }

        if (tick > 0) ratio = type(uint).max / ratio;

        // this divides by 1<<32 rounding up to go from a Q128.128 to a Q128.96.
        // we then downcast because we know the result always fits within 160 bits due to our tick input constraint
        // we round up in the division so getTickAtSqrtRatio of the output price is always consistent
        sqrtPriceX96 = uint160((ratio >> 32) + (ratio % (1 << 32) == 0 ? 0 : 1));
    }
}

File 10 of 17 : IAmmAdapter.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

/// @dev Get price, swap, liquidity calculations. Used by strategies and swapper
/// @author Alien Deployer (https://github.com/a17)
/// @author JodsMigel (https://github.com/JodsMigel)
interface IAmmAdapter is IERC165 {
    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                       CUSTOM ERRORS                        */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    error PriceIncreased();
    error WrongCallbackAmount();
    error NotSupportedByCAMM();

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

    event SwapInPool(
        address pool,
        address tokenIn,
        address tokenOut,
        address recipient,
        uint priceImpactTolerance,
        uint amountIn,
        uint amountOut
    );

    /*´:°•.°+.*•´.*:˚.°*.˚•´.°:°•.°•.*•´.*:˚.°*.˚•´.°:°•.°+.*•´.*:*/
    /*                         DATA TYPES                         */
    /*.•°:°.´+˚.*°.˚:*.´•*.+°.•°:´*.´•*.•°.•°:°.´:•˚°.*°.˚:*.´+°.•*/

    struct SwapCallbackData {
        address tokenIn;
        uint amount;
    }

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

    /// @notice String ID of the adapter
    function ammAdapterId() external view returns (string memory);

    /// @notice Tokens of a pool supported by the adapter
    function poolTokens(address pool) external view returns (address[] memory);

    /// @notice Computes the maximum amount of liquidity received for given amounts of pool assets and the current
    /// pool price.
    /// This function signature can be used only for non-concentrated AMMs.
    /// @param pool Address of a pool supported by the adapter
    /// @param amounts Amounts of pool assets
    /// @return liquidity Liquidity out value
    /// @return amountsConsumed Amounts of consumed assets when providing liquidity
    function getLiquidityForAmounts(
        address pool,
        uint[] memory amounts
    ) external view returns (uint liquidity, uint[] memory amountsConsumed);

    /// @notice Priced proportions of pool assets
    /// @param pool Address of a pool supported by the adapter
    /// @return Proportions with 18 decimals precision. Max is 1e18, min is 0.
    function getProportions(address pool) external view returns (uint[] memory);

    /// @notice Current price in pool without amount impact
    /// @param pool Address of a pool supported by the adapter
    /// @param tokenIn Token for sell
    /// @param tokenOut Token for buy
    /// @param amount Amount of tokenIn. For zero value provided amount 1.0 (10 ** decimals of tokenIn) will be used.
    /// @return Amount of tokenOut with tokenOut decimals precision
    function getPrice(address pool, address tokenIn, address tokenOut, uint amount) external view returns (uint);

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

    /// @notice Swap given tokenIn for tokenOut. Assume that tokenIn already sent to this contract.
    /// @param pool Address of a pool supported by the adapter
    /// @param tokenIn Token for sell
    /// @param tokenOut Token for buy
    /// @param recipient Recipient for tokenOut
    /// @param priceImpactTolerance Price impact tolerance. Must include fees at least. Denominator is 100_000.
    function swap(
        address pool,
        address tokenIn,
        address tokenOut,
        address recipient,
        uint priceImpactTolerance
    ) external;

    /// @dev Initializer for proxied adapter
    function init(address platform) external;
}

File 11 of 17 : IERC165.sol
// 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);
}

File 12 of 17 : IUniswapV3PoolImmutables.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.23;

/// @title Pool state that never changes
/// @notice These parameters are fixed for a pool forever, i.e., the methods will always return the same values
interface IUniswapV3PoolImmutables {
    /// @notice The contract that deployed the pool, which must adhere to the IUniswapV3Factory interface
    /// @return The contract address
    function factory() external view returns (address);

    /// @notice The first of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token0() external view returns (address);

    /// @notice The second of the two tokens of the pool, sorted by address
    /// @return The token contract address
    function token1() external view returns (address);

    /// @notice The pool's fee in hundredths of a bip, i.e. 1e-6
    /// @return The fee
    function fee() external view returns (uint24);

    /// @notice The pool tick spacing
    /// @dev Ticks can only be used at multiples of this value, minimum of 1 and always positive
    /// e.g.: a tickSpacing of 3 means ticks can be initialized every 3rd tick, i.e., ..., -6, -3, 0, 3, 6, ...
    /// This value is an int24 to avoid casting even though it is always positive.
    /// @return The tick spacing
    function tickSpacing() external view returns (int24);

    /// @notice The maximum amount of position liquidity that can use any tick in the range
    /// @dev This parameter is enforced per tick to prevent liquidity from overflowing a uint128 at any point, and
    /// also prevents out-of-range liquidity from being used to prevent adding in-range liquidity to a pool
    /// @return The max amount of liquidity per tick
    function maxLiquidityPerTick() external view returns (uint128);
}

File 13 of 17 : IUniswapV3PoolState.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.23;

/// @title Pool state that can change
/// @notice These methods compose the pool's state, and can change with any frequency including multiple times
/// per transaction
interface IUniswapV3PoolState {
    /// @notice The 0th storage slot in the pool stores many values, and is exposed as a single method to save gas
    /// when accessed externally.
    /// @return sqrtPriceX96 The current price of the pool as a sqrt(token1/token0) Q64.96 value
    /// tick The current tick of the pool, i.e. according to the last tick transition that was run.
    /// This value may not always be equal to SqrtTickMath.getTickAtSqrtRatio(sqrtPriceX96) if the price is on a tick
    /// boundary.
    /// observationIndex The index of the last oracle observation that was written,
    /// observationCardinality The current maximum number of observations stored in the pool,
    /// observationCardinalityNext The next maximum number of observations, to be updated when the observation.
    /// feeProtocol The protocol fee for both tokens of the pool.
    /// Encoded as two 4 bit values, where the protocol fee of token1 is shifted 4 bits and the protocol fee of token0
    /// is the lower 4 bits. Used as the denominator of a fraction of the swap fee, e.g. 4 means 1/4th of the swap fee.
    /// unlocked Whether the pool is currently locked to reentrancy
    function slot0()
        external
        view
        returns (
            uint160 sqrtPriceX96,
            int24 tick,
            uint16 observationIndex,
            uint16 observationCardinality,
            uint16 observationCardinalityNext,
            uint8 feeProtocol,
            bool unlocked
        );

    /// @notice The fee growth as a Q128.128 fees of token0 collected per unit of liquidity for the entire life of the pool
    /// @dev This value can overflow the uint256
    function feeGrowthGlobal0X128() external view returns (uint);

    /// @notice The fee growth as a Q128.128 fees of token1 collected per unit of liquidity for the entire life of the pool
    /// @dev This value can overflow the uint256
    function feeGrowthGlobal1X128() external view returns (uint);

    /// @notice The amounts of token0 and token1 that are owed to the protocol
    /// @dev Protocol fees will never exceed uint128 max in either token
    function protocolFees() external view returns (uint128 token0, uint128 token1);

    /// @notice The currently in range liquidity available to the pool
    /// @dev This value has no relationship to the total liquidity across all ticks
    function liquidity() external view returns (uint128);

    /// @notice Look up information about a specific tick in the pool
    /// @param tick The tick to look up
    /// @return liquidityGross the total amount of position liquidity that uses the pool either as tick lower or
    /// tick upper,
    /// liquidityNet how much liquidity changes when the pool price crosses the tick,
    /// feeGrowthOutside0X128 the fee growth on the other side of the tick from the current tick in token0,
    /// feeGrowthOutside1X128 the fee growth on the other side of the tick from the current tick in token1,
    /// tickCumulativeOutside the cumulative tick value on the other side of the tick from the current tick
    /// secondsPerLiquidityOutsideX128 the seconds spent per liquidity on the other side of the tick from the current tick,
    /// secondsOutside the seconds spent on the other side of the tick from the current tick,
    /// initialized Set to true if the tick is initialized, i.e. liquidityGross is greater than 0, otherwise equal to false.
    /// Outside values can only be used if the tick is initialized, i.e. if liquidityGross is greater than 0.
    /// In addition, these values are only relative and must be used only in comparison to previous snapshots for
    /// a specific position.
    function ticks(int24 tick)
        external
        view
        returns (
            uint128 liquidityGross,
            int128 liquidityNet,
            uint feeGrowthOutside0X128,
            uint feeGrowthOutside1X128,
            int56 tickCumulativeOutside,
            uint160 secondsPerLiquidityOutsideX128,
            uint32 secondsOutside,
            bool initialized
        );

    /// @notice Returns 256 packed tick initialized boolean values. See TickBitmap for more information
    function tickBitmap(int16 wordPosition) external view returns (uint);

    /// @notice Returns the information about a position by the position's key
    /// @param key The position's key is a hash of a preimage composed by the owner, tickLower and tickUpper
    /// @return _liquidity The amount of liquidity in the position,
    /// Returns feeGrowthInside0LastX128 fee growth of token0 inside the tick range as of the last mint/burn/poke,
    /// Returns feeGrowthInside1LastX128 fee growth of token1 inside the tick range as of the last mint/burn/poke,
    /// Returns tokensOwed0 the computed amount of token0 owed to the position as of the last mint/burn/poke,
    /// Returns tokensOwed1 the computed amount of token1 owed to the position as of the last mint/burn/poke
    function positions(bytes32 key)
        external
        view
        returns (
            uint128 _liquidity,
            uint feeGrowthInside0LastX128,
            uint feeGrowthInside1LastX128,
            uint128 tokensOwed0,
            uint128 tokensOwed1
        );

    /// @notice Returns data about a specific observation index
    /// @param index The element of the observations array to fetch
    /// @dev You most likely want to use #observe() instead of this method to get an observation as of some amount of time
    /// ago, rather than at a specific index in the array.
    /// @return blockTimestamp The timestamp of the observation,
    /// Returns tickCumulative the tick multiplied by seconds elapsed for the life of the pool as of the observation timestamp,
    /// Returns secondsPerLiquidityCumulativeX128 the seconds per in range liquidity for the life of the pool as of the observation timestamp,
    /// Returns initialized whether the observation has been initialized and the values are safe to use
    function observations(uint index)
        external
        view
        returns (
            uint32 blockTimestamp,
            int56 tickCumulative,
            uint160 secondsPerLiquidityCumulativeX128,
            bool initialized
        );
}

File 14 of 17 : IUniswapV3PoolDerivedState.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.23;

/// @title Pool state that is not stored
/// @notice Contains view functions to provide information about the pool that is computed rather than stored on the
/// blockchain. The functions here may have variable gas costs.
interface IUniswapV3PoolDerivedState {
    /// @notice Returns the cumulative tick and liquidity as of each timestamp `secondsAgo` from the current block timestamp
    /// @dev To get a time weighted average tick or liquidity-in-range, you must call this with two values, one representing
    /// the beginning of the period and another for the end of the period. E.g., to get the last hour time-weighted average tick,
    /// you must call it with secondsAgos = [3600, 0].
    /// @dev The time weighted average tick represents the geometric time weighted average price of the pool, in
    /// log base sqrt(1.0001) of token1 / token0. The TickMath library can be used to go from a tick value to a ratio.
    /// @param secondsAgos From how long ago each cumulative tick and liquidity value should be returned
    /// @return tickCumulatives Cumulative tick values as of each `secondsAgos` from the current block timestamp
    /// @return secondsPerLiquidityCumulativeX128s Cumulative seconds per liquidity-in-range value as of each `secondsAgos` from the current block
    /// timestamp
    function observe(uint32[] calldata secondsAgos)
        external
        view
        returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s);

    /// @notice Returns a snapshot of the tick cumulative, seconds per liquidity and seconds inside a tick range
    /// @dev Snapshots must only be compared to other snapshots, taken over a period for which a position existed.
    /// I.e., snapshots cannot be compared if a position is not held for the entire period between when the first
    /// snapshot is taken and the second snapshot is taken.
    /// @param tickLower The lower tick of the range
    /// @param tickUpper The upper tick of the range
    /// @return tickCumulativeInside The snapshot of the tick accumulator for the range
    /// @return secondsPerLiquidityInsideX128 The snapshot of seconds per liquidity for the range
    /// @return secondsInside The snapshot of seconds per liquidity for the range
    function snapshotCumulativesInside(
        int24 tickLower,
        int24 tickUpper
    ) external view returns (int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside);
}

File 15 of 17 : IUniswapV3PoolActions.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.23;

/// @title Permissionless pool actions
/// @notice Contains pool methods that can be called by anyone
interface IUniswapV3PoolActions {
    /// @notice Sets the initial price for the pool
    /// @dev Price is represented as a sqrt(amountToken1/amountToken0) Q64.96 value
    /// @param sqrtPriceX96 the initial sqrt price of the pool as a Q64.96
    function initialize(uint160 sqrtPriceX96) external;

    /// @notice Adds liquidity for the given recipient/tickLower/tickUpper position
    /// @dev The caller of this method receives a callback in the form of IUniswapV3MintCallback#uniswapV3MintCallback
    /// in which they must pay any token0 or token1 owed for the liquidity. The amount of token0/token1 due depends
    /// on tickLower, tickUpper, the amount of liquidity, and the current price.
    /// @param recipient The address for which the liquidity will be created
    /// @param tickLower The lower tick of the position in which to add liquidity
    /// @param tickUpper The upper tick of the position in which to add liquidity
    /// @param amount The amount of liquidity to mint
    /// @param data Any data that should be passed through to the callback
    /// @return amount0 The amount of token0 that was paid to mint the given amount of liquidity. Matches the value in the callback
    /// @return amount1 The amount of token1 that was paid to mint the given amount of liquidity. Matches the value in the callback
    function mint(
        address recipient,
        int24 tickLower,
        int24 tickUpper,
        uint128 amount,
        bytes calldata data
    ) external returns (uint amount0, uint amount1);

    /// @notice Collects tokens owed to a position
    /// @dev Does not recompute fees earned, which must be done either via mint or burn of any amount of liquidity.
    /// Collect must be called by the position owner. To withdraw only token0 or only token1, amount0Requested or
    /// amount1Requested may be set to zero. To withdraw all tokens owed, caller may pass any value greater than the
    /// actual tokens owed, e.g. type(uint128).max. Tokens owed may be from accumulated swap fees or burned liquidity.
    /// @param recipient The address which should receive the fees collected
    /// @param tickLower The lower tick of the position for which to collect fees
    /// @param tickUpper The upper tick of the position for which to collect fees
    /// @param amount0Requested How much token0 should be withdrawn from the fees owed
    /// @param amount1Requested How much token1 should be withdrawn from the fees owed
    /// @return amount0 The amount of fees collected in token0
    /// @return amount1 The amount of fees collected in token1
    function collect(
        address recipient,
        int24 tickLower,
        int24 tickUpper,
        uint128 amount0Requested,
        uint128 amount1Requested
    ) external returns (uint128 amount0, uint128 amount1);

    /// @notice Burn liquidity from the sender and account tokens owed for the liquidity to the position
    /// @dev Can be used to trigger a recalculation of fees owed to a position by calling with an amount of 0
    /// @dev Fees must be collected separately via a call to #collect
    /// @param tickLower The lower tick of the position for which to burn liquidity
    /// @param tickUpper The upper tick of the position for which to burn liquidity
    /// @param amount How much liquidity to burn
    /// @return amount0 The amount of token0 sent to the recipient
    /// @return amount1 The amount of token1 sent to the recipient
    function burn(int24 tickLower, int24 tickUpper, uint128 amount) external returns (uint amount0, uint amount1);

    /// @notice Swap token0 for token1, or token1 for token0
    /// @dev The caller of this method receives a callback in the form of IUniswapV3SwapCallback#uniswapV3SwapCallback
    /// @param recipient The address to receive the output of the swap
    /// @param zeroForOne The direction of the swap, true for token0 to token1, false for token1 to token0
    /// @param amountSpecified The amount of the swap, which implicitly configures the swap as exact input (positive), or exact output (negative)
    /// @param sqrtPriceLimitX96 The Q64.96 sqrt price limit. If zero for one, the price cannot be less than this
    /// value after the swap. If one for zero, the price cannot be greater than this value after the swap
    /// @param data Any data to be passed through to the callback
    /// @return amount0 The delta of the balance of token0 of the pool, exact when negative, minimum when positive
    /// @return amount1 The delta of the balance of token1 of the pool, exact when negative, minimum when positive
    function swap(
        address recipient,
        bool zeroForOne,
        int amountSpecified,
        uint160 sqrtPriceLimitX96,
        bytes calldata data
    ) external returns (int amount0, int amount1);

    /// @notice Receive token0 and/or token1 and pay it back, plus a fee, in the callback
    /// @dev The caller of this method receives a callback in the form of IUniswapV3FlashCallback#uniswapV3FlashCallback
    /// @dev Can be used to donate underlying tokens pro-rata to currently in-range liquidity providers by calling
    /// with 0 amount{0,1} and sending the donation amount(s) from the callback
    /// @param recipient The address which will receive the token0 and token1 amounts
    /// @param amount0 The amount of token0 to send
    /// @param amount1 The amount of token1 to send
    /// @param data Any data to be passed through to the callback
    function flash(address recipient, uint amount0, uint amount1, bytes calldata data) external;

    /// @notice Increase the maximum number of price and liquidity observations that this pool will store
    /// @dev This method is no-op if the pool already has an observationCardinalityNext greater than or equal to
    /// the input observationCardinalityNext.
    /// @param observationCardinalityNext The desired minimum number of observations for the pool to store
    function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external;
}

File 16 of 17 : IUniswapV3PoolOwnerActions.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.23;

/// @title Permissioned pool actions
/// @notice Contains pool methods that may only be called by the factory owner
interface IUniswapV3PoolOwnerActions {
    /// @notice Set the denominator of the protocol's % share of the fees
    /// @param feeProtocol0 new protocol fee for token0 of the pool
    /// @param feeProtocol1 new protocol fee for token1 of the pool
    function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external;

    /// @notice Collect the protocol fee accrued to the pool
    /// @param recipient The address to which collected protocol fees should be sent
    /// @param amount0Requested The maximum amount of token0 to send, can be 0 to collect fees in only token1
    /// @param amount1Requested The maximum amount of token1 to send, can be 0 to collect fees in only token0
    /// @return amount0 The protocol fee collected in token0
    /// @return amount1 The protocol fee collected in token1
    function collectProtocol(
        address recipient,
        uint128 amount0Requested,
        uint128 amount1Requested
    ) external returns (uint128 amount0, uint128 amount1);
}

File 17 of 17 : IUniswapV3PoolEvents.sol
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.23;

/// @title Events emitted by a pool
/// @notice Contains all events emitted by the pool
interface IUniswapV3PoolEvents {
    /// @notice Emitted exactly once by a pool when #initialize is first called on the pool
    /// @dev Mint/Burn/Swap cannot be emitted by the pool before Initialize
    /// @param sqrtPriceX96 The initial sqrt price of the pool, as a Q64.96
    /// @param tick The initial tick of the pool, i.e. log base 1.0001 of the starting price of the pool
    event Initialize(uint160 sqrtPriceX96, int24 tick);

    /// @notice Emitted when liquidity is minted for a given position
    /// @param sender The address that minted the liquidity
    /// @param owner The owner of the position and recipient of any minted liquidity
    /// @param tickLower The lower tick of the position
    /// @param tickUpper The upper tick of the position
    /// @param amount The amount of liquidity minted to the position range
    /// @param amount0 How much token0 was required for the minted liquidity
    /// @param amount1 How much token1 was required for the minted liquidity
    event Mint(
        address sender,
        address indexed owner,
        int24 indexed tickLower,
        int24 indexed tickUpper,
        uint128 amount,
        uint amount0,
        uint amount1
    );

    /// @notice Emitted when fees are collected by the owner of a position
    /// @dev Collect events may be emitted with zero amount0 and amount1 when the caller chooses not to collect fees
    /// @param owner The owner of the position for which fees are collected
    /// @param tickLower The lower tick of the position
    /// @param tickUpper The upper tick of the position
    /// @param amount0 The amount of token0 fees collected
    /// @param amount1 The amount of token1 fees collected
    event Collect(
        address indexed owner,
        address recipient,
        int24 indexed tickLower,
        int24 indexed tickUpper,
        uint128 amount0,
        uint128 amount1
    );

    /// @notice Emitted when a position's liquidity is removed
    /// @dev Does not withdraw any fees earned by the liquidity position, which must be withdrawn via #collect
    /// @param owner The owner of the position for which liquidity is removed
    /// @param tickLower The lower tick of the position
    /// @param tickUpper The upper tick of the position
    /// @param amount The amount of liquidity to remove
    /// @param amount0 The amount of token0 withdrawn
    /// @param amount1 The amount of token1 withdrawn
    event Burn(
        address indexed owner,
        int24 indexed tickLower,
        int24 indexed tickUpper,
        uint128 amount,
        uint amount0,
        uint amount1
    );

    /// @notice Emitted by the pool for any swaps between token0 and token1
    /// @param sender The address that initiated the swap call, and that received the callback
    /// @param recipient The address that received the output of the swap
    /// @param amount0 The delta of the token0 balance of the pool
    /// @param amount1 The delta of the token1 balance of the pool
    /// @param sqrtPriceX96 The sqrt(price) of the pool after the swap, as a Q64.96
    /// @param liquidity The liquidity of the pool after the swap
    /// @param tick The log base 1.0001 of price of the pool after the swap
    event Swap(
        address indexed sender,
        address indexed recipient,
        int amount0,
        int amount1,
        uint160 sqrtPriceX96,
        uint128 liquidity,
        int24 tick
    );

    /// @notice Emitted by the pool for any flashes of token0/token1
    /// @param sender The address that initiated the swap call, and that received the callback
    /// @param recipient The address that received the tokens from flash
    /// @param amount0 The amount of token0 that was flashed
    /// @param amount1 The amount of token1 that was flashed
    /// @param paid0 The amount of token0 paid for the flash, which can exceed the amount0 plus the fee
    /// @param paid1 The amount of token1 paid for the flash, which can exceed the amount1 plus the fee
    event Flash(address indexed sender, address indexed recipient, uint amount0, uint amount1, uint paid0, uint paid1);

    /// @notice Emitted by the pool for increases to the number of observations that can be stored
    /// @dev observationCardinalityNext is not the observation cardinality until an observation is written at the index
    /// just before a mint/swap/burn.
    /// @param observationCardinalityNextOld The previous value of the next observation cardinality
    /// @param observationCardinalityNextNew The updated value of the next observation cardinality
    event IncreaseObservationCardinalityNext(
        uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew
    );

    /// @notice Emitted when the protocol fee is changed by the pool
    /// @param feeProtocol0Old The previous value of the token0 protocol fee
    /// @param feeProtocol1Old The previous value of the token1 protocol fee
    /// @param feeProtocol0New The updated value of the token0 protocol fee
    /// @param feeProtocol1New The updated value of the token1 protocol fee
    event SetFeeProtocol(uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New);

    /// @notice Emitted when the collected protocol fees are withdrawn by the factory owner
    /// @param sender The address that collects the protocol fees
    /// @param recipient The address that receives the collected protocol fees
    /// @param amount0 The amount of token0 protocol fees that is withdrawn
    /// @param amount0 The amount of token1 protocol fees that is withdrawn
    event CollectProtocol(address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1);
}

Settings
{
  "remappings": [
    "@openzeppelin/contracts/=lib/openzeppelin-contracts/contracts/",
    "@openzeppelin/contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "@solady/=lib/solady/src/",
    "ds-test/=lib/forge-std/lib/ds-test/src/",
    "erc4626-tests/=lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
    "forge-std/=lib/forge-std/src/",
    "openzeppelin-contracts-upgradeable/=lib/openzeppelin-contracts-upgradeable/",
    "openzeppelin/=lib/openzeppelin-contracts-upgradeable/contracts/",
    "solady/=lib/solady/",
    "openzeppelin-contracts/=lib/openzeppelin-contracts/"
  ],
  "optimizer": {
    "enabled": true,
    "runs": 200
  },
  "metadata": {
    "useLiteralContent": false,
    "bytecodeHash": "ipfs",
    "appendCBOR": true
  },
  "outputSelection": {
    "*": {
      "*": [
        "evm.bytecode",
        "evm.deployedBytecode",
        "devdoc",
        "userdoc",
        "metadata",
        "abi"
      ]
    }
  },
  "evmVersion": "shanghai",
  "viaIR": false,
  "libraries": {
    "src/strategies/libs/ALMLib.sol": {
      "ALMLib": "0x2ED4d0Bbd7f16ED33269243Eb3Fe993Bd5561e93"
    },
    "src/strategies/libs/UniswapV3MathLib.sol": {
      "UniswapV3MathLib": "0xbbc63ee4a06bf1F2432ccC4d70103e3D465fcA39"
    }
  }
}

Contract Security Audit

Contract ABI

[{"inputs":[{"internalType":"uint256","name":"price","type":"uint256"},{"internalType":"uint256","name":"priceBefore","type":"uint256"},{"internalType":"uint256","name":"priceThreshold","type":"uint256"},{"internalType":"uint32","name":"twapInterval","type":"uint32"}],"name":"PriceChangeProtection","type":"error"},{"inputs":[],"name":"ALGO_FILL_UP","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PRECISION","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"token","type":"address"}],"name":"balance","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"int24","name":"tick","type":"int24"},{"internalType":"int24","name":"tickRange","type":"int24"},{"internalType":"int24","name":"tickSpacing","type":"int24"}],"name":"calcFillUpBaseTicks","outputs":[{"internalType":"int24","name":"lowerTick","type":"int24"},{"internalType":"int24","name":"upperTick","type":"int24"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"},{"internalType":"uint32","name":"twapInterval","type":"uint32"},{"internalType":"uint256","name":"priceThreshold","type":"uint256"}],"name":"checkPriceChange","outputs":[],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"algoId","type":"uint256"}],"name":"getAlgoNamyById","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"uint256","name":"algoId","type":"uint256"},{"internalType":"int24[]","name":"params","type":"int24[]"}],"name":"getPresetNameByAlgoAndParams","outputs":[{"internalType":"string","name":"","type":"string"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"getUniswapV3CurrentTick","outputs":[{"internalType":"int24","name":"tick","type":"int24"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"getUniswapV3PoolPrice","outputs":[{"internalType":"uint256","name":"price","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"pool","type":"address"}],"name":"getUniswapV3TickSpacing","outputs":[{"internalType":"int24","name":"tickSpacing","type":"int24"}],"stateMutability":"view","type":"function"}]

6125d6610035600b8282823980515f1a60731461002957634e487b7160e01b5f525f60045260245ffd5b305f52607381538281f3fe73000000000000000000000000000000000000000030146080604052600436106100fb575f3560e01c806398b700051161009e578063aaf5eb681161006e578063aaf5eb6814610268578063be6317e41461027d578063dd60934714610290578063e3d670d714610297575f80fd5b806398b70005146101e4578063a14c1fd314610211578063a38f374814610224578063aa31ecc314610245575f80fd5b806352906146116100d957806352906146146101665780637a7d493e1461018957806388c829ee146101a957806393708485146101cf575f80fd5b80632895a3bb146100ff5780632a2dfd621461012557806350071adf14610146575b5f80fd5b61011261010d366004611adf565b6102aa565b6040519081526020015b60405180910390f35b610138610133366004611b62565b6103cf565b60405161011c929190611c42565b610159610154366004611c63565b610af1565b60405161011c9190611c8c565b610179610174366004611c9e565b610db7565b60405161011c9493929190611cf8565b61019c610197366004611c9e565b610e4b565b60405161011c9190611d6c565b6101bc6101b7366004611adf565b610e98565b60405160029190910b815260200161011c565b6101e26101dd366004611d7e565b610f06565b005b6101f76101f2366004611dd2565b611160565b60408051600293840b81529190920b60208201520161011c565b61019c61021f366004611e1a565b611218565b610237610232366004611c63565b6113d5565b60405161011c929190611ebb565b610258610253366004611f1b565b611699565b604051901515815260200161011c565b6101126a0c097ce7bc90715b34b9f160241b81565b6101bc61028b366004611adf565b6117c9565b6101125f81565b6101126102a5366004611adf565b61182a565b5f80826001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa1580156102e8573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061030c9190611f51565b505050505050905073bbc63ee4a06bf1f2432ccc4d70103e3d465fca3963aa9a0912826001600160a01b0316836001600160a01b031661034c9190611ffb565b6040516001600160e01b031960e084901b16815260048101919091526a0c097ce7bc90715b34b9f160241b6024820152600160c01b6044820152606401602060405180830381865af41580156103a4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103c89190612012565b9392505050565b82546060905f90610ae857600184015484546001600160a01b0391821691165f6103f8826102aa565b905085600101545f0361059b575f61040f83610e98565b6040805160028082526060820183529293505f929091602083019080368337019050509050610477828b6001015f8154811061044d5761044d612029565b905f5260205f2090600a91828204019190066003029054906101000a900460020b6101f2876117c9565b825f8151811061048957610489612029565b60200260200101836001815181106104a3576104a3612029565b600293840b602091820292909201015291900b90526040516302bd797360e01b81526001600160a01b038616906302bd7973906104e89087908f90869060040161206f565b5f60405180830381865afa158015610502573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610529919081019061213e565b9050809750506a0c097ce7bc90715b34b9f160241b83885f8151811061055157610551612029565b60200260200101516105639190611ffb565b61056d9190612196565b8760018151811061058057610580612029565b602002602001015161059291906121a9565b95505050610ae4565b6002888101546040805183815260608101825291925f92916020830190803683370190505090505f8a6002015f815481106105d8576105d8612029565b5f9182526020808320604080516080810182526002948502909201805483526001015480850b9383018490526301000000810490940b90820152600160301b9092046001600160801b03166060830152845191935091849161063c5761063c612029565b602002602001019060020b908160020b8152505080604001518260018151811061066857610668612029565b602002602001019060020b908160020b81525050856001600160a01b03166302bd7973868e856040518463ffffffff1660e01b81526004016106ac9392919061206f565b5f60405180830381865afa1580156106c6573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526106ed919081019061213e565b9850506002839003610990576040805160028082526060820183525f92602083019080368337019050509050885f8151811061072b5761072b612029565b60200260200101518d5f8151811061074557610745612029565b602002602001015161075791906121bc565b815f8151811061076957610769612029565b6020026020010181815250508860018151811061078857610788612029565b60200260200101518d6001815181106107a3576107a3612029565b60200260200101516107b591906121bc565b816001815181106107c8576107c8612029565b6020026020010181815250508b6002016001815481106107ea576107ea612029565b5f9182526020808320604080516080810182526002948502909201805483526001015480850b9383018490526301000000810490940b90820152600160301b9092046001600160801b03166060830152855191945091859161084e5761084e612029565b602002602001019060020b908160020b8152505081604001518360018151811061087a5761087a612029565b602002602001019060020b908160020b815250505f876001600160a01b03166302bd79738884876040518463ffffffff1660e01b81526004016108bf9392919061206f565b5f60405180830381865afa1580156108d9573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610900919081019061213e565b915050805f8151811061091557610915612029565b60200260200101518a5f8151811061092f5761092f612029565b6020026020010181815161094391906121a9565b90525080518190600190811061095b5761095b612029565b60200260200101518a60018151811061097657610976612029565b6020026020010181815161098a91906121a9565b90525050505b6a0c097ce7bc90715b34b9f160241b84895f815181106109b2576109b2612029565b60200260200101516109c49190611ffb565b6109ce9190612196565b886001815181106109e1576109e1612029565b60200260200101516109f391906121a9565b96505f306001600160a01b03166359498ab76040518163ffffffff1660e01b81526004015f60405180830381865afa158015610a31573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610a5891908101906121cf565b9150505f6a0c097ce7bc90715b34b9f160241b86835f81518110610a7e57610a7e612029565b6020026020010151610a909190611ffb565b610a9a9190612196565b82600181518110610aad57610aad612029565b6020026020010151610abf91906121a9565b9050808b600101548a610ad29190611ffb565b610adc9190612196565b985050505050505b5050505b94509492505050565b60608082600101545f14610b1357610b0a8585856113d5565b9150610c9e9050565b600184015484546040805160028082526060820183526001600160a01b0394851694909316925f9260208301908036833750506040805160028082526060820183529394505f939092509060208301908036833701905050905068056bc75e2d63100000815f81518110610b8957610b89612029565b60200260200101818152505068056bc75e2d6310000081600181518110610bb257610bb2612029565b6020026020010181815250505f610bc884610e98565b9050610be3818b6001015f8154811061044d5761044d612029565b845f81518110610bf557610bf5612029565b6020026020010185600181518110610c0f57610c0f612029565b600293840b602091820292909201015291900b90526040516302bd797360e01b81526001600160a01b038616906302bd797390610c549087908690889060040161206f565b5f60405180830381865afa158015610c6e573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610c95919081019061213e565b96505050505050505b83545f90610cb4906001600160a01b03166102aa565b90505f6a0c097ce7bc90715b34b9f160241b82845f81518110610cd957610cd9612029565b6020026020010151610ceb9190611ffb565b610cf59190612196565b90505f83600181518110610d0b57610d0b612029565b602002602001015182610d1e91906121a9565b610d3083670de0b6b3a7640000611ffb565b610d3a9190612196565b604080516002808252606082018352929350919060208301908036833701905050945080855f81518110610d7057610d70612029565b6020908102919091010152610d8d81670de0b6b3a76400006121bc565b85600181518110610da057610da0612029565b602002602001018181525050505050509392505050565b805460608080610dc684610e4b565b60018601805460408051602080840282018101909252828152939650830182828015610e3157602002820191905f5260205f20905f905b825461010083900a900460020b8152602060058301819004938401936001036003909301929092029101808411610dfd5790505b50505050509050610e428482611218565b91509193509193565b606081610e75575050604080518082019091526007815266046696c6c2d55760cc1b602082015290565b50506040805180820190915260078152662ab735b737bbb760c91b602082015290565b5f816001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015610ed5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ef99190611f51565b5093979650505050505050565b5f836001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015610f43573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f679190611f51565b50505050505090505f73bbc63ee4a06bf1f2432ccc4d70103e3d465fca3963aa9a0912836001600160a01b0316846001600160a01b0316610fa89190611ffb565b6040516001600160e01b031960e084901b16815260048101919091526a0c097ce7bc90715b34b9f160241b6024820152600160c01b6044820152606401602060405180830381865af4158015611000573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110249190612012565b90505f6110318686611892565b90505f73bbc63ee4a06bf1f2432ccc4d70103e3d465fca3963aa9a09126110616001600160a01b03851680611ffb565b6040516001600160e01b031960e084901b16815260048101919091526a0c097ce7bc90715b34b9f160241b6024820152600160c01b6044820152606401602060405180830381865af41580156110b9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110dd9190612012565b905084816110ed85612710611ffb565b6110f79190612196565b11806111185750848361110c83612710611ffb565b6111169190612196565b115b15611157576040516381aaaa3b60e01b815260048101849052602481018290526044810186905263ffffffff8716606482015260840160405180910390fd5b50505050505050565b5f808061116e600286612284565b90505f8660020b12801561119d5750600286900b8461118d8189612284565b61119791906122bc565b60020b14155b156111d557836001816111b0848a6122e2565b6111ba9190612284565b6111c491906122e2565b6111ce91906122bc565b92506111f8565b83806111e183896122e2565b6111eb9190612284565b6111f591906122bc565b92505b6112038160026122bc565b61120d9084612307565b915050935093915050565b6060826113af57612710825f8151811061123457611234612029565b602002602001015160020b1261126a575060408051808201909152600981526814dd1c995d18da195960ba1b60208201526113cf565b610fa0825f8151811061127f5761127f612029565b602002602001015160020b126112b357506040805180820190915260078152665061737369766560c81b60208201526113cf565b6107d0825f815181106112c8576112c8612029565b602002602001015160020b126112f957506040805180820190915260048152635769646560e01b60208201526113cf565b6103e8825f8151811061130e5761130e612029565b602002602001015160020b1261134157506040805180820190915260068152654e6172726f7760d01b60208201526113cf565b6064825f8151811061135557611355612029565b602002602001015160020b1261138c575060408051808201909152600a8152694167677265737369766560b01b60208201526113cf565b50604080518082019091526006815265496e73616e6560d01b60208201526113cf565b506040805180820190915260078152662ab735b737bbb760c91b60208201525b92915050565b600182015482546005830180546040805160208084028201810190925282815260609586956001600160a01b0391821695911693919290919083018282801561144557602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611427575b509397506002935061145692505050565b60405190808252806020026020018201604052801561147f578160200160208202803683370190505b5060028801549093505f5b8181101561168d575f8960020182815481106114a8576114a8612029565b5f9182526020808320604080516080810182526002948502909201805483526001015480850b9383019390935263010000008304840b90820152600160301b9091046001600160801b031660608201529250604051908082528060200260200182016040528015611523578160200160208202803683370190505b5090508160200151815f8151811061153d5761153d612029565b602002602001019060020b908160020b8152505081604001518160018151811061156957611569612029565b60029290920b6020928302919091019091015260608201516040516351c17eaf60e01b81525f916001600160a01b038916916351c17eaf916115b1918a91879160040161232c565b5f60405180830381865afa1580156115cb573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526115f29190810190612368565b9050805f8151811061160657611606612029565b6020026020010151885f8151811061162057611620612029565b6020026020010181815161163491906121a9565b90525080518190600190811061164c5761164c612029565b60200260200101518860018151811061166757611667612029565b6020026020010181815161167b91906121a9565b905250506001909201915061148a9050565b50505050935093915050565b81545f906113cf5760028301545f8190036116b7575f9150506113cf565b5f6002856001015f815481106116cf576116cf612029565b905f5260205f2090600a91828204019190066003029054906101000a900460020b6116fa9190612284565b90505f60028660010160018154811061171557611715612029565b905f5260205f2090600a91828204019190066003029054906101000a900460020b6117409190612284565b90505f82876002015f8154811061175957611759612029565b5f91825260209091206002918202016001015461177792910b612307565b86549091505f90611790906001600160a01b0316610e98565b905061179c8383612307565b60020b8160020b13806117bd57506117b483836122e2565b60020b8160020b125b955050505050506113cf565b5f816001600160a01b031663d0c93a7c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611806573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113cf91906123a2565b6040516370a0823160e01b81523060048201525f906001600160a01b038316906370a0823190602401602060405180830381865afa15801561186e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113cf9190612012565b5f8163ffffffff165f0361190f57826001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa1580156118dc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119009190611f51565b509495506113cf945050505050565b6040805160028082526060820183525f9260208301908036833701905050905082815f8151811061194257611942612029565b602002602001019063ffffffff16908163ffffffff16815250505f8160018151811061197057611970612029565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81525f906001600160a01b0386169063883bdbfd906119b39085906004016123bd565b5f60405180830381865afa1580156119cd573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526119f4919081019061246b565b50905073bbc63ee4a06bf1f2432ccc4d70103e3d465fca3963986cfba38560030b835f81518110611a2757611a27612029565b602002602001015184600181518110611a4257611a42612029565b6020026020010151611a549190612525565b611a5e9190612552565b6040516001600160e01b031960e084901b16815260029190910b6004820152602401602060405180830381865af4158015611a9b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611abf9190612585565b95945050505050565b6001600160a01b0381168114611adc575f80fd5b50565b5f60208284031215611aef575f80fd5b81356103c881611ac8565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715611b3757611b37611afa565b604052919050565b5f67ffffffffffffffff821115611b5857611b58611afa565b5060051b60200190565b5f805f8060808587031215611b75575f80fd5b843567ffffffffffffffff811115611b8b575f80fd5b8501601f81018713611b9b575f80fd5b80356020611bb0611bab83611b3f565b611b0e565b82815260059290921b8301810191818101908a841115611bce575f80fd5b938201935b83851015611bec57843582529382019390820190611bd3565b9a91890135995050604088013597606001359650945050505050565b5f815180845260208085019450602084015f5b83811015611c3757815187529582019590820190600101611c1b565b509495945050505050565b604081525f611c546040830185611c08565b90508260208301529392505050565b5f805f60608486031215611c75575f80fd5b505081359360208301359350604090920135919050565b602081525f6103c86020830184611c08565b5f60208284031215611cae575f80fd5b5035919050565b5f81518084525f5b81811015611cd957602081850181015186830182015201611cbd565b505f602082860101526020601f19601f83011685010191505092915050565b8481525f602060806020840152611d126080840187611cb5565b8381036040850152611d248187611cb5565b8481036060860152855180825260208088019350909101905f5b81811015611d5d57835160020b83529284019291840191600101611d3e565b50909998505050505050505050565b602081525f6103c86020830184611cb5565b5f805f60608486031215611d90575f80fd5b8335611d9b81611ac8565b9250602084013563ffffffff81168114611db3575f80fd5b929592945050506040919091013590565b8060020b8114611adc575f80fd5b5f805f60608486031215611de4575f80fd5b8335611def81611dc4565b92506020840135611dff81611dc4565b91506040840135611e0f81611dc4565b809150509250925092565b5f8060408385031215611e2b575f80fd5b8235915060208084013567ffffffffffffffff811115611e49575f80fd5b8401601f81018613611e59575f80fd5b8035611e67611bab82611b3f565b81815260059190911b82018301908381019088831115611e85575f80fd5b928401925b82841015611eac578335611e9d81611dc4565b82529284019290840190611e8a565b80955050505050509250929050565b604080825283519082018190525f906020906060840190828701845b82811015611efc5781516001600160a01b031684529284019290840190600101611ed7565b5050508381036020850152611f118186611c08565b9695505050505050565b5f8060408385031215611f2c575f80fd5b50508035926020909101359150565b805161ffff81168114611f4c575f80fd5b919050565b5f805f805f805f60e0888a031215611f67575f80fd5b8751611f7281611ac8565b6020890151909750611f8381611dc4565b9550611f9160408901611f3b565b9450611f9f60608901611f3b565b9350611fad60808901611f3b565b925060a088015160ff81168114611fc2575f80fd5b60c08901519092508015158114611fd7575f80fd5b8091505092959891949750929550565b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176113cf576113cf611fe7565b5f60208284031215612022575f80fd5b5051919050565b634e487b7160e01b5f52603260045260245ffd5b5f815180845260208085019450602084015f5b83811015611c3757815160020b87529582019590820190600101612050565b6001600160a01b03841681526060602080830182905284519183018290525f9185820191906080850190845b818110156120b75784518352938301939183019160010161209b565b505084810360408601526120cb818761203d565b98975050505050505050565b5f82601f8301126120e6575f80fd5b815160206120f6611bab83611b3f565b8083825260208201915060208460051b870101935086841115612117575f80fd5b602086015b84811015612133578051835291830191830161211c565b509695505050505050565b5f806040838503121561214f575f80fd5b82519150602083015167ffffffffffffffff81111561216c575f80fd5b612178858286016120d7565b9150509250929050565b634e487b7160e01b5f52601260045260245ffd5b5f826121a4576121a4612182565b500490565b808201808211156113cf576113cf611fe7565b818103818111156113cf576113cf611fe7565b5f80604083850312156121e0575f80fd5b825167ffffffffffffffff808211156121f7575f80fd5b818501915085601f83011261220a575f80fd5b8151602061221a611bab83611b3f565b82815260059290921b84018101918181019089841115612238575f80fd5b948201945b8386101561225f57855161225081611ac8565b8252948201949082019061223d565b91880151919650909350505080821115612277575f80fd5b50612178858286016120d7565b5f8160020b8360020b8061229a5761229a612182565b627fffff1982145f19821416156122b3576122b3611fe7565b90059392505050565b5f8260020b8260020b028060020b91508082146122db576122db611fe7565b5092915050565b600282810b9082900b03627fffff198112627fffff821317156113cf576113cf611fe7565b600281810b9083900b01627fffff8113627fffff19821217156113cf576113cf611fe7565b6001600160a01b03841681526060602082018190525f9061234f9083018561203d565b90506001600160801b0383166040830152949350505050565b5f60208284031215612378575f80fd5b815167ffffffffffffffff81111561238e575f80fd5b61239a848285016120d7565b949350505050565b5f602082840312156123b2575f80fd5b81516103c881611dc4565b602080825282518282018190525f9190848201906040850190845b818110156123fa57835163ffffffff16835292840192918401916001016123d8565b50909695505050505050565b5f82601f830112612415575f80fd5b81516020612425611bab83611b3f565b8083825260208201915060208460051b870101935086841115612446575f80fd5b602086015b8481101561213357805161245e81611ac8565b835291830191830161244b565b5f806040838503121561247c575f80fd5b825167ffffffffffffffff80821115612493575f80fd5b818501915085601f8301126124a6575f80fd5b815160206124b6611bab83611b3f565b82815260059290921b840181019181810190898411156124d4575f80fd5b948201945b838610156125005785518060060b81146124f1575f80fd5b825294820194908201906124d9565b91880151919650909350505080821115612518575f80fd5b5061217885828601612406565b600682810b9082900b03667fffffffffffff198112667fffffffffffff821317156113cf576113cf611fe7565b5f8160060b8360060b8061256857612568612182565b667fffffffffffff1982145f19821416156122b3576122b3611fe7565b5f60208284031215612595575f80fd5b81516103c881611ac856fea264697066735822122013268f06ab911b5017e3641521b4f0aa237e48a545b96bc2bc0f15a9f05f783364736f6c63430008170033

Deployed Bytecode

0x732ed4d0bbd7f16ed33269243eb3fe993bd5561e9330146080604052600436106100fb575f3560e01c806398b700051161009e578063aaf5eb681161006e578063aaf5eb6814610268578063be6317e41461027d578063dd60934714610290578063e3d670d714610297575f80fd5b806398b70005146101e4578063a14c1fd314610211578063a38f374814610224578063aa31ecc314610245575f80fd5b806352906146116100d957806352906146146101665780637a7d493e1461018957806388c829ee146101a957806393708485146101cf575f80fd5b80632895a3bb146100ff5780632a2dfd621461012557806350071adf14610146575b5f80fd5b61011261010d366004611adf565b6102aa565b6040519081526020015b60405180910390f35b610138610133366004611b62565b6103cf565b60405161011c929190611c42565b610159610154366004611c63565b610af1565b60405161011c9190611c8c565b610179610174366004611c9e565b610db7565b60405161011c9493929190611cf8565b61019c610197366004611c9e565b610e4b565b60405161011c9190611d6c565b6101bc6101b7366004611adf565b610e98565b60405160029190910b815260200161011c565b6101e26101dd366004611d7e565b610f06565b005b6101f76101f2366004611dd2565b611160565b60408051600293840b81529190920b60208201520161011c565b61019c61021f366004611e1a565b611218565b610237610232366004611c63565b6113d5565b60405161011c929190611ebb565b610258610253366004611f1b565b611699565b604051901515815260200161011c565b6101126a0c097ce7bc90715b34b9f160241b81565b6101bc61028b366004611adf565b6117c9565b6101125f81565b6101126102a5366004611adf565b61182a565b5f80826001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa1580156102e8573d5f803e3d5ffd5b505050506040513d601f19601f8201168201806040525081019061030c9190611f51565b505050505050905073bbc63ee4a06bf1f2432ccc4d70103e3d465fca3963aa9a0912826001600160a01b0316836001600160a01b031661034c9190611ffb565b6040516001600160e01b031960e084901b16815260048101919091526a0c097ce7bc90715b34b9f160241b6024820152600160c01b6044820152606401602060405180830381865af41580156103a4573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906103c89190612012565b9392505050565b82546060905f90610ae857600184015484546001600160a01b0391821691165f6103f8826102aa565b905085600101545f0361059b575f61040f83610e98565b6040805160028082526060820183529293505f929091602083019080368337019050509050610477828b6001015f8154811061044d5761044d612029565b905f5260205f2090600a91828204019190066003029054906101000a900460020b6101f2876117c9565b825f8151811061048957610489612029565b60200260200101836001815181106104a3576104a3612029565b600293840b602091820292909201015291900b90526040516302bd797360e01b81526001600160a01b038616906302bd7973906104e89087908f90869060040161206f565b5f60405180830381865afa158015610502573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610529919081019061213e565b9050809750506a0c097ce7bc90715b34b9f160241b83885f8151811061055157610551612029565b60200260200101516105639190611ffb565b61056d9190612196565b8760018151811061058057610580612029565b602002602001015161059291906121a9565b95505050610ae4565b6002888101546040805183815260608101825291925f92916020830190803683370190505090505f8a6002015f815481106105d8576105d8612029565b5f9182526020808320604080516080810182526002948502909201805483526001015480850b9383018490526301000000810490940b90820152600160301b9092046001600160801b03166060830152845191935091849161063c5761063c612029565b602002602001019060020b908160020b8152505080604001518260018151811061066857610668612029565b602002602001019060020b908160020b81525050856001600160a01b03166302bd7973868e856040518463ffffffff1660e01b81526004016106ac9392919061206f565b5f60405180830381865afa1580156106c6573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526106ed919081019061213e565b9850506002839003610990576040805160028082526060820183525f92602083019080368337019050509050885f8151811061072b5761072b612029565b60200260200101518d5f8151811061074557610745612029565b602002602001015161075791906121bc565b815f8151811061076957610769612029565b6020026020010181815250508860018151811061078857610788612029565b60200260200101518d6001815181106107a3576107a3612029565b60200260200101516107b591906121bc565b816001815181106107c8576107c8612029565b6020026020010181815250508b6002016001815481106107ea576107ea612029565b5f9182526020808320604080516080810182526002948502909201805483526001015480850b9383018490526301000000810490940b90820152600160301b9092046001600160801b03166060830152855191945091859161084e5761084e612029565b602002602001019060020b908160020b8152505081604001518360018151811061087a5761087a612029565b602002602001019060020b908160020b815250505f876001600160a01b03166302bd79738884876040518463ffffffff1660e01b81526004016108bf9392919061206f565b5f60405180830381865afa1580156108d9573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610900919081019061213e565b915050805f8151811061091557610915612029565b60200260200101518a5f8151811061092f5761092f612029565b6020026020010181815161094391906121a9565b90525080518190600190811061095b5761095b612029565b60200260200101518a60018151811061097657610976612029565b6020026020010181815161098a91906121a9565b90525050505b6a0c097ce7bc90715b34b9f160241b84895f815181106109b2576109b2612029565b60200260200101516109c49190611ffb565b6109ce9190612196565b886001815181106109e1576109e1612029565b60200260200101516109f391906121a9565b96505f306001600160a01b03166359498ab76040518163ffffffff1660e01b81526004015f60405180830381865afa158015610a31573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610a5891908101906121cf565b9150505f6a0c097ce7bc90715b34b9f160241b86835f81518110610a7e57610a7e612029565b6020026020010151610a909190611ffb565b610a9a9190612196565b82600181518110610aad57610aad612029565b6020026020010151610abf91906121a9565b9050808b600101548a610ad29190611ffb565b610adc9190612196565b985050505050505b5050505b94509492505050565b60608082600101545f14610b1357610b0a8585856113d5565b9150610c9e9050565b600184015484546040805160028082526060820183526001600160a01b0394851694909316925f9260208301908036833750506040805160028082526060820183529394505f939092509060208301908036833701905050905068056bc75e2d63100000815f81518110610b8957610b89612029565b60200260200101818152505068056bc75e2d6310000081600181518110610bb257610bb2612029565b6020026020010181815250505f610bc884610e98565b9050610be3818b6001015f8154811061044d5761044d612029565b845f81518110610bf557610bf5612029565b6020026020010185600181518110610c0f57610c0f612029565b600293840b602091820292909201015291900b90526040516302bd797360e01b81526001600160a01b038616906302bd797390610c549087908690889060040161206f565b5f60405180830381865afa158015610c6e573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f19168201604052610c95919081019061213e565b96505050505050505b83545f90610cb4906001600160a01b03166102aa565b90505f6a0c097ce7bc90715b34b9f160241b82845f81518110610cd957610cd9612029565b6020026020010151610ceb9190611ffb565b610cf59190612196565b90505f83600181518110610d0b57610d0b612029565b602002602001015182610d1e91906121a9565b610d3083670de0b6b3a7640000611ffb565b610d3a9190612196565b604080516002808252606082018352929350919060208301908036833701905050945080855f81518110610d7057610d70612029565b6020908102919091010152610d8d81670de0b6b3a76400006121bc565b85600181518110610da057610da0612029565b602002602001018181525050505050509392505050565b805460608080610dc684610e4b565b60018601805460408051602080840282018101909252828152939650830182828015610e3157602002820191905f5260205f20905f905b825461010083900a900460020b8152602060058301819004938401936001036003909301929092029101808411610dfd5790505b50505050509050610e428482611218565b91509193509193565b606081610e75575050604080518082019091526007815266046696c6c2d55760cc1b602082015290565b50506040805180820190915260078152662ab735b737bbb760c91b602082015290565b5f816001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015610ed5573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610ef99190611f51565b5093979650505050505050565b5f836001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa158015610f43573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190610f679190611f51565b50505050505090505f73bbc63ee4a06bf1f2432ccc4d70103e3d465fca3963aa9a0912836001600160a01b0316846001600160a01b0316610fa89190611ffb565b6040516001600160e01b031960e084901b16815260048101919091526a0c097ce7bc90715b34b9f160241b6024820152600160c01b6044820152606401602060405180830381865af4158015611000573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110249190612012565b90505f6110318686611892565b90505f73bbc63ee4a06bf1f2432ccc4d70103e3d465fca3963aa9a09126110616001600160a01b03851680611ffb565b6040516001600160e01b031960e084901b16815260048101919091526a0c097ce7bc90715b34b9f160241b6024820152600160c01b6044820152606401602060405180830381865af41580156110b9573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906110dd9190612012565b905084816110ed85612710611ffb565b6110f79190612196565b11806111185750848361110c83612710611ffb565b6111169190612196565b115b15611157576040516381aaaa3b60e01b815260048101849052602481018290526044810186905263ffffffff8716606482015260840160405180910390fd5b50505050505050565b5f808061116e600286612284565b90505f8660020b12801561119d5750600286900b8461118d8189612284565b61119791906122bc565b60020b14155b156111d557836001816111b0848a6122e2565b6111ba9190612284565b6111c491906122e2565b6111ce91906122bc565b92506111f8565b83806111e183896122e2565b6111eb9190612284565b6111f591906122bc565b92505b6112038160026122bc565b61120d9084612307565b915050935093915050565b6060826113af57612710825f8151811061123457611234612029565b602002602001015160020b1261126a575060408051808201909152600981526814dd1c995d18da195960ba1b60208201526113cf565b610fa0825f8151811061127f5761127f612029565b602002602001015160020b126112b357506040805180820190915260078152665061737369766560c81b60208201526113cf565b6107d0825f815181106112c8576112c8612029565b602002602001015160020b126112f957506040805180820190915260048152635769646560e01b60208201526113cf565b6103e8825f8151811061130e5761130e612029565b602002602001015160020b1261134157506040805180820190915260068152654e6172726f7760d01b60208201526113cf565b6064825f8151811061135557611355612029565b602002602001015160020b1261138c575060408051808201909152600a8152694167677265737369766560b01b60208201526113cf565b50604080518082019091526006815265496e73616e6560d01b60208201526113cf565b506040805180820190915260078152662ab735b737bbb760c91b60208201525b92915050565b600182015482546005830180546040805160208084028201810190925282815260609586956001600160a01b0391821695911693919290919083018282801561144557602002820191905f5260205f20905b81546001600160a01b03168152600190910190602001808311611427575b509397506002935061145692505050565b60405190808252806020026020018201604052801561147f578160200160208202803683370190505b5060028801549093505f5b8181101561168d575f8960020182815481106114a8576114a8612029565b5f9182526020808320604080516080810182526002948502909201805483526001015480850b9383019390935263010000008304840b90820152600160301b9091046001600160801b031660608201529250604051908082528060200260200182016040528015611523578160200160208202803683370190505b5090508160200151815f8151811061153d5761153d612029565b602002602001019060020b908160020b8152505081604001518160018151811061156957611569612029565b60029290920b6020928302919091019091015260608201516040516351c17eaf60e01b81525f916001600160a01b038916916351c17eaf916115b1918a91879160040161232c565b5f60405180830381865afa1580156115cb573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526115f29190810190612368565b9050805f8151811061160657611606612029565b6020026020010151885f8151811061162057611620612029565b6020026020010181815161163491906121a9565b90525080518190600190811061164c5761164c612029565b60200260200101518860018151811061166757611667612029565b6020026020010181815161167b91906121a9565b905250506001909201915061148a9050565b50505050935093915050565b81545f906113cf5760028301545f8190036116b7575f9150506113cf565b5f6002856001015f815481106116cf576116cf612029565b905f5260205f2090600a91828204019190066003029054906101000a900460020b6116fa9190612284565b90505f60028660010160018154811061171557611715612029565b905f5260205f2090600a91828204019190066003029054906101000a900460020b6117409190612284565b90505f82876002015f8154811061175957611759612029565b5f91825260209091206002918202016001015461177792910b612307565b86549091505f90611790906001600160a01b0316610e98565b905061179c8383612307565b60020b8160020b13806117bd57506117b483836122e2565b60020b8160020b125b955050505050506113cf565b5f816001600160a01b031663d0c93a7c6040518163ffffffff1660e01b8152600401602060405180830381865afa158015611806573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113cf91906123a2565b6040516370a0823160e01b81523060048201525f906001600160a01b038316906370a0823190602401602060405180830381865afa15801561186e573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906113cf9190612012565b5f8163ffffffff165f0361190f57826001600160a01b0316633850c7bd6040518163ffffffff1660e01b815260040160e060405180830381865afa1580156118dc573d5f803e3d5ffd5b505050506040513d601f19601f820116820180604052508101906119009190611f51565b509495506113cf945050505050565b6040805160028082526060820183525f9260208301908036833701905050905082815f8151811061194257611942612029565b602002602001019063ffffffff16908163ffffffff16815250505f8160018151811061197057611970612029565b63ffffffff9092166020928302919091019091015260405163883bdbfd60e01b81525f906001600160a01b0386169063883bdbfd906119b39085906004016123bd565b5f60405180830381865afa1580156119cd573d5f803e3d5ffd5b505050506040513d5f823e601f3d908101601f191682016040526119f4919081019061246b565b50905073bbc63ee4a06bf1f2432ccc4d70103e3d465fca3963986cfba38560030b835f81518110611a2757611a27612029565b602002602001015184600181518110611a4257611a42612029565b6020026020010151611a549190612525565b611a5e9190612552565b6040516001600160e01b031960e084901b16815260029190910b6004820152602401602060405180830381865af4158015611a9b573d5f803e3d5ffd5b505050506040513d601f19601f82011682018060405250810190611abf9190612585565b95945050505050565b6001600160a01b0381168114611adc575f80fd5b50565b5f60208284031215611aef575f80fd5b81356103c881611ac8565b634e487b7160e01b5f52604160045260245ffd5b604051601f8201601f1916810167ffffffffffffffff81118282101715611b3757611b37611afa565b604052919050565b5f67ffffffffffffffff821115611b5857611b58611afa565b5060051b60200190565b5f805f8060808587031215611b75575f80fd5b843567ffffffffffffffff811115611b8b575f80fd5b8501601f81018713611b9b575f80fd5b80356020611bb0611bab83611b3f565b611b0e565b82815260059290921b8301810191818101908a841115611bce575f80fd5b938201935b83851015611bec57843582529382019390820190611bd3565b9a91890135995050604088013597606001359650945050505050565b5f815180845260208085019450602084015f5b83811015611c3757815187529582019590820190600101611c1b565b509495945050505050565b604081525f611c546040830185611c08565b90508260208301529392505050565b5f805f60608486031215611c75575f80fd5b505081359360208301359350604090920135919050565b602081525f6103c86020830184611c08565b5f60208284031215611cae575f80fd5b5035919050565b5f81518084525f5b81811015611cd957602081850181015186830182015201611cbd565b505f602082860101526020601f19601f83011685010191505092915050565b8481525f602060806020840152611d126080840187611cb5565b8381036040850152611d248187611cb5565b8481036060860152855180825260208088019350909101905f5b81811015611d5d57835160020b83529284019291840191600101611d3e565b50909998505050505050505050565b602081525f6103c86020830184611cb5565b5f805f60608486031215611d90575f80fd5b8335611d9b81611ac8565b9250602084013563ffffffff81168114611db3575f80fd5b929592945050506040919091013590565b8060020b8114611adc575f80fd5b5f805f60608486031215611de4575f80fd5b8335611def81611dc4565b92506020840135611dff81611dc4565b91506040840135611e0f81611dc4565b809150509250925092565b5f8060408385031215611e2b575f80fd5b8235915060208084013567ffffffffffffffff811115611e49575f80fd5b8401601f81018613611e59575f80fd5b8035611e67611bab82611b3f565b81815260059190911b82018301908381019088831115611e85575f80fd5b928401925b82841015611eac578335611e9d81611dc4565b82529284019290840190611e8a565b80955050505050509250929050565b604080825283519082018190525f906020906060840190828701845b82811015611efc5781516001600160a01b031684529284019290840190600101611ed7565b5050508381036020850152611f118186611c08565b9695505050505050565b5f8060408385031215611f2c575f80fd5b50508035926020909101359150565b805161ffff81168114611f4c575f80fd5b919050565b5f805f805f805f60e0888a031215611f67575f80fd5b8751611f7281611ac8565b6020890151909750611f8381611dc4565b9550611f9160408901611f3b565b9450611f9f60608901611f3b565b9350611fad60808901611f3b565b925060a088015160ff81168114611fc2575f80fd5b60c08901519092508015158114611fd7575f80fd5b8091505092959891949750929550565b634e487b7160e01b5f52601160045260245ffd5b80820281158282048414176113cf576113cf611fe7565b5f60208284031215612022575f80fd5b5051919050565b634e487b7160e01b5f52603260045260245ffd5b5f815180845260208085019450602084015f5b83811015611c3757815160020b87529582019590820190600101612050565b6001600160a01b03841681526060602080830182905284519183018290525f9185820191906080850190845b818110156120b75784518352938301939183019160010161209b565b505084810360408601526120cb818761203d565b98975050505050505050565b5f82601f8301126120e6575f80fd5b815160206120f6611bab83611b3f565b8083825260208201915060208460051b870101935086841115612117575f80fd5b602086015b84811015612133578051835291830191830161211c565b509695505050505050565b5f806040838503121561214f575f80fd5b82519150602083015167ffffffffffffffff81111561216c575f80fd5b612178858286016120d7565b9150509250929050565b634e487b7160e01b5f52601260045260245ffd5b5f826121a4576121a4612182565b500490565b808201808211156113cf576113cf611fe7565b818103818111156113cf576113cf611fe7565b5f80604083850312156121e0575f80fd5b825167ffffffffffffffff808211156121f7575f80fd5b818501915085601f83011261220a575f80fd5b8151602061221a611bab83611b3f565b82815260059290921b84018101918181019089841115612238575f80fd5b948201945b8386101561225f57855161225081611ac8565b8252948201949082019061223d565b91880151919650909350505080821115612277575f80fd5b50612178858286016120d7565b5f8160020b8360020b8061229a5761229a612182565b627fffff1982145f19821416156122b3576122b3611fe7565b90059392505050565b5f8260020b8260020b028060020b91508082146122db576122db611fe7565b5092915050565b600282810b9082900b03627fffff198112627fffff821317156113cf576113cf611fe7565b600281810b9083900b01627fffff8113627fffff19821217156113cf576113cf611fe7565b6001600160a01b03841681526060602082018190525f9061234f9083018561203d565b90506001600160801b0383166040830152949350505050565b5f60208284031215612378575f80fd5b815167ffffffffffffffff81111561238e575f80fd5b61239a848285016120d7565b949350505050565b5f602082840312156123b2575f80fd5b81516103c881611dc4565b602080825282518282018190525f9190848201906040850190845b818110156123fa57835163ffffffff16835292840192918401916001016123d8565b50909695505050505050565b5f82601f830112612415575f80fd5b81516020612425611bab83611b3f565b8083825260208201915060208460051b870101935086841115612446575f80fd5b602086015b8481101561213357805161245e81611ac8565b835291830191830161244b565b5f806040838503121561247c575f80fd5b825167ffffffffffffffff80821115612493575f80fd5b818501915085601f8301126124a6575f80fd5b815160206124b6611bab83611b3f565b82815260059290921b840181019181810190898411156124d4575f80fd5b948201945b838610156125005785518060060b81146124f1575f80fd5b825294820194908201906124d9565b91880151919650909350505080821115612518575f80fd5b5061217885828601612406565b600682810b9082900b03667fffffffffffff198112667fffffffffffff821317156113cf576113cf611fe7565b5f8160060b8360060b8061256857612568612182565b667fffffffffffff1982145f19821416156122b3576122b3611fe7565b5f60208284031215612595575f80fd5b81516103c881611ac856fea264697066735822122013268f06ab911b5017e3641521b4f0aa237e48a545b96bc2bc0f15a9f05f783364736f6c63430008170033

Block Transaction Gas Used Reward
view all blocks produced

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

Validator Index Block Amount
View All Withdrawals

Transaction Hash Block Value Eth2 PubKey Valid
View All Deposits
[ 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.