Contract Address Details

0xfdB0d62Fc929fD53D266B969Bfe4250b205D0899

Contract Name
ElectroSwapLockerV3
Creator
0xd6cf49–0769d0 at 0xdfdea9–a7a221
Balance
0 ETN ( )
Tokens
Fetching tokens...
Transactions
20 Transactions
Transfers
19 Transfers
Gas Used
2,991,855
Last Balance Update
5472036
Contract name:
ElectroSwapLockerV3




Optimization enabled
true
Compiler version
v0.8.26+commit.8a97fa7a




Optimization runs
200
Verified at
2024-06-30T17:38:09.995775Z

Constructor Arguments

0000000000000000000000003a7f64c57433555b23dac4409a0ac7e84275398d000000000000000000000000043faa1b5c5fc9a7dc35171f290c29ecde0ccff1

Arg [0] (address) : 0x3a7f64c57433555b23dac4409a0ac7e84275398d
Arg [1] (address) : 0x043faa1b5c5fc9a7dc35171f290c29ecde0ccff1

              

contracts/ElectroSwapLockerV3/ElectroSwapLockerV3.sol

// SPDX-License-Identifier: CC-BY-NC-ND-4.0
pragma solidity ^0.8.0;
pragma abicoder v2;

import "../UniswapV3/open-zeppelin/token/ERC721/ERC721Holder.sol";
import "../UniswapV3/open-zeppelin/token/ERC20/IERC20.sol";
import "../UniswapV3/v3-periphery/libraries/LiquidityAmounts.sol";
import "./TickMath.sol";
import "./ReentrancyGuard.sol";
import "./TransferHelper.sol";

 
contract ElectroSwapLockerV3 is ERC721Holder, ReentrancyGuard {
    
    INonfungiblePositionManager private immutable positionManager;
    address private immutable boltAddress;
    address private immutable deadAddress = address(0x000000000000000000000000000000000000dEaD);
    address payable private teamWallet;

    mapping(uint256 => LockInfo) private lockedPositions;
    mapping(address => uint256[]) private ownerToLocks;
    mapping(address => uint256[]) private poolToLocks;
    mapping(address => bool) private noFeeList;

    struct LockInfo {
        uint256 tokenId;
        address owner;
        uint256 created;
        uint256 duration;
        bool active;
        address token0;
        address token1;
        uint24 fee;
        uint256 lockedAmount0; 
        uint256 lockedAmount1;
        address pool;
        uint256 poolAmount0; 
        uint256 poolAmount1;
    }

    struct Fees {
        uint etnFlatFee; // Amount of ETN paid as lock creation fee to stop spam
        uint boltFlatFee; // Amount of BOLT to be burned when paying lock creation fee in BOLT
        uint128 lpPercentFee; // Platform fee as a percentage of locked ElectroSwap LP tokens
    }

    Fees public fees; // Stores the current fee structure
    IMigrator private migrator;

    modifier onlyTeam() {
        require(msg.sender == teamWallet, "Only contract owner can call this function");
        _;
    }

    constructor(address _positionManager, address _boltAddress) {
        positionManager = INonfungiblePositionManager(_positionManager);
        teamWallet = payable(msg.sender);
        boltAddress = _boltAddress;

        fees.etnFlatFee = 1000; // 1000 ETN
        fees.boltFlatFee = 500; // 500 BOLT
        fees.lpPercentFee = 1; // 1%
    }


    // Owner must first approve this contract to interact with the LP position NFT by calling approve on the NFT Position Manager
    function lockPosition(uint256 tokenId, uint256 lockDuration, bool _flatFeeInETN) external payable nonReentrant returns (bool success){
        require(lockDuration > 0, "Lock duration must be greater than zero");
        require(!lockedPositions[tokenId].active, "Position already locked");

        // Transfer the NFT to the locker contract
        positionManager.safeTransferFrom(msg.sender, address(this), tokenId);
        (,,address token0, address token1, uint24 fee,,,uint128 liquidity,,,,) = positionManager.positions(tokenId);
        
        //Process fees if applicable
        if (!noFeeList[msg.sender]) {

            // Process flat fee
            if (_flatFeeInETN) { // Flat rate fee to be paid in ETN
                require(msg.value == (fees.etnFlatFee * 1e18), "Flat rate ETN fee not met");
                (bool etnTransferSuccess, ) = teamWallet.call{value: msg.value}("");
                require(etnTransferSuccess, "Failed to transfer ETN");
            } else { // Flat rate fee to be paid in BOLT Token, and then burned
                TransferHelper.safeTransferFrom(boltAddress, msg.sender, deadAddress, (fees.boltFlatFee * 1e18));
            }

            // Process percentage of liquidity fee
            // Check if fees are collectable, if they are, then revert to ensure we don't collect more than the percent fee
            (uint256 amount0Collected, uint256 amount1Collected) = _collect(tokenId, address(this));
            require(amount0Collected == 0 && amount1Collected == 0, "Must collect fees before locking");

            // Decrease liquidity by fee percent
            
            INonfungiblePositionManager.DecreaseLiquidityParams memory params = INonfungiblePositionManager.DecreaseLiquidityParams({
                tokenId: tokenId,
                liquidity: (liquidity * fees.lpPercentFee) / 100, // 1% of total liquidity in position to be removed
                amount0Min: 0,
                amount1Min: 0,
                deadline: block.timestamp + 60 // 1 minute deadline
            });
            positionManager.decreaseLiquidity(params);
            
            //Collect percentage fee to team wallet
            (amount0Collected, amount1Collected) = _collect(tokenId, teamWallet);
            require(amount0Collected > 0 || amount1Collected > 0, "Sufficient fee not paid");

        } else if (msg.value > 0){
            // refund ETN if whitelisted
            (bool etnRefundSuccess, ) = payable(msg.sender).call{value: msg.value}("");
            require(etnRefundSuccess, "Failed to refund ETN");
        }

        address poolAddress = IElectroSwapV3Factory(positionManager.factory()).getPool(token0, token1, fee);
        require(poolAddress != address(0), "ElectroSwap pool does not exist");

        lockedPositions[tokenId] = LockInfo({
            tokenId: tokenId,
            owner: msg.sender,
            created: block.timestamp,
            duration: lockDuration,
            active: true,
            token0: token0,
            token1: token1,
            fee: fee,
            lockedAmount0: 0, // amounts are transient - populated at time of retrieval
            lockedAmount1: 0,
            pool: poolAddress,
            poolAmount0: 0,
            poolAmount1: 0
        });

        ownerToLocks[msg.sender].push(tokenId);
        poolToLocks[poolAddress].push(tokenId);

        emit LockedV3(msg.sender, tokenId, block.timestamp, lockDuration, poolAddress, token0, token1);

        return true;
    }

    function unlockPosition(uint256 tokenId) external nonReentrant {
        LockInfo memory lockInfo = lockedPositions[tokenId];
        require(lockInfo.active, "Position not locked");
        require(msg.sender == lockInfo.owner, "Only the owner can unlock");
        require(block.timestamp >= (lockInfo.created + lockInfo.duration), "Position still locked");

        delete lockedPositions[tokenId];
        removeLockId(true, lockInfo.owner, tokenId);
        removeLockId(false, lockInfo.pool, tokenId);

        // Transfer the NFT back to the owner
        positionManager.safeTransferFrom(address(this), lockInfo.owner, tokenId);

        emit UnlockedV3(lockInfo.owner, tokenId);
    }

    function withdrawFees(uint256 tokenId) external nonReentrant {
        LockInfo memory lockInfo = lockedPositions[tokenId];
        require(lockInfo.active, "Position not locked");
        require(msg.sender == lockInfo.owner, "Only the owner can withdraw fees");

        (uint256 amount0, uint256 amount1) = _collect(tokenId, lockInfo.owner);

        emit FeesWithdrawnV3(lockInfo.owner, tokenId, amount0, amount1);
    }

    // Caller must first approve this LP Locker contract in the amounts passed in for each of the ERC-20 tokens requesting to be added to the Liquidity Pool
    function increaseLiquidity(uint256 tokenId, uint256 amount0Desired, uint256 amount1Desired) external nonReentrant {
        LockInfo memory lockInfo = lockedPositions[tokenId];
        require(lockInfo.active, "Position not locked");
        require(msg.sender == lockInfo.owner, "Only the owner can increase liquidity");

        // Transfer tokens from the original owner to the contract
        IERC20(lockInfo.token0).transferFrom(msg.sender, address(this), amount0Desired);
        IERC20(lockInfo.token1).transferFrom(msg.sender, address(this), amount1Desired);

        // Approve the position manager to spend tokens
        IERC20(lockInfo.token0).approve(address(positionManager), amount0Desired);
        IERC20(lockInfo.token1).approve(address(positionManager), amount1Desired);

        // Increase liquidity
        INonfungiblePositionManager.IncreaseLiquidityParams memory params = INonfungiblePositionManager.IncreaseLiquidityParams({
            tokenId: tokenId,
            amount0Desired: amount0Desired,
            amount1Desired: amount1Desired,
            amount0Min: 0,
            amount1Min: 0,
            deadline: block.timestamp + 60 // 1 minute deadline
        });
        (uint128 addedLiquidity, uint256 amount0Actual, uint256 amount1Actual) = positionManager.increaseLiquidity(params);

        // Return any unspent tokens to the original owner
        if (amount0Actual < amount0Desired) {
            IERC20(lockInfo.token0).transfer(msg.sender, amount0Desired - amount0Actual);
        }
        if (amount1Actual < amount1Desired) {
            IERC20(lockInfo.token1).transfer(msg.sender, amount1Desired - amount1Actual);
        }

        emit LiquidityIncreasedV3(lockInfo.owner, tokenId, addedLiquidity, amount0Actual, amount1Actual);
    }

    function extendLock(uint256 tokenId, uint256 additionalDuration) external nonReentrant {
        LockInfo storage lockInfo = lockedPositions[tokenId];
        require(lockInfo.active, "Position not locked");
        require(msg.sender == lockInfo.owner, "Only the owner can extend the lock");

        lockInfo.duration += additionalDuration;

        emit LockExtendedV3(lockInfo.owner, tokenId, lockInfo.created, lockInfo.duration);
    }

    function splitLock( uint256 tokenId, uint128 percentLiquidityToNewLock, uint256 durationPastOriginal) external nonReentrant {
        LockInfo memory lockInfo = lockedPositions[tokenId];
        require(lockInfo.active, "Position not locked");
        require(msg.sender == lockInfo.owner, "Only the owner can split the lock");
        require(percentLiquidityToNewLock < 100 && percentLiquidityToNewLock > 0, "Invalid percentage");

        // Check if fees are collectable, if they are, then revert to ensure we don't collect more than the percent fee
        (uint256 amount0Collected, uint256 amount1Collected) = _collect(tokenId, address(this));
        require(amount0Collected == 0 && amount1Collected == 0, "Must collect fees before splitting lock");

        (,,,,, int24 tickLower, int24 tickUpper, uint128 liquidity,,,,) = positionManager.positions(tokenId);

        // Step 1: Decrease liquidity
        INonfungiblePositionManager.DecreaseLiquidityParams memory decreaseParams = INonfungiblePositionManager.DecreaseLiquidityParams({
            tokenId: tokenId,
            liquidity: (liquidity * percentLiquidityToNewLock) / 100,
            amount0Min: 0,
            amount1Min: 0,
            deadline: block.timestamp + 60
        });
        positionManager.decreaseLiquidity(decreaseParams);

        // Step 2: Collect tokens from decreased liquidity
        (amount0Collected, amount1Collected) = _collect(tokenId, address(this));

        // Step 3: Create new position with removed liquidity
        IERC20(lockInfo.token0).approve(address(positionManager), amount0Collected);
        IERC20(lockInfo.token1).approve(address(positionManager), amount1Collected);

        INonfungiblePositionManager.MintParams memory mintParams = INonfungiblePositionManager.MintParams({
            token0: lockInfo.token0,
            token1: lockInfo.token1,
            fee: lockInfo.fee,
            tickLower: tickLower,
            tickUpper: tickUpper,
            amount0Desired: amount0Collected,
            amount1Desired: amount1Collected,
            amount0Min: 0,
            amount1Min: 0,
            recipient: address(this),
            deadline: block.timestamp + 60
        });
        (uint256 newTokenId, , , ) = positionManager.mint(mintParams);

        // Step 4: Lock the new position
        uint newDuration = (lockInfo.created + lockInfo.duration + durationPastOriginal - block.timestamp);
        lockedPositions[newTokenId] = LockInfo({
            tokenId: newTokenId,
            owner: lockInfo.owner,
            created: block.timestamp,
            duration: newDuration,
            active: true,
            token0: lockInfo.token0,
            token1: lockInfo.token1,
            fee: lockInfo.fee,
            lockedAmount0: 0, // amounts are transient - populated at time of retrieval
            lockedAmount1: 0,
            pool: lockInfo.pool,
            poolAmount0: 0,
            poolAmount1: 0
        });
        ownerToLocks[msg.sender].push(newTokenId);
        poolToLocks[lockInfo.pool].push(newTokenId);

        // emit an event
        emit LockSplitV3(lockInfo.owner, tokenId, newTokenId, block.timestamp, newDuration);
    }

    function transferLockOwner(uint256 tokenId, address newOwner) external nonReentrant returns (bool success) {
        LockInfo storage lockInfo = lockedPositions[tokenId];
        require(msg.sender == lockInfo.owner, "Only the owner can transfer the lock to new owner");
        require(newOwner != address(0), "New owner cannot be zero address");
        
        address oldOwner = lockInfo.owner;
        lockInfo.owner = newOwner;

        removeLockId(true, oldOwner, tokenId);
        ownerToLocks[newOwner].push(tokenId);

        emit LockTransferredV3(tokenId, oldOwner, newOwner);

        return true;
    }

    // To be used in future if Uniswap V4 type liquidity support is added
    function migrateLock(uint tokenId) external nonReentrant {
        require(address(migrator) != address(0), "Migrator not set");

        LockInfo storage lockInfo = lockedPositions[tokenId];
        require(lockInfo.active, "Position not locked");
        require(lockInfo.owner == msg.sender, "Only the owner can migrate the lock");
        
        // Transfer the NFT to the Migrator
        positionManager.safeTransferFrom(address(this), address(migrator), tokenId);

        // Attempt to migrate the lock
        require(migrator.migrate(lockInfo.pool, lockInfo.owner, tokenId, lockInfo.created, lockInfo.duration), "Migration failed");

        delete lockedPositions[tokenId];
        removeLockId(true, lockInfo.owner, tokenId);
        removeLockId(false, lockInfo.pool, tokenId);

        emit LockMigratedFromV3(tokenId);
    }

    function getLockById(uint256 tokenId) external view returns (LockInfo memory lockInfo) {
        lockInfo = lockedPositions[tokenId];
        require(lockInfo.active, "No lock exists");

        (,,,,, int24 tickLower, int24 tickUpper, uint128 liquidity,,,,) = positionManager.positions(tokenId);

        IElectroSwapV3Pool pool = IElectroSwapV3Pool(lockInfo.pool);
        (uint160 sqrtPriceX96,,,,,,) = pool.slot0();
        uint128 poolLiquidity = pool.liquidity();

        (uint256 amount0, uint256 amount1) = LiquidityAmounts.getAmountsForLiquidity(
            sqrtPriceX96,
            TickMath.getSqrtRatioAtTick(tickLower),
            TickMath.getSqrtRatioAtTick(tickUpper),
            liquidity
        );

        (uint256 poolAmount0, uint256 poolAmount1) = LiquidityAmounts.getAmountsForLiquidity(
            sqrtPriceX96,
            TickMath.getSqrtRatioAtTick(TickMath.MIN_TICK),
            TickMath.getSqrtRatioAtTick(TickMath.MAX_TICK),
            poolLiquidity
        );

        lockInfo.lockedAmount0 = amount0;
        lockInfo.lockedAmount1 = amount1;
        lockInfo.poolAmount0 = poolAmount0;
        lockInfo.poolAmount1 = poolAmount1;

        return lockInfo;
    }

    function getLockIdsByOwner(address owner) external view returns (uint256[] memory) {
        return ownerToLocks[owner];
    }

    function getLockIdsByPool(address poolAddress) external view returns (uint256[] memory) {
        return poolToLocks[poolAddress];
    }


    function _collect(uint256 tokenId, address recipient) internal returns (uint256 amount0Collected, uint256 amount1Collected) {
        INonfungiblePositionManager.CollectParams memory collectParams = INonfungiblePositionManager.CollectParams({
            tokenId: tokenId,
            recipient: recipient, 
            amount0Max: type(uint128).max,
            amount1Max: type(uint128).max
        });
        (amount0Collected, amount1Collected) = positionManager.collect(collectParams);
    }

    function removeLockId(bool _owner, address _address, uint256 _lockIdToRemove) internal {
        uint256[] storage values = _owner ? ownerToLocks[_address] : poolToLocks[_address];
        uint256 length = values.length;

        // Find the index of the value to be removed
        for (uint256 i = 0; i < length; i++) {
            if (values[i] == _lockIdToRemove) {
                // Shift elements to the left to fill the gap
                for (uint256 j = i; j < length - 1; j++) {
                    values[j] = values[j + 1];
                }
                // Remove the last element
                values.pop();
                break;
            }
        }
    }

    function adminSetFees(uint _etnFlatFee, uint _boltFlatFee, uint128 _lpPercentFee) external onlyTeam {
        require(_etnFlatFee <= 2000, "Flat fee cannot be greater than 2000 ETN");
        require(_boltFlatFee <= 1000, "Flat fee cannot be greater than 1000 BOLT");
        require(_lpPercentFee <= 2, "LP percentage cannot be greater than 2%");

        fees.etnFlatFee = _etnFlatFee;
        fees.boltFlatFee = _boltFlatFee;
        fees.lpPercentFee = _lpPercentFee;

        emit LockerFeesUpdated(_etnFlatFee, _boltFlatFee, _lpPercentFee);
    }

    function adminSetTeamWallet(address payable _teamWallet) external onlyTeam {
        require(_teamWallet != address(0), "Team wallet cannot be zero address");
        teamWallet = _teamWallet;
        emit TeamWalletUpdated(_teamWallet);
    }

    function adminUpdateFeeWhitelist(address _address, bool _whitelisted) external onlyTeam {
        noFeeList[_address] = _whitelisted;
        emit FeeWhitelistUpdated(_address, _whitelisted);
    }

    function adminSetMigrator(IMigrator _migrator) external onlyTeam {
        migrator = _migrator;
        emit MigratorUpdated(address(_migrator));
    }


    event LockedV3(address indexed owner, uint256 indexed tokenId, uint256 created, uint256 duration, address poolAddress, address token0, address token1);
    event UnlockedV3(address indexed owner, uint256 indexed tokenId);
    event FeesWithdrawnV3(address indexed owner, uint256 indexed tokenId, uint256 amount0, uint256 amount1);
    event LiquidityIncreasedV3(address indexed owner, uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1);
    event LockExtendedV3(address indexed owner, uint256 indexed tokenId, uint256 created, uint256 newDuration);
    event LockSplitV3(address indexed owner, uint256 indexed existingTokenId, uint256 indexed newTokenId, uint256 created, uint256 newDuration);
    event LockTransferredV3(uint256 indexed tokenId, address indexed oldOwner, address indexed newOwner);
    event LockMigratedFromV3(uint256 indexed tokenId);
    
    // Team/admin events
    event LockerFeesUpdated(uint256 etnFlatFee, uint boltFlatFee, uint128 lpPercentFee);
    event FeeWhitelistUpdated(address _address, bool whitelisted);
    event TeamWalletUpdated(address indexed newTeamWallet);
    event MigratorUpdated(address indexed migratorAddress);
}


