UniswapV4Connector

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/connectors/uniswap/UniswapV4Connector.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {
    IPositionManager
} from "contracts/interfaces/external/uniswap/v4/IPositionManager.sol";
import { IHooks } from "contracts/interfaces/external/uniswap/v4/IHooks.sol";
import {
    IStateView
} from "contracts/interfaces/external/uniswap/v4/IStateView.sol";
import {
    PoolId
} from "contracts/interfaces/external/uniswap/v4/types/PoolId.sol";
import {
    PoolKey
} from "contracts/interfaces/external/uniswap/v4/types/PoolKey.sol";
import {
    PositionInfo
} from "contracts/interfaces/external/uniswap/v4/libraries/PositionInfoLibrary.sol";
import {
    Actions
} from "contracts/interfaces/external/uniswap/v4/libraries/Actions.sol";
import {
    Currency
} from "contracts/interfaces/external/uniswap/v4/types/Currency.sol";
import {
    IAllowanceTransfer
} from "contracts/interfaces/external/IAllowanceTransfer.sol";

import { INftFarmConnector } from "contracts/interfaces/INftFarmConnector.sol";
import {
    INftLiquidityConnector,
    NftPoolInfo,
    NftPoolKey,
    NftPositionInfo
} from "contracts/interfaces/INftLiquidityConnector.sol";
import {
    NftAddLiquidity,
    NftRemoveLiquidity
} from "contracts/structs/NftLiquidityStructs.sol";
import { NftPosition } from "contracts/structs/NftFarmStrategyStructs.sol";
import {
    FullMath
} from "contracts/interfaces/external/uniswap/v4/libraries/FullMath.sol";
import {
    FixedPoint128
} from "contracts/interfaces/external/uniswap/v4/libraries/FixedPoint128.sol";
import {
    Position
} from "contracts/interfaces/external/uniswap/v4/libraries/Position.sol";

struct UniswapV4MintExtraData {
    uint24 tickSpacing;
    IHooks hooks;
    bytes hookData;
    uint128 liquidity;
}

struct UniswapV4RemoveExtraData {
    Currency currency0;
    Currency currency1;
    bytes hookData;
}

address constant UNISWAP_ETH = 0x0000000000000000000000000000000000000000;

address constant PERMIT_2 = 0x000000000022D473030F116dDEE9F6B43aC78BA3;

