Skip to content

Commit

Permalink
OZ-L13: Docstrings n cleanup (#335)
Browse files Browse the repository at this point in the history
* oz-L13: natspec for action constants

* oz-L13: natspec for BaseHook

* oz-N05: rename ERC721PermitHashLibrary -> ERC721PermitHash

* oz-L13: natspec for ERC721PermitHash

* sb-i69: (nice), natspec for IERC721Permit_v4

* oz-L13: IPositionManager natspec

* oz-L13: natspec immutable state, notifier, permit2forwarder, poolinitializer, pathkey, pooltickscounter

* oz-N09: permit2 forwarder natspec

* rename PathKeyLib for consistency

* oz-L13: natspec for safecallback

* forge fmt
  • Loading branch information
saucepoint authored Sep 4, 2024
1 parent e198c55 commit 4899de7
Show file tree
Hide file tree
Showing 18 changed files with 117 additions and 30 deletions.
4 changes: 2 additions & 2 deletions src/V4Router.sol
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {Currency} from "@uniswap/v4-core/src/types/Currency.sol";
import {TickMath} from "@uniswap/v4-core/src/libraries/TickMath.sol";
import {SafeCast} from "@uniswap/v4-core/src/libraries/SafeCast.sol";

import {PathKey, PathKeyLib} from "./libraries/PathKey.sol";
import {PathKey, PathKeyLibrary} from "./libraries/PathKey.sol";
import {CalldataDecoder} from "./libraries/CalldataDecoder.sol";
import {BipsLibrary} from "./libraries/BipsLibrary.sol";
import {IV4Router} from "./interfaces/IV4Router.sol";
Expand All @@ -25,7 +25,7 @@ import {ActionConstants} from "./libraries/ActionConstants.sol";
abstract contract V4Router is IV4Router, BaseActionsRouter, DeltaResolver {
using SafeCastTemp for *;
using SafeCast for *;
using PathKeyLib for PathKey;
using PathKeyLibrary for PathKey;
using CalldataDecoder for bytes;
using BipsLibrary for uint256;

Expand Down
7 changes: 4 additions & 3 deletions src/base/ERC721Permit_v4.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ pragma solidity ^0.8.0;

import {ERC721} from "solmate/src/tokens/ERC721.sol";
import {EIP712_v4} from "./EIP712_v4.sol";
import {ERC721PermitHashLibrary} from "../libraries/ERC721PermitHash.sol";
import {ERC721PermitHash} from "../libraries/ERC721PermitHash.sol";
import {SignatureVerification} from "permit2/src/libraries/SignatureVerification.sol";

import {IERC721Permit_v4} from "../interfaces/IERC721Permit_v4.sol";
Expand All @@ -17,6 +17,7 @@ abstract contract ERC721Permit_v4 is ERC721, IERC721Permit_v4, EIP712_v4, Unorde
/// @notice Computes the nameHash and versionHash
constructor(string memory name_, string memory symbol_) ERC721(name_, symbol_) EIP712_v4(name_) {}

/// @notice Checks if the block's timestamp is before a signature's deadline
modifier checkSignatureDeadline(uint256 deadline) {
if (block.timestamp > deadline) revert SignatureDeadlineExpired();
_;
Expand All @@ -31,7 +32,7 @@ abstract contract ERC721Permit_v4 is ERC721, IERC721Permit_v4, EIP712_v4, Unorde
// the .verify function checks the owner is non-0
address owner = _ownerOf[tokenId];

bytes32 digest = ERC721PermitHashLibrary.hashPermit(spender, tokenId, nonce, deadline);
bytes32 digest = ERC721PermitHash.hashPermit(spender, tokenId, nonce, deadline);
signature.verify(_hashTypedData(digest), owner);

_useUnorderedNonce(owner, nonce);
Expand All @@ -47,7 +48,7 @@ abstract contract ERC721Permit_v4 is ERC721, IERC721Permit_v4, EIP712_v4, Unorde
uint256 nonce,
bytes calldata signature
) external payable checkSignatureDeadline(deadline) {
bytes32 digest = ERC721PermitHashLibrary.hashPermitForAll(operator, approved, nonce, deadline);
bytes32 digest = ERC721PermitHash.hashPermitForAll(operator, approved, nonce, deadline);
signature.verify(_hashTypedData(digest), owner);

_useUnorderedNonce(owner, nonce);
Expand Down
3 changes: 3 additions & 0 deletions src/base/ImmutableState.sol
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,10 @@ pragma solidity ^0.8.0;

import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";

/// @title Immutable State
/// @notice A collection of immutable state variables, commonly used across multiple contracts
contract ImmutableState {
/// @notice The Uniswap v4 PoolManager contract
IPoolManager public immutable poolManager;

constructor(IPoolManager _poolManager) {
Expand Down
9 changes: 9 additions & 0 deletions src/base/Notifier.sol
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,16 @@ abstract contract Notifier is INotifier {
/// @inheritdoc INotifier
mapping(uint256 tokenId => ISubscriber subscriber) public subscriber;

/// @notice Only allow callers that are approved as spenders or operators of the tokenId
/// @dev to be implemented by the parent contract (PositionManager)
/// @param caller the address of the caller
/// @param tokenId the tokenId of the position
modifier onlyIfApproved(address caller, uint256 tokenId) virtual;

/// @notice Only allow callers that provide the correct config for the tokenId
/// @dev to be implemented by the parent contract (PositionManager)
/// @param tokenId the tokenId of the position
/// @param config the config of the tokenId
modifier onlyValidConfig(uint256 tokenId, PositionConfig calldata config) virtual;

function _positionConfigs(uint256 tokenId) internal view virtual returns (PositionConfigId storage);
Expand Down
7 changes: 7 additions & 0 deletions src/base/Permit2Forwarder.sol
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {IAllowanceTransfer} from "permit2/src/interfaces/IAllowanceTransfer.sol"
/// @notice PermitForwarder allows permitting this contract as a spender on permit2
/// @dev This contract does not enforce the spender to be this contract, but that is the intended use case
contract Permit2Forwarder {
/// @notice the Permit2 contract to forward approvals
IAllowanceTransfer public immutable permit2;

error Wrap__Permit2Reverted(address _permit2, bytes reason);
Expand All @@ -16,6 +17,9 @@ contract Permit2Forwarder {

/// @notice allows forwarding a single permit to permit2
/// @dev this function is payable to allow multicall with NATIVE based actions
/// @param owner the owner of the tokens
/// @param permitSingle the permit data
/// @param signature the signature of the permit; abi.encodePacked(r, s, v)
function permit(address owner, IAllowanceTransfer.PermitSingle calldata permitSingle, bytes calldata signature)
external
payable
Expand All @@ -30,6 +34,9 @@ contract Permit2Forwarder {

/// @notice allows forwarding batch permits to permit2
/// @dev this function is payable to allow multicall with NATIVE based actions
/// @param owner the owner of the tokens
/// @param _permitBatch a batch of approvals
/// @param signature the signature of the permit; abi.encodePacked(r, s, v)
function permitBatch(address owner, IAllowanceTransfer.PermitBatch calldata _permitBatch, bytes calldata signature)
external
payable
Expand Down
7 changes: 7 additions & 0 deletions src/base/PoolInitializer.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ import {ImmutableState} from "./ImmutableState.sol";

import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";

/// @title Pool Initializer
/// @notice Initializes a Uniswap v4 Pool
/// @dev Enables create pool + mint liquidity in a single transaction with multicall
abstract contract PoolInitializer is ImmutableState {
/// @notice Initialize a Uniswap v4 Pool
/// @param key the PoolKey of the pool to initialize
/// @param sqrtPriceX96 the initial sqrtPriceX96 of the pool
/// @param hookData the optional data passed to the hook's initialize functions
function initializePool(PoolKey calldata key, uint160 sqrtPriceX96, bytes calldata hookData)
external
payable
Expand Down
6 changes: 6 additions & 0 deletions src/base/SafeCallback.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,26 @@ import {IUnlockCallback} from "@uniswap/v4-core/src/interfaces/callback/IUnlockC
import {IPoolManager} from "@uniswap/v4-core/src/interfaces/IPoolManager.sol";
import {ImmutableState} from "./ImmutableState.sol";

/// @title Safe Callback
/// @notice A contract that only allows the Uniswap v4 PoolManager to call the unlockCallback
abstract contract SafeCallback is ImmutableState, IUnlockCallback {
/// @notice Thrown when calling unlockCallback where the caller is not PoolManager
error NotPoolManager();

constructor(IPoolManager _poolManager) ImmutableState(_poolManager) {}

/// @notice Only allow calls from the PoolManager contract
modifier onlyPoolManager() {
if (msg.sender != address(poolManager)) revert NotPoolManager();
_;
}

/// @inheritdoc IUnlockCallback
/// @dev We force the onlyPoolManager modifier by exposing a virtual function after the onlyPoolManager check.
function unlockCallback(bytes calldata data) external onlyPoolManager returns (bytes memory) {
return _unlockCallback(data);
}

/// @dev to be implemented by the child contract, to safely guarantee the logic is only executed by the PoolManager
function _unlockCallback(bytes calldata data) internal virtual returns (bytes memory);
}
21 changes: 18 additions & 3 deletions src/base/hooks/BaseHook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {BeforeSwapDelta} from "@uniswap/v4-core/src/types/BeforeSwapDelta.sol";
import {SafeCallback} from "../SafeCallback.sol";

/// @title Base Hook
/// @notice abstract contract for hook implementations
abstract contract BaseHook is IHooks, SafeCallback {
error NotSelf();
error InvalidPool();
Expand All @@ -31,11 +33,14 @@ abstract contract BaseHook is IHooks, SafeCallback {
_;
}

/// @notice Returns a struct of permissions to signal which hook functions are to be implemented
/// @dev Used at deployment to validate the address correctly represents the expected permissions
function getHookPermissions() public pure virtual returns (Hooks.Permissions memory);

// this function is virtual so that we can override it during testing,
// which allows us to deploy an implementation to any address
// and then etch the bytecode into the correct address
/// @notice Validates the deployed hook address agrees with the expected permissions of the hook
/// @dev this function is virtual so that we can override it during testing,
/// which allows us to deploy an implementation to any address
/// and then etch the bytecode into the correct address
function validateHookAddress(BaseHook _this) internal pure virtual {
Hooks.validateHookPermissions(_this, getHookPermissions());
}
Expand All @@ -50,10 +55,12 @@ abstract contract BaseHook is IHooks, SafeCallback {
}
}

/// @inheritdoc IHooks
function beforeInitialize(address, PoolKey calldata, uint160, bytes calldata) external virtual returns (bytes4) {
revert HookNotImplemented();
}

/// @inheritdoc IHooks
function afterInitialize(address, PoolKey calldata, uint160, int24, bytes calldata)
external
virtual
Expand All @@ -62,6 +69,7 @@ abstract contract BaseHook is IHooks, SafeCallback {
revert HookNotImplemented();
}

/// @inheritdoc IHooks
function beforeAddLiquidity(address, PoolKey calldata, IPoolManager.ModifyLiquidityParams calldata, bytes calldata)
external
virtual
Expand All @@ -70,6 +78,7 @@ abstract contract BaseHook is IHooks, SafeCallback {
revert HookNotImplemented();
}

/// @inheritdoc IHooks
function beforeRemoveLiquidity(
address,
PoolKey calldata,
Expand All @@ -79,6 +88,7 @@ abstract contract BaseHook is IHooks, SafeCallback {
revert HookNotImplemented();
}

/// @inheritdoc IHooks
function afterAddLiquidity(
address,
PoolKey calldata,
Expand All @@ -89,6 +99,7 @@ abstract contract BaseHook is IHooks, SafeCallback {
revert HookNotImplemented();
}

/// @inheritdoc IHooks
function afterRemoveLiquidity(
address,
PoolKey calldata,
Expand All @@ -99,6 +110,7 @@ abstract contract BaseHook is IHooks, SafeCallback {
revert HookNotImplemented();
}

/// @inheritdoc IHooks
function beforeSwap(address, PoolKey calldata, IPoolManager.SwapParams calldata, bytes calldata)
external
virtual
Expand All @@ -107,6 +119,7 @@ abstract contract BaseHook is IHooks, SafeCallback {
revert HookNotImplemented();
}

/// @inheritdoc IHooks
function afterSwap(address, PoolKey calldata, IPoolManager.SwapParams calldata, BalanceDelta, bytes calldata)
external
virtual
Expand All @@ -115,6 +128,7 @@ abstract contract BaseHook is IHooks, SafeCallback {
revert HookNotImplemented();
}

/// @inheritdoc IHooks
function beforeDonate(address, PoolKey calldata, uint256, uint256, bytes calldata)
external
virtual
Expand All @@ -123,6 +137,7 @@ abstract contract BaseHook is IHooks, SafeCallback {
revert HookNotImplemented();
}

/// @inheritdoc IHooks
function afterDonate(address, PoolKey calldata, uint256, uint256, bytes calldata)
external
virtual
Expand Down
3 changes: 2 additions & 1 deletion src/interfaces/IERC721Permit_v4.sol
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ interface IERC721Permit_v4 {
/// @param spender The account that is being approved
/// @param tokenId The ID of the token that is being approved for spending
/// @param deadline The deadline timestamp by which the call must be mined for the approve to work
/// @param nonce a unique value, for an owner, to prevent replay attacks; an unordered nonce where the top 248 bits correspond to a word and the bottom 8 bits calculate the bit position of the word
/// @param signature Concatenated data from a valid secp256k1 signature from the holder, i.e. abi.encodePacked(r, s, v)
/// @dev payable so it can be multicalled with NATIVE related actions
function permit(address spender, uint256 tokenId, uint256 deadline, uint256 nonce, bytes calldata signature)
Expand All @@ -23,7 +24,7 @@ interface IERC721Permit_v4 {
/// @param operator The address that will be set as an operator for the owner
/// @param approved The permission to set on the operator
/// @param deadline The deadline timestamp by which the call must be mined for the approve to work
/// @param nonce The nonce of the owner's permit
/// @param nonce a unique value, for an owner, to prevent replay attacks; an unordered nonce where the top 248 bits correspond to a word and the bottom 8 bits calculate the bit position of the word
/// @param signature Concatenated data from a valid secp256k1 signature from the holder, i.e. abi.encodePacked(r, s, v)
/// @dev payable so it can be multicalled with NATIVE related actions
function permitForAll(
Expand Down
6 changes: 6 additions & 0 deletions src/interfaces/IPositionManager.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,17 @@ import {PositionConfig} from "../libraries/PositionConfig.sol";

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

/// @title IPositionManager
/// @notice Interface for the PositionManager contract
interface IPositionManager is INotifier {
/// @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 the caller provides the incorrect PositionConfig for a corresponding tokenId when modifying liquidity
error IncorrectPositionConfigForTokenId(uint256 tokenId);

/// @notice Emitted when a new liquidity position is minted
event MintPosition(uint256 indexed tokenId, PositionConfig config);

/// @notice Unlocks Uniswap v4 PoolManager and batches actions for modifying liquidity
Expand Down
6 changes: 3 additions & 3 deletions src/lens/Quoter.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@ import {PoolKey} from "@uniswap/v4-core/src/types/PoolKey.sol";
import {PoolIdLibrary} from "@uniswap/v4-core/src/types/PoolId.sol";
import {IQuoter} from "../interfaces/IQuoter.sol";
import {PoolTicksCounter} from "../libraries/PoolTicksCounter.sol";
import {PathKey, PathKeyLib} from "../libraries/PathKey.sol";
import {PathKey, PathKeyLibrary} from "../libraries/PathKey.sol";
import {StateLibrary} from "@uniswap/v4-core/src/libraries/StateLibrary.sol";
import {SafeCallback} from "../base/SafeCallback.sol";

contract Quoter is IQuoter, SafeCallback {
using PoolIdLibrary for PoolKey;
using PathKeyLib for PathKey;
using PathKeyLibrary for PathKey;
using StateLibrary for IPoolManager;

/// @dev cache used to check a safety condition in exact output swaps.
Expand Down Expand Up @@ -231,7 +231,7 @@ contract Quoter is IQuoter, SafeCallback {
curAmountOut = i == pathLength ? params.exactAmount : cache.prevAmount;
amountOutCached = curAmountOut;

(PoolKey memory poolKey, bool oneForZero) = PathKeyLib.getPoolAndSwapDirection(
(PoolKey memory poolKey, bool oneForZero) = PathKeyLibrary.getPoolAndSwapDirection(
params.path[i - 1], i == pathLength ? params.exactCurrency : cache.prevCurrency
);

Expand Down
7 changes: 6 additions & 1 deletion src/libraries/ActionConstants.sol
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

/// @title Action Constants
/// @notice Common constants used in actions
/// @dev Constants are gas efficient alternatives to their literal values
library ActionConstants {
/// @notice used to signal that an action should use the input value of the open delta on the pool manager
/// or of the balance that the contract holds
Expand All @@ -9,7 +12,9 @@ library ActionConstants {
/// This value is equivalent to 1<<255, i.e. a singular 1 in the most significant bit.
uint256 internal constant CONTRACT_BALANCE = 0x8000000000000000000000000000000000000000000000000000000000000000;

/// @notice used to signal that the recipient of an action should be the msgSender or address(this)
/// @notice used to signal that the recipient of an action should be the msgSender
address internal constant MSG_SENDER = address(1);

/// @notice used to signal that the recipient of an action should be the address(this)
address internal constant ADDRESS_THIS = address(2);
}
14 changes: 13 additions & 1 deletion src/libraries/ERC721PermitHash.sol
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
// SPDX-License-Identifier: GPL-2.0-or-later
pragma solidity ^0.8.0;

library ERC721PermitHashLibrary {
library ERC721PermitHash {
/// @dev Value is equal to keccak256("Permit(address spender,uint256 tokenId,uint256 nonce,uint256 deadline)");
bytes32 constant PERMIT_TYPEHASH = 0x49ecf333e5b8c95c40fdafc95c1ad136e8914a8fb55e9dc8bb01eaa83a2df9ad;

/// @dev Value is equal to keccak256("PermitForAll(address operator,bool approved,uint256 nonce,uint256 deadline)");
bytes32 constant PERMIT_FOR_ALL_TYPEHASH = 0x6673cb397ee2a50b6b8401653d3638b4ac8b3db9c28aa6870ffceb7574ec2f76;

/// @notice Hashes the data that will be signed for IERC721Permit_v4.permit()
/// @param spender The address which may spend the tokenId
/// @param tokenId The tokenId of the owner, which may be spent by spender
/// @param nonce A unique non-ordered value for each signature to prevent replay attacks
/// @param deadline The time at which the signature expires
/// @return digest The hash of the data to be signed; the equivalent to keccak256(abi.encode(PERMIT_TYPEHASH, spender, tokenId, nonce, deadline));
function hashPermit(address spender, uint256 tokenId, uint256 nonce, uint256 deadline)
internal
pure
Expand All @@ -32,6 +38,12 @@ library ERC721PermitHashLibrary {
}
}

/// @notice Hashes the data that will be signed for IERC721Permit_v4.permit()
/// @param operator The address which may spend any of the owner's tokenIds
/// @param approved true if the operator is to have full permission over the owner's tokenIds; false otherwise
/// @param nonce A unique non-ordered value for each signature to prevent replay attacks
/// @param deadline The time at which the signature expires
/// @return digest The hash of the data to be signed; the equivalent to keccak256(abi.encode(PERMIT_FOR_ALL_TYPEHASH, operator, approved, nonce, deadline));
function hashPermitForAll(address operator, bool approved, uint256 nonce, uint256 deadline)
internal
pure
Expand Down
9 changes: 8 additions & 1 deletion src/libraries/PathKey.sol
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,14 @@ struct PathKey {
bytes hookData;
}

library PathKeyLib {
/// @title PathKey Library
/// @notice Functions for working with PathKeys
library PathKeyLibrary {
/// @notice Get the pool and swap direction for a given PathKey
/// @param params the given PathKey
/// @param currencyIn the input currency
/// @return poolKey the pool key of the swap
/// @return zeroForOne the direction of the swap, true if currency0 is being swapped for currency1
function getPoolAndSwapDirection(PathKey calldata params, Currency currencyIn)
internal
pure
Expand Down
Loading

0 comments on commit 4899de7

Please sign in to comment.