interface IMigrator {
    function migrate(address poolAddress, address owner, uint256 tokenId, uint256 lockCreated, uint256 lockDuration) external returns (bool);
}

interface IElectroSwapV3Factory {
    function getPool(address tokenA, address tokenB, uint24 fee) external view returns (address pool);
}

interface IElectroSwapV3Pool {
    function slot0() external view returns (uint160 sqrtPriceX96, int24 tick, uint16 observationIndex, uint16 observationCardinality, uint16 observationCardinalityNext, uint8 feeProtocol, bool unlocked);
    function liquidity() external view returns (uint128 poolLiquidity);
}

interface INonfungiblePositionManager {
    
    function positions(uint256 tokenId)
        external
        view
        returns (
            uint96 nonce,
            address operator,
            address token0,
            address token1,
            uint24 fee,
            int24 tickLower,
            int24 tickUpper,
            uint128 liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128,
            uint128 tokensOwed0,
            uint128 tokensOwed1
        );

    struct MintParams {
        address token0;
        address token1;
        uint24 fee;
        int24 tickLower;
        int24 tickUpper;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        address recipient;
        uint256 deadline;
    }

    function mint(MintParams calldata params)
        external
        payable
        returns (
            uint256 tokenId,
            uint128 liquidity,
            uint256 amount0,
            uint256 amount1
        );