contract UniswapV4Connector is INftLiquidityConnector, INftFarmConnector {
    error InvalidParameters();
    error InvalidDesiredAmount();
    error InvalidTokenOwner();
    error NotImplemented();

    IPositionManager public immutable positionManager;
    IStateView public immutable stateView;

    constructor(
        IPositionManager positionManager_,
        IStateView stateView_
    ) {
        positionManager = positionManager_;
        stateView = stateView_;
    }

    function addLiquidity(
        NftAddLiquidity memory addLiquidityParams
    )
        external
        override
        checkAmountsForOverflow(
            addLiquidityParams.amount0Desired, addLiquidityParams.amount1Desired
        )
    {
        if (addLiquidityParams.pool.token0 != UNISWAP_ETH) {
            IERC20(addLiquidityParams.pool.token0)
                .approve(address(PERMIT_2), addLiquidityParams.amount0Desired);
            IAllowanceTransfer(PERMIT_2)
                .approve(
                    addLiquidityParams.pool.token0,
                    address(addLiquidityParams.nft),
                    uint160(addLiquidityParams.amount0Desired),
                    uint48(block.timestamp)
                );
        }
        IERC20(addLiquidityParams.pool.token1)
            .approve(address(PERMIT_2), addLiquidityParams.amount1Desired);
        IAllowanceTransfer(PERMIT_2)
            .approve(
                addLiquidityParams.pool.token1,
                address(addLiquidityParams.nft),
                uint160(addLiquidityParams.amount1Desired),
                uint48(block.timestamp)
            );

        if (addLiquidityParams.tokenId == 0) {
            _mint(addLiquidityParams);
        } else {
            _increaseLiquidity(addLiquidityParams);
        }

        // Revoke approvals
        if (addLiquidityParams.pool.token0 != UNISWAP_ETH) {
            IERC20(addLiquidityParams.pool.token0).approve(address(PERMIT_2), 0);
        }
        IERC20(addLiquidityParams.pool.token1).approve(address(PERMIT_2), 0);
    }

    function _getCurrentLiquidity(
        NftRemoveLiquidity memory removeLiquidityParams
    ) internal view virtual returns (uint128 currentLiquidity) {
        return IPositionManager(address(removeLiquidityParams.nft))
            .getPositionLiquidity(removeLiquidityParams.tokenId);
    }

    function removeLiquidity(
        NftRemoveLiquidity memory removeLiquidityParams
    )
        external
        override
        checkAmountsForOverflow(
            removeLiquidityParams.amount0Min, removeLiquidityParams.amount1Min
        )
    {
        uint128 currentLiquidity = _getCurrentLiquidity(removeLiquidityParams);
        if (removeLiquidityParams.liquidity == type(uint128).max) {
            removeLiquidityParams.liquidity = currentLiquidity;
        }

        if (removeLiquidityParams.liquidity == 0) {
            revert InvalidParameters();
        }

        if (removeLiquidityParams.liquidity == currentLiquidity) {
            _burnNft(removeLiquidityParams);
        } else {
            _decreaseLiquidity(removeLiquidityParams);
        }
    }

    function depositExistingNft(
        NftPosition calldata, // position,
        bytes calldata // extraData
    ) external virtual override { }

    function withdrawNft(
        NftPosition calldata, // position,
        bytes calldata // extraData
    ) external virtual override { }

    function claim(
        NftPosition calldata position,
        address[] memory, // rewardTokens
        uint128, // amount0Max
        uint128, // amount1Max
        bytes calldata extraData
    ) external virtual override {
        _collect(
            IPositionManager(address(position.nft)), position.tokenId, extraData
        );
    }

    function fee(
        address, // pool
        uint256 tokenId
    ) external view virtual override returns (uint24) {
        (PoolKey memory poolKey,) =
            positionManager.getPoolAndPositionInfo(tokenId);
        (,,, uint24 lpFee) = stateView.getSlot0(poolKey.toId());
        return lpFee;
    }

    function poolInfo(
        address, // pool
        bytes32 poolId
    ) external view virtual override returns (NftPoolInfo memory) {
        (uint160 sqrtPriceX96, int24 tick_,, uint24 lpFee) =
            stateView.getSlot0(PoolId.wrap(poolId));
        (uint256 feeGrowthGlobal0X128, uint256 feeGrowthGlobal1X128) =
            stateView.getFeeGrowthGlobals(PoolId.wrap(poolId));
        uint128 liquidity = stateView.getLiquidity(PoolId.wrap(poolId));
        PoolKey memory poolKey = positionManager.poolKeys(bytes25(poolId));

        return NftPoolInfo({
            token0: Currency.unwrap(poolKey.currency0),
            token1: Currency.unwrap(poolKey.currency1),
            fee: lpFee,
            tickSpacing: uint24(poolKey.tickSpacing),
            sqrtPriceX96: sqrtPriceX96,
            tick: tick_,
            liquidity: liquidity,
            feeGrowthGlobal0X128: feeGrowthGlobal0X128,
            feeGrowthGlobal1X128: feeGrowthGlobal1X128
        });
    }

    function positionLiquidity(
        address, // nftManager
        uint256 tokenId
    )
        public
        view
        virtual
        override
        returns (int24 tickLower, int24 tickUpper, uint128 liquidity)
    {
        PositionInfo positionInfo_ = positionManager.positionInfo(tokenId);
        tickLower = positionInfo_.tickLower();
        tickUpper = positionInfo_.tickUpper();
        liquidity = positionManager.getPositionLiquidity(tokenId);
    }

    function positionPoolKey(
        address, // poolFactory
        address, // nftManager
        uint256 tokenId
    ) external view virtual override returns (NftPoolKey memory) {
        (PoolKey memory poolKey,) =
            positionManager.getPoolAndPositionInfo(tokenId);
        return NftPoolKey({
            poolAddress: address(0), // not used by Uniswap V4
            poolId: PoolId.unwrap(poolKey.toId())
        });
    }

    function getTokenId(
        address, // nftManager
        address owner
    ) external view virtual override returns (uint256 tokenId) {
        tokenId = positionManager.nextTokenId() - 1;
        if (positionManager.ownerOf(tokenId) != owner) {
            revert InvalidTokenOwner();
        }
        return tokenId;
    }

    function totalSupply(
        address // nftManager
    ) external view virtual override returns (uint256) {
        return positionManager.nextTokenId() - 1;
    }

    function _mint(
        NftAddLiquidity memory addLiquidityParams
    ) internal virtual {
        UniswapV4MintExtraData memory extraData =
            abi.decode(addLiquidityParams.extraData, (UniswapV4MintExtraData));

        bytes memory actions = abi.encodePacked(
            uint8(Actions.MINT_POSITION),
            uint8(Actions.SETTLE_PAIR),
            uint8(Actions.SWEEP)
        );
        bytes[] memory params = new bytes[](3);

        Currency currency0 = Currency.wrap(addLiquidityParams.pool.token0);
        Currency currency1 = Currency.wrap(addLiquidityParams.pool.token1);
        PoolKey memory poolKey = PoolKey({
            currency0: currency0,
            currency1: currency1,
            fee: addLiquidityParams.pool.fee,
            tickSpacing: int24(extraData.tickSpacing),
            hooks: IHooks(extraData.hooks)
        });

        params[0] = abi.encode(
            poolKey,
            addLiquidityParams.tickLower,
            addLiquidityParams.tickUpper,
            extraData.liquidity,
            addLiquidityParams.amount0Desired,
            addLiquidityParams.amount1Desired,
            address(this),
            extraData.hookData
        );
        params[1] = abi.encode(currency0, currency1);
        params[2] = abi.encode(UNISWAP_ETH, address(this));

        uint256 valueToPass =
            currency0.isAddressZero() ? addLiquidityParams.amount0Desired : 0;

        IPositionManager(address(addLiquidityParams.nft))
        .modifyLiquidities{
            value: valueToPass
        }(abi.encode(actions, params), block.timestamp);
    }

    function _increaseLiquidity(
        NftAddLiquidity memory addLiquidityParams
    ) internal {
        UniswapV4MintExtraData memory extraData =
            abi.decode(addLiquidityParams.extraData, (UniswapV4MintExtraData));

        Currency currency0 = Currency.wrap(addLiquidityParams.pool.token0);
        Currency currency1 = Currency.wrap(addLiquidityParams.pool.token1);

        // Determine which currencies need to be settled based on amounts
        bool needsToken0 = addLiquidityParams.amount0Desired > 0;
        bool needsToken1 = addLiquidityParams.amount1Desired > 0;

        bytes memory actions;
        bytes[] memory params;

        if (needsToken0 && needsToken1) {
            // Both tokens needed - use SETTLE_PAIR
            actions = abi.encodePacked(
                uint8(Actions.INCREASE_LIQUIDITY),
                uint8(Actions.SETTLE_PAIR),
                uint8(Actions.SWEEP)
            );
            params = new bytes[](3);
            params[0] = abi.encode(
                addLiquidityParams.tokenId,
                extraData.liquidity,
                addLiquidityParams.amount0Desired,
                addLiquidityParams.amount1Desired,
                extraData.hookData
            );
            params[1] = abi.encode(currency0, currency1);
            params[2] = abi.encode(UNISWAP_ETH, address(this));
        } else {
            // Only one token needed - use individual SETTLE and CLOSE_CURRENCY
            Currency currencyToSettle = needsToken0 ? currency0 : currency1;
            Currency currencyToClose = needsToken0 ? currency1 : currency0;
            actions = abi.encodePacked(
                uint8(Actions.INCREASE_LIQUIDITY),
                uint8(Actions.SETTLE),
                uint8(Actions.CLOSE_CURRENCY),
                uint8(Actions.SWEEP)
            );
            params = new bytes[](4);
            params[0] = abi.encode(
                addLiquidityParams.tokenId,
                extraData.liquidity,
                addLiquidityParams.amount0Desired,
                addLiquidityParams.amount1Desired,
                extraData.hookData
            );
            // SETTLE params: (Currency currency, uint256 amount, bool
            // payerIsUser) amount: 0 (ActionConstants.OPEN_DELTA) means settle
            // all outstanding delta
            // payerIsUser: false means payer is the contract (this)
            params[1] = abi.encode(currencyToSettle, uint256(0), false);
            // CLOSE_CURRENCY params: (Currency currency)
            params[2] = abi.encode(currencyToClose);
            params[3] = abi.encode(UNISWAP_ETH, address(this));
        }

        uint256 valueToPass =
            currency0.isAddressZero() ? addLiquidityParams.amount0Desired : 0;
        IPositionManager(address(addLiquidityParams.nft))
        .modifyLiquidities{
            value: valueToPass
        }(abi.encode(actions, params), block.timestamp);
    }

    function _burnNft(
        NftRemoveLiquidity memory removeLiquidityParams
    ) internal {
        UniswapV4RemoveExtraData memory extraData = abi.decode(
            removeLiquidityParams.extraData, (UniswapV4RemoveExtraData)
        );

        bytes memory actions = abi.encodePacked(
            uint8(Actions.BURN_POSITION), uint8(Actions.TAKE_PAIR)
        );
        bytes[] memory params = new bytes[](2);
        params[0] = abi.encode(
            removeLiquidityParams.tokenId,
            uint128(0),
            uint128(0),
            extraData.hookData
        );
        params[1] =
            abi.encode(extraData.currency0, extraData.currency1, address(this));
        IPositionManager(address(removeLiquidityParams.nft))
            .modifyLiquidities(abi.encode(actions, params), block.timestamp);
    }

    function _decreaseLiquidity(
        NftRemoveLiquidity memory removeLiquidityParams
    ) internal {
        UniswapV4RemoveExtraData memory extraData = abi.decode(
            removeLiquidityParams.extraData, (UniswapV4RemoveExtraData)
        );

        bytes memory actions = abi.encodePacked(
            uint8(Actions.DECREASE_LIQUIDITY), uint8(Actions.TAKE_PAIR)
        );

        bytes[] memory params = new bytes[](2);
        params[0] = abi.encode(
            removeLiquidityParams.tokenId,
            removeLiquidityParams.liquidity,
            removeLiquidityParams.amount0Min,
            removeLiquidityParams.amount1Min,
            extraData.hookData
        );

        params[1] =
            abi.encode(extraData.currency0, extraData.currency1, address(this));

        IPositionManager(address(removeLiquidityParams.nft))
            .modifyLiquidities(abi.encode(actions, params), block.timestamp);
    }

    function _collect(
        IPositionManager nft,
        uint256 tokenId,
        bytes memory extraData
    ) internal {
        UniswapV4RemoveExtraData memory removeExtraData =
            abi.decode(extraData, (UniswapV4RemoveExtraData));

        bytes memory actions = abi.encodePacked(
            uint8(Actions.DECREASE_LIQUIDITY), uint8(Actions.TAKE_PAIR)
        );

        bytes[] memory params = new bytes[](2);
        params[0] = abi.encode(tokenId, 0, 0, 0, removeExtraData.hookData);

        params[1] = abi.encode(
            removeExtraData.currency0, removeExtraData.currency1, address(this)
        );

        nft.modifyLiquidities(abi.encode(actions, params), block.timestamp);
    }

    function isStaked(
        address, // user
        NftPosition calldata
    ) external view virtual override returns (bool) {
        return false; // Uniswap V4 does not support staking
    }

    function earned(
        address, // user
        NftPosition calldata, // position
        address[] memory rewardTokens
    ) external view virtual override returns (uint256[] memory) {
        // Uniswap V4 does not support token incentives
        return new uint256[](rewardTokens.length);
    }

    modifier checkAmountsForOverflow(
        uint256 amount0,
        uint256 amount1
    ) {
        if (amount0 > type(uint128).max) {
            revert InvalidDesiredAmount();
        }
        if (amount1 > type(uint128).max) {
            revert InvalidDesiredAmount();
        }
        _;
    }

    function earnedFees(
        address, // nftManager
        address, // pool
        uint256 tokenId
    ) external view override returns (uint256 fees0, uint256 fees1) {
        (PoolKey memory poolKey,) =
            positionManager.getPoolAndPositionInfo(tokenId);

        (int24 tickLower, int24 tickUpper, uint128 liquidity) =
            positionLiquidity(address(0), tokenId);

        PoolId poolId = PoolId.wrap(PoolId.unwrap(poolKey.toId()));
        (
            uint256 poolFeeGrowthInside0LastX128,
            uint256 poolFeeGrowthInside1LastX128
        ) = stateView.getFeeGrowthInside(poolId, tickLower, tickUpper);

        bytes32 positionKey = Position.calculatePositionKey(
            address(positionManager), tickLower, tickUpper, bytes32(tokenId)
        );
        (
            ,
            uint256 positionFeeGrowthInside0LastX128,
            uint256 positionFeeGrowthInside1LastX128
        ) = stateView.getPositionInfo(poolId, positionKey);

        unchecked {
            fees0 = FullMath.mulDiv(
                poolFeeGrowthInside0LastX128 - positionFeeGrowthInside0LastX128,
                liquidity,
                FixedPoint128.Q128
            );

            fees1 = FullMath.mulDiv(
                poolFeeGrowthInside1LastX128 - positionFeeGrowthInside1LastX128,
                liquidity,
                FixedPoint128.Q128
            );
        }
    }

    function positionInfo(
        address nftManager,
        uint256 tokenId
    ) external view virtual override returns (NftPositionInfo memory) {
        (int24 tickLower, int24 tickUpper, uint128 liquidity) =
            positionLiquidity(nftManager, tokenId);
        return NftPositionInfo({
            liquidity: liquidity, tickLower: tickLower, tickUpper: tickUpper
        });
    }
}
"
    },
    "lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v4.6.0) (token/ERC20/IERC20.sol)

