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
Submitted on: 2025-10-18 11:15:29
Comments
Log in to comment.
No comments yet.