    struct IncreaseLiquidityParams {
        uint256 tokenId;
        uint256 amount0Desired;
        uint256 amount1Desired;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    function increaseLiquidity(IncreaseLiquidityParams calldata params)
        external
        payable
        returns (
            uint128 liquidity,
            uint256 amount0,
            uint256 amount1
        );

    struct DecreaseLiquidityParams {
        uint256 tokenId;
        uint128 liquidity;
        uint256 amount0Min;
        uint256 amount1Min;
        uint256 deadline;
    }

    function decreaseLiquidity(DecreaseLiquidityParams calldata params)
        external
        payable
        returns (uint256 amount0, uint256 amount1);

    struct CollectParams {
        uint256 tokenId;
        address recipient;
        uint128 amount0Max;
        uint128 amount1Max;
    }

    function collect(CollectParams calldata params) external payable returns (uint256 amount0, uint256 amount1);

    function safeTransferFrom(address from, address to, uint256 tokenId) external;

    function factory() external view returns (address factory);
}
        

contracts/UniswapV3/open-zeppelin/token/ERC721/ERC721Holder.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "./IERC721Receiver.sol";

  /**
   * @dev Implementation of the {IERC721Receiver} interface.
   *
   * Accepts all token transfers. 
   * Make sure the contract is able to use its token with {IERC721-safeTransferFrom}, {IERC721-approve} or {IERC721-setApprovalForAll}.
   */
contract ERC721Holder is IERC721Receiver {

    /**
     * @dev See {IERC721Receiver-onERC721Received}.
     *
     * Always returns `IERC721Receiver.onERC721Received.selector`.
     */
    function onERC721Received(address, address, uint256, bytes memory) public virtual override returns (bytes4) {
        return this.onERC721Received.selector;
    }
}
          

contracts/UniswapV3/open-zeppelin/token/ERC721/IERC721Receiver.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @title ERC721 token receiver interface
 * @dev Interface for any contract that wants to support safeTransfers
 * from ERC721 asset contracts.
 */
interface IERC721Receiver {
    /**
     * @dev Whenever an {IERC721} `tokenId` token is transferred to this contract via {IERC721-safeTransferFrom}
     * by `operator` from `from`, this function is called.
     *
     * It must return its Solidity selector to confirm the token transfer.
     * If any other value is returned or the interface is not implemented by the recipient, the transfer will be reverted.
     *
     * The selector can be obtained in Solidity with `IERC721.onERC721Received.selector`.
     */
    function onERC721Received(address operator, address from, uint256 tokenId, bytes calldata data) external returns (bytes4);
}
          

contracts/UniswapV3/v3-core/libraries/FixedPoint96.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.4.0;

/// @title FixedPoint96
/// @notice A library for handling binary fixed point numbers, see https://en.wikipedia.org/wiki/Q_(number_format)
/// @dev Used in SqrtPriceMath.sol
library FixedPoint96 {
    uint8 internal constant RESOLUTION = 96;
    uint256 internal constant Q96 = 0x1000000000000000000000000;
}
          

contracts/UniswapV3/v3-core/libraries/FullMath.sol

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

/// @title Contains 512-bit math functions
/// @notice Facilitates multiplication and division that can have overflow of an intermediate value without any loss of precision
/// @dev Handles "phantom overflow" i.e., allows multiplication and division where an intermediate value overflows 256 bits
library FullMath {
    /// @notice Calculates floor(a×b÷denominator) with full precision. Throws if result overflows a uint256 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(
        uint256 a,
        uint256 b,
        uint256 denominator
    ) internal pure returns (uint256 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
            uint256 prod0; // Least significant 256 bits of the product
            uint256 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
            uint256 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.
            uint256 twos = (0 - denominator) & denominator;
            // 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
            uint256 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 uint256 or denominator == 0
    /// @param a The multiplicand
    /// @param b The multiplier
    /// @param denominator The divisor
    /// @return result The 256-bit result
    function mulDivRoundingUp(
        uint256 a,
        uint256 b,
        uint256 denominator
    ) internal pure returns (uint256 result) {
        unchecked {
            result = mulDiv(a, b, denominator);
            if (mulmod(a, b, denominator) > 0) {
                require(result < type(uint256).max);
                result++;
            }
        }
    }
}
          

contracts/ElectroSwapLockerV3/ReentrancyGuard.sol

// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.0;

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

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

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

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

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

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

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

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}
          