pragma solidity ^0.8.0;

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

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

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

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

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

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

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

    /**
     * @dev Moves `amount` tokens from `from` to `to` using the
     * allowance mechanism. `amount` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(
        address from,
        address to,
        uint256 amount
    ) external returns (bool);
}
"
    },
    "contracts/interfaces/external/uniswap/v4/IPositionManager.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { PoolKey } from "./types/PoolKey.sol";
import { PositionInfo } from "./libraries/PositionInfoLibrary.sol";
import { IPoolManager } from "./IPoolManager.sol";

interface IImmutableState {
    /// @notice The Uniswap v4 PoolManager contract
    function poolManager() external view returns (IPoolManager);
}

/// @title IPositionManager
/// @notice Interface for the PositionManager contract
interface IPositionManager is IImmutableState {
    /// @notice Thrown when the caller is not approved to modify a position
    error NotApproved(address caller);
    /// @notice Thrown when the block.timestamp exceeds the user-provided
    /// deadline
    error DeadlinePassed(uint256 deadline);
    /// @notice Thrown when calling transfer, subscribe, or unsubscribe when the
    /// PoolManager is unlocked.
    /// @dev This is to prevent hooks from being able to trigger notifications
    /// at the same time the position is being modified.
    error PoolManagerMustBeLocked();

    /// @notice Unlocks Uniswap v4 PoolManager and batches actions for modifying
    /// liquidity
    /// @dev This is the standard entrypoint for the PositionManager
    /// @param unlockData is an encoding of actions, and parameters for those
    /// actions
    /// @param deadline is the deadline for the batched actions to be executed
    function modifyLiquidities(
        bytes calldata unlockData,
        uint256 deadline
    ) external payable;

    /// @notice Batches actions for modifying liquidity without unlocking v4
    /// PoolManager
    /// @dev This must be called by a contract that has already unlocked the v4
    /// PoolManager
    /// @param actions the actions to perform
    /// @param params the parameters to provide for the actions
    function modifyLiquiditiesWithoutUnlock(
        bytes calldata actions,
        bytes[] calldata params
    ) external payable;

    /// @notice Used to get the ID that will be used for the next minted
    /// liquidity position
    /// @return uint256 The next token ID
    function nextTokenId() external view returns (uint256);

    /// @notice Returns the liquidity of a position
    /// @param tokenId the ERC721 tokenId
    /// @return liquidity the position's liquidity, as a liquidityAmount
    /// @dev this value can be processed as an amount0 and amount1 by using the
    /// LiquidityAmounts library
    function getPositionLiquidity(
        uint256 tokenId
    ) external view returns (uint128 liquidity);

    /// @notice Returns the pool key and position info of a position
    /// @param tokenId the ERC721 tokenId
    /// @return poolKey the pool key of the position
    /// @return PositionInfo a uint256 packed value holding information about
    /// the position including the range (tickLower, tickUpper)
    function getPoolAndPositionInfo(
        uint256 tokenId
    ) external view returns (PoolKey memory, PositionInfo);

    /// @notice Returns the position info of a position
    /// @param tokenId the ERC721 tokenId
    /// @return a uint256 packed value holding information about the position
    /// including the range (tickLower, tickUpper)
    function positionInfo(
        uint256 tokenId
    ) external view returns (PositionInfo);

    /// @notice Returns the pool key of a pool
    /// @param poolId the pool ID
    /// @return poolKey the pool key of the pool
    function poolKeys(
        bytes25 poolId
    ) external view returns (PoolKey memory);

    /// @notice Returns the owner of a token
    /// @param tokenId the ERC721 tokenId
    /// @return owner the owner of the token
    function ownerOf(
        uint256 tokenId
    ) external view returns (address);
}
"
    },
    "contracts/interfaces/external/uniswap/v4/IHooks.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { PoolKey } from "./types/PoolKey.sol";
import { BalanceDelta } from "./types/BalanceDelta.sol";
import { IPoolManager } from "./IPoolManager.sol";
import { BeforeSwapDelta } from "./types/BeforeSwapDelta.sol";

/// @notice V4 decides whether to invoke specific hooks by inspecting the least
/// significant bits
/// of the address that the hooks contract is deployed to.
/// For example, a hooks contract deployed to address:
/// 0x0000000000000000000000000000000000002400
/// has the lowest bits '10 0100 0000 0000' which would cause the 'before
/// initialize' and 'after add liquidity' hooks to be used.
/// See the Hooks library for the full spec.
/// @dev Should only be callable by the v4 PoolManager.
interface IHooks {
    /// @notice The hook called before the state of a pool is initialized
    /// @param sender The initial msg.sender for the initialize call
    /// @param key The key for the pool being initialized
    /// @param sqrtPriceX96 The sqrt(price) of the pool as a Q64.96
    /// @return bytes4 The function selector for the hook
    function beforeInitialize(
        address sender,
        PoolKey calldata key,
        uint160 sqrtPriceX96
    ) external returns (bytes4);

    /// @notice The hook called after the state of a pool is initialized
    /// @param sender The initial msg.sender for the initialize call
    /// @param key The key for the pool being initialized
    /// @param sqrtPriceX96 The sqrt(price) of the pool as a Q64.96
    /// @param tick The current tick after the state of a pool is initialized
    /// @return bytes4 The function selector for the hook
    function afterInitialize(
        address sender,
        PoolKey calldata key,
        uint160 sqrtPriceX96,
        int24 tick
    ) external returns (bytes4);

    /// @notice The hook called before liquidity is added
    /// @param sender The initial msg.sender for the add liquidity call
    /// @param key The key for the pool
    /// @param params The parameters for adding liquidity
    /// @param hookData Arbitrary data handed into the PoolManager by the
    /// liquidity provider to be passed on to the hook
    /// @return bytes4 The function selector for the hook
    function beforeAddLiquidity(
        address sender,
        PoolKey calldata key,
        IPoolManager.ModifyLiquidityParams calldata params,
        bytes calldata hookData
    ) external returns (bytes4);

    /// @notice The hook called after liquidity is added
    /// @param sender The initial msg.sender for the add liquidity call
    /// @param key The key for the pool
    /// @param params The parameters for adding liquidity
    /// @param delta The caller's balance delta after adding liquidity; the sum
    /// of principal delta, fees accrued, and hook delta
    /// @param feesAccrued The fees accrued since the last time fees were
    /// collected from this position
    /// @param hookData Arbitrary data handed into the PoolManager by the
    /// liquidity provider to be passed on to the hook
    /// @return bytes4 The function selector for the hook
    /// @return BalanceDelta The hook's delta in token0 and token1. Positive:
    /// the hook is owed/took currency, negative: the hook owes/sent currency
    function afterAddLiquidity(
        address sender,
        PoolKey calldata key,
        IPoolManager.ModifyLiquidityParams calldata params,
        BalanceDelta delta,
        BalanceDelta feesAccrued,
        bytes calldata hookData
    ) external returns (bytes4, BalanceDelta);

    /// @notice The hook called before liquidity is removed
    /// @param sender The initial msg.sender for the remove liquidity call
    /// @param key The key for the pool
    /// @param params The parameters for removing liquidity
    /// @param hookData Arbitrary data handed into the PoolManager by the
    /// liquidity provider to be be passed on to the hook
    /// @return bytes4 The function selector for the hook
    function beforeRemoveLiquidity(
        address sender,
        PoolKey calldata key,
        IPoolManager.ModifyLiquidityParams calldata params,
        bytes calldata hookData
    ) external returns (bytes4);

    /// @notice The hook called after liquidity is removed
    /// @param sender The initial msg.sender for the remove liquidity call
    /// @param key The key for the pool
    /// @param params The parameters for removing liquidity
    /// @param delta The caller's balance delta after removing liquidity; the
    /// sum of principal delta, fees accrued, and hook delta
    /// @param feesAccrued The fees accrued since the last time fees were
    /// collected from this position
    /// @param hookData Arbitrary data handed into the PoolManager by the
    /// liquidity provider to be be passed on to the hook
    /// @return bytes4 The function selector for the hook
    /// @return BalanceDelta The hook's delta in token0 and token1. Positive:
    /// the hook is owed/took currency, negative: the hook owes/sent currency
    function afterRemoveLiquidity(
        address sender,
        PoolKey calldata key,
        IPoolManager.ModifyLiquidityParams calldata params,
        BalanceDelta delta,
        BalanceDelta feesAccrued,
        bytes calldata hookData
    ) external returns (bytes4, BalanceDelta);

    /// @notice The hook called before a swap
    /// @param sender The initial msg.sender for the swap call
    /// @param key The key for the pool
    /// @param params The parameters for the swap
    /// @param hookData Arbitrary data handed into the PoolManager by the
    /// swapper to be be passed on to the hook
    /// @return bytes4 The function selector for the hook
    /// @return BeforeSwapDelta The hook's delta in specified and unspecified
    /// currencies. Positive: the hook is owed/took currency, negative: the hook
    /// owes/sent currency
    /// @return uint24 Optionally override the lp fee, only used if three
    /// conditions are met: 1. the Pool has a dynamic fee, 2. the value's 2nd
    /// highest bit is set (23rd bit, 0x400000), and 3. the value is less than
    /// or equal to the maximum fee (1 million)
    function beforeSwap(
        address sender,
        PoolKey calldata key,
        IPoolManager.SwapParams calldata params,
        bytes calldata hookData
    ) external returns (bytes4, BeforeSwapDelta, uint24);

    /// @notice The hook called after a swap
    /// @param sender The initial msg.sender for the swap call
    /// @param key The key for the pool
    /// @param params The parameters for the swap
    /// @param delta The amount owed to the caller (positive) or owed to the
    /// pool (negative)
    /// @param hookData Arbitrary data handed into the PoolManager by the
    /// swapper to be be passed on to the hook
    /// @return bytes4 The function selector for the hook
    /// @return int128 The hook's delta in unspecified currency. Positive: the
    /// hook is owed/took currency, negative: the hook owes/sent currency
    function afterSwap(
        address sender,
        PoolKey calldata key,
        IPoolManager.SwapParams calldata params,
        BalanceDelta delta,
        bytes calldata hookData
    ) external returns (bytes4, int128);

    /// @notice The hook called before donate
    /// @param sender The initial msg.sender for the donate call
    /// @param key The key for the pool
    /// @param amount0 The amount of token0 being donated
    /// @param amount1 The amount of token1 being donated
    /// @param hookData Arbitrary data handed into the PoolManager by the donor
    /// to be be passed on to the hook
    /// @return bytes4 The function selector for the hook
    function beforeDonate(
        address sender,
        PoolKey calldata key,
        uint256 amount0,
        uint256 amount1,
        bytes calldata hookData
    ) external returns (bytes4);

    /// @notice The hook called after donate
    /// @param sender The initial msg.sender for the donate call
    /// @param key The key for the pool
    /// @param amount0 The amount of token0 being donated
    /// @param amount1 The amount of token1 being donated
    /// @param hookData Arbitrary data handed into the PoolManager by the donor
    /// to be be passed on to the hook
    /// @return bytes4 The function selector for the hook
    function afterDonate(
        address sender,
        PoolKey calldata key,
        uint256 amount0,
        uint256 amount1,
        bytes calldata hookData
    ) external returns (bytes4);
}
"
    },
    "contracts/interfaces/external/uniswap/v4/IStateView.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { PoolId } from
    "contracts/interfaces/external/uniswap/v4/types/PoolId.sol";
import { IPoolManager } from
    "contracts/interfaces/external/uniswap/v4/IPoolManager.sol";

/// @title IImmutableState
/// @notice Interface for the ImmutableState contract
interface IImmutableState {
    /// @notice The Uniswap v4 PoolManager contract
    function poolManager() external view returns (IPoolManager);
}

/// @title IStateView
/// @notice Interface for the StateView contract
interface IStateView is IImmutableState {
    /// @notice Get Slot0 of the pool: sqrtPriceX96, tick, protocolFee, lpFee
    /// @dev Corresponds to pools[poolId].slot0
    /// @param poolId The ID of the pool.
    /// @return sqrtPriceX96 The square root of the price of the pool, in Q96
    /// precision.
    /// @return tick The current tick of the pool.
    /// @return protocolFee The protocol fee of the pool.
    /// @return lpFee The swap fee of the pool.
    function getSlot0(
        PoolId poolId
    )
        external
        view
        returns (
            uint160 sqrtPriceX96,
            int24 tick,
            uint24 protocolFee,
            uint24 lpFee
        );

    /// @notice Retrieves the tick information of a pool at a specific tick.
    /// @dev Corresponds to pools[poolId].ticks[tick]
    /// @param poolId The ID of the pool.
    /// @param tick The tick to retrieve information for.
    /// @return liquidityGross The total position liquidity that references this
    /// tick
    /// @return liquidityNet The amount of net liquidity added (subtracted) when
    /// tick is crossed from left to right (right to left)
    /// @return feeGrowthOutside0X128 fee growth per unit of liquidity on the
    /// _other_ side of this tick (relative to the current tick)
    /// @return feeGrowthOutside1X128 fee growth per unit of liquidity on the
    /// _other_ side of this tick (relative to the current tick)
    function getTickInfo(
        PoolId poolId,
        int24 tick
    )
        external
        view
        returns (
            uint128 liquidityGross,
            int128 liquidityNet,
            uint256 feeGrowthOutside0X128,
            uint256 feeGrowthOutside1X128
        );

    /// @notice Retrieves the liquidity information of a pool at a specific
    /// tick.
    /// @dev Corresponds to pools[poolId].ticks[tick].liquidityGross and
    /// pools[poolId].ticks[tick].liquidityNet. A more gas efficient version of
    /// getTickInfo
    /// @param poolId The ID of the pool.
    /// @param tick The tick to retrieve liquidity for.
    /// @return liquidityGross The total position liquidity that references this
    /// tick
    /// @return liquidityNet The amount of net liquidity added (subtracted) when
    /// tick is crossed from left to right (right to left)
    function getTickLiquidity(
        PoolId poolId,
        int24 tick
    ) external view returns (uint128 liquidityGross, int128 liquidityNet);

    /// @notice Retrieves the fee growth outside a tick range of a pool
    /// @dev Corresponds to pools[poolId].ticks[tick].feeGrowthOutside0X128 and
    /// pools[poolId].ticks[tick].feeGrowthOutside1X128. A more gas efficient
    /// version of getTickInfo
    /// @param poolId The ID of the pool.
    /// @param tick The tick to retrieve fee growth for.
    /// @return feeGrowthOutside0X128 fee growth per unit of liquidity on the
    /// _other_ side of this tick (relative to the current tick)
    /// @return feeGrowthOutside1X128 fee growth per unit of liquidity on the
    /// _other_ side of this tick (relative to the current tick)
    function getTickFeeGrowthOutside(
        PoolId poolId,
        int24 tick
    )
        external
        view
        returns (uint256 feeGrowthOutside0X128, uint256 feeGrowthOutside1X128);

    /// @notice Retrieves the global fee growth of a pool.
    /// @dev Corresponds to pools[poolId].feeGrowthGlobal0X128 and
    /// pools[poolId].feeGrowthGlobal1X128
    /// @param poolId The ID of the pool.
    /// @return feeGrowthGlobal0 The global fee growth for token0.
    /// @return feeGrowthGlobal1 The global fee growth for token1.
    function getFeeGrowthGlobals(
        PoolId poolId
    )
        external
        view
        returns (uint256 feeGrowthGlobal0, uint256 feeGrowthGlobal1);

    /// @notice Retrieves the total liquidity of a pool.
    /// @dev Corresponds to pools[poolId].liquidity
    /// @param poolId The ID of the pool.
    /// @return liquidity The liquidity of the pool.
    function getLiquidity(
        PoolId poolId
    ) external view returns (uint128 liquidity);

    /// @notice Retrieves the tick bitmap of a pool at a specific tick.
    /// @dev Corresponds to pools[poolId].tickBitmap[tick]
    /// @param poolId The ID of the pool.
    /// @param tick The tick to retrieve the bitmap for.
    /// @return tickBitmap The bitmap of the tick.
    function getTickBitmap(
        PoolId poolId,
        int16 tick
    ) external view returns (uint256 tickBitmap);

    /// @notice Retrieves the position info without needing to calculate the
    /// `positionId`.
    /// @dev Corresponds to pools[poolId].positions[positionId]
    /// @param poolId The ID of the pool.
    /// @param owner The owner of the liquidity position.
    /// @param tickLower The lower tick of the liquidity range.
    /// @param tickUpper The upper tick of the liquidity range.
    /// @param salt The bytes32 randomness to further distinguish position
    /// state.
    /// @return liquidity The liquidity of the position.
    /// @return feeGrowthInside0LastX128 The fee growth inside the position for
    /// token0.
    /// @return feeGrowthInside1LastX128 The fee growth inside the position for
    /// token1.
    function getPositionInfo(
        PoolId poolId,
        address owner,
        int24 tickLower,
        int24 tickUpper,
        bytes32 salt
    )
        external
        view
        returns (
            uint128 liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128
        );

    /// @notice Retrieves the position information of a pool at a specific
    /// position ID.
    /// @dev Corresponds to pools[poolId].positions[positionId]
    /// @param poolId The ID of the pool.
    /// @param positionId The ID of the position.
    /// @return liquidity The liquidity of the position.
    /// @return feeGrowthInside0LastX128 The fee growth inside the position for
    /// token0.
    /// @return feeGrowthInside1LastX128 The fee growth inside the position for
    /// token1.
    function getPositionInfo(
        PoolId poolId,
        bytes32 positionId
    )
        external
        view
        returns (
            uint128 liquidity,
            uint256 feeGrowthInside0LastX128,
            uint256 feeGrowthInside1LastX128
        );

    /// @notice Retrieves the liquidity of a position.
    /// @dev Corresponds to pools[poolId].positions[positionId].liquidity. More
    /// gas efficient for just retrieving liquidity as compared to
    /// getPositionInfo
    /// @param poolId The ID of the pool.
    /// @param positionId The ID of the position.
    /// @return liquidity The liquidity of the position.
    function getPositionLiquidity(
        PoolId poolId,
        bytes32 positionId
    ) external view returns (uint128 liquidity);

    /// @notice Calculate the fee growth inside a tick range of a pool
    /// @dev pools[poolId].feeGrowthInside0LastX128 in Position.Info is cached
    /// and can become stale. This function will calculate the up to date
    /// feeGrowthInside
    /// @param poolId The ID of the pool.
    /// @param tickLower The lower tick of the range.
    /// @param tickUpper The upper tick of the range.
    /// @return feeGrowthInside0X128 The fee growth inside the tick range for
    /// token0.
    /// @return feeGrowthInside1X128 The fee growth inside the tick range for
    /// token1.
    function getFeeGrowthInside(
        PoolId poolId,
        int24 tickLower,
        int24 tickUpper
    )
        external
        view
        returns (uint256 feeGrowthInside0X128, uint256 feeGrowthInside1X128);
}
"
    },
    "contracts/interfaces/external/uniswap/v4/types/PoolId.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

type PoolId is bytes32;

/// @notice Library for computing the ID of a pool
library PoolIdLibrary {
    /// @notice Returns value equal to keccak256(abi.encode(poolKey))
    function toId(
        PoolKey memory poolKey
    ) internal pure returns (PoolId poolId) {
        assembly ("memory-safe") {
            // 0xa0 represents the total size of the poolKey struct (5 slots of
            // 32 bytes)
            poolId := keccak256(poolKey, 0xa0)
        }
    }
}
"
    },
    "contracts/interfaces/external/uniswap/v4/types/PoolKey.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { Currency } from "./Currency.sol";
import { IHooks } from "../IHooks.sol";
import { PoolIdLibrary } from "./PoolId.sol";

using PoolIdLibrary for PoolKey global;

/// @notice Returns the key for identifying a pool
struct PoolKey {
    /// @notice The lower currency of the pool, sorted numerically
    Currency currency0;
    /// @notice The higher currency of the pool, sorted numerically
    Currency currency1;
    /// @notice The pool LP fee, capped at 1_000_000. If the highest bit is 1,
    /// the pool has a dynamic fee and must be exactly equal to 0x800000
    uint24 fee;
    /// @notice Ticks that involve positions must be a multiple of tick spacing
    int24 tickSpacing;
    /// @notice The hooks of the pool
    IHooks hooks;
}
"
    },
    "contracts/interfaces/external/uniswap/v4/libraries/PositionInfoLibrary.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;

import { PoolKey } from "../types/PoolKey.sol";
import { PoolId } from "../types/PoolId.sol";

/**
 * @dev PositionInfo is a packed version of solidity structure.
 * Using the packaged version saves gas and memory by not storing the structure
 * fields in memory slots.
 *
 * Layout:
 * 200 bits poolId | 24 bits tickUpper | 24 bits tickLower | 8 bits
 * hasSubscriber
 *
 * Fields in the direction from the least significant bit:
 *
 * A flag to know if the tokenId is subscribed to an address
 * uint8 hasSubscriber;
 *
 * The tickUpper of the position
 * int24 tickUpper;
 *
 * The tickLower of the position
 * int24 tickLower;
 *
 * The truncated poolId. Truncates a bytes32 value so the most signifcant
 * (highest) 200 bits are used.
 * bytes25 poolId;
 *
 * Note: If more bits are needed, hasSubscriber can be a single bit.
 *
 */
