diff --git a/periphery/script/Deploy.s.sol b/periphery/script/Deploy.s.sol index d8e96baa..371f9ad0 100644 --- a/periphery/script/Deploy.s.sol +++ b/periphery/script/Deploy.s.sol @@ -11,10 +11,11 @@ import {Router, IPermit2} from "src/Router.sol"; import {BorrowerNFT, IBorrowerURISource} from "src/borrower-nft/BorrowerNFT.sol"; +import {IUniswapPositionNFT} from "src/interfaces/IUniswapPositionNFT.sol"; import {BoostManager} from "src/managers/BoostManager.sol"; import {FrontendManager} from "src/managers/FrontendManager.sol"; import {SimpleManager} from "src/managers/SimpleManager.sol"; -import {UniswapNFTManager, INFTManager} from "src/managers/UniswapNFTManager.sol"; +import {UniswapNFTManager} from "src/managers/UniswapNFTManager.sol"; bytes32 constant TAG = bytes32(uint256(0xA10EBE1A3)); @@ -33,10 +34,10 @@ contract DeployScript is Script { IBorrowerURISource(0x0000000000000000000000000000000000000000) ]; - INFTManager[] uniswapNfts = [ - INFTManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88), - INFTManager(0xC36442b4a4522E871399CD717aBDD847Ab11FE88), - INFTManager(0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1) + IUniswapPositionNFT[] uniswapNfts = [ + IUniswapPositionNFT(0xC36442b4a4522E871399CD717aBDD847Ab11FE88), + IUniswapPositionNFT(0xC36442b4a4522E871399CD717aBDD847Ab11FE88), + IUniswapPositionNFT(0x03a520b32C04BF3bEEf7BEb72E919cf822Ed34f1) ]; IPermit2[] permit2s = [ @@ -49,7 +50,7 @@ contract DeployScript is Script { for (uint256 i = 0; i < chains.length; i++) { Factory factory = factories[i]; IBorrowerURISource uriSource = uriSources[i]; - INFTManager uniswapNft = uniswapNfts[i]; + IUniswapPositionNFT uniswapNft = uniswapNfts[i]; IPermit2 permit2 = permit2s[i]; vm.createSelectFork(vm.rpcUrl(chains[i])); diff --git a/periphery/src/interfaces/IERC721Permit.sol b/periphery/src/interfaces/IERC721Permit.sol deleted file mode 100644 index 3eaf7adf..00000000 --- a/periphery/src/interfaces/IERC721Permit.sol +++ /dev/null @@ -1,30 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0-or-later -pragma solidity 0.8.17; - -import {IERC721} from "openzeppelin-contracts/contracts/token/ERC721/IERC721.sol"; - -/* solhint-disable */ - -/// @title ERC721 with permit -/// @author Uniswap Labs -/// @notice Extension to ERC721 that includes a permit function for signature based approvals -interface IERC721Permit is IERC721 { - /// @notice The permit typehash used in the permit signature - /// @return The typehash for the permit - function PERMIT_TYPEHASH() external pure returns (bytes32); - - /// @notice The domain separator used in the permit signature - /// @return The domain seperator used in encoding of permit signature - function DOMAIN_SEPARATOR() external view returns (bytes32); - - /// @notice Approve of a specific token ID for spending by spender via signature - /// @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 v Must produce valid secp256k1 signature from the holder along with `r` and `s` - /// @param r Must produce valid secp256k1 signature from the holder along with `v` and `s` - /// @param s Must produce valid secp256k1 signature from the holder along with `r` and `v` - function permit(address spender, uint256 tokenId, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external payable; -} - -/* solhint-enable */ diff --git a/periphery/src/interfaces/IPermit2.sol b/periphery/src/interfaces/IPermit2.sol index e01894c0..8e01aecf 100644 --- a/periphery/src/interfaces/IPermit2.sol +++ b/periphery/src/interfaces/IPermit2.sol @@ -6,33 +6,26 @@ import {ERC20} from "solmate/tokens/ERC20.sol"; /// @notice Minimal Permit2 interface, derived from // https://github.com/Uniswap/permit2/blob/main/src/interfaces/ISignatureTransfer.sol interface IPermit2 { - // Token and amount in a permit message. + // Token and amount in a permit message struct TokenPermissions { - // Token to transfer. ERC20 token; - // Amount to transfer. uint256 amount; } - // The permit2 message. + // The permit2 message struct PermitTransferFrom { - // Permitted token and amount. TokenPermissions permitted; - // Unique identifier for this permit. uint256 nonce; - // Expiration for this permit. uint256 deadline; } - // Transfer details for permitTransferFrom(). + // Transfer details for permitTransferFrom() struct SignatureTransferDetails { - // Recipient of tokens. address to; - // Amount to transfer. uint256 requestedAmount; } - // Consume a permit2 message and transfer tokens. + // Consume a permit2 message and transfer tokens function permitTransferFrom( PermitTransferFrom calldata permit, SignatureTransferDetails calldata transferDetails, diff --git a/periphery/src/interfaces/INonfungiblePositionManager.sol b/periphery/src/interfaces/IUniswapPositionNFT.sol similarity index 71% rename from periphery/src/interfaces/INonfungiblePositionManager.sol rename to periphery/src/interfaces/IUniswapPositionNFT.sol index ae94552a..f0da1e5d 100644 --- a/periphery/src/interfaces/INonfungiblePositionManager.sol +++ b/periphery/src/interfaces/IUniswapPositionNFT.sol @@ -1,41 +1,47 @@ // SPDX-License-Identifier: GPL-2.0-or-later pragma solidity 0.8.17; -import {IERC721Enumerable} from "openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Enumerable.sol"; -import {IERC721Metadata} from "openzeppelin-contracts/contracts/token/ERC721/extensions/IERC721Metadata.sol"; +import {IERC721} from "openzeppelin-contracts/contracts/token/ERC721/IERC721.sol"; -import {IERC721Permit} from "./IERC721Permit.sol"; +/// @dev Docs @ https://github.com/Uniswap/v3-periphery/blob/main/contracts/interfaces/INonfungiblePositionManager.sol +interface IUniswapPositionNFT is IERC721 { + struct MintParams { + address token0; + address token1; + uint24 fee; + int24 tickLower; + int24 tickUpper; + uint256 amount0Desired; + uint256 amount1Desired; + uint256 amount0Min; + uint256 amount1Min; + address recipient; + uint256 deadline; + } -/* solhint-disable */ + struct IncreaseLiquidityParams { + uint256 tokenId; + uint256 amount0Desired; + uint256 amount1Desired; + uint256 amount0Min; + uint256 amount1Min; + uint256 deadline; + } -/// @title Non-fungible token for positions -/// @author Uniswap Labs -/// @notice Wraps Uniswap V3 positions in a non-fungible token interface which allows for them to be transferred -/// and authorized. -interface INonfungiblePositionManager is IERC721Metadata, IERC721Enumerable, IERC721Permit { - /// @notice Emitted when liquidity is increased for a position NFT - /// @dev Also emitted when a token is minted - /// @param tokenId The ID of the token for which liquidity was increased - /// @param liquidity The amount by which liquidity for the NFT position was increased - /// @param amount0 The amount of token0 that was paid for the increase in liquidity - /// @param amount1 The amount of token1 that was paid for the increase in liquidity - event IncreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1); - /// @notice Emitted when liquidity is decreased for a position NFT - /// @param tokenId The ID of the token for which liquidity was decreased - /// @param liquidity The amount by which liquidity for the NFT position was decreased - /// @param amount0 The amount of token0 that was accounted for the decrease in liquidity - /// @param amount1 The amount of token1 that was accounted for the decrease in liquidity - event DecreaseLiquidity(uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1); - /// @notice Emitted when tokens are collected for a position NFT - /// @dev The amounts reported may not be exactly equivalent to the amounts transferred, due to rounding behavior - /// @param tokenId The ID of the token for which underlying tokens were collected - /// @param recipient The address of the account that received the collected tokens - /// @param amount0 The amount of token0 owed to the position that was collected - /// @param amount1 The amount of token1 owed to the position that was collected - event Collect(uint256 indexed tokenId, address recipient, uint256 amount0, uint256 amount1); + struct DecreaseLiquidityParams { + uint256 tokenId; + uint128 liquidity; + uint256 amount0Min; + uint256 amount1Min; + uint256 deadline; + } - /// @notice Returns the address of the Uniswap V3 factory - function factory() external view returns (address); + struct CollectParams { + uint256 tokenId; + address recipient; + uint128 amount0Max; + uint128 amount1Max; + } /// @notice Returns the position information associated with a given token ID. /// @dev Throws if the token ID is not valid. @@ -72,20 +78,6 @@ interface INonfungiblePositionManager is IERC721Metadata, IERC721Enumerable, IER uint128 tokensOwed1 ); - struct MintParams { - address token0; - address token1; - uint24 fee; - int24 tickLower; - int24 tickUpper; - uint256 amount0Desired; - uint256 amount1Desired; - uint256 amount0Min; - uint256 amount1Min; - address recipient; - uint256 deadline; - } - /// @notice Creates a new position wrapped in a NFT /// @dev Call this when the pool does exist and is initialized. Note that if the pool is created but not initialized /// a method does not exist, i.e. the pool is assumed to be initialized. @@ -98,15 +90,6 @@ interface INonfungiblePositionManager is IERC721Metadata, IERC721Enumerable, IER MintParams calldata params ) external payable returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1); - struct IncreaseLiquidityParams { - uint256 tokenId; - uint256 amount0Desired; - uint256 amount1Desired; - uint256 amount0Min; - uint256 amount1Min; - uint256 deadline; - } - /// @notice Increases the amount of liquidity in a position, with tokens paid by the `msg.sender` /// @param params tokenId The ID of the token for which liquidity is being increased, /// amount0Desired The desired amount of token0 to be spent, @@ -121,14 +104,6 @@ interface INonfungiblePositionManager is IERC721Metadata, IERC721Enumerable, IER IncreaseLiquidityParams calldata params ) external payable returns (uint128 liquidity, uint256 amount0, uint256 amount1); - struct DecreaseLiquidityParams { - uint256 tokenId; - uint128 liquidity; - uint256 amount0Min; - uint256 amount1Min; - uint256 deadline; - } - /// @notice Decreases the amount of liquidity in a position and accounts it to the position /// @param params tokenId The ID of the token for which liquidity is being decreased, /// amount The amount by which liquidity will be decreased, @@ -141,13 +116,6 @@ interface INonfungiblePositionManager is IERC721Metadata, IERC721Enumerable, IER DecreaseLiquidityParams calldata params ) external payable returns (uint256 amount0, uint256 amount1); - struct CollectParams { - uint256 tokenId; - address recipient; - uint128 amount0Max; - uint128 amount1Max; - } - /// @notice Collects up to a maximum amount of fees owed to a specific position to the recipient /// @param params tokenId The ID of the NFT for which tokens are being collected, /// recipient The account that should receive the tokens, @@ -162,5 +130,3 @@ interface INonfungiblePositionManager is IERC721Metadata, IERC721Enumerable, IER /// @param tokenId The ID of the token that is being burned function burn(uint256 tokenId) external payable; } - -/* solhint-enable */ diff --git a/periphery/src/managers/BoostManager.sol b/periphery/src/managers/BoostManager.sol index a4a933ac..c719a19e 100644 --- a/periphery/src/managers/BoostManager.sol +++ b/periphery/src/managers/BoostManager.sol @@ -11,7 +11,7 @@ import {Borrower, IManager} from "aloe-ii-core/Borrower.sol"; import {Factory} from "aloe-ii-core/Factory.sol"; import {Lender} from "aloe-ii-core/Lender.sol"; -import {INonfungiblePositionManager as IUniswapNFT} from "../interfaces/INonfungiblePositionManager.sol"; +import {IUniswapPositionNFT} from "../interfaces/IUniswapPositionNFT.sol"; contract BoostManager is IManager, IUniswapV3SwapCallback { using SafeTransferLib for ERC20; @@ -20,9 +20,9 @@ contract BoostManager is IManager, IUniswapV3SwapCallback { address public immutable BORROWER_NFT; - IUniswapNFT public immutable UNISWAP_NFT; + IUniswapPositionNFT public immutable UNISWAP_NFT; - constructor(Factory factory, address borrowerNft, IUniswapNFT uniswapNft) { + constructor(Factory factory, address borrowerNft, IUniswapPositionNFT uniswapNft) { FACTORY = factory; BORROWER_NFT = borrowerNft; UNISWAP_NFT = uniswapNft; @@ -157,7 +157,7 @@ contract BoostManager is IManager, IUniswapV3SwapCallback { address recipient ) private returns (uint256 burned0, uint256 burned1) { (burned0, burned1) = UNISWAP_NFT.decreaseLiquidity( - IUniswapNFT.DecreaseLiquidityParams({ + IUniswapPositionNFT.DecreaseLiquidityParams({ tokenId: tokenId, liquidity: liquidity, amount0Min: 0, @@ -167,7 +167,7 @@ contract BoostManager is IManager, IUniswapV3SwapCallback { ); UNISWAP_NFT.collect( - IUniswapNFT.CollectParams({ + IUniswapPositionNFT.CollectParams({ tokenId: tokenId, recipient: recipient, amount0Max: type(uint128).max, diff --git a/periphery/src/managers/UniswapNFTManager.sol b/periphery/src/managers/UniswapNFTManager.sol index a95f783f..8c4f1824 100644 --- a/periphery/src/managers/UniswapNFTManager.sol +++ b/periphery/src/managers/UniswapNFTManager.sol @@ -6,18 +6,18 @@ import {ERC20, SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; import {Borrower, IManager} from "aloe-ii-core/Borrower.sol"; import {Factory} from "aloe-ii-core/Factory.sol"; -import {INonfungiblePositionManager as INFTManager} from "../interfaces/INonfungiblePositionManager.sol"; +import {IUniswapPositionNFT} from "../interfaces/IUniswapPositionNFT.sol"; contract UniswapNFTManager is IManager { using SafeTransferLib for ERC20; Factory public immutable FACTORY; - INFTManager public immutable NFT_MANAGER; + IUniswapPositionNFT public immutable UNISWAP_NFT; - constructor(Factory factory, INFTManager nftManager) { + constructor(Factory factory, IUniswapPositionNFT uniswapNft) { FACTORY = factory; - NFT_MANAGER = nftManager; + UNISWAP_NFT = uniswapNft; } function callback(bytes calldata data, address owner, uint208) external override returns (uint208 positions) { @@ -36,7 +36,7 @@ contract UniswapNFTManager is IManager { // move position from NonfungiblePositionManager to Borrower if (liquidity < 0) { // safety checks since this contract will be approved to manager users' positions - require(FACTORY.isBorrower(msg.sender) && owner == NFT_MANAGER.ownerOf(tokenId)); + require(FACTORY.isBorrower(msg.sender) && owner == UNISWAP_NFT.ownerOf(tokenId)); _withdrawFromNFT(tokenId, uint128(-liquidity), msg.sender); borrower.uniswapDeposit(lower, upper, uint128(-liquidity)); @@ -53,10 +53,10 @@ contract UniswapNFTManager is IManager { address(this) ); - token0.safeApprove(address(NFT_MANAGER), burned0); - token1.safeApprove(address(NFT_MANAGER), burned1); - NFT_MANAGER.increaseLiquidity( - INFTManager.IncreaseLiquidityParams({ + token0.safeApprove(address(UNISWAP_NFT), burned0); + token1.safeApprove(address(UNISWAP_NFT), burned1); + UNISWAP_NFT.increaseLiquidity( + IUniswapPositionNFT.IncreaseLiquidityParams({ tokenId: tokenId, amount0Desired: burned0, amount1Desired: burned1, @@ -72,8 +72,8 @@ contract UniswapNFTManager is IManager { } function _withdrawFromNFT(uint256 tokenId, uint128 liquidity, address recipient) private { - NFT_MANAGER.decreaseLiquidity( - INFTManager.DecreaseLiquidityParams({ + UNISWAP_NFT.decreaseLiquidity( + IUniswapPositionNFT.DecreaseLiquidityParams({ tokenId: tokenId, liquidity: liquidity, amount0Min: 0, @@ -82,8 +82,8 @@ contract UniswapNFTManager is IManager { }) ); - NFT_MANAGER.collect( - INFTManager.CollectParams({ + UNISWAP_NFT.collect( + IUniswapPositionNFT.CollectParams({ tokenId: tokenId, recipient: recipient, amount0Max: type(uint128).max,