contracts/ElectroSwapLockerV3/TickMath.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @title Math library for computing sqrt prices from ticks and vice versa
/// @notice Computes sqrt price for ticks of size 1.0001, i.e. sqrt(1.0001^tick) as fixed point Q64.96 numbers. Supports
/// prices between 2**-128 and 2**128
library TickMath {
    error T();
    error R();

    /// @dev The minimum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**-128
    int24 internal constant MIN_TICK = -887272;
    /// @dev The maximum tick that may be passed to #getSqrtRatioAtTick computed from log base 1.0001 of 2**128
    int24 internal constant MAX_TICK = -MIN_TICK;

    /// @dev The minimum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MIN_TICK)
    uint160 internal constant MIN_SQRT_RATIO = 4295128739;
    /// @dev The maximum value that can be returned from #getSqrtRatioAtTick. Equivalent to getSqrtRatioAtTick(MAX_TICK)
    uint160 internal constant MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342;

    /// @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) internal pure returns (uint160 sqrtPriceX96) {
        unchecked {
            uint256 absTick = tick < 0 ? uint256(-int256(tick)) : uint256(int256(tick));
            if (absTick > uint256(int256(MAX_TICK))) revert T();

            uint256 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(uint256).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));
        }
    }

    /// @notice Calculates the greatest tick value such that getRatioAtTick(tick) <= ratio
    /// @dev Throws in case sqrtPriceX96 < MIN_SQRT_RATIO, as MIN_SQRT_RATIO is the lowest value getRatioAtTick may
    /// ever return.
    /// @param sqrtPriceX96 The sqrt ratio for which to compute the tick as a Q64.96
    /// @return tick The greatest tick for which the ratio is less than or equal to the input ratio
    function getTickAtSqrtRatio(uint160 sqrtPriceX96) internal pure returns (int24 tick) {
        unchecked {
            // second inequality must be < because the price can never reach the price at the max tick
            if (!(sqrtPriceX96 >= MIN_SQRT_RATIO && sqrtPriceX96 < MAX_SQRT_RATIO)) revert R();
            uint256 ratio = uint256(sqrtPriceX96) << 32;

            uint256 r = ratio;
            uint256 msb = 0;

            assembly {
                let f := shl(7, gt(r, 0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(6, gt(r, 0xFFFFFFFFFFFFFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(5, gt(r, 0xFFFFFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(4, gt(r, 0xFFFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(3, gt(r, 0xFF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(2, gt(r, 0xF))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := shl(1, gt(r, 0x3))
                msb := or(msb, f)
                r := shr(f, r)
            }
            assembly {
                let f := gt(r, 0x1)
                msb := or(msb, f)
            }

            if (msb >= 128) r = ratio >> (msb - 127);
            else r = ratio << (127 - msb);

            int256 log_2 = (int256(msb) - 128) << 64;

            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(63, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(62, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(61, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(60, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(59, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(58, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(57, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(56, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(55, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(54, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(53, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(52, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(51, f))
                r := shr(f, r)
            }
            assembly {
                r := shr(127, mul(r, r))
                let f := shr(128, r)
                log_2 := or(log_2, shl(50, f))
            }

            int256 log_sqrt10001 = log_2 * 255738958999603826347141; // 128.128 number

            int24 tickLow = int24((log_sqrt10001 - 3402992956809132418596140100660247210) >> 128);
            int24 tickHi = int24((log_sqrt10001 + 291339464771989622907027621153398088495) >> 128);

            tick = tickLow == tickHi ? tickLow : getSqrtRatioAtTick(tickHi) <= sqrtPriceX96 ? tickHi : tickLow;
        }
    }
}
          

contracts/ElectroSwapLockerV3/TransferHelper.sol

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

// helper methods for interacting with ERC20 tokens that do not consistently return true/false
library TransferHelper {
    function safeApprove(address token, address to, uint value) internal {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x095ea7b3, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: APPROVE_FAILED');
    }

    function safeTransfer(address token, address to, uint value) internal {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0xa9059cbb, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FAILED');
    }

    function safeTransferFrom(address token, address from, address to, uint value) internal {
        (bool success, bytes memory data) = token.call(abi.encodeWithSelector(0x23b872dd, from, to, value));
        require(success && (data.length == 0 || abi.decode(data, (bool))), 'TransferHelper: TRANSFER_FROM_FAILED');
    }

}
          

contracts/UniswapV3/open-zeppelin/token/ERC20/IERC20.sol

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

/**
 * @dev Interface of the ERC20 standard as defined in the EIP.
 */
interface IERC20 {
    /**
     * @dev Returns the amount of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

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

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

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

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

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

    /**
     * @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);
}
          

contracts/UniswapV3/v3-periphery/libraries/LiquidityAmounts.sol

// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity >=0.5.0;

import '../../v3-core/libraries/FullMath.sol';
import '../../v3-core/libraries/FixedPoint96.sol';

/// @title Liquidity amount functions
/// @notice Provides functions for computing liquidity amounts from token amounts and prices
library LiquidityAmounts {
    /// @notice Downcasts uint256 to uint128
    /// @param x The uint258 to be downcasted
    /// @return y The passed value, downcasted to uint128
    function toUint128(uint256 x) private pure returns (uint128 y) {
        require((y = uint128(x)) == x);
    }

    /// @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 representing the first tick boundary
    /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
    /// @param amount0 The amount0 being sent in
    /// @return liquidity The amount of returned liquidity
    function getLiquidityForAmount0(
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint256 amount0
    ) internal pure returns (uint128 liquidity) {
        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
        uint256 intermediate = FullMath.mulDiv(sqrtRatioAX96, sqrtRatioBX96, FixedPoint96.Q96);
        return toUint128(FullMath.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 representing the first tick boundary
    /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
    /// @param amount1 The amount1 being sent in
    /// @return liquidity The amount of returned liquidity
    function getLiquidityForAmount1(
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint256 amount1
    ) internal pure returns (uint128 liquidity) {
        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);
        return toUint128(FullMath.mulDiv(amount1, FixedPoint96.Q96, sqrtRatioBX96 - sqrtRatioAX96));
    }

    /// @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
    /// @param sqrtRatioX96 A sqrt price representing the current pool prices
    /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
    /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
    /// @param amount0 The amount of token0 being sent in
    /// @param amount1 The amount of token1 being sent in
    /// @return liquidity The maximum amount of liquidity received
    function getLiquidityForAmounts(
        uint160 sqrtRatioX96,
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint256 amount0,
        uint256 amount1
    ) internal pure returns (uint128 liquidity) {
        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 amount of token0 for a given amount of liquidity and a price range
    /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
    /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
    /// @param liquidity The liquidity being valued
    /// @return amount0 The amount of token0
    function getAmount0ForLiquidity(
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint128 liquidity
    ) internal pure returns (uint256 amount0) {
        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);

        return
            FullMath.mulDiv(
                uint256(liquidity) << FixedPoint96.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 representing the first tick boundary
    /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
    /// @param liquidity The liquidity being valued
    /// @return amount1 The amount of token1
    function getAmount1ForLiquidity(
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint128 liquidity
    ) internal pure returns (uint256 amount1) {
        if (sqrtRatioAX96 > sqrtRatioBX96) (sqrtRatioAX96, sqrtRatioBX96) = (sqrtRatioBX96, sqrtRatioAX96);

        return FullMath.mulDiv(liquidity, sqrtRatioBX96 - sqrtRatioAX96, FixedPoint96.Q96);
    }

    /// @notice Computes the token0 and token1 value for a given amount of liquidity, the current
    /// pool prices and the prices at the tick boundaries
    /// @param sqrtRatioX96 A sqrt price representing the current pool prices
    /// @param sqrtRatioAX96 A sqrt price representing the first tick boundary
    /// @param sqrtRatioBX96 A sqrt price representing the second tick boundary
    /// @param liquidity The liquidity being valued
    /// @return amount0 The amount of token0
    /// @return amount1 The amount of token1
    function getAmountsForLiquidity(
        uint160 sqrtRatioX96,
        uint160 sqrtRatioAX96,
        uint160 sqrtRatioBX96,
        uint128 liquidity
    ) internal pure returns (uint256 amount0, uint256 amount1) {
        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);
        }
    }
}
          

Contract ABI

[{"type":"constructor","stateMutability":"nonpayable","inputs":[{"type":"address","name":"_positionManager","internalType":"address"},{"type":"address","name":"_boltAddress","internalType":"address"}]},{"type":"error","name":"ReentrancyGuardReentrantCall","inputs":[]},{"type":"error","name":"T","inputs":[]},{"type":"event","name":"FeeWhitelistUpdated","inputs":[{"type":"address","name":"_address","internalType":"address","indexed":false},{"type":"bool","name":"whitelisted","internalType":"bool","indexed":false}],"anonymous":false},{"type":"event","name":"FeesWithdrawnV3","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"uint256","name":"tokenId","internalType":"uint256","indexed":true},{"type":"uint256","name":"amount0","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LiquidityIncreasedV3","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"uint256","name":"tokenId","internalType":"uint256","indexed":true},{"type":"uint128","name":"liquidity","internalType":"uint128","indexed":false},{"type":"uint256","name":"amount0","internalType":"uint256","indexed":false},{"type":"uint256","name":"amount1","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LockExtendedV3","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"uint256","name":"tokenId","internalType":"uint256","indexed":true},{"type":"uint256","name":"created","internalType":"uint256","indexed":false},{"type":"uint256","name":"newDuration","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LockMigratedFromV3","inputs":[{"type":"uint256","name":"tokenId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"event","name":"LockSplitV3","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"uint256","name":"existingTokenId","internalType":"uint256","indexed":true},{"type":"uint256","name":"newTokenId","internalType":"uint256","indexed":true},{"type":"uint256","name":"created","internalType":"uint256","indexed":false},{"type":"uint256","name":"newDuration","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LockTransferredV3","inputs":[{"type":"uint256","name":"tokenId","internalType":"uint256","indexed":true},{"type":"address","name":"oldOwner","internalType":"address","indexed":true},{"type":"address","name":"newOwner","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"LockedV3","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"uint256","name":"tokenId","internalType":"uint256","indexed":true},{"type":"uint256","name":"created","internalType":"uint256","indexed":false},{"type":"uint256","name":"duration","internalType":"uint256","indexed":false},{"type":"address","name":"poolAddress","internalType":"address","indexed":false},{"type":"address","name":"token0","internalType":"address","indexed":false},{"type":"address","name":"token1","internalType":"address","indexed":false}],"anonymous":false},{"type":"event","name":"LockerFeesUpdated","inputs":[{"type":"uint256","name":"etnFlatFee","internalType":"uint256","indexed":false},{"type":"uint256","name":"boltFlatFee","internalType":"uint256","indexed":false},{"type":"uint128","name":"lpPercentFee","internalType":"uint128","indexed":false}],"anonymous":false},{"type":"event","name":"MigratorUpdated","inputs":[{"type":"address","name":"migratorAddress","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"TeamWalletUpdated","inputs":[{"type":"address","name":"newTeamWallet","internalType":"address","indexed":true}],"anonymous":false},{"type":"event","name":"UnlockedV3","inputs":[{"type":"address","name":"owner","internalType":"address","indexed":true},{"type":"uint256","name":"tokenId","internalType":"uint256","indexed":true}],"anonymous":false},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminSetFees","inputs":[{"type":"uint256","name":"_etnFlatFee","internalType":"uint256"},{"type":"uint256","name":"_boltFlatFee","internalType":"uint256"},{"type":"uint128","name":"_lpPercentFee","internalType":"uint128"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminSetMigrator","inputs":[{"type":"address","name":"_migrator","internalType":"contract IMigrator"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminSetTeamWallet","inputs":[{"type":"address","name":"_teamWallet","internalType":"address payable"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"adminUpdateFeeWhitelist","inputs":[{"type":"address","name":"_address","internalType":"address"},{"type":"bool","name":"_whitelisted","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"extendLock","inputs":[{"type":"uint256","name":"tokenId","internalType":"uint256"},{"type":"uint256","name":"additionalDuration","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256","name":"etnFlatFee","internalType":"uint256"},{"type":"uint256","name":"boltFlatFee","internalType":"uint256"},{"type":"uint128","name":"lpPercentFee","internalType":"uint128"}],"name":"fees","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"tuple","name":"lockInfo","internalType":"struct ElectroSwapLockerV3.LockInfo","components":[{"type":"uint256","name":"tokenId","internalType":"uint256"},{"type":"address","name":"owner","internalType":"address"},{"type":"uint256","name":"created","internalType":"uint256"},{"type":"uint256","name":"duration","internalType":"uint256"},{"type":"bool","name":"active","internalType":"bool"},{"type":"address","name":"token0","internalType":"address"},{"type":"address","name":"token1","internalType":"address"},{"type":"uint24","name":"fee","internalType":"uint24"},{"type":"uint256","name":"lockedAmount0","internalType":"uint256"},{"type":"uint256","name":"lockedAmount1","internalType":"uint256"},{"type":"address","name":"pool","internalType":"address"},{"type":"uint256","name":"poolAmount0","internalType":"uint256"},{"type":"uint256","name":"poolAmount1","internalType":"uint256"}]}],"name":"getLockById","inputs":[{"type":"uint256","name":"tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"getLockIdsByOwner","inputs":[{"type":"address","name":"owner","internalType":"address"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"uint256[]","name":"","internalType":"uint256[]"}],"name":"getLockIdsByPool","inputs":[{"type":"address","name":"poolAddress","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"increaseLiquidity","inputs":[{"type":"uint256","name":"tokenId","internalType":"uint256"},{"type":"uint256","name":"amount0Desired","internalType":"uint256"},{"type":"uint256","name":"amount1Desired","internalType":"uint256"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"bool","name":"success","internalType":"bool"}],"name":"lockPosition","inputs":[{"type":"uint256","name":"tokenId","internalType":"uint256"},{"type":"uint256","name":"lockDuration","internalType":"uint256"},{"type":"bool","name":"_flatFeeInETN","internalType":"bool"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"migrateLock","inputs":[{"type":"uint256","name":"tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bytes4","name":"","internalType":"bytes4"}],"name":"onERC721Received","inputs":[{"type":"address","name":"","internalType":"address"},{"type":"address","name":"","internalType":"address"},{"type":"uint256","name":"","internalType":"uint256"},{"type":"bytes","name":"","internalType":"bytes"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"splitLock","inputs":[{"type":"uint256","name":"tokenId","internalType":"uint256"},{"type":"uint128","name":"percentLiquidityToNewLock","internalType":"uint128"},{"type":"uint256","name":"durationPastOriginal","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[{"type":"bool","name":"success","internalType":"bool"}],"name":"transferLockOwner","inputs":[{"type":"uint256","name":"tokenId","internalType":"uint256"},{"type":"address","name":"newOwner","internalType":"address"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"unlockPosition","inputs":[{"type":"uint256","name":"tokenId","internalType":"uint256"}]},{"type":"function","stateMutability":"nonpayable","outputs":[],"name":"withdrawFees","inputs":[{"type":"uint256","name":"tokenId","internalType":"uint256"}]}]
            

Deployed ByteCode

0x6080604052600436101561001257600080fd5b6000803560e01c8063070d66bf14612c1157806308f12470146127c3578063150b7a02146127385780631db28f3d1461269f5780633ac9f4ec1461262a5780634963e51d146125b8578063563cdb73146124555780635e318e07146122c1578063687cb35314611fe757806397afc30014611f195780639af1d35a14611ede578063a4375fcd14611bd6578063dd0228f514611b54578063e115bc5c1461154a578063e4feb61b14610c24578063eb6bc69b14610a5a5763ffc124e0146100d857600080fd5b6060366003190112610a57576004356024356044358015158103610a53576100fe6130de565b81156109fe57828452600260205260ff6004604086200154166109b9577f0000000000000000000000003a7f64c57433555b23dac4409a0ac7e84275398d6001600160a01b031690813b156109aa57604051632142170760e11b8152336004820152306024820152604481018590528590818160648183885af180156109ae57610995575b505060405163133f757160e31b8152600481018590529161018083602481845afa801561098a578690879388958992610943575b503389526005602052604089205460ff166108e0571561079557600654670de0b6b3a7640000810290808204670de0b6b3a764000014901517156106b157340361075057878080803460018060a01b03600154165af16102156130ae565b5015610712575b610226308861367f565b90159081610709575b50156106c5576001600160801b0361024e606492826008541690613038565b1604603c42018042116106b1576102a8916040916001600160801b0383519261027684612d65565b8b84521660208301528a838301528a60608301526080820152815180938192630624e65f60e11b835260048301613071565b03818b875af180156106a657610678575b506001546102d0906001600160a01b03168761367f565b90159081159161066e575b50156106295760206004925b60405163c45a015560e01b815293849182905afa9182156105db5787926105e6575b50604051630b4c774160e11b81526001600160a01b03918216600482018190529382166024820181905262ffffff959095166044820181905292909160209183916064918391165afa9081156105db57879161059d575b506001600160a01b03169081156105585786808386938960405195869261038684612d32565b828452602084013381528a8d604087014281526060880191825260808801936001855260a0890193845260c0890196875260e089019586528a6101008a0198818a526101208b019a828c5261014081019c8d5261016081019d8e52610180019d8e5281526002602052604090209c518d55600160a01b6001900390511660018d0190600160a01b60019003166001600160601b0360a01b8254161790555160028c01555160038b015560048a019151151561044d90839060ff801983541691151516179055565b518154610100600160a81b031916600891821b610100600160a81b031617909155915160058901805492516001600160b81b03199093166001600160a01b039283161760a09390931b62ffffff60a01b16929092179091559151600688015591516007870155915190850180546001600160a01b0319169190921617905551600983015551600a909101553386526003602052604086206104ef908690612fe8565b808652600460205284604087209061050691612fe8565b604051934285526020850152604084015260608301526080820152339060a07fae141140f9e9f455b9444c2388d1f08b9203d1a4d301bb20b4b7ce07ed3f82f491a36001905560405160018152602090f35b60405162461bcd60e51b815260206004820152601f60248201527f456c656374726f5377617020706f6f6c20646f6573206e6f74206578697374006044820152606490fd5b90506020813d6020116105d3575b816105b860209383612d81565b810103126105cf576105c990612e5e565b38610360565b8680fd5b3d91506105ab565b6040513d89823e3d90fd5b9091506020813d602011610621575b8161060260209383612d81565b810103126105cf57602061061962ffffff92612e5e565b929150610309565b3d91506105f5565b60405162461bcd60e51b815260206004820152601760248201527f53756666696369656e7420666565206e6f7420706169640000000000000000006044820152606490fd5b90501515386102db565b6106999060403d60401161069f575b6106918183612d81565b81019061305b565b506102b9565b503d610687565b6040513d8a823e3d90fd5b634e487b7160e01b89526011600452602489fd5b606460405162461bcd60e51b815260206004820152602060248201527f4d75737420636f6c6c6563742066656573206265666f7265206c6f636b696e676044820152fd5b9050153861022f565b60405162461bcd60e51b81526020600482015260166024820152752330b4b632b2103a37903a3930b739b332b91022aa2760511b6044820152606490fd5b60405162461bcd60e51b815260206004820152601960248201527f466c617420726174652045544e20666565206e6f74206d6574000000000000006044820152606490fd5b600754670de0b6b3a7640000810290808204670de0b6b3a764000014901517156106b1576040516323b872dd60e01b602082019081523360248301527f000000000000000000000000000000000000000000000000000000000000dead6001600160a01b031660448301526064808301939093529181528991829161081b608482612d81565b5190827f000000000000000000000000043faa1b5c5fc9a7dc35171f290c29ecde0ccff15af16108496130ae565b816108a5575b5061021c5760405162461bcd60e51b8152602060048201526024808201527f5472616e7366657248656c7065723a205452414e534645525f46524f4d5f46416044820152631253115160e21b6064820152608490fd5b80518015925082156108ba575b50503861084f565b81925090602091810103126108dc5760206108d59101612f4e565b38806108b2565b8880fd5b5050346108f2575b60206004926102e7565b8680808034335af16109026130ae565b506108e85760405162461bcd60e51b81526020600482015260146024820152732330b4b632b2103a37903932b33ab7321022aa2760611b6044820152606490fd5b925093505061096b9193506101803d8111610983575b6109638183612d81565b810190612e94565b505050509896505050949190925091939490386101b7565b503d610959565b6040513d88823e3d90fd5b8161099f91612d81565b6109aa578438610183565b8480fd5b6040513d84823e3d90fd5b60405162461bcd60e51b815260206004820152601760248201527f506f736974696f6e20616c7265616479206c6f636b65640000000000000000006044820152606490fd5b60405162461bcd60e51b815260206004820152602760248201527f4c6f636b206475726174696f6e206d7573742062652067726561746572207468604482015266616e207a65726f60c81b6064820152608490fd5b8380fd5b80fd5b5034610a57576060366003190112610a57576044356024356004356001600160801b0383168084036109aa57610a9b60018060a01b03600154163314612f5b565b6107d08211610bce576103e88311610b775760028111610b22577fe135e4e3f2308313ec0b6ced5f49746591fa28ff7363f9c5bd30f160b42b84c193610b1c9183600655846007556001600160801b03196008541617600855604051938493849160409194936001600160801b039160608501968552602085015216910152565b0390a180f35b60405162461bcd60e51b815260206004820152602760248201527f4c502070657263656e746167652063616e6e6f742062652067726561746572206044820152667468616e20322560c81b6064820152608490fd5b60405162461bcd60e51b815260206004820152602960248201527f466c6174206665652063616e6e6f742062652067726561746572207468616e206044820152680c4c0c0c081093d31560ba1b6064820152608490fd5b60405162461bcd60e51b815260206004820152602860248201527f466c6174206665652063616e6e6f742062652067726561746572207468616e20604482015267191818181022aa2760c11b6064820152608490fd5b5034610a57576060366003190112610a57576024356001600160801b03811680820361133357610c526130de565b600435835260026020526040832090610d1860405192610c7184612d32565b8054845260018101546001600160a01b0390811660208601526002820154604086015260038201546060860152600482015460ff8116151560808701819052600891821c831660a080890191909152600585015480851660c08a0152901c62ffffff1660e088015260068401546101008801526007840154610120880152908301549091166101408601526009820154610160860152600a90910154610180850152612df9565b60208201516001600160a01b031633036114fb576064811090816114f1575b50156114b757610d493060043561367f565b901590816114ae575b50156114595760405163133f757160e31b81526004803590820152610180816024817f0000000000000000000000003a7f64c57433555b23dac4409a0ac7e84275398d6001600160a01b03165afa92831561144e57849385928691611409575b50610dc76064926001600160801b0392613038565b1604603c420142116113f5576040610e25916001600160801b03825191610ded83612d65565b60043583521660208201528682820152866060820152603c42016080820152815180938192630624e65f60e11b835260048301613071565b0381887f0000000000000000000000003a7f64c57433555b23dac4409a0ac7e84275398d6001600160a01b03165af180156113ea576113cc575b50610e6c3060043561367f565b60a084015160405163095ea7b360e01b81526001600160a01b037f0000000000000000000000003a7f64c57433555b23dac4409a0ac7e84275398d81166004830152602482018590529294929091602091839160449183918c91165af180156105db57611395575b5060c084015160405163095ea7b360e01b81526001600160a01b037f0000000000000000000000003a7f64c57433555b23dac4409a0ac7e84275398d81166004830152602482018690529091602091839160449183918c91165af180156105db5761135e575b5060a084015160c085015160e0860151604051979262ffffff909116916001600160a01b039081169116610160890189811067ffffffffffffffff82111761134a5760405288526020880152604087015260020b606086015260020b608085015260a084015260c08301528260e08301528261010083015230610120830152603c420161014083015261014060405192634418b22b60e11b845260018060a01b03815116600485015260018060a01b03602082015116602485015262ffffff6040820151166044850152606081015160020b6064850152608081015160020b608485015260a081015160a485015260c081015160c485015260e081015160e485015261010081015161010485015260018060a01b03610120820151166101248501520151610144830152608082610164818660018060a01b037f0000000000000000000000003a7f64c57433555b23dac4409a0ac7e84275398d165af191821561133f5783926112f9575b50604081015142906044359060608401516110bf91612e3b565b906110c991612e3b565b906110d39161302b565b602082015160a083015160c084015160e085015161014086015160405195969594899485946001600160a01b039384169488948c949281169362ffffff909216928116911661112186612d32565b848652602086019182526040860142815260608701908d825260808801936001855260a0890193845260c0890196875260e089019586528a6101008a0198818a526101208b019a828c5261014081019c8d5261016081019d8e52610180019d8e5281526002602052604090209c518d55600160a01b6001900390511660018d0190600160a01b60019003166001600160601b0360a01b8254161790555160028c01555160038b015560048a01915115156111e790839060ff801983541691151516179055565b518154610100600160a81b031916600891821b610100600160a81b031617909155915160058901805492516001600160b81b03199093166001600160a01b039283161760a09390931b62ffffff60a01b16929092179091559151600688015591516007870155915190850180546001600160a01b0319169190921617905551600983015551600a90910155338452600360205260408420611289908490612fe8565b6101408101516001600160a01b031684526004602052604084206112ae908490612fe8565b600160a01b600190039060200151166040519142835260208301526004359160407f57ff7b05cc052545e51cd622b269e1d4091fa9ddde19a862f601825bce4a79c891a46001815580f35b9091506080813d608011611337575b8161131560809383612d81565b810103126113335761132b602082519201612e80565b5090386110a5565b8280fd5b3d9150611308565b6040513d85823e3d90fd5b634e487b7160e01b8b52604160045260248bfd5b6020813d60201161138d575b8161137760209383612d81565b810103126105cf5761138890612f4e565b610f3a565b3d915061136a565b6020813d6020116113c4575b816113ae60209383612d81565b810103126105cf576113bf90612f4e565b610ed4565b3d91506113a1565b6113e49060403d60401161069f576106918183612d81565b50610e5f565b6040513d87823e3d90fd5b634e487b7160e01b85526011600452602485fd5b6064929550610dc793506001600160801b039150611435906101803d8111610983576109638183612d81565b505050509996509a945092505050969492509250610db2565b6040513d86823e3d90fd5b60405162461bcd60e51b815260206004820152602760248201527f4d75737420636f6c6c6563742066656573206265666f72652073706c697474696044820152666e67206c6f636b60c81b6064820152608490fd5b90501538610d52565b60405162461bcd60e51b8152602060048201526012602482015271496e76616c69642070657263656e7461676560701b6044820152606490fd5b9050151538610d37565b60405162461bcd60e51b815260206004820152602160248201527f4f6e6c7920746865206f776e65722063616e2073706c697420746865206c6f636044820152606b60f81b6064820152608490fd5b5034610a57576060366003190112610a575760043560243560443561156d6130de565b8284526002602052604084209160405161158681612d32565b8354815260018401546001600160a01b03908116602083019081526002860154604084015260038601546060840152600486015460ff8116151560808501819052600891821c841660a080870191825260058a015480871660c08901908152911c62ffffff1660e088015260068a015461010088015260078a0154610120880152928901549094166101408601526009880154610160860152600a909701546101809094019390935291949193909161163e90612df9565b83516001600160a01b03163303611b015781516040516323b872dd60e01b81523360048201523060248201526044810185905290602090829060649082908c906001600160a01b03165af180156106a657611aca575b5084516040516323b872dd60e01b81523360048201523060248201526044810183905290602090829060649082908c906001600160a01b03165af180156106a657611a8f575b50815160405163095ea7b360e01b81526001600160a01b037f0000000000000000000000003a7f64c57433555b23dac4409a0ac7e84275398d811660048301819052602483018790529792602091839160449183918e91165af18015611a4d57611a58575b50805160405163095ea7b360e01b81526001600160a01b038881166004830152602482018590529091602091839160449183918e91165af18015611a4d57611a16575b50603c4201958642116106b1576040519060c0820182811067ffffffffffffffff82111761134a57604090815289835260208301878152818401868152606085018d8152608086018e815260a087019c8d52935163219f5d1760e01b81529551600487015291516024860152516044850152516064840152516084830152965160a4820152958892919083908890815a9360c492606095f193841561133f578395849885966119c5575b509088828693821061195a575b5050505081841061189a575b505050906060917f4943191b10db7dcc51f77a22d3de748d2c233e28ebb36e071f0acf9d56ceecf59360018060a01b03905116946001600160801b036040519316835260208301526040820152a36001815580f35b516020916118e1916001600160a01b0316906118b790869061302b565b60405163a9059cbb60e01b8152336004820152602481019190915293849283919082906044820190565b03925af180156105db576118f8575b808791611845565b91906020833d602011611952575b8161191360209383612d81565b810103126105cf577f4943191b10db7dcc51f77a22d3de748d2c233e28ebb36e071f0acf9d56ceecf593611948606094612f4e565b50935090916118f0565b3d9150611906565b6118b76119749260209460018060a01b039051169261302b565b03925af1801561144e5761198c575b80888592611839565b6020813d6020116119bd575b816119a560209383612d81565b81010312610a53576119b690612f4e565b5038611983565b3d9150611998565b9650945096506060853d606011611a0e575b816119e460609383612d81565b8101031261133357826119f686612e80565b9760406020880151970151989680999691925061182c565b3d91506119d7565b6020813d602011611a45575b81611a2f60209383612d81565b810103126108dc57611a4090612f4e565b611782565b3d9150611a22565b6040513d8b823e3d90fd5b6020813d602011611a87575b81611a7160209383612d81565b810103126108dc57611a8290612f4e565b61173f565b3d9150611a64565b6020813d602011611ac2575b81611aa860209383612d81565b81010312611abe57611ab990612f4e565b6116da565b8780fd5b3d9150611a9b565b6020813d602011611af9575b81611ae360209383612d81565b81010312611abe57611af490612f4e565b611694565b3d9150611ad6565b60405162461bcd60e51b815260206004820152602560248201527f4f6e6c7920746865206f776e65722063616e20696e637265617365206c697175604482015264696469747960d81b6064820152608490fd5b5034610a57576020366003190112610a57576001600160a01b03611b76612d01565b168152600360205260408120604051918260208354918281520192825260208220915b818110611bc057611bbc85611bb081870382612d81565b60405191829182612dbf565b0390f35b8254845260209093019260019283019201611b99565b5034610a57576020366003190112610a5757600435611bf36130de565b6009546001600160a01b03168015611ea65781835260026020526040832090611c2260ff600484015416612df9565b6001820180549091906001600160a01b03163303611e555784907f0000000000000000000000003a7f64c57433555b23dac4409a0ac7e84275398d6001600160a01b0316803b1561133357604051632142170760e11b81523060048201526001600160a01b039290921660248301526044820186905282908290606490829084905af180156109ae57611e40575b505060095460088301805483546002860154600390960154604051633bdf278d60e21b81526001600160a01b03938416600482015291831660248301526044820188905260648201969096526084810195909552909391602091839160a49183918a91165af19081156113ea578591611e06575b5015611dce57611da391611d948480938188526002602052611d8560408920600a6000918281558260018201558260028201558260038201558260048201558260058201558260068201558260078201558260088201558260098201550155565b546001600160a01b031661350f565b546001600160a01b03166135f8565b7f9ee75c43a6302c49a446052e0e6d399b8f2096b5661e7f2eac49659d5ebb9f4c8280a26001815580f35b60405162461bcd60e51b815260206004820152601060248201526f135a59dc985d1a5bdb8819985a5b195960821b6044820152606490fd5b90506020813d602011611e38575b81611e2160209383612d81565b810103126109aa57611e3290612f4e565b38611d24565b3d9150611e14565b81611e4a91612d81565b610a53578338611cb0565b60405162461bcd60e51b815260206004820152602360248201527f4f6e6c7920746865206f776e65722063616e206d69677261746520746865206c6044820152626f636b60e81b6064820152608490fd5b60405162461bcd60e51b815260206004820152601060248201526f135a59dc985d1bdc881b9bdd081cd95d60821b6044820152606490fd5b5034610a575780600319360112610a57576006546007546008546040805193845260208401929092526001600160801b031690820152606090f35b5034610a57576020366003190112610a57576004356001600160a01b03811690819003611fe357600154611f57336001600160a01b03831614612f5b565b8115611f93576001600160a01b03191681176001557ff6215f245bfd24e51265c56ef650fdd856aa4ece6221ee1ef395bbe0a55580108280a280f35b60405162461bcd60e51b815260206004820152602260248201527f5465616d2077616c6c65742063616e6e6f74206265207a65726f206164647265604482015261737360f01b6064820152608490fd5b5080fd5b5034610a57576020366003190112610a57576004356120046130de565b8082526002602052604082206040519061201d82612d32565b8054825260018101546001600160a01b0390811660208401908152600283015460408501908152600384015460608601908152600485015460ff8116151560808801819052600891821c861660a0808a0191909152600588015480881660c08b0152901c62ffffff1660e0890152600687015461010089015260078701546101208901529086015490941661014087019081526009860154610160880152600a9095015461018090960195909552909390916120d890612df9565b83516001600160a01b0316330361227c576120f69151905190612e3b565b421061223f578261217791818652600260205261215260408720600a6000918281558260018201558260028201558260038201558260048201558260058201558260068201558260078201558260088201558260098201550155565b83516121689083906001600160a01b031661350f565b516001600160a01b03166135f8565b805183906001600160a01b03908116907f0000000000000000000000003a7f64c57433555b23dac4409a0ac7e84275398d16803b1561133357604051632142170760e11b81523060048201526001600160a01b039290921660248301526044820185905282908290606490829084905af180156109ae5761222a575b5050516001600160a01b03167fdefba4de08ec6c6f819a41857212db2623a7b0236f321b0ea44a099716a359978380a36001815580f35b8161223491612d81565b6113335782386121f3565b60405162461bcd60e51b8152602060048201526015602482015274141bdcda5d1a5bdb881cdd1a5b1b081b1bd8dad959605a1b6044820152606490fd5b60405162461bcd60e51b815260206004820152601960248201527f4f6e6c7920746865206f776e65722063616e20756e6c6f636b000000000000006044820152606490fd5b5034610a57576020366003190112610a57576004356122de6130de565b8082526002602052604082206123a8604051916122fa83612d32565b8054835260018101546001600160a01b03908116602085019081526002830154604086015260038301546060860152600483015460ff8116151560808701819052600891821c841660a080890191909152600586015480861660c08a0152901c62ffffff1660e088015260068501546101008801526007850154610120880152908401549092166101408601526009830154610160860152600a909201546101809094019390935291612df9565b80516001600160a01b031633036124115780517f65c3bc47cb9901213d61a2b375a8f343502e95b1c75e44108e4dc18239930d33906040906123f3906001600160a01b03168561367f565b9360018060a01b039051169382519182526020820152a36001815580f35b606460405162461bcd60e51b815260206004820152602060248201527f4f6e6c7920746865206f776e65722063616e20776974686472617720666565736044820152fd5b5034610a57576040366003190112610a5757600435612472612d1c565b9161247b6130de565b81815260026020526040812060010180546001600160a01b03169390919033859003612559576001600160a01b0316801561251557602094600193826001600160601b0360a01b8254161790556124d2858261350f565b818352600386526124e68560408520612fe8565b604051947f59782bedaa591ede99ca79908a0d72a01dd5728a328add02e64a21a5ab92a73f8480a45560018152f35b606460405162461bcd60e51b815260206004820152602060248201527f4e6577206f776e65722063616e6e6f74206265207a65726f20616464726573736044820152fd5b60405162461bcd60e51b815260206004820152603160248201527f4f6e6c7920746865206f776e65722063616e207472616e7366657220746865206044820152703637b1b5903a37903732bb9037bbb732b960791b6064820152608490fd5b5034610a57576020366003190112610a57576001600160a01b036125da612d01565b168152600460205260408120604051918260208354918281520192825260208220915b81811061261457611bbc85611bb081870382612d81565b82548452602090930192600192830192016125fd565b5034610a57576020366003190112610a57576004356001600160a01b03811690819003611fe35761266660018060a01b03600154163314612f5b565b600980546001600160a01b031916821790557f6d4faaba9390b6bfbd5cb72e0cd8dfb4781f53d262654f8aa7eca81a0e24b3158280a280f35b5034610a57576040366003190112610a57576126b9612d01565b6024359081151591828103610a53577f1edda118cbc893a00b312565b4e15205c24b89f3e2fe94825eeebb435318ed1c9261272a60409361270560018060a01b03600154163314612f5b565b60018060a01b03169283875260056020528487209060ff801983541691151516179055565b82519182526020820152a180f35b5034610a57576080366003190112610a5757612752612d01565b5061275b612d1c565b5060643567ffffffffffffffff8111611fe35736602382011215611fe357806004013561278781612da3565b6127946040519182612d81565b8181523660248385010111610a53578160246020940184830137010152604051630a85bd0160e11b8152602090f35b5034610a57576020366003190112610a5757600435816101806040516127e881612d32565b8281528260208201528260408201528260608201528260808201528260a08201528260c08201528260e0820152826101008201528261012082015282610140820152826101608201520152808252600260205260408220906040519161284d83612d32565b8054835260018101546001600160a01b0390811660208501526002820154604085015260038201546060850152600482015460ff81161580156080870152600891821c831660a080880191909152600585015480851660c0890152901c62ffffff1660e087015260068401546101008701526007840154610120870152908301549091166101408501526009820154610160850152600a90910154610180840152612bdb5760405163133f757160e31b81526004810191909152610180816024817f0000000000000000000000003a7f64c57433555b23dac4409a0ac7e84275398d6001600160a01b03165afa801561133f57839084928591612bab575b50610140840151604051633850c7bd60e01b81526001600160a01b0390911693909260e084600481885afa9384156105db578794612b12575b5060206004949560405195868092630d34328160e11b82525afa9384156105db578794612abc575b506129ef926101a09794926129cc6129c66129d394613100565b91613100565b908761348d565b949093506401000276a36129e9620d89e8613100565b9161348d565b929091610100850152610120840152610160830152610180820152610180604051918051835260018060a01b036020820151166020840152604081015160408401526060810151606084015260808101511515608084015260018060a01b0360a08201511660a084015260018060a01b0360c08201511660c084015262ffffff60e08201511660e084015261010081015161010084015261012081015161012084015260018060a01b03610140820151166101408401526101608101516101608401520151610180820152f35b9093506020813d602011612b0a575b81612ad860209383612d81565b810103126105cf576129ef926101a09794926129cc6129c6612afc6129d395612e80565b979a505050929150926129ac565b3d9150612acb565b935060e0843d60e011612ba3575b81612b2d60e09383612d81565b810103126105cf578351936001600160a01b0385168503611abe57612b5460208201612e72565b50612b6160408201612f3f565b50612b6e60608201612f3f565b50612b7b60808201612f3f565b5060a081015160ff811603611abe57600494612b9b60c060209301612f4e565b509450612984565b3d9150612b20565b915050612bc791506101803d8111610983576109638183612d81565b50505050965094509250505090913861294b565b60405162461bcd60e51b815260206004820152600e60248201526d4e6f206c6f636b2065786973747360901b6044820152606490fd5b5034610a57576040366003190112610a5757600435612c2e6130de565b808252600260205260408220612c4a60ff600483015416612df9565b60018101546001600160a01b03169033829003612cb157604081600260037fa53e7cbf68cc072c52a1cdd1598b3f8cad3c4b16109e5891377317b531e4ce61940191612c996024358454612e3b565b80935501549082519182526020820152a36001815580f35b60405162461bcd60e51b815260206004820152602260248201527f4f6e6c7920746865206f776e65722063616e20657874656e6420746865206c6f604482015261636b60f01b6064820152608490fd5b600435906001600160a01b0382168203612d1757565b600080fd5b602435906001600160a01b0382168203612d1757565b6101a0810190811067ffffffffffffffff821117612d4f57604052565b634e487b7160e01b600052604160045260246000fd5b60a0810190811067ffffffffffffffff821117612d4f57604052565b90601f8019910116810190811067ffffffffffffffff821117612d4f57604052565b67ffffffffffffffff8111612d4f57601f01601f191660200190565b602060408183019282815284518094520192019060005b818110612de35750505090565b8251845260209384019390920191600101612dd6565b15612e0057565b60405162461bcd60e51b8152602060048201526013602482015272141bdcda5d1a5bdb881b9bdd081b1bd8dad959606a1b6044820152606490fd5b91908201809211612e4857565b634e487b7160e01b600052601160045260246000fd5b51906001600160a01b0382168203612d1757565b51908160020b8203612d1757565b51906001600160801b0382168203612d1757565b919082610180910312612d175781516001600160601b0381168103612d175791612ec060208201612e5e565b91612ecd60408301612e5e565b91612eda60608201612e5e565b91608082015162ffffff81168103612d175791612ef960a08201612e72565b91612f0660c08301612e72565b91612f1360e08201612e80565b916101008201519161012081015191612f3c610160612f356101408501612e80565b9301612e80565b90565b519061ffff82168203612d1757565b51908115158203612d1757565b15612f6257565b60405162461bcd60e51b815260206004820152602a60248201527f4f6e6c7920636f6e7472616374206f776e65722063616e2063616c6c207468696044820152693990333ab731ba34b7b760b11b6064820152608490fd5b8054821015612fd25760005260206000200190600090565b634e487b7160e01b600052603260045260246000fd5b9081549168010000000000000000831015612d4f578261301091600161302995018155612fba565b90919082549060031b91821b91600019901b1916179055565b565b91908203918211612e4857565b906001600160801b03809116911602906001600160801b038216918203612e4857565b9190826040910312612d17576020825192015190565b91909160808060a0830194805184526001600160801b03602082015116602085015260408101516040850152606081015160608501520151910152565b3d156130d9573d906130bf82612da3565b916130cd6040519384612d81565b82523d6000602084013e565b606090565b6002600054146130ef576002600055565b633ee5aeb560e01b60005260046000fd5b60020b60008112156134875780600003905b620d89e88211613476576001821615613464576001600160881b036ffffcb933bd6fad37aa2d162d1a5940015b169160028116613448575b6004811661342c575b60088116613410575b601081166133f4575b602081166133d8575b604081166133bc575b608081166133a0575b6101008116613384575b6102008116613368575b610400811661334c575b6108008116613330575b6110008116613314575b61200081166132f8575b61400081166132dc575b61800081166132c0575b6201000081166132a4575b620200008116613289575b62040000811661326e575b6208000016613255575b600012613230575b63ffffffff8116613228576000905b60201c60ff91909116016001600160a01b031690565b600190613212565b801561323f5760001904613203565b634e487b7160e01b600052601260045260246000fd5b6b048a170391f7dc42444e8fa290910260801c906131fb565b6d2216e584f5fa1ea926041bedfe9890920260801c916131f1565b916e5d6af8dedb81196699c329225ee6040260801c916131e6565b916f09aa508b5b7a84e1c677de54f3e99bc90260801c916131db565b916f31be135f97d08fd981231505542fcfa60260801c916131d0565b916f70d869a156d2a1b890bb3df62baf32f70260801c916131c6565b916fa9f746462d870fdf8a65dc1f90e061e50260801c916131bc565b916fd097f3bdfd2022b8845ad8f792aa58250260801c916131b2565b916fe7159475a2c29b7443b29c7fa6e889d90260801c916131a8565b916ff3392b0822b70005940c7a398e4b70f30260801c9161319e565b916ff987a7253ac413176f2b074cf7815e540260801c91613194565b916ffcbe86c7900a88aedcffc83b479aa3a40260801c9161318a565b916ffe5dee046a99a2a811c461f1969c30530260801c91613180565b916fff2ea16466c96a3843ec78b326b528610260801c91613177565b916fff973b41fa98c081472e6896dfb254c00260801c9161316e565b916fffcb9843d60f6159c9db58835c9266440260801c91613165565b916fffe5caca7e10e4e61c3624eaa0941cd00260801c9161315c565b916ffff2e50f5f656932ef12357cf3c7fdcc0260801c91613153565b916ffff97272373d413259a46990580e213a0260801c9161314a565b6001600160881b03600160801b61313f565b6315e4079d60e11b60005260046000fd5b80613112565b9093926000929091836001600160a01b0380841690881611613507575b6001600160a01b0382811690881681116134cf575050506134cc9293946137bf565b91565b93945090926001600160a01b03831611156134fb5750906134f583612f3c9493836137bf565b9461377c565b94612f3c93925061377c565b9591956134aa565b6001600160a01b0316600090815260036020526040812080549092915b81811061353a575b50505050565b826135458286612fba565b90549060031b1c146135595760010161352c565b9293600019820191821193909250835b612e4857818110156135a8576001810190818111612e48576135a161359060019388612fba565b90549060031b1c6130108389612fba565b0183613569565b5050919050805480156135e25760001901906135d86135c78383612fba565b8154906000199060031b1b19169055565b5538808080613534565b634e487b7160e01b600052603160045260246000fd5b6001600160a01b0316600090815260046020526040812080549092915b8181106136225750505050565b8261362d8286612fba565b90549060031b1c1461364157600101613615565b9293600019820191821193909250835b612e4857818110156135a8576001810190818111612e485761367861359060019388612fba565b0183613651565b60405191608083019183831067ffffffffffffffff841117612d4f57604092835283526001600160a01b03908116602084019081526001600160801b0384840181815260608601828152855163fc6f786560e01b81529651600488015292518416602487015251811660448601529051166064840152829060849082906000907f0000000000000000000000003a7f64c57433555b23dac4409a0ac7e84275398d165af180156137575760009160009161373857509091565b9050613753915060403d60401161069f576106918183612d81565b9091565b6040513d6000823e3d90fd5b6001600160a01b039182169082160391908211612e4857565b612f3c92916001600160801b03916001600160a01b03808316908216116137b9575b6001600160a01b03916137b19190613763565b169116613832565b9061379e565b91613816916001600160a01b038082169085161161382c575b6001600160a01b036137ea8583613763565b6001600160a01b039092169291169060601b6fffffffffffffffffffffffffffffffff60601b16613881565b6001600160a01b0390911690811561323f570490565b926137d8565b60009190600019828209918082029384808510940393808503941461387757600160601b841015610a575750600160601b910990828211900360a01b910360601c1790565b5050505060601c90565b9160001982840992828102928380861095039480860395146138fb5784831115612d1757829109818060000316809204600281600302188082026002030280820260020302808202600203028082026002030280820260020302809102600203029360018380600003040190848311900302920304170290565b505080925015612d1757049056fea26469706673582212207afcb4f9cf4a48a95fbd1e72dbe3c4cc77cf6d08a415e2686b450b9b0a23de8564736f6c634300081a0033