type PositionInfo is uint256;

using PositionInfoLibrary for PositionInfo global;

library PositionInfoLibrary {
    PositionInfo internal constant EMPTY_POSITION_INFO = PositionInfo.wrap(0);

    uint256 internal constant MASK_UPPER_200_BITS =
        0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000000000;
    uint256 internal constant MASK_8_BITS = 0xFF;
    uint24 internal constant MASK_24_BITS = 0xFFFFFF;
    uint256 internal constant SET_UNSUBSCRIBE =
        0xFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00;
    uint256 internal constant SET_SUBSCRIBE = 0x01;
    uint8 internal constant TICK_LOWER_OFFSET = 8;
    uint8 internal constant TICK_UPPER_OFFSET = 32;

    /// @dev This poolId is NOT compatible with the poolId used in UniswapV4
    /// core. It is truncated to 25 bytes, and just used to lookup PoolKey in
    /// the poolKeys mapping.
    function poolId(
        PositionInfo info
    ) internal pure returns (bytes25 _poolId) {
        assembly ("memory-safe") {
            _poolId := and(MASK_UPPER_200_BITS, info)
        }
    }

    function tickLower(
        PositionInfo info
    ) internal pure returns (int24 _tickLower) {
        assembly ("memory-safe") {
            _tickLower := signextend(2, shr(TICK_LOWER_OFFSET, info))
        }
    }

    function tickUpper(
        PositionInfo info
    ) internal pure returns (int24 _tickUpper) {
        assembly ("memory-safe") {
            _tickUpper := signextend(2, shr(TICK_UPPER_OFFSET, info))
        }
    }

    function hasSubscriber(
        PositionInfo info
    ) internal pure returns (bool _hasSubscriber) {
        assembly ("memory-safe") {
            _hasSubscriber := and(MASK_8_BITS, info)
        }
    }

    /// @dev this does not actually set any storage
    function setSubscribe(
        PositionInfo info
    ) internal pure returns (PositionInfo _info) {
        assembly ("memory-safe") {
            _info := or(info, SET_SUBSCRIBE)
        }
    }

    /// @dev this does not actually set any storage
    function setUnsubscribe(
        PositionInfo info
    ) internal pure returns (PositionInfo _info) {
        assembly ("memory-safe") {
            _info := and(info, SET_UNSUBSCRIBE)
        }
    }

    /// @notice Creates the default PositionInfo struct
    /// @dev Called when minting a new position
    /// @param _poolKey the pool key of the position
    /// @param _tickLower the lower tick of the position
    /// @param _tickUpper the upper tick of the position
    /// @return info packed position info, with the truncated poolId and the
    /// hasSubscriber flag set to false
    function initialize(
        PoolKey memory _poolKey,
        int24 _tickLower,
        int24 _tickUpper
    ) internal pure returns (PositionInfo info) {
        bytes25 _poolId = bytes25(PoolId.unwrap(_poolKey.toId()));
        assembly {
            info :=
                or(
                    or(
                        and(MASK_UPPER_200_BITS, _poolId),
                        shl(TICK_UPPER_OFFSET, and(MASK_24_BITS, _tickUpper))
                    ),
                    shl(TICK_LOWER_OFFSET, and(MASK_24_BITS, _tickLower))
                )
        }
    }
}
"
    },
    "contracts/interfaces/external/uniswap/v4/libraries/Actions.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

/// @notice Library to define different pool actions.
/// @dev These are suggested common commands, however additional commands should
/// be defined as required
/// Some of these actions are not supported in the Router contracts or Position
/// Manager contracts, but are left as they may be helpful commands for other
/// peripheral contracts.
library Actions {
    // pool actions
    // liquidity actions
    uint256 internal constant INCREASE_LIQUIDITY = 0x00;
    uint256 internal constant DECREASE_LIQUIDITY = 0x01;
    uint256 internal constant MINT_POSITION = 0x02;
    uint256 internal constant BURN_POSITION = 0x03;
    uint256 internal constant INCREASE_LIQUIDITY_FROM_DELTAS = 0x04;
    uint256 internal constant MINT_POSITION_FROM_DELTAS = 0x05;

    // swapping
    uint256 internal constant SWAP_EXACT_IN_SINGLE = 0x06;
    uint256 internal constant SWAP_EXACT_IN = 0x07;
    uint256 internal constant SWAP_EXACT_OUT_SINGLE = 0x08;
    uint256 internal constant SWAP_EXACT_OUT = 0x09;

    // donate
    // note this is not supported in the position manager or router
    uint256 internal constant DONATE = 0x0a;

    // closing deltas on the pool manager
    // settling
    uint256 internal constant SETTLE = 0x0b;
    uint256 internal constant SETTLE_ALL = 0x0c;
    uint256 internal constant SETTLE_PAIR = 0x0d;
    // taking
    uint256 internal constant TAKE = 0x0e;
    uint256 internal constant TAKE_ALL = 0x0f;
    uint256 internal constant TAKE_PORTION = 0x10;
    uint256 internal constant TAKE_PAIR = 0x11;

    uint256 internal constant CLOSE_CURRENCY = 0x12;
    uint256 internal constant CLEAR_OR_TAKE = 0x13;
    uint256 internal constant SWEEP = 0x14;

    uint256 internal constant WRAP = 0x15;
    uint256 internal constant UNWRAP = 0x16;

    // minting/burning 6909s to close deltas
    // note this is not supported in the position manager or router
    uint256 internal constant MINT_6909 = 0x17;
    uint256 internal constant BURN_6909 = 0x18;
}
"
    },
    "contracts/interfaces/external/uniswap/v4/types/Currency.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import { IERC20Minimal } from "../IERC20Minimal.sol";
import { CustomRevert } from "../libraries/CustomRevert.sol";

type Currency is address;

using {
    greaterThan as >,
    lessThan as <,
    greaterThanOrEqualTo as >=,
    equals as ==
} for Currency global;
using CurrencyLibrary for Currency global;

function equals(Currency currency, Currency other) pure returns (bool) {
    return Currency.unwrap(currency) == Currency.unwrap(other);
}

function greaterThan(Currency currency, Currency other) pure returns (bool) {
    return Currency.unwrap(currency) > Currency.unwrap(other);
}

function lessThan(Currency currency, Currency other) pure returns (bool) {
    return Currency.unwrap(currency) < Currency.unwrap(other);
}

function greaterThanOrEqualTo(
    Currency currency,
    Currency other
) pure returns (bool) {
    return Currency.unwrap(currency) >= Currency.unwrap(other);
}

/// @title CurrencyLibrary
/// @dev This library allows for transferring and holding native tokens and
/// ERC20 tokens
library CurrencyLibrary {
    /// @notice Additional context for ERC-7751 wrapped error when a native
    /// transfer fails
    error NativeTransferFailed();

    /// @notice Additional context for ERC-7751 wrapped error when an ERC20
    /// transfer fails
    error ERC20TransferFailed();

    /// @notice A constant to represent the native currency
    Currency public constant ADDRESS_ZERO = Currency.wrap(address(0));

    function transfer(Currency currency, address to, uint256 amount) internal {
        // altered from
        // https://github.com/transmissions11/solmate/blob/44a9963d4c78111f77caa0e65d677b8b46d6f2e6/src/utils/SafeTransferLib.sol
        // modified custom error selectors

        bool success;
        if (currency.isAddressZero()) {
            assembly ("memory-safe") {
                // Transfer the ETH and revert if it fails.
                success := call(gas(), to, amount, 0, 0, 0, 0)
            }
            // revert with NativeTransferFailed, containing the bubbled up error
            // as an argument
            if (!success) {
                CustomRevert.bubbleUpAndRevertWith(
                    to, bytes4(0), NativeTransferFailed.selector
                );
            }
        } else {
            assembly ("memory-safe") {
                // Get a pointer to some free memory.
                let fmp := mload(0x40)

                // Write the abi-encoded calldata into memory, beginning with
                // the function selector.
                mstore(
                    fmp,
                    0xa9059cbb00000000000000000000000000000000000000000000000000000000
                )
                mstore(
                    add(fmp, 4),
                    and(to, 0xffffffffffffffffffffffffffffffffffffffff)
                ) // Append and mask the "to" argument.
                mstore(add(fmp, 36), amount) // Append the "amount" argument.
                    // Masking not required as it's a full 32 byte type.

                success :=
                    and(
                        // Set success to whether the call reverted, if not we check
                        // it either
                        // returned exactly 1 (can't just be non-zero data), or had
                        // no return data.
                        or(
                            and(eq(mload(0), 1), gt(returndatasize(), 31)),
                            iszero(returndatasize())
                        ),
                        // We use 68 because the length of our calldata totals up
                        // like so: 4 + 32 * 2.
                        // We use 0 and 32 to copy up to 32 bytes of return data
                        // into the scratch space.
                        // Counterintuitively, this call must be positioned second
                        // to the or() call in the
                        // surrounding and() call or else returndatasize() will be
                        // zero during the computation.
                        call(gas(), currency, 0, fmp, 68, 0, 32)
                    )

                // Now clean the memory we used
                mstore(fmp, 0) // 4 byte `selector` and 28 bytes of `to` were
                    // stored here
                mstore(add(fmp, 0x20), 0) // 4 bytes of `to` and 28 bytes of
                    // `amount` were stored here
                mstore(add(fmp, 0x40), 0) // 4 bytes of `amount` were stored
                    // here
            }
            // revert with ERC20TransferFailed, containing the bubbled up error
            // as an argument
            if (!success) {
                CustomRevert.bubbleUpAndRevertWith(
                    Currency.unwrap(currency),
                    IERC20Minimal.transfer.selector,
                    ERC20TransferFailed.selector
                );
            }
        }
    }

    function balanceOfSelf(
        Currency currency
    ) internal view returns (uint256) {
        if (currency.isAddressZero()) {
            return address(this).balance;
        } else {
            return IERC20Minimal(Currency.unwrap(currency)).balanceOf(
                address(this)
            );
        }
    }

    function balanceOf(
        Currency currency,
        address owner
    ) internal view returns (uint256) {
        if (currency.isAddressZero()) {
            return owner.balance;
        } else {
            return IERC20Minimal(Currency.unwrap(currency)).balanceOf(owner);
        }
    }

    function isAddressZero(
        Currency currency
    ) internal pure returns (bool) {
        return Currency.unwrap(currency) == Currency.unwrap(ADDRESS_ZERO);
    }

    function toId(
        Currency currency
    ) internal pure returns (uint256) {
        return uint160(Currency.unwrap(currency));
    }

    // If the upper 12 bytes are non-zero, they will be zero-ed out
    // Therefore, fromId() and toId() are not inverses of each other
    function fromId(
        uint256 id
    ) internal pure returns (Currency) {
        return Currency.wrap(address(uint160(id)));
    }
}
"
    },
    "contracts/interfaces/external/IAllowanceTransfer.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

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

/// @title AllowanceTransfer
/// @notice Handles ERC20 token permissions through signature based allowance
/// setting and ERC20 token transfers by checking allowed amounts
/// @dev Requires user's token approval on the Permit2 contract
interface IAllowanceTransfer {
    //is IEIP712
    /// @notice Thrown when an allowance on a token has expired.
    /// @param deadline The timestamp at which the allowed amount is no longer
    /// valid
    error AllowanceExpired(uint256 deadline);

    /// @notice Thrown when an allowance on a token has been depleted.
    /// @param amount The maximum amount allowed
    error InsufficientAllowance(uint256 amount);

    /// @notice Thrown when too many nonces are invalidated.
    error ExcessiveInvalidation();

    /// @notice Emits an event when the owner successfully invalidates an
    /// ordered nonce.
    event NonceInvalidation(
        address indexed owner,
        address indexed token,
        address indexed spender,
        uint48 newNonce,
        uint48 oldNonce
    );

    /// @notice Emits an event when the owner successfully sets permissions on a
    /// token for the spender.
    event Approval(
        address indexed owner,
        address indexed token,
        address indexed spender,
        uint160 amount,
        uint48 expiration
    );

    /// @notice Emits an event when the owner successfully sets permissions
    /// using a permit signature on a token for the spender.
    event Permit(
        address indexed owner,
        address indexed token,
        address indexed spender,
        uint160 amount,
        uint48 expiration,
        uint48 nonce
    );

    /// @notice Emits an event when the owner sets the allowance back to 0 with
    /// the lockdown function.
    event Lockdown(address indexed owner, address token, address spender);

    /// @notice The permit data for a token
    struct PermitDetails {
        // ERC20 token address
        address token;
        // the maximum amount allowed to spend
        uint160 amount;
        // timestamp at which a spender's token allowances become invalid
        uint48 expiration;
        // an incrementing value indexed per owner,token,and spender for each
        // signature
        uint48 nonce;
    }

    /// @notice The permit message signed for a single token allowance
    struct PermitSingle {
        // the permit data for a single token alownce
        PermitDetails details;
        // address permissioned on the allowed tokens
        address spender;
        // deadline on the permit signature
        uint256 sigDeadline;
    }

    /// @notice The permit message signed for multiple token allowances
    struct PermitBatch {
        // the permit data for multiple token allowances
        PermitDetails[] details;
        // address permissioned on the allowed tokens
        address spender;
        // deadline on the permit signature
        uint256 sigDeadline;
    }

    /// @notice The saved permissions
    /// @dev This info is saved per owner, per token, per spender and all signed
    /// over in the permit message
    /// @dev Setting amount to type(uint160).max sets an unlimited approval
    struct PackedAllowance {
        // amount allowed
        uint160 amount;
        // permission expiry
        uint48 expiration;
        // an incrementing value indexed per owner,token,and spender for each
        // signature
        uint48 nonce;
    }

    /// @notice A token spender pair.
    struct TokenSpenderPair {
        // the token the spender is approved
        address token;
        // the spender address
        address spender;
    }

    /// @notice Details for a token transfer.
    struct AllowanceTransferDetails {
        // the owner of the token
        address from;
        // the recipient of the token
        address to;
        // the amount of the token
        uint160 amount;
        // the token to be transferred
        address token;
    }

    /// @notice A mapping from owner address to token address to spender address
    /// to PackedAllowance struct, which contains details and conditions of the
    /// approval.
    /// @notice The mapping is indexed in the above order see:
    /// allowance[ownerAddress][tokenAddress][spenderAddress]
    /// @dev The packed slot holds the allowed amount, expiration at which the
    /// allowed amount is no longer valid, and current nonce thats updated on
    /// any signature based approvals.
    function allowance(
        address user,
        address token,
        address spender
    ) external view returns (uint160 amount, uint48 expiration, uint48 nonce);

    /// @notice Approves the spender to use up to amount of the specified token
    /// up until the expiration
    /// @param token The token to approve
    /// @param spender The spender address to approve
    /// @param amount The approved amount of the token
    /// @param expiration The timestamp at which the approval is no longer valid
    /// @dev The packed allowance also holds a nonce, which will stay unchanged
    /// in approve
    /// @dev Setting amount to type(uint160).max sets an unlimited approval
    function approve(
        address token,
        address spender,
        uint160 amount,
        uint48 expiration
    ) external;

    /// @notice Permit a spender to a given amount of the owners token via the
    /// owner's EIP-712 signature
    /// @dev May fail if the owner's nonce was invalidated in-flight by
    /// invalidateNonce
    /// @param owner The owner of the tokens being approved
    /// @param permitSingle Data signed over by the owner specifying the terms
    /// of approval
    /// @param signature The owner's signature over the permit data
    function permit(
        address owner,
        PermitSingle memory permitSingle,
        bytes calldata signature
    ) external;

    /// @notice Permit a spender to the signed amounts of the owners tokens via
    /// the owner's EIP-712 signature
    /// @dev May fail if the owner's nonce was invalidated in-flight by
    /// invalidateNonce
    /// @param owner The owner of the tokens being approved
    /// @param permitBatch Data signed over by the owner specifying the terms of
    /// approval
    /// @param signature The owner's signature over the permit data
    function permit(
        address owner,
        PermitBatch memory permitBatch,
        bytes calldata signature
    ) external;

    /// @notice Transfer approved tokens from one address to another
    /// @param from The address to transfer from
    /// @param to The address of the recipient
    /// @param amount The amount of the token to transfer
    /// @param token The token address to transfer
    /// @dev Requires the from address to have approved at least the desired
    /// amount
    /// of tokens to msg.sender.
    function transferFrom(
        address from,
        address to,
        uint160 amount,
        address token
    ) external;

    /// @notice Transfer approved tokens in a batch
    /// @param transferDetails Array of owners, recipients, amounts, and tokens
    /// for the transfers
    /// @dev Requires the from addresses to have approved at least the desired
    /// amount
    /// of tokens to msg.sender.
    function transferFrom(
        AllowanceTransferDetails[] calldata transferDetails
  

Tags:
ERC20, ERC721, ERC165, Multisig, Mintable, Burnable, Non-Fungible, Swap, Liquidity, Staking, Multi-Signature, Factory, Oracle|addr:0x4e8333d289130f1aa0defad0fe761217841f0ca3|verified:true|block:23602839|tx:0x2c95965024f3e118d94e7cebf75a11fb24cb28e892d6f58fcdc3841380f485be|first_check:1760778927

Submitted on: 2025-10-18 11:15:29

Comments

Log in to comment.

No comments yet.