From 03c4aefe0d4f2daa656116582e4295b76760489a Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 22 Jul 2024 15:35:32 +0400 Subject: [PATCH 01/47] Initial version of Ramses V1 DTL --- .../liquidity/Ramses/RamsesV1DTL.sol | 157 ++++++++++++++ .../liquidity/Ramses/lib/IRamsesV1Factory.sol | 61 ++++++ .../liquidity/Ramses/lib/IRamsesV1Router.sol | 200 ++++++++++++++++++ 3 files changed, 418 insertions(+) create mode 100644 src/callbacks/liquidity/Ramses/RamsesV1DTL.sol create mode 100644 src/callbacks/liquidity/Ramses/lib/IRamsesV1Factory.sol create mode 100644 src/callbacks/liquidity/Ramses/lib/IRamsesV1Router.sol diff --git a/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol new file mode 100644 index 00000000..68775c5b --- /dev/null +++ b/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +// Libraries +import {ERC20} from "@solmate-6.7.0/tokens/ERC20.sol"; + +// Callbacks +import {BaseDirectToLiquidity} from "../BaseDTL.sol"; + +// Ramses +import {IRamsesV1Factory} from "./lib/IRamsesV1Factory.sol"; +import {IRamsesV1Router} from "./lib/IRamsesV1Router.sol"; + +/// @title RamsesV1DirectToLiquidity +/// @notice This Callback contract deposits the proceeds from a batch auction into a Ramses V1 pool +/// in order to create liquidity immediately. +/// +/// The LP tokens are transferred to `DTLConfiguration.recipient`, or can optionally vest to the auction seller. +/// +/// An important risk to consider: if the auction's base token is available and liquid, a third-party +/// could front-run the auction by creating the pool before the auction ends. This would allow them to +/// manipulate the price of the pool and potentially profit from the eventual deposit of the auction proceeds. +/// +/// @dev As a general rule, this callback contract does not retain balances of tokens between calls. +/// Transfers are performed within the same function that requires the balance. +contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { + // ========== STRUCTS ========== // + + /// @notice Parameters for the onCreate callback + /// @dev This will be encoded in the `callbackData_` parameter + /// + /// @param stable Whether the pool is stable or volatile + struct RamsesV1OnCreateParams { + bool stable; + } + + /// @notice Parameters for the onSettle callback + /// @dev This will be encoded in the `callbackData_` parameter + /// + /// @param maxSlippage The maximum slippage allowed when adding liquidity (in terms of `ONE_HUNDRED_PERCENT`) + struct RamsesV1OnSettleParams { + uint24 maxSlippage; + } + + // ========== STATE VARIABLES ========== // + + /// @notice The Ramses PairFactory contract + /// @dev This contract is used to create Ramses pairs + IRamsesV1Factory public pairFactory; + + /// @notice The Ramses Router contract + /// @dev This contract is used to add liquidity to Ramses pairs + IRamsesV1Router public router; + + /// @notice Records whether a pool should be stable or volatile + mapping(uint96 => bool) public lotIdToStable; + + // ========== CONSTRUCTOR ========== // + + constructor( + address auctionHouse_, + address pairFactory_, + address payable router_ + ) BaseDirectToLiquidity(auctionHouse_) { + if (pairFactory_ == address(0)) { + revert Callback_Params_InvalidAddress(); + } + if (router_ == address(0)) { + revert Callback_Params_InvalidAddress(); + } + pairFactory = IRamsesV1Factory(pairFactory_); + router = IRamsesV1Router(router_); + } + + // ========== CALLBACK FUNCTIONS ========== // + + /// @inheritdoc BaseDirectToLiquidity + /// @dev This function implements the following: + /// - Validates the parameters + /// + /// This function reverts if: + /// - The pool for the token combination already exists + function __onCreate( + uint96 lotId_, + address, + address baseToken_, + address quoteToken_, + uint256, + bool, + bytes calldata callbackData_ + ) internal virtual override { + // Decode the callback data + RamsesV1OnCreateParams memory params = abi.decode(callbackData_, (RamsesV1OnCreateParams)); + + // Check that the pool does not exist + if (pairFactory.getPair(baseToken_, quoteToken_, params.stable) != address(0)) { + revert Callback_Params_PoolExists(); + } + + // Record whether the pool should be stable or volatile + lotIdToStable[lotId_] = params.stable; + } + + /// @inheritdoc BaseDirectToLiquidity + /// @dev This function implements the following: + /// - Validates the parameters + /// - Creates the pool if necessary + /// - Deposits the tokens into the pool + function _mintAndDeposit( + uint96 lotId_, + address quoteToken_, + uint256 quoteTokenAmount_, + address baseToken_, + uint256 baseTokenAmount_, + bytes memory callbackData_ + ) internal virtual override returns (ERC20 poolToken) { + // Decode the callback data + RamsesV1OnSettleParams memory params = abi.decode(callbackData_, (RamsesV1OnSettleParams)); + + // Create and initialize the pool if necessary + // Token orientation is irrelevant + bool stable = lotIdToStable[lotId_]; + address pairAddress = pairFactory.getPair(baseToken_, quoteToken_, stable); + if (pairAddress == address(0)) { + pairAddress = pairFactory.createPair(baseToken_, quoteToken_, stable); + } + + // Calculate the minimum amount out for each token + uint256 quoteTokenAmountMin = _getAmountWithSlippage(quoteTokenAmount_, params.maxSlippage); + uint256 baseTokenAmountMin = _getAmountWithSlippage(baseTokenAmount_, params.maxSlippage); + + // Approve the router to spend the tokens + ERC20(quoteToken_).approve(address(router), quoteTokenAmount_); + ERC20(baseToken_).approve(address(router), baseTokenAmount_); + + // Deposit into the pool + // Token orientation is irrelevant + router.addLiquidity( + quoteToken_, + baseToken_, + stable, + quoteTokenAmount_, + baseTokenAmount_, + quoteTokenAmountMin, + baseTokenAmountMin, + address(this), + block.timestamp + ); + + // Remove any dangling approvals + // This is necessary, since the router may not spend all available tokens + ERC20(quoteToken_).approve(address(router), 0); + ERC20(baseToken_).approve(address(router), 0); + + return ERC20(pairAddress); + } +} diff --git a/src/callbacks/liquidity/Ramses/lib/IRamsesV1Factory.sol b/src/callbacks/liquidity/Ramses/lib/IRamsesV1Factory.sol new file mode 100644 index 00000000..2bf46f44 --- /dev/null +++ b/src/callbacks/liquidity/Ramses/lib/IRamsesV1Factory.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +/// @dev Generated from https://arbiscan.io/address/0xaa9b8a7430474119a442ef0c2bf88f7c3c776f2f +interface IRamsesV1Factory { + event Initialized(uint8 version); + event PairCreated( + address indexed token0, address indexed token1, bool stable, address pair, uint256 + ); + event SetFee(bool stable, uint256 fee); + event SetFeeSplit(uint8 toFeesOld, uint8 toTreasuryOld, uint8 toFeesNew, uint8 toTreasuryNew); + event SetPairFee(address pair, uint256 fee); + event SetPoolFeeSplit( + address pool, uint8 toFeesOld, uint8 toTreasuryOld, uint8 toFeesNew, uint8 toTreasuryNew + ); + + function MAX_FEE() external view returns (uint256); + function acceptFeeManager() external; + function acceptPauser() external; + function allPairs(uint256) external view returns (address); + function allPairsLength() external view returns (uint256); + function createPair( + address tokenA, + address tokenB, + bool stable + ) external returns (address pair); + function feeManager() external view returns (address); + function feeSplit() external view returns (uint8); + function getFee(bool _stable) external view returns (uint256); + function getPair(address, address, bool) external view returns (address); + function getPoolFeeSplit(address _pool) external view returns (uint8 _poolFeeSplit); + function initialize( + address _proxyAdmin, + address _pairImplementation, + address _voter, + address msig + ) external; + function initializeTreasury() external; + function isPair(address) external view returns (bool); + function isPaused() external view returns (bool); + function pairCodeHash() external view returns (bytes32); + function pairFee(address _pool) external view returns (uint256 fee); + function pairImplementation() external view returns (address); + function pauser() external view returns (address); + function pendingFeeManager() external view returns (address); + function pendingPauser() external view returns (address); + function proxyAdmin() external view returns (address); + function setFee(bool _stable, uint256 _fee) external; + function setFeeManager(address _feeManager) external; + function setFeeSplit(uint8 _toFees, uint8 _toTreasury) external; + function setImplementation(address _implementation) external; + function setPairFee(address _pair, uint256 _fee) external; + function setPause(bool _state) external; + function setPauser(address _pauser) external; + function setPoolFeeSplit(address _pool, uint8 _toFees, uint8 _toTreasury) external; + function setTreasury(address _treasury) external; + function stableFee() external view returns (uint256); + function treasury() external view returns (address); + function volatileFee() external view returns (uint256); + function voter() external view returns (address); +} diff --git a/src/callbacks/liquidity/Ramses/lib/IRamsesV1Router.sol b/src/callbacks/liquidity/Ramses/lib/IRamsesV1Router.sol new file mode 100644 index 00000000..7daceedd --- /dev/null +++ b/src/callbacks/liquidity/Ramses/lib/IRamsesV1Router.sol @@ -0,0 +1,200 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +/// @dev Generated from https://arbiscan.io/address/0x0e216dd4f1b5ea81006d41b79f9a1a69a38f3e37 +interface IRamsesV1Router { + struct route { + address from; + address to; + bool stable; + } + + event Initialized(uint8 version); + + receive() external payable; + + function UNSAFE_swapExactTokensForTokens( + uint256[] memory amounts, + route[] memory routes, + address to, + uint256 deadline + ) external returns (uint256[] memory); + function addLiquidity( + address tokenA, + address tokenB, + bool stable, + uint256 amountADesired, + uint256 amountBDesired, + uint256 amountAMin, + uint256 amountBMin, + address to, + uint256 deadline + ) external returns (uint256 amountA, uint256 amountB, uint256 liquidity); + function addLiquidityETH( + address token, + bool stable, + uint256 amountTokenDesired, + uint256 amountTokenMin, + uint256 amountETHMin, + address to, + uint256 deadline + ) external payable returns (uint256 amountToken, uint256 amountETH, uint256 liquidity); + function factory() external view returns (address); + function getAmountOut( + uint256 amountIn, + address tokenIn, + address tokenOut + ) external view returns (uint256 amount, bool stable); + function getAmountsOut( + uint256 amountIn, + route[] memory routes + ) external view returns (uint256[] memory amounts); + function getReserves( + address tokenA, + address tokenB, + bool stable + ) external view returns (uint256 reserveA, uint256 reserveB); + function initialize(address _factory, address _weth) external; + function isPair(address pair) external view returns (bool); + function pairFor( + address tokenA, + address tokenB, + bool stable + ) external view returns (address pair); + function quoteAddLiquidity( + address tokenA, + address tokenB, + bool stable, + uint256 amountADesired, + uint256 amountBDesired + ) external view returns (uint256 amountA, uint256 amountB, uint256 liquidity); + function quoteRemoveLiquidity( + address tokenA, + address tokenB, + bool stable, + uint256 liquidity + ) external view returns (uint256 amountA, uint256 amountB); + function recoverFunds(address token, address to, uint256 amount) external; + function removeLiquidity( + address tokenA, + address tokenB, + bool stable, + uint256 liquidity, + uint256 amountAMin, + uint256 amountBMin, + address to, + uint256 deadline + ) external returns (uint256 amountA, uint256 amountB); + function removeLiquidityETH( + address token, + bool stable, + uint256 liquidity, + uint256 amountTokenMin, + uint256 amountETHMin, + address to, + uint256 deadline + ) external returns (uint256 amountToken, uint256 amountETH); + function removeLiquidityETHSupportingFeeOnTransferTokens( + address token, + bool stable, + uint256 liquidity, + uint256 amountTokenMin, + uint256 amountETHMin, + address to, + uint256 deadline + ) external returns (uint256 amountToken, uint256 amountETH); + function removeLiquidityETHWithPermit( + address token, + bool stable, + uint256 liquidity, + uint256 amountTokenMin, + uint256 amountETHMin, + address to, + uint256 deadline, + bool approveMax, + uint8 v, + bytes32 r, + bytes32 s + ) external returns (uint256 amountToken, uint256 amountETH); + function removeLiquidityETHWithPermitSupportingFeeOnTransferTokens( + address token, + bool stable, + uint256 liquidity, + uint256 amountTokenMin, + uint256 amountETHMin, + address to, + uint256 deadline, + bool approveMax, + uint8 v, + bytes32 r, + bytes32 s + ) external returns (uint256 amountToken, uint256 amountETH); + function removeLiquidityWithPermit( + address tokenA, + address tokenB, + bool stable, + uint256 liquidity, + uint256 amountAMin, + uint256 amountBMin, + address to, + uint256 deadline, + bool approveMax, + uint8 v, + bytes32 r, + bytes32 s + ) external returns (uint256 amountA, uint256 amountB); + function sortTokens( + address tokenA, + address tokenB + ) external pure returns (address token0, address token1); + function swapExactETHForTokens( + uint256 amountOutMin, + route[] memory routes, + address to, + uint256 deadline + ) external payable returns (uint256[] memory amounts); + function swapExactETHForTokensSupportingFeeOnTransferTokens( + uint256 amountOutMin, + route[] memory routes, + address to, + uint256 deadline + ) external payable; + function swapExactTokensForETH( + uint256 amountIn, + uint256 amountOutMin, + route[] memory routes, + address to, + uint256 deadline + ) external returns (uint256[] memory amounts); + function swapExactTokensForETHSupportingFeeOnTransferTokens( + uint256 amountIn, + uint256 amountOutMin, + route[] memory routes, + address to, + uint256 deadline + ) external; + function swapExactTokensForTokens( + uint256 amountIn, + uint256 amountOutMin, + route[] memory routes, + address to, + uint256 deadline + ) external returns (uint256[] memory amounts); + function swapExactTokensForTokensSimple( + uint256 amountIn, + uint256 amountOutMin, + address tokenFrom, + address tokenTo, + bool stable, + address to, + uint256 deadline + ) external returns (uint256[] memory amounts); + function swapExactTokensForTokensSupportingFeeOnTransferTokens( + uint256 amountIn, + uint256 amountOutMin, + route[] memory routes, + address to, + uint256 deadline + ) external; + function weth() external view returns (address); +} From 01dbcac6dca274c970ffe1270e692fec03b85a97 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 22 Jul 2024 15:35:47 +0400 Subject: [PATCH 02/47] Add Ramses imported files into .solhintignore --- .solhintignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.solhintignore b/.solhintignore index afa64c5b..17c2fd75 100644 --- a/.solhintignore +++ b/.solhintignore @@ -5,3 +5,4 @@ test/lib/uniswap-v2/** test/lib/uniswap-v3/** src/callbacks/liquidity/BaselineV2/lib/** +src/callbacks/liquidity/Ramses/lib/** From 89afe88f02c3776d94c31be316f6d6fd26f0c26d Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 22 Jul 2024 15:50:21 +0400 Subject: [PATCH 03/47] Add Ramses V2 interfaces --- .../liquidity/Ramses/lib/IRamsesV2Factory.sol | 63 ++++++ .../Ramses/lib/IRamsesV2PositionManager.sol | 183 ++++++++++++++++++ 2 files changed, 246 insertions(+) create mode 100644 src/callbacks/liquidity/Ramses/lib/IRamsesV2Factory.sol create mode 100644 src/callbacks/liquidity/Ramses/lib/IRamsesV2PositionManager.sol diff --git a/src/callbacks/liquidity/Ramses/lib/IRamsesV2Factory.sol b/src/callbacks/liquidity/Ramses/lib/IRamsesV2Factory.sol new file mode 100644 index 00000000..84034926 --- /dev/null +++ b/src/callbacks/liquidity/Ramses/lib/IRamsesV2Factory.sol @@ -0,0 +1,63 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +/// @dev Generated from https://arbiscan.io/address/0xf896d16fa56a625802b6013f9f9202790ec69908 +interface IRamsesV2Factory { + event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing); + event FeeCollectorChanged(address indexed oldFeeCollector, address indexed newFeeCollector); + event FeeSetterChanged(address indexed oldSetter, address indexed newSetter); + event ImplementationChanged( + address indexed oldImplementation, address indexed newImplementation + ); + event OwnerChanged(address indexed oldOwner, address indexed newOwner); + event PoolCreated( + address indexed token0, + address indexed token1, + uint24 indexed fee, + int24 tickSpacing, + address pool + ); + event SetFeeProtocol( + uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New + ); + event SetPoolFeeProtocol( + address pool, + uint8 feeProtocol0Old, + uint8 feeProtocol1Old, + uint8 feeProtocol0New, + uint8 feeProtocol1New + ); + + function POOL_INIT_CODE_HASH() external view returns (bytes32); + function createPool( + address tokenA, + address tokenB, + uint24 fee + ) external returns (address pool); + function enableFeeAmount(uint24 fee, int24 tickSpacing) external; + function feeAmountTickSpacing(uint24) external view returns (int24); + function feeCollector() external view returns (address); + function feeProtocol() external view returns (uint8); + function feeSetter() external view returns (address); + function getPool(address, address, uint24) external view returns (address); + function implementation() external view returns (address); + function initialize( + address _nfpManager, + address _veRam, + address _voter, + address _implementation + ) external; + function nfpManager() external view returns (address); + function owner() external view returns (address); + function poolFeeProtocol(address pool) external view returns (uint8 __poolFeeProtocol); + function setFee(address _pool, uint24 _fee) external; + function setFeeCollector(address _feeCollector) external; + function setFeeProtocol(uint8 _feeProtocol) external; + function setFeeSetter(address _newFeeSetter) external; + function setImplementation(address _implementation) external; + function setOwner(address _owner) external; + function setPoolFeeProtocol(address pool, uint8 _feeProtocol) external; + function setPoolFeeProtocol(address pool, uint8 feeProtocol0, uint8 feeProtocol1) external; + function veRam() external view returns (address); + function voter() external view returns (address); +} diff --git a/src/callbacks/liquidity/Ramses/lib/IRamsesV2PositionManager.sol b/src/callbacks/liquidity/Ramses/lib/IRamsesV2PositionManager.sol new file mode 100644 index 00000000..bae0eb39 --- /dev/null +++ b/src/callbacks/liquidity/Ramses/lib/IRamsesV2PositionManager.sol @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +/// @dev Generated from https://arbiscan.io/address/0xac9d1dfa5483ebb06e623df31547b2a4dc8bf7ca +interface IRamsesV2PositionManager { + struct CollectParams { + uint256 tokenId; + address recipient; + uint128 amount0Max; + uint128 amount1Max; + } + + struct DecreaseLiquidityParams { + uint256 tokenId; + uint128 liquidity; + uint256 amount0Min; + uint256 amount1Min; + uint256 deadline; + } + + struct IncreaseLiquidityParams { + uint256 tokenId; + uint256 amount0Desired; + uint256 amount1Desired; + uint256 amount0Min; + uint256 amount1Min; + uint256 deadline; + } + + struct MintParams { + address token0; + address token1; + uint24 fee; + int24 tickLower; + int24 tickUpper; + uint256 amount0Desired; + uint256 amount1Desired; + uint256 amount0Min; + uint256 amount1Min; + address recipient; + uint256 deadline; + uint256 veRamTokenId; + } + + event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); + event ApprovalForAll(address indexed owner, address indexed operator, bool approved); + event Collect(uint256 indexed tokenId, address recipient, uint256 amount0, uint256 amount1); + event DecreaseLiquidity( + uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1 + ); + event IncreaseLiquidity( + uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1 + ); + event SwitchAttachment( + uint256 indexed tokenId, uint256 oldVeRamTokenId, uint256 newVeRamTokenId + ); + event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); + + receive() external payable; + + function DOMAIN_SEPARATOR() external view returns (bytes32); + function PERMIT_TYPEHASH() external view returns (bytes32); + function WETH9() external view returns (address); + function approve(address to, uint256 tokenId) external; + function balanceOf(address owner) external view returns (uint256); + function baseURI() external pure returns (string memory); + function batchSwitchAttachment(uint256[] memory tokenIds, uint256 veRamTokenId) external; + function burn(uint256 tokenId) external payable; + function collect(CollectParams memory params) + external + payable + returns (uint256 amount0, uint256 amount1); + function createAndInitializePoolIfNecessary( + address token0, + address token1, + uint24 fee, + uint160 sqrtPriceX96 + ) external payable returns (address pool); + function decreaseLiquidity(DecreaseLiquidityParams memory params) + external + payable + returns (uint256 amount0, uint256 amount1); + function factory() external view returns (address); + function getApproved(uint256 tokenId) external view returns (address); + function getReward(uint256 tokenId, address[] memory tokens) external; + function increaseLiquidity(IncreaseLiquidityParams memory params) + external + payable + returns (uint128 liquidity, uint256 amount0, uint256 amount1); + function initialize() external; + function isApprovedForAll(address owner, address operator) external view returns (bool); + function mint(MintParams memory params) + external + payable + returns (uint256 tokenId, uint128 liquidity, uint256 amount0, uint256 amount1); + function multicall(bytes[] memory data) external payable returns (bytes[] memory results); + function name() external view returns (string memory); + function ownerOf(uint256 tokenId) external view returns (address); + function permit( + address spender, + uint256 tokenId, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external payable; + function positions(uint256 tokenId) + external + view + returns ( + uint96 nonce, + address operator, + address token0, + address token1, + uint24 fee, + int24 tickLower, + int24 tickUpper, + uint128 liquidity, + uint256 feeGrowthInside0LastX128, + uint256 feeGrowthInside1LastX128, + uint128 tokensOwed0, + uint128 tokensOwed1 + ); + function ramsesV2MintCallback( + uint256 amount0Owed, + uint256 amount1Owed, + bytes memory data + ) external; + function refundETH() external payable; + function safeTransferFrom(address from, address to, uint256 tokenId) external; + function safeTransferFrom( + address from, + address to, + uint256 tokenId, + bytes memory _data + ) external; + function selfPermit( + address token, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external payable; + function selfPermitAllowed( + address token, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) external payable; + function selfPermitAllowedIfNecessary( + address token, + uint256 nonce, + uint256 expiry, + uint8 v, + bytes32 r, + bytes32 s + ) external payable; + function selfPermitIfNecessary( + address token, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external payable; + function setApprovalForAll(address operator, bool approved) external; + function supportsInterface(bytes4 interfaceId) external view returns (bool); + function sweepToken(address token, uint256 amountMinimum, address recipient) external payable; + function switchAttachment(uint256 tokenId, uint256 veRamTokenId) external; + function symbol() external view returns (string memory); + function timelock() external pure returns (address _timelock); + function tokenByIndex(uint256 index) external view returns (uint256); + function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); + function tokenURI(uint256 tokenId) external view returns (string memory); + function totalSupply() external view returns (uint256); + function transferFrom(address from, address to, uint256 tokenId) external; + function unwrapWETH9(uint256 amountMinimum, address recipient) external payable; + function veRam() external view returns (address); + function voter() external pure returns (address _voter); +} From 0675ad4102d1aeba90b8c1128af98e3e0e6e19d9 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 23 Jul 2024 13:30:24 +0400 Subject: [PATCH 04/47] Amend BaseDirectToLiquidity to shift pool token transfers into an implementation function. Stub for Ramses V2 DTL. --- src/callbacks/liquidity/BaseDTL.sol | 93 +++++++++++++----- .../liquidity/Ramses/RamsesV2DTL.sol | 96 +++++++++++++++++++ src/callbacks/liquidity/UniswapV2DTL.sol | 47 ++++++++- src/callbacks/liquidity/UniswapV3DTL.sol | 49 +++++++++- 4 files changed, 254 insertions(+), 31 deletions(-) create mode 100644 src/callbacks/liquidity/Ramses/RamsesV2DTL.sol diff --git a/src/callbacks/liquidity/BaseDTL.sol b/src/callbacks/liquidity/BaseDTL.sol index 379a9b7f..8eebf5da 100644 --- a/src/callbacks/liquidity/BaseDTL.sol +++ b/src/callbacks/liquidity/BaseDTL.sol @@ -15,6 +15,12 @@ import {LinearVesting} from "@axis-core-1.0.0/modules/derivatives/LinearVesting. import {AuctionHouse} from "@axis-core-1.0.0/bases/AuctionHouse.sol"; import {Keycode, wrapVeecode} from "@axis-core-1.0.0/modules/Modules.sol"; +/// @notice Base contract for DirectToLiquidity callbacks +/// @dev This contract is intended to be inherited by a callback contract that supports a particular liquidity platform, such as Uniswap V2 or V3. +/// +/// It provides integration points that enable the implementing contract to support different liquidity platforms. +/// +/// NOTE: The parameters to the functions in this contract refer to linear vesting, which is currently only supported for ERC20 pool tokens. A future version could improve upon this by shifting the (ERC20) linear vesting functionality into a variant that inherits from this contract. abstract contract BaseDirectToLiquidity is BaseCallback { using SafeTransferLib for ERC20; @@ -34,6 +40,8 @@ abstract contract BaseDirectToLiquidity is BaseCallback { error Callback_LinearVestingModuleNotFound(); + error Callback_PoolTokenNotFound(); + // ========== STRUCTS ========== // /// @notice Configuration for the DTL callback @@ -68,6 +76,7 @@ abstract contract BaseDirectToLiquidity is BaseCallback { /// @param implParams The implementation-specific parameters struct OnCreateParams { uint24 proceedsUtilisationPercent; + // TODO ideally move the vesting parameters into an inheriting contract that accesses implParams uint48 vestingStart; uint48 vestingExpiry; address recipient; @@ -323,7 +332,18 @@ abstract contract BaseDirectToLiquidity is BaseCallback { _tokensRequiredForPool(proceeds_, config.proceedsUtilisationPercent); } - // Ensure the required tokens are present before minting + // Ensure the required quote tokens are present in this contract before minting + { + // Check that sufficient balance exists + uint256 quoteTokenBalance = ERC20(quoteToken).balanceOf(address(this)); + if (quoteTokenBalance < quoteTokensRequired) { + revert Callback_InsufficientBalance( + quoteToken, address(this), quoteTokensRequired, quoteTokenBalance + ); + } + } + + // Ensure the required base tokens are present on the seller address before minting { // Check that sufficient balance exists uint256 baseTokenBalance = ERC20(baseToken).balanceOf(seller); @@ -333,33 +353,17 @@ abstract contract BaseDirectToLiquidity is BaseCallback { ); } + // Transfer the base tokens from the seller to this contract ERC20(baseToken).safeTransferFrom(seller, address(this), baseTokensRequired); } // Mint and deposit into the pool - (ERC20 poolToken) = _mintAndDeposit( + _mintAndDeposit( lotId_, quoteToken, quoteTokensRequired, baseToken, baseTokensRequired, callbackData_ ); - uint256 poolTokenQuantity = poolToken.balanceOf(address(this)); - - // If vesting is enabled, create the vesting tokens - if (address(config.linearVestingModule) != address(0)) { - // Approve spending of the tokens - poolToken.approve(address(config.linearVestingModule), poolTokenQuantity); - - // Mint the vesting tokens (it will deploy if necessary) - config.linearVestingModule.mint( - config.recipient, - address(poolToken), - _getEncodedVestingParams(config.vestingStart, config.vestingExpiry), - poolTokenQuantity, - true // Wrap vesting LP tokens so they are easily visible - ); - } - // Send the LP tokens to the seller - else { - poolToken.safeTransfer(config.recipient, poolTokenQuantity); - } + + // Transfer the pool token to the recipient + _transferPoolToken(lotId_); // Send any remaining quote tokens to the seller { @@ -385,7 +389,7 @@ abstract contract BaseDirectToLiquidity is BaseCallback { /// - Create and initialize the pool /// - Deposit the quote and base tokens into the pool /// - The pool tokens should be received by this contract - /// - Return the ERC20 pool token + /// - Store the details of the pool token (ERC20 or ERC721) in the state /// /// @param lotId_ The lot ID /// @param quoteToken_ The quote token address @@ -393,7 +397,6 @@ abstract contract BaseDirectToLiquidity is BaseCallback { /// @param baseToken_ The base token address /// @param baseTokenAmount_ The amount of base tokens to deposit /// @param callbackData_ Implementation-specific data - /// @return poolToken The ERC20 pool token function _mintAndDeposit( uint96 lotId_, address quoteToken_, @@ -401,7 +404,16 @@ abstract contract BaseDirectToLiquidity is BaseCallback { address baseToken_, uint256 baseTokenAmount_, bytes memory callbackData_ - ) internal virtual returns (ERC20 poolToken); + ) internal virtual; + + /// @notice Transfer the pool token to the recipient + /// @dev This function should be implemented by the Uniswap-specific callback + /// + /// It is expected to: + /// - Transfer the pool token (ERC20 or ERC721) to the recipient + /// + /// @param lotId_ The lot ID + function _transferPoolToken(uint96 lotId_) internal virtual; // ========== INTERNAL FUNCTIONS ========== // @@ -445,4 +457,35 @@ abstract contract BaseDirectToLiquidity is BaseCallback { ) internal pure returns (bytes memory) { return abi.encode(ILinearVesting.VestingParams({start: start_, expiry: expiry_})); } + + /// @notice Mint vesting tokens from an ERC20 token + /// + /// @param token_ The ERC20 token to mint the vesting tokens from + /// @param quantity_ The quantity of tokens to mint + /// @param module_ The LinearVesting module to mint the tokens with + /// @param recipient_ The recipient of the vesting tokens + /// @param vestingStart_ The start of the vesting period + /// @param vestingExpiry_ The end of the vesting period + function _mintVestingTokens( + ERC20 token_, + uint256 quantity_, + LinearVesting module_, + address recipient_, + uint48 vestingStart_, + uint48 vestingExpiry_ + ) internal { + // Approve spending of the tokens + token_.approve(address(module_), quantity_); + + // Mint the vesting tokens (it will deploy if necessary) + module_.mint( + recipient_, + address(token_), + _getEncodedVestingParams(vestingStart_, vestingExpiry_), + quantity_, + true // Wrap vesting LP tokens so they are easily visible + ); + + // The LinearVesting module will use all of `poolTokenQuantity`, so there is no need to clean up dangling approvals + } } diff --git a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol new file mode 100644 index 00000000..0c16f258 --- /dev/null +++ b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.19; + +// Callbacks +import {BaseDirectToLiquidity} from "../BaseDTL.sol"; + +// Ramses +import {IRamsesV2Factory} from "./lib/IRamsesV2Factory.sol"; +import {IRamsesV2PositionManager} from "./lib/IRamsesV2PositionManager.sol"; + +contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { + // ========== STRUCTS ========== // + + /// @notice Parameters for the onSettle callback + /// @dev This will be encoded in the `callbackData_` parameter + /// + /// @param maxSlippage The maximum slippage allowed when adding liquidity (in terms of `ONE_HUNDRED_PERCENT`) + struct OnSettleParams { + uint24 maxSlippage; + } + + // ========== STATE VARIABLES ========== // + + IRamsesV2Factory public ramsesV2Factory; + + IRamsesV2PositionManager public ramsesV2PositionManager; + + mapping(uint96 => uint256 tokenId) public lotIdToTokenId; + + // ========== CONSTRUCTOR ========== // + + constructor( + address auctionHouse_, + address ramsesV2Factory_, + address payable ramsesV2PositionManager_ + ) BaseDirectToLiquidity(auctionHouse_) { + if (ramsesV2Factory_ == address(0)) { + revert Callback_Params_InvalidAddress(); + } + ramsesV2Factory = IRamsesV2Factory(ramsesV2Factory_); + + if (ramsesV2PositionManager_ == address(0)) { + revert Callback_Params_InvalidAddress(); + } + ramsesV2PositionManager = IRamsesV2PositionManager(ramsesV2PositionManager_); + } + + // ========== CALLBACK FUNCTIONS ========== // + + /// @inheritdoc BaseDirectToLiquidity + /// @dev This function implements the following: + /// - Validates the parameters + /// + /// This function reverts if: + /// - The pool for the token combination already exists + function __onCreate( + uint96 lotId_, + address seller_, + address baseToken_, + address quoteToken_, + uint256 capacity_, + bool prefund_, + bytes calldata callbackData_ + ) internal virtual override { + // Check whether a pool exists for the token combination + } + + /// @inheritdoc BaseDirectToLiquidity + /// @dev This function implements the following: + /// - Creates the pool if necessary + /// - Deposits the tokens into the pool + function _mintAndDeposit( + uint96 lotId_, + address quoteToken_, + uint256 quoteTokenAmount_, + address baseToken_, + uint256 baseTokenAmount_, + bytes memory callbackData_ + ) internal virtual override { + // Use the position manager to mint the pool token + + // Add liquidity to the pool + } + + /// @inheritdoc BaseDirectToLiquidity + /// @dev This function implements the following: + /// - Transfers the pool token to the recipient + function _transferPoolToken(uint96 lotId_) internal virtual override { + uint256 tokenId = lotIdToTokenId[lotId_]; + if (tokenId == 0) { + revert Callback_PoolTokenNotFound(); + } + + // Use the position manager to transfer the token to the recipient + } +} diff --git a/src/callbacks/liquidity/UniswapV2DTL.sol b/src/callbacks/liquidity/UniswapV2DTL.sol index 3dfb43b5..ba8489ac 100644 --- a/src/callbacks/liquidity/UniswapV2DTL.sol +++ b/src/callbacks/liquidity/UniswapV2DTL.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.19; // Libraries import {ERC20} from "@solmate-6.7.0/tokens/ERC20.sol"; +import {SafeTransferLib} from "@solmate-6.7.0/utils/SafeTransferLib.sol"; // Uniswap import {IUniswapV2Factory} from "@uniswap-v2-core-1.0.1/interfaces/IUniswapV2Factory.sol"; @@ -24,9 +25,11 @@ import {BaseDirectToLiquidity} from "./BaseDTL.sol"; /// @dev As a general rule, this callback contract does not retain balances of tokens between calls. /// Transfers are performed within the same function that requires the balance. contract UniswapV2DirectToLiquidity is BaseDirectToLiquidity { + using SafeTransferLib for ERC20; + // ========== STRUCTS ========== // - /// @notice Parameters for the onClaimProceeds callback + /// @notice Parameters for the onSettle callback /// @dev This will be encoded in the `callbackData_` parameter /// /// @param maxSlippage The maximum slippage allowed when adding liquidity (in terms of `ONE_HUNDRED_PERCENT`) @@ -44,6 +47,10 @@ contract UniswapV2DirectToLiquidity is BaseDirectToLiquidity { /// @dev This contract is used to add liquidity to Uniswap V2 pools IUniswapV2Router02 public uniV2Router; + /// @notice Mapping of lot ID to pool token + /// @dev This is used to track the pool token for each lot + mapping(uint96 lotId => address poolToken) public lotIdToPoolToken; + // ========== CONSTRUCTOR ========== // constructor( @@ -90,13 +97,13 @@ contract UniswapV2DirectToLiquidity is BaseDirectToLiquidity { /// - Creates the pool if necessary /// - Deposits the tokens into the pool function _mintAndDeposit( - uint96, + uint96 lotId_, address quoteToken_, uint256 quoteTokenAmount_, address baseToken_, uint256 baseTokenAmount_, bytes memory callbackData_ - ) internal virtual override returns (ERC20 poolToken) { + ) internal virtual override { // Decode the callback data OnSettleParams memory params = abi.decode(callbackData_, (OnSettleParams)); @@ -132,6 +139,38 @@ contract UniswapV2DirectToLiquidity is BaseDirectToLiquidity { ERC20(quoteToken_).approve(address(uniV2Router), 0); ERC20(baseToken_).approve(address(uniV2Router), 0); - return ERC20(pairAddress); + // Store the pool token for later + lotIdToPoolToken[lotId_] = pairAddress; + } + + /// @inheritdoc BaseDirectToLiquidity + /// @dev This function implements the following: + /// - If LinearVesting is enabled, mints derivative tokens + /// - Otherwise, transfers the pool tokens to the recipient + function _transferPoolToken(uint96 lotId_) internal virtual override { + address poolTokenAddress = lotIdToPoolToken[lotId_]; + if (poolTokenAddress == address(0)) { + revert Callback_PoolTokenNotFound(); + } + + ERC20 poolToken = ERC20(poolTokenAddress); + uint256 poolTokenQuantity = poolToken.balanceOf(address(this)); + DTLConfiguration memory config = lotConfiguration[lotId_]; + + // If vesting is enabled, create the vesting tokens + if (address(config.linearVestingModule) != address(0)) { + _mintVestingTokens( + poolToken, + poolTokenQuantity, + config.linearVestingModule, + config.recipient, + config.vestingStart, + config.vestingExpiry + ); + } + // Otherwise, send the LP tokens to the seller + else { + poolToken.safeTransfer(config.recipient, poolTokenQuantity); + } } } diff --git a/src/callbacks/liquidity/UniswapV3DTL.sol b/src/callbacks/liquidity/UniswapV3DTL.sol index 4efdfeec..9c6adcb6 100644 --- a/src/callbacks/liquidity/UniswapV3DTL.sol +++ b/src/callbacks/liquidity/UniswapV3DTL.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.19; // Libraries import {ERC20} from "@solmate-6.7.0/tokens/ERC20.sol"; +import {SafeTransferLib} from "@solmate-6.7.0/utils/SafeTransferLib.sol"; // Uniswap import {IUniswapV3Pool} from @@ -34,6 +35,8 @@ import {BaseDirectToLiquidity} from "./BaseDTL.sol"; /// @dev As a general rule, this callback contract does not retain balances of tokens between calls. /// Transfers are performed within the same function that requires the balance. contract UniswapV3DirectToLiquidity is BaseDirectToLiquidity { + using SafeTransferLib for ERC20; + // ========== ERRORS ========== // error Callback_Params_PoolFeeNotEnabled(); @@ -60,6 +63,11 @@ contract UniswapV3DirectToLiquidity is BaseDirectToLiquidity { /// @dev This contract is used to create the ERC20 LP tokens IGUniFactory public gUniFactory; + /// @notice Mapping of lot ID to pool token + /// @dev This is used to track the pool token for each lot. + /// As this contract uses G-UNI to tokenise the Uniswap V3 position, the pool token is an ERC-20. + mapping(uint96 => address) public lotIdToPoolToken; + // ========== CONSTRUCTOR ========== // constructor( @@ -129,7 +137,7 @@ contract UniswapV3DirectToLiquidity is BaseDirectToLiquidity { address baseToken_, uint256 baseTokenAmount_, bytes memory callbackData_ - ) internal virtual override returns (ERC20 poolToken) { + ) internal virtual override { // Decode the callback data OnSettleParams memory params = abi.decode(callbackData_, (OnSettleParams)); @@ -214,9 +222,46 @@ contract UniswapV3DirectToLiquidity is BaseDirectToLiquidity { // Mint the LP tokens // The parent callback is responsible for transferring any leftover quote and base tokens gUniPoolToken.mint(poolTokenQuantity, address(this)); + + // Remove any dangling approvals + // This is necessary, since the G-UNI pool may not spend all available tokens + ERC20(quoteToken_).approve(address(poolTokenAddress), 0); + ERC20(baseToken_).approve(address(poolTokenAddress), 0); + } + + // Store the pool token for later + lotIdToPoolToken[lotId_] = poolTokenAddress; + } + + /// @inheritdoc BaseDirectToLiquidity + /// @dev This function implements the following: + /// - If LinearVesting is enabled, mints derivative tokens + /// - Otherwise, transfers the pool tokens to the recipient + function _transferPoolToken(uint96 lotId_) internal virtual override { + address poolTokenAddress = lotIdToPoolToken[lotId_]; + if (poolTokenAddress == address(0)) { + revert Callback_PoolTokenNotFound(); } - poolToken = ERC20(poolTokenAddress); + ERC20 poolToken = ERC20(poolTokenAddress); + uint256 poolTokenQuantity = poolToken.balanceOf(address(this)); + DTLConfiguration memory config = lotConfiguration[lotId_]; + + // If vesting is enabled, create the vesting tokens + if (address(config.linearVestingModule) != address(0)) { + _mintVestingTokens( + poolToken, + poolTokenQuantity, + config.linearVestingModule, + config.recipient, + config.vestingStart, + config.vestingExpiry + ); + } + // Otherwise, send the LP tokens to the seller + else { + poolToken.safeTransfer(config.recipient, poolTokenQuantity); + } } // ========== INTERNAL FUNCTIONS ========== // From eabbf8f1e313c77475dc4752ff3b42f3260014b2 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 23 Jul 2024 13:32:31 +0400 Subject: [PATCH 05/47] Amend Ramses V1 DTL to adhere to new interface --- .../liquidity/Ramses/RamsesV1DTL.sol | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol index 68775c5b..f25ad14c 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol +++ b/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol @@ -3,6 +3,7 @@ pragma solidity ^0.8.19; // Libraries import {ERC20} from "@solmate-6.7.0/tokens/ERC20.sol"; +import {SafeTransferLib} from "@solmate-6.7.0/utils/SafeTransferLib.sol"; // Callbacks import {BaseDirectToLiquidity} from "../BaseDTL.sol"; @@ -24,6 +25,8 @@ import {IRamsesV1Router} from "./lib/IRamsesV1Router.sol"; /// @dev As a general rule, this callback contract does not retain balances of tokens between calls. /// Transfers are performed within the same function that requires the balance. contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { + using SafeTransferLib for ERC20; + // ========== STRUCTS ========== // /// @notice Parameters for the onCreate callback @@ -55,6 +58,9 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { /// @notice Records whether a pool should be stable or volatile mapping(uint96 => bool) public lotIdToStable; + /// @notice Mapping of lot ID to pool token + mapping(uint96 => address) public lotIdToPoolToken; + // ========== CONSTRUCTOR ========== // constructor( @@ -113,7 +119,7 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { address baseToken_, uint256 baseTokenAmount_, bytes memory callbackData_ - ) internal virtual override returns (ERC20 poolToken) { + ) internal virtual override { // Decode the callback data RamsesV1OnSettleParams memory params = abi.decode(callbackData_, (RamsesV1OnSettleParams)); @@ -152,6 +158,38 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { ERC20(quoteToken_).approve(address(router), 0); ERC20(baseToken_).approve(address(router), 0); - return ERC20(pairAddress); + // Store the pool token for later + lotIdToPoolToken[lotId_] = pairAddress; + } + + /// @inheritdoc BaseDirectToLiquidity + /// @dev This function implements the following: + /// - If LinearVesting is enabled, mints derivative tokens + /// - Otherwise, transfers the pool tokens to the recipient + function _transferPoolToken(uint96 lotId_) internal virtual override { + address poolTokenAddress = lotIdToPoolToken[lotId_]; + if (poolTokenAddress == address(0)) { + revert Callback_PoolTokenNotFound(); + } + + ERC20 poolToken = ERC20(poolTokenAddress); + uint256 poolTokenQuantity = poolToken.balanceOf(address(this)); + DTLConfiguration memory config = lotConfiguration[lotId_]; + + // If vesting is enabled, create the vesting tokens + if (address(config.linearVestingModule) != address(0)) { + _mintVestingTokens( + poolToken, + poolTokenQuantity, + config.linearVestingModule, + config.recipient, + config.vestingStart, + config.vestingExpiry + ); + } + // Otherwise, send the LP tokens to the seller + else { + poolToken.safeTransfer(config.recipient, poolTokenQuantity); + } } } From 78e1170c32e760c9ff60fea47d420922a4f42f43 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 23 Jul 2024 16:18:33 +0400 Subject: [PATCH 06/47] Implement DTL for Ramses V2 --- src/callbacks/liquidity/BaseDTL.sol | 15 +- .../liquidity/Ramses/RamsesV2DTL.sol | 154 ++++++++-- .../liquidity/Ramses/lib/IRamsesV2Pool.sol | 284 ++++++++++++++++++ 3 files changed, 430 insertions(+), 23 deletions(-) create mode 100644 src/callbacks/liquidity/Ramses/lib/IRamsesV2Pool.sol diff --git a/src/callbacks/liquidity/BaseDTL.sol b/src/callbacks/liquidity/BaseDTL.sol index 8eebf5da..2eec9d2a 100644 --- a/src/callbacks/liquidity/BaseDTL.sol +++ b/src/callbacks/liquidity/BaseDTL.sol @@ -42,6 +42,9 @@ abstract contract BaseDirectToLiquidity is BaseCallback { error Callback_PoolTokenNotFound(); + /// @notice The auction lot has already been completed + error Callback_AlreadyComplete(); + // ========== STRUCTS ========== // /// @notice Configuration for the DTL callback @@ -288,6 +291,7 @@ abstract contract BaseDirectToLiquidity is BaseCallback { /// /// This function reverts if: /// - The lot is not registered + /// - The lot is already complete /// /// @param lotId_ The lot ID /// @param proceeds_ The proceeds from the auction @@ -299,7 +303,16 @@ abstract contract BaseDirectToLiquidity is BaseCallback { uint256 refund_, bytes calldata callbackData_ ) internal virtual override { - DTLConfiguration memory config = lotConfiguration[lotId_]; + DTLConfiguration storage config = lotConfiguration[lotId_]; + + // Check that the lot is active + if (!config.active) { + revert Callback_AlreadyComplete(); + } + + // Mark the lot as inactive + lotConfiguration[lotId_].active = false; + address seller; address baseToken; address quoteToken; diff --git a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol index 0c16f258..32ca72ff 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol +++ b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol @@ -1,32 +1,46 @@ // SPDX-License-Identifier: MIT pragma solidity ^0.8.19; +// Libraries +import {ERC20} from "@solmate-6.7.0/tokens/ERC20.sol"; + // Callbacks import {BaseDirectToLiquidity} from "../BaseDTL.sol"; // Ramses +import {IRamsesV2Pool} from "./lib/IRamsesV2Pool.sol"; import {IRamsesV2Factory} from "./lib/IRamsesV2Factory.sol"; import {IRamsesV2PositionManager} from "./lib/IRamsesV2PositionManager.sol"; +// Uniswap +import {TickMath} from "@uniswap-v3-core-1.0.1-solc-0.8-simulate/libraries/TickMath.sol"; +import {SqrtPriceMath} from "../../../lib/uniswap-v3/SqrtPriceMath.sol"; + contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { + // ========== ERRORS ========== // + + error Callback_Params_PoolFeeNotEnabled(); + // ========== STRUCTS ========== // /// @notice Parameters for the onSettle callback /// @dev This will be encoded in the `callbackData_` parameter /// /// @param maxSlippage The maximum slippage allowed when adding liquidity (in terms of `ONE_HUNDRED_PERCENT`) + /// @param veRamTokenId The token ID of the veRAM token to use for the position (optional) struct OnSettleParams { uint24 maxSlippage; + uint256 veRamTokenId; } // ========== STATE VARIABLES ========== // + /// @notice The Ramses V2 factory IRamsesV2Factory public ramsesV2Factory; + /// @notice The Ramses V2 position manager IRamsesV2PositionManager public ramsesV2PositionManager; - mapping(uint96 => uint256 tokenId) public lotIdToTokenId; - // ========== CONSTRUCTOR ========== // constructor( @@ -48,27 +62,46 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { // ========== CALLBACK FUNCTIONS ========== // /// @inheritdoc BaseDirectToLiquidity - /// @dev This function implements the following: - /// - Validates the parameters + /// @dev This function performs the following: + /// - Validates the input data /// /// This function reverts if: - /// - The pool for the token combination already exists + /// - OnCreateParams.implParams.poolFee is not enabled + /// - The pool for the token and fee combination already exists function __onCreate( - uint96 lotId_, - address seller_, + uint96, + address, address baseToken_, address quoteToken_, - uint256 capacity_, - bool prefund_, + uint256, + bool, bytes calldata callbackData_ ) internal virtual override { - // Check whether a pool exists for the token combination + OnCreateParams memory params = abi.decode(callbackData_, (OnCreateParams)); + uint24 poolFee = abi.decode(params.implParams, (uint24)); + + // Validate the parameters + // Pool fee + // Fee not enabled + if (ramsesV2Factory.feeAmountTickSpacing(poolFee) == 0) { + revert Callback_Params_PoolFeeNotEnabled(); + } + + // Check that the pool does not exist + if (ramsesV2Factory.getPool(baseToken_, quoteToken_, poolFee) != address(0)) { + revert Callback_Params_PoolExists(); + } } /// @inheritdoc BaseDirectToLiquidity /// @dev This function implements the following: - /// - Creates the pool if necessary - /// - Deposits the tokens into the pool + /// - Creates and initializes the pool, if necessary + /// - Creates a new position and adds liquidity + /// - Transfers the ERC721 pool token to the recipient + /// + /// The assumptions are: + /// - the callback has `quoteTokenAmount_` quantity of quote tokens (as `receiveQuoteTokens` flag is set) + /// - the callback has `baseTokenAmount_` quantity of base tokens function _mintAndDeposit( uint96 lotId_, address quoteToken_, @@ -77,20 +110,97 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { uint256 baseTokenAmount_, bytes memory callbackData_ ) internal virtual override { - // Use the position manager to mint the pool token + // Decode the callback data + OnSettleParams memory params = abi.decode(callbackData_, (OnSettleParams)); + + // Extract the pool fee from the implParams + uint24 poolFee = abi.decode(lotConfiguration[lotId_].implParams, (uint24)); + + // Determine the ordering of tokens + bool quoteTokenIsToken0 = quoteToken_ < baseToken_; + + // Create and initialize the pool if necessary + { + // Determine sqrtPriceX96 + uint160 sqrtPriceX96 = SqrtPriceMath.getSqrtPriceX96( + quoteToken_, baseToken_, quoteTokenAmount_, baseTokenAmount_ + ); + + // If the pool already exists and is initialized, it will have no effect + // Please see the risks section in the contract documentation for more information + _createAndInitializePoolIfNecessary( + quoteTokenIsToken0 ? quoteToken_ : baseToken_, + quoteTokenIsToken0 ? baseToken_ : quoteToken_, + poolFee, + sqrtPriceX96 + ); + } - // Add liquidity to the pool + // Mint the position and add liquidity + { + IRamsesV2PositionManager.MintParams memory mintParams; + { + int24 tickSpacing = ramsesV2Factory.feeAmountTickSpacing(poolFee); + uint256 amount0 = quoteTokenIsToken0 ? quoteTokenAmount_ : baseTokenAmount_; + uint256 amount1 = quoteTokenIsToken0 ? baseTokenAmount_ : quoteTokenAmount_; + + mintParams = IRamsesV2PositionManager.MintParams({ + token0: quoteTokenIsToken0 ? quoteToken_ : baseToken_, + token1: quoteTokenIsToken0 ? baseToken_ : quoteToken_, + fee: poolFee, + tickLower: (TickMath.MIN_TICK / tickSpacing) * tickSpacing, + tickUpper: (TickMath.MAX_TICK / tickSpacing) * tickSpacing, + amount0Desired: amount0, + amount1Desired: amount1, + amount0Min: _getAmountWithSlippage(amount0, params.maxSlippage), + amount1Min: _getAmountWithSlippage(amount1, params.maxSlippage), + recipient: lotConfiguration[lotId_].recipient, // Transfer directly to the recipient. The position manager will check if it can receive ERC721 tokens. + deadline: block.timestamp, + veRamTokenId: params.veRamTokenId + }); + } + + // Approve spending + ERC20(quoteToken_).approve(address(ramsesV2PositionManager), quoteTokenAmount_); + ERC20(baseToken_).approve(address(ramsesV2PositionManager), baseTokenAmount_); + + // Mint the position + ramsesV2PositionManager.mint(mintParams); + + // Reset dangling approvals + // The position manager may not spend all tokens + ERC20(quoteToken_).approve(address(ramsesV2PositionManager), 0); + ERC20(baseToken_).approve(address(ramsesV2PositionManager), 0); + } } /// @inheritdoc BaseDirectToLiquidity - /// @dev This function implements the following: - /// - Transfers the pool token to the recipient - function _transferPoolToken(uint96 lotId_) internal virtual override { - uint256 tokenId = lotIdToTokenId[lotId_]; - if (tokenId == 0) { - revert Callback_PoolTokenNotFound(); - } + /// @dev This function does not perform any actions, + /// as `_mintAndDeposit()` directly transfers the token to the recipient + function _transferPoolToken(uint96) internal virtual override { + // Nothing to do + } - // Use the position manager to transfer the token to the recipient + // ========== INTERNAL FUNCTIONS ========== // + + /// @dev Copied from UniswapV3's PoolInitializer (which is GPL >= 2) + function _createAndInitializePoolIfNecessary( + address token0, + address token1, + uint24 fee, + uint160 sqrtPriceX96 + ) internal returns (address pool) { + require(token0 < token1); + pool = ramsesV2Factory.getPool(token0, token1, fee); + + if (pool == address(0)) { + pool = ramsesV2Factory.createPool(token0, token1, fee); + IRamsesV2Pool(pool).initialize(sqrtPriceX96); + } else { + (uint160 sqrtPriceX96Existing,,,,,,) = IRamsesV2Pool(pool).slot0(); + if (sqrtPriceX96Existing == 0) { + IRamsesV2Pool(pool).initialize(sqrtPriceX96); + } + } } } diff --git a/src/callbacks/liquidity/Ramses/lib/IRamsesV2Pool.sol b/src/callbacks/liquidity/Ramses/lib/IRamsesV2Pool.sol new file mode 100644 index 00000000..0c2c862e --- /dev/null +++ b/src/callbacks/liquidity/Ramses/lib/IRamsesV2Pool.sol @@ -0,0 +1,284 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +interface IRamsesV2Pool { + event Burn( + address indexed owner, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount, + uint256 amount0, + uint256 amount1 + ); + event Collect( + address indexed owner, + address recipient, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount0, + uint128 amount1 + ); + event CollectProtocol( + address indexed sender, address indexed recipient, uint128 amount0, uint128 amount1 + ); + event Flash( + address indexed sender, + address indexed recipient, + uint256 amount0, + uint256 amount1, + uint256 paid0, + uint256 paid1 + ); + event IncreaseObservationCardinalityNext( + uint16 observationCardinalityNextOld, uint16 observationCardinalityNextNew + ); + event Initialize(uint160 sqrtPriceX96, int24 tick); + event Mint( + address sender, + address indexed owner, + int24 indexed tickLower, + int24 indexed tickUpper, + uint128 amount, + uint256 amount0, + uint256 amount1 + ); + event SetFeeProtocol( + uint8 feeProtocol0Old, uint8 feeProtocol1Old, uint8 feeProtocol0New, uint8 feeProtocol1New + ); + event Swap( + address indexed sender, + address indexed recipient, + int256 amount0, + int256 amount1, + uint160 sqrtPriceX96, + uint128 liquidity, + int24 tick + ); + + function _advancePeriod() external; + function boostInfos(uint256 period) + external + view + returns (uint128 totalBoostAmount, int128 totalVeRamAmount); + function boostInfos( + uint256 period, + bytes32 key + ) + external + view + returns ( + uint128 boostAmount, + int128 veRamAmount, + int256 secondsDebtX96, + int256 boostedSecondsDebtX96 + ); + function boostedLiquidity() external view returns (uint128); + function burn( + uint256 index, + int24 tickLower, + int24 tickUpper, + uint128 amount + ) external returns (uint256 amount0, uint256 amount1); + function burn( + int24 tickLower, + int24 tickUpper, + uint128 amount + ) external returns (uint256 amount0, uint256 amount1); + function burn( + uint256 index, + int24 tickLower, + int24 tickUpper, + uint128 amount, + uint256 veRamTokenId + ) external returns (uint256 amount0, uint256 amount1); + function collect( + address recipient, + int24 tickLower, + int24 tickUpper, + uint128 amount0Requested, + uint128 amount1Requested + ) external returns (uint128 amount0, uint128 amount1); + function collect( + address recipient, + uint256 index, + int24 tickLower, + int24 tickUpper, + uint128 amount0Requested, + uint128 amount1Requested + ) external returns (uint128 amount0, uint128 amount1); + function collectProtocol( + address recipient, + uint128 amount0Requested, + uint128 amount1Requested + ) external returns (uint128 amount0, uint128 amount1); + function currentFee() external view returns (uint24); + function factory() external view returns (address); + function fee() external view returns (uint24); + function feeGrowthGlobal0X128() external view returns (uint256); + function feeGrowthGlobal1X128() external view returns (uint256); + function flash( + address recipient, + uint256 amount0, + uint256 amount1, + bytes memory data + ) external; + function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external; + function initialize( + address _factory, + address _nfpManager, + address _veRam, + address _voter, + address _token0, + address _token1, + uint24 _fee, + int24 _tickSpacing + ) external; + function initialize(uint160 sqrtPriceX96) external; + function lastPeriod() external view returns (uint256); + function liquidity() external view returns (uint128); + function maxLiquidityPerTick() external view returns (uint128); + function mint( + address recipient, + int24 tickLower, + int24 tickUpper, + uint128 amount, + bytes memory data + ) external returns (uint256 amount0, uint256 amount1); + function mint( + address recipient, + uint256 index, + int24 tickLower, + int24 tickUpper, + uint128 amount, + uint256 veRamTokenId, + bytes memory data + ) external returns (uint256 amount0, uint256 amount1); + function nfpManager() external view returns (address); + function observations(uint256 index) + external + view + returns ( + uint32 blockTimestamp, + int56 tickCumulative, + uint160 secondsPerLiquidityCumulativeX128, + bool initialized, + uint160 secondsPerBoostedLiquidityPeriodX128 + ); + function observe(uint32[] memory secondsAgos) + external + view + returns ( + int56[] memory tickCumulatives, + uint160[] memory secondsPerLiquidityCumulativeX128s, + uint160[] memory secondsPerBoostedLiquidityPeriodX128s + ); + function periodCumulativesInside( + uint32 period, + int24 tickLower, + int24 tickUpper + ) + external + view + returns ( + uint160 secondsPerLiquidityInsideX128, + uint160 secondsPerBoostedLiquidityInsideX128 + ); + function periods(uint256 period) + external + view + returns ( + uint32 previousPeriod, + int24 startTick, + int24 lastTick, + uint160 endSecondsPerLiquidityPeriodX128, + uint160 endSecondsPerBoostedLiquidityPeriodX128, + uint32 boostedInRange + ); + function positionPeriodDebt( + uint256 period, + address owner, + uint256 index, + int24 tickLower, + int24 tickUpper + ) external view returns (int256 secondsDebtX96, int256 boostedSecondsDebtX96); + function positionPeriodSecondsInRange( + uint256 period, + address owner, + uint256 index, + int24 tickLower, + int24 tickUpper + ) + external + view + returns (uint256 periodSecondsInsideX96, uint256 periodBoostedSecondsInsideX96); + function positions(bytes32 key) + external + view + returns ( + uint128 liquidity, + uint256 feeGrowthInside0LastX128, + uint256 feeGrowthInside1LastX128, + uint128 tokensOwed0, + uint128 tokensOwed1, + uint256 attachedVeRamId + ); + function protocolFees() external view returns (uint128 token0, uint128 token1); + function readStorage(bytes32[] memory slots) + external + view + returns (bytes32[] memory returnData); + function setFee(uint24 _fee) external; + function setFeeProtocol() external; + function slot0() + external + view + returns ( + uint160 sqrtPriceX96, + int24 tick, + uint16 observationIndex, + uint16 observationCardinality, + uint16 observationCardinalityNext, + uint8 feeProtocol, + bool unlocked + ); + function snapshotCumulativesInside( + int24 tickLower, + int24 tickUpper + ) + external + view + returns ( + int56 tickCumulativeInside, + uint160 secondsPerLiquidityInsideX128, + uint160 secondsPerBoostedLiquidityInsideX128, + uint32 secondsInside + ); + function swap( + address recipient, + bool zeroForOne, + int256 amountSpecified, + uint160 sqrtPriceLimitX96, + bytes memory data + ) external returns (int256 amount0, int256 amount1); + function tickBitmap(int16 tick) external view returns (uint256); + function tickSpacing() external view returns (int24); + function ticks(int24 tick) + external + view + returns ( + uint128 liquidityGross, + int128 liquidityNet, + uint128 boostedLiquidityGross, + int128 boostedLiquidityNet, + uint256 feeGrowthOutside0X128, + uint256 feeGrowthOutside1X128, + int56 tickCumulativeOutside, + uint160 secondsPerLiquidityOutsideX128, + uint32 secondsOutside, + bool initialized + ); + function token0() external view returns (address); + function token1() external view returns (address); + function veRam() external view returns (address); + function voter() external view returns (address); +} From 2595b1c82b8dfa33e732839aead6f340fb038fc1 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 23 Jul 2024 16:26:40 +0400 Subject: [PATCH 07/47] Fix stack too deep error --- .../liquidity/Ramses/RamsesV2DTL.sol | 63 ++++++++++++------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol index 32ca72ff..201e49e2 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol +++ b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol @@ -138,27 +138,9 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { // Mint the position and add liquidity { - IRamsesV2PositionManager.MintParams memory mintParams; - { - int24 tickSpacing = ramsesV2Factory.feeAmountTickSpacing(poolFee); - uint256 amount0 = quoteTokenIsToken0 ? quoteTokenAmount_ : baseTokenAmount_; - uint256 amount1 = quoteTokenIsToken0 ? baseTokenAmount_ : quoteTokenAmount_; - - mintParams = IRamsesV2PositionManager.MintParams({ - token0: quoteTokenIsToken0 ? quoteToken_ : baseToken_, - token1: quoteTokenIsToken0 ? baseToken_ : quoteToken_, - fee: poolFee, - tickLower: (TickMath.MIN_TICK / tickSpacing) * tickSpacing, - tickUpper: (TickMath.MAX_TICK / tickSpacing) * tickSpacing, - amount0Desired: amount0, - amount1Desired: amount1, - amount0Min: _getAmountWithSlippage(amount0, params.maxSlippage), - amount1Min: _getAmountWithSlippage(amount1, params.maxSlippage), - recipient: lotConfiguration[lotId_].recipient, // Transfer directly to the recipient. The position manager will check if it can receive ERC721 tokens. - deadline: block.timestamp, - veRamTokenId: params.veRamTokenId - }); - } + IRamsesV2PositionManager.MintParams memory mintParams = _getMintParams( + lotId_, quoteToken_, quoteTokenAmount_, baseToken_, baseTokenAmount_, params + ); // Approve spending ERC20(quoteToken_).approve(address(ramsesV2PositionManager), quoteTokenAmount_); @@ -203,4 +185,43 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { } } } + + function _getMintParams( + uint96 lotId_, + address quoteToken_, + uint256 quoteTokenAmount_, + address baseToken_, + uint256 baseTokenAmount_, + OnSettleParams memory onSettleParams_ + ) internal view returns (IRamsesV2PositionManager.MintParams memory) { + // Extract the pool fee from the implParams + uint24 poolFee; + int24 tickSpacing; + { + poolFee = abi.decode(lotConfiguration[lotId_].implParams, (uint24)); + tickSpacing = ramsesV2Factory.feeAmountTickSpacing(poolFee); + } + + // Determine the ordering of tokens + bool quoteTokenIsToken0 = quoteToken_ < baseToken_; + + // Calculate the minimum amount out for each token + uint256 amount0 = quoteTokenIsToken0 ? quoteTokenAmount_ : baseTokenAmount_; + uint256 amount1 = quoteTokenIsToken0 ? baseTokenAmount_ : quoteTokenAmount_; + + return IRamsesV2PositionManager.MintParams({ + token0: quoteTokenIsToken0 ? quoteToken_ : baseToken_, + token1: quoteTokenIsToken0 ? baseToken_ : quoteToken_, + fee: poolFee, + tickLower: (TickMath.MIN_TICK / tickSpacing) * tickSpacing, + tickUpper: (TickMath.MAX_TICK / tickSpacing) * tickSpacing, + amount0Desired: amount0, + amount1Desired: amount1, + amount0Min: _getAmountWithSlippage(amount0, onSettleParams_.maxSlippage), + amount1Min: _getAmountWithSlippage(amount1, onSettleParams_.maxSlippage), + recipient: lotConfiguration[lotId_].recipient, + deadline: block.timestamp, + veRamTokenId: onSettleParams_.veRamTokenId + }); + } } From eb8edf982c16a8994a25f7da0ad0496e7173ed67 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 23 Jul 2024 16:31:41 +0400 Subject: [PATCH 08/47] Documentation --- src/callbacks/liquidity/Ramses/RamsesV2DTL.sol | 12 ++++++++++++ src/callbacks/liquidity/UniswapV3DTL.sol | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol index 201e49e2..e6142d49 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol +++ b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol @@ -16,6 +16,18 @@ import {IRamsesV2PositionManager} from "./lib/IRamsesV2PositionManager.sol"; import {TickMath} from "@uniswap-v3-core-1.0.1-solc-0.8-simulate/libraries/TickMath.sol"; import {SqrtPriceMath} from "../../../lib/uniswap-v3/SqrtPriceMath.sol"; +/// @title RamsesV2DirectToLiquidity +/// @notice This Callback contract deposits the proceeds from a batch auction into a Ramses V2 pool +/// in order to create full-range liquidity immediately. +/// +/// The LP tokens are transferred to `DTLConfiguration.recipient`, which must be an EOA or a contract that can receive ERC721 tokens. +/// +/// An important risk to consider: if the auction's base token is available and liquid, a third-party +/// could front-run the auction by creating the pool before the auction ends. This would allow them to +/// manipulate the price of the pool and potentially profit from the eventual deposit of the auction proceeds. +/// +/// @dev As a general rule, this callback contract does not retain balances of tokens between calls. +/// Transfers are performed within the same function that requires the balance. contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { // ========== ERRORS ========== // diff --git a/src/callbacks/liquidity/UniswapV3DTL.sol b/src/callbacks/liquidity/UniswapV3DTL.sol index 9c6adcb6..504164b6 100644 --- a/src/callbacks/liquidity/UniswapV3DTL.sol +++ b/src/callbacks/liquidity/UniswapV3DTL.sol @@ -22,7 +22,7 @@ import {BaseDirectToLiquidity} from "./BaseDTL.sol"; /// @title UniswapV3DirectToLiquidity /// @notice This Callback contract deposits the proceeds from a batch auction into a Uniswap V3 pool -/// in order to create liquidity immediately. +/// in order to create full-range liquidity immediately. /// /// The Uniswap V3 position is tokenised as an ERC-20 using [G-UNI](https://github.com/gelatodigital/g-uni-v1-core). /// From 4c51e715286c45f9244b5f1fff879a2303e48bf6 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 23 Jul 2024 16:33:05 +0400 Subject: [PATCH 09/47] Update test salts --- script/salts/salts.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/salts/salts.json b/script/salts/salts.json index 32707af5..74649f91 100644 --- a/script/salts/salts.json +++ b/script/salts/salts.json @@ -102,13 +102,13 @@ "0xdb5689fd93b97ad35ee469bbcb38d4472c005f1ae951b154f2437bd1207a03f3": "0xf35c7565f04f89db7d1783343067728c4db35f70d7aea4d7dbd732fa462bb956" }, "Test_UniswapV2DirectToLiquidity": { - "0x44d9f5974232d8dc047dadc9a93a66dde3b1938d906428d1c52dc9e356988d87": "0xd3cddc8f40b0d22205b1eaf044af0e3131bbd63922c5c14a38e2440552c8fd5f" + "0xf3cf5a017c393b255bc51f7bc4327a42384669b656c57e12841c3f45e8f261c7": "0x4a019a12c26020c0311e6f902b4cd4774e042aaba59fdeedf186a7e644b26827" }, "Test_UniswapV2Router": { "0x3aeb5c743a058e3c9d871533d2537013b819c4e401acaca3619f046cd9422258": "0x4c096423447dffd4ffce23f8e15e2d1b08619f72abc81536b5492d95b7c8f03f" }, "Test_UniswapV3DirectToLiquidity": { - "0x5bd1c45b9f8ee81f6de61284469b1e580289694e339da3bf7f89422b2f6acee2": "0xf4188c3dde8973a334f65d1f532e5b4e022e75a69140173cd2ee38fa05f1d789" + "0xd87004414e9285e9c723ac4d3311b9b9540dd5390e4c74bd1aa3bdb13f3e9cf0": "0x307f1610a69d585a53a3692ad1f188d88a710be5e0def4247c4bef89dea49c9e" }, "Test_UniswapV3Factory": { "0x33479a3955b5801da918af0aa61f4501368d483ac02e8ae036d4e6f9c56d2038": "0x715638753ed78c13d0d385c1758204daad3b308083604fb0d555eb285199ed30" From 0816228da3624eb3160562b4bb86957a01612c3e Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 23 Jul 2024 16:35:33 +0400 Subject: [PATCH 10/47] Test TODOs --- test/callbacks/liquidity/UniswapV2DTL/onSettle.t.sol | 2 ++ test/callbacks/liquidity/UniswapV3DTL/onSettle.t.sol | 2 ++ 2 files changed, 4 insertions(+) diff --git a/test/callbacks/liquidity/UniswapV2DTL/onSettle.t.sol b/test/callbacks/liquidity/UniswapV2DTL/onSettle.t.sol index f073c4a0..95ad89b9 100644 --- a/test/callbacks/liquidity/UniswapV2DTL/onSettle.t.sol +++ b/test/callbacks/liquidity/UniswapV2DTL/onSettle.t.sol @@ -269,6 +269,8 @@ contract UniswapV2DirectToLiquidityOnSettleTest is UniswapV2DirectToLiquidityTes // ========== Tests ========== // + // [ ] given the onSettle callback has already been called + // [ ] it reverts // [X] given the pool is created // [X] it initializes the pool // [X] given the pool is created and initialized diff --git a/test/callbacks/liquidity/UniswapV3DTL/onSettle.t.sol b/test/callbacks/liquidity/UniswapV3DTL/onSettle.t.sol index 4a4b5511..f949c833 100644 --- a/test/callbacks/liquidity/UniswapV3DTL/onSettle.t.sol +++ b/test/callbacks/liquidity/UniswapV3DTL/onSettle.t.sol @@ -279,6 +279,8 @@ contract UniswapV3DirectToLiquidityOnSettleTest is UniswapV3DirectToLiquidityTes // ========== Tests ========== // + // [ ] given the onSettle callback has already been called + // [ ] it reverts // [X] given the pool is created // [X] it initializes the pool // [X] given the pool is created and initialized From 0ae487971b7dbb80f217b3733608caba39cfe884 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Tue, 23 Jul 2024 16:44:48 +0400 Subject: [PATCH 11/47] chore: linting --- test/callbacks/liquidity/BaselineV2/onCreate.t.sol | 4 ++-- test/callbacks/liquidity/UniswapV2DTL/onCreate.t.sol | 2 +- test/callbacks/liquidity/UniswapV2DTL/onSettle.t.sol | 8 ++++---- test/callbacks/liquidity/UniswapV3DTL/onCreate.t.sol | 2 +- test/callbacks/liquidity/UniswapV3DTL/onSettle.t.sol | 10 +++++----- test/lib/SqrtPriceMath.t.sol | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/test/callbacks/liquidity/BaselineV2/onCreate.t.sol b/test/callbacks/liquidity/BaselineV2/onCreate.t.sol index 75d59e51..4604f11c 100644 --- a/test/callbacks/liquidity/BaselineV2/onCreate.t.sol +++ b/test/callbacks/liquidity/BaselineV2/onCreate.t.sol @@ -29,7 +29,7 @@ contract BaselineOnCreateTest is BaselineAxisLaunchTest { vm.expectRevert(err); } - function _assertBaseTokenBalances() internal { + function _assertBaseTokenBalances() internal view { assertEq(_baseToken.balanceOf(_SELLER), 0, "seller balance"); assertEq(_baseToken.balanceOf(_NOT_SELLER), 0, "not seller balance"); assertEq(_baseToken.balanceOf(_dtlAddress), 0, "dtl balance"); @@ -130,7 +130,7 @@ contract BaselineOnCreateTest is BaselineAxisLaunchTest { return roundedTick; } - function _assertTicks(int24 fixedPriceTick_) internal { + function _assertTicks(int24 fixedPriceTick_) internal view { assertEq(_baseToken.activeTick(), fixedPriceTick_, "active tick"); console2.log("Active tick: ", _baseToken.activeTick()); diff --git a/test/callbacks/liquidity/UniswapV2DTL/onCreate.t.sol b/test/callbacks/liquidity/UniswapV2DTL/onCreate.t.sol index d65cae54..ca3294cc 100644 --- a/test/callbacks/liquidity/UniswapV2DTL/onCreate.t.sol +++ b/test/callbacks/liquidity/UniswapV2DTL/onCreate.t.sol @@ -42,7 +42,7 @@ contract UniswapV2DirectToLiquidityOnCreateTest is UniswapV2DirectToLiquidityTes vm.expectRevert(err); } - function _assertBaseTokenBalances() internal { + function _assertBaseTokenBalances() internal view { assertEq(_baseToken.balanceOf(_SELLER), 0, "seller balance"); assertEq(_baseToken.balanceOf(_NOT_SELLER), 0, "not seller balance"); assertEq(_baseToken.balanceOf(_dtlAddress), 0, "dtl balance"); diff --git a/test/callbacks/liquidity/UniswapV2DTL/onSettle.t.sol b/test/callbacks/liquidity/UniswapV2DTL/onSettle.t.sol index 95ad89b9..4a401bf0 100644 --- a/test/callbacks/liquidity/UniswapV2DTL/onSettle.t.sol +++ b/test/callbacks/liquidity/UniswapV2DTL/onSettle.t.sol @@ -54,7 +54,7 @@ contract UniswapV2DirectToLiquidityOnSettleTest is UniswapV2DirectToLiquidityTes // ========== Assertions ========== // - function _assertLpTokenBalance() internal { + function _assertLpTokenBalance() internal view { // Get the pools deployed by the DTL callback IUniswapV2Pair pool = _getUniswapV2Pool(); @@ -122,15 +122,15 @@ contract UniswapV2DirectToLiquidityOnSettleTest is UniswapV2DirectToLiquidityTes ); } - function _assertQuoteTokenBalance() internal { + function _assertQuoteTokenBalance() internal view { assertEq(_quoteToken.balanceOf(_dtlAddress), 0, "DTL: quote token balance"); } - function _assertBaseTokenBalance() internal { + function _assertBaseTokenBalance() internal view { assertEq(_baseToken.balanceOf(_dtlAddress), 0, "DTL: base token balance"); } - function _assertApprovals() internal { + function _assertApprovals() internal view { // Ensure there are no dangling approvals assertEq( _quoteToken.allowance(_dtlAddress, address(_uniV2Router)), diff --git a/test/callbacks/liquidity/UniswapV3DTL/onCreate.t.sol b/test/callbacks/liquidity/UniswapV3DTL/onCreate.t.sol index 15122668..097d2dcb 100644 --- a/test/callbacks/liquidity/UniswapV3DTL/onCreate.t.sol +++ b/test/callbacks/liquidity/UniswapV3DTL/onCreate.t.sol @@ -43,7 +43,7 @@ contract UniswapV3DirectToLiquidityOnCreateTest is UniswapV3DirectToLiquidityTes vm.expectRevert(err); } - function _assertBaseTokenBalances() internal { + function _assertBaseTokenBalances() internal view { assertEq(_baseToken.balanceOf(_SELLER), 0, "seller balance"); assertEq(_baseToken.balanceOf(_NOT_SELLER), 0, "not seller balance"); assertEq(_baseToken.balanceOf(_dtlAddress), 0, "dtl balance"); diff --git a/test/callbacks/liquidity/UniswapV3DTL/onSettle.t.sol b/test/callbacks/liquidity/UniswapV3DTL/onSettle.t.sol index f949c833..716004d2 100644 --- a/test/callbacks/liquidity/UniswapV3DTL/onSettle.t.sol +++ b/test/callbacks/liquidity/UniswapV3DTL/onSettle.t.sol @@ -63,7 +63,7 @@ contract UniswapV3DirectToLiquidityOnSettleTest is UniswapV3DirectToLiquidityTes // ========== Assertions ========== // - function _assertPoolState(uint160 sqrtPriceX96_) internal { + function _assertPoolState(uint160 sqrtPriceX96_) internal view { // Get the pool address pool = _getPool(); @@ -71,7 +71,7 @@ contract UniswapV3DirectToLiquidityOnSettleTest is UniswapV3DirectToLiquidityTes assertEq(sqrtPriceX96, sqrtPriceX96_, "pool sqrt price"); } - function _assertLpTokenBalance() internal { + function _assertLpTokenBalance() internal view { // Get the pools deployed by the DTL callback GUniPool pool = _getGUniPool(); @@ -136,15 +136,15 @@ contract UniswapV3DirectToLiquidityOnSettleTest is UniswapV3DirectToLiquidityTes ); } - function _assertQuoteTokenBalance() internal { + function _assertQuoteTokenBalance() internal view { assertEq(_quoteToken.balanceOf(_dtlAddress), 0, "DTL: quote token balance"); } - function _assertBaseTokenBalance() internal { + function _assertBaseTokenBalance() internal view { assertEq(_baseToken.balanceOf(_dtlAddress), 0, "DTL: base token balance"); } - function _assertApprovals() internal { + function _assertApprovals() internal view { // Ensure there are no dangling approvals assertEq( _quoteToken.allowance(_dtlAddress, address(_getGUniPool())), diff --git a/test/lib/SqrtPriceMath.t.sol b/test/lib/SqrtPriceMath.t.sol index 5fa175b2..35854c43 100644 --- a/test/lib/SqrtPriceMath.t.sol +++ b/test/lib/SqrtPriceMath.t.sol @@ -28,12 +28,12 @@ contract SqrtPriceMathTest is Test { // [X] when tokenA decimals is less than tokenB decimals // [X] it calculates the correct sqrtPriceX96 - function test_whenTokenAIs_TOKEN0() public { + function test_whenTokenAIs_TOKEN0() public pure { uint160 sqrtPriceX96 = SqrtPriceMath.getSqrtPriceX96(_TOKEN0, _TOKEN1, _AMOUNT0, _AMOUNT1); assertEq(sqrtPriceX96, _SQRTPRICEX96, "SqrtPriceX96"); } - function test_whenTokenAIs_TOKEN1() public { + function test_whenTokenAIs_TOKEN1() public pure { uint160 sqrtPriceX96 = SqrtPriceMath.getSqrtPriceX96(_TOKEN1, _TOKEN0, _AMOUNT1, _AMOUNT0); assertEq(sqrtPriceX96, _SQRTPRICEX96, "SqrtPriceX96"); } From 0c60eef24ede8702094d07d0063323fe636b1cdf Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 25 Jul 2024 15:04:49 +0400 Subject: [PATCH 12/47] Ramses V1: shift maxSlippage into onCreate params. Remove pool check. --- .../liquidity/Ramses/RamsesV1DTL.sol | 41 ++++++++++--------- 1 file changed, 21 insertions(+), 20 deletions(-) diff --git a/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol index f25ad14c..fbb1fd57 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol +++ b/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol @@ -32,16 +32,10 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { /// @notice Parameters for the onCreate callback /// @dev This will be encoded in the `callbackData_` parameter /// - /// @param stable Whether the pool is stable or volatile + /// @param stable Whether the pool will be stable or volatile + /// @param maxSlippage The maximum slippage allowed when adding liquidity (in terms of basis points, where 1% = 1e2) struct RamsesV1OnCreateParams { bool stable; - } - - /// @notice Parameters for the onSettle callback - /// @dev This will be encoded in the `callbackData_` parameter - /// - /// @param maxSlippage The maximum slippage allowed when adding liquidity (in terms of `ONE_HUNDRED_PERCENT`) - struct RamsesV1OnSettleParams { uint24 maxSlippage; } @@ -58,6 +52,9 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { /// @notice Records whether a pool should be stable or volatile mapping(uint96 => bool) public lotIdToStable; + /// @notice Mapping of lot ID to maximum slippage + mapping(uint96 => uint24) public lotIdToMaxSlippage; + /// @notice Mapping of lot ID to pool token mapping(uint96 => address) public lotIdToPoolToken; @@ -85,12 +82,12 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { /// - Validates the parameters /// /// This function reverts if: - /// - The pool for the token combination already exists + /// - `RamsesV1OnCreateParams.maxSlippage` is out of bounds function __onCreate( uint96 lotId_, address, - address baseToken_, - address quoteToken_, + address, + address, uint256, bool, bytes calldata callbackData_ @@ -98,10 +95,13 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { // Decode the callback data RamsesV1OnCreateParams memory params = abi.decode(callbackData_, (RamsesV1OnCreateParams)); - // Check that the pool does not exist - if (pairFactory.getPair(baseToken_, quoteToken_, params.stable) != address(0)) { - revert Callback_Params_PoolExists(); + // Check that the slippage amount is within bounds + if (params.maxSlippage > ONE_HUNDRED_PERCENT) { + revert Callback_Params_PercentOutOfBounds(params.maxSlippage, 0, ONE_HUNDRED_PERCENT); } + // The maxSlippage is stored during onCreate, as the callback data is passed in by the auction seller. + // As AuctionHouse.settle() can be called by anyone, a value for maxSlippage could be passed that would result in a loss for the auction seller. + lotIdToMaxSlippage[lotId_] = params.maxSlippage; // Record whether the pool should be stable or volatile lotIdToStable[lotId_] = params.stable; @@ -118,11 +118,8 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { uint256 quoteTokenAmount_, address baseToken_, uint256 baseTokenAmount_, - bytes memory callbackData_ + bytes memory ) internal virtual override { - // Decode the callback data - RamsesV1OnSettleParams memory params = abi.decode(callbackData_, (RamsesV1OnSettleParams)); - // Create and initialize the pool if necessary // Token orientation is irrelevant bool stable = lotIdToStable[lotId_]; @@ -132,8 +129,10 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { } // Calculate the minimum amount out for each token - uint256 quoteTokenAmountMin = _getAmountWithSlippage(quoteTokenAmount_, params.maxSlippage); - uint256 baseTokenAmountMin = _getAmountWithSlippage(baseTokenAmount_, params.maxSlippage); + uint256 quoteTokenAmountMin = + _getAmountWithSlippage(quoteTokenAmount_, lotIdToMaxSlippage[lotId_]); + uint256 baseTokenAmountMin = + _getAmountWithSlippage(baseTokenAmount_, lotIdToMaxSlippage[lotId_]); // Approve the router to spend the tokens ERC20(quoteToken_).approve(address(router), quoteTokenAmount_); @@ -141,6 +140,8 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { // Deposit into the pool // Token orientation is irrelevant + // If the pool is liquid and initialised at a price different to the auction, this will revert + // The auction would fail to settle, and bidders could be refunded by an abort() call router.addLiquidity( quoteToken_, baseToken_, From 4451130feb878dae251a1ae303de749d3e54f78a Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 25 Jul 2024 15:11:33 +0400 Subject: [PATCH 13/47] Ramses V1: Store parameters in a single mapping. Add check on length of callback data. --- .../liquidity/Ramses/RamsesV1DTL.sol | 31 +++++++++---------- 1 file changed, 15 insertions(+), 16 deletions(-) diff --git a/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol index fbb1fd57..3751f43b 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol +++ b/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol @@ -49,11 +49,8 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { /// @dev This contract is used to add liquidity to Ramses pairs IRamsesV1Router public router; - /// @notice Records whether a pool should be stable or volatile - mapping(uint96 => bool) public lotIdToStable; - - /// @notice Mapping of lot ID to maximum slippage - mapping(uint96 => uint24) public lotIdToMaxSlippage; + /// @notice Mapping of lot ID to configuration parameters + mapping(uint96 => RamsesV1OnCreateParams) public lotParameters; /// @notice Mapping of lot ID to pool token mapping(uint96 => address) public lotIdToPoolToken; @@ -92,6 +89,11 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { bool, bytes calldata callbackData_ ) internal virtual override { + // Validate that the callback data is of the correct length + if (callbackData_.length != 64) { + revert Callback_InvalidParams(); + } + // Decode the callback data RamsesV1OnCreateParams memory params = abi.decode(callbackData_, (RamsesV1OnCreateParams)); @@ -101,10 +103,8 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { } // The maxSlippage is stored during onCreate, as the callback data is passed in by the auction seller. // As AuctionHouse.settle() can be called by anyone, a value for maxSlippage could be passed that would result in a loss for the auction seller. - lotIdToMaxSlippage[lotId_] = params.maxSlippage; - // Record whether the pool should be stable or volatile - lotIdToStable[lotId_] = params.stable; + lotParameters[lotId_] = params; } /// @inheritdoc BaseDirectToLiquidity @@ -120,19 +120,18 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { uint256 baseTokenAmount_, bytes memory ) internal virtual override { + RamsesV1OnCreateParams memory params = lotParameters[lotId_]; + // Create and initialize the pool if necessary // Token orientation is irrelevant - bool stable = lotIdToStable[lotId_]; - address pairAddress = pairFactory.getPair(baseToken_, quoteToken_, stable); + address pairAddress = pairFactory.getPair(baseToken_, quoteToken_, params.stable); if (pairAddress == address(0)) { - pairAddress = pairFactory.createPair(baseToken_, quoteToken_, stable); + pairAddress = pairFactory.createPair(baseToken_, quoteToken_, params.stable); } // Calculate the minimum amount out for each token - uint256 quoteTokenAmountMin = - _getAmountWithSlippage(quoteTokenAmount_, lotIdToMaxSlippage[lotId_]); - uint256 baseTokenAmountMin = - _getAmountWithSlippage(baseTokenAmount_, lotIdToMaxSlippage[lotId_]); + uint256 quoteTokenAmountMin = _getAmountWithSlippage(quoteTokenAmount_, params.maxSlippage); + uint256 baseTokenAmountMin = _getAmountWithSlippage(baseTokenAmount_, params.maxSlippage); // Approve the router to spend the tokens ERC20(quoteToken_).approve(address(router), quoteTokenAmount_); @@ -145,7 +144,7 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { router.addLiquidity( quoteToken_, baseToken_, - stable, + params.stable, quoteTokenAmount_, baseTokenAmount_, quoteTokenAmountMin, From a290588600492b709389d41a59e724f9594514c0 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 25 Jul 2024 15:32:38 +0400 Subject: [PATCH 14/47] Ramses V1: cleanup --- .../liquidity/Ramses/RamsesV1DTL.sol | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol index 3751f43b..a1a25122 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol +++ b/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol @@ -89,20 +89,25 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { bool, bytes calldata callbackData_ ) internal virtual override { - // Validate that the callback data is of the correct length - if (callbackData_.length != 64) { - revert Callback_InvalidParams(); - } + RamsesV1OnCreateParams memory params; + { + OnCreateParams memory onCreateParams = abi.decode(callbackData_, (OnCreateParams)); + + // Validate that the callback data is of the correct length + if (onCreateParams.implParams.length != 64) { + revert Callback_InvalidParams(); + } - // Decode the callback data - RamsesV1OnCreateParams memory params = abi.decode(callbackData_, (RamsesV1OnCreateParams)); + // Decode the callback data + params = abi.decode(onCreateParams.implParams, (RamsesV1OnCreateParams)); + } // Check that the slippage amount is within bounds + // The maxSlippage is stored during onCreate, as the callback data is passed in by the auction seller. + // As AuctionHouse.settle() can be called by anyone, a value for maxSlippage could be passed that would result in a loss for the auction seller. if (params.maxSlippage > ONE_HUNDRED_PERCENT) { revert Callback_Params_PercentOutOfBounds(params.maxSlippage, 0, ONE_HUNDRED_PERCENT); } - // The maxSlippage is stored during onCreate, as the callback data is passed in by the auction seller. - // As AuctionHouse.settle() can be called by anyone, a value for maxSlippage could be passed that would result in a loss for the auction seller. lotParameters[lotId_] = params; } From 2eee49a60e1d44029d85c2c43289959e00bc52b1 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 25 Jul 2024 15:33:27 +0400 Subject: [PATCH 15/47] Ramses V2: shift maxSlippage and veRamTokenId into onCreate callback data --- .../liquidity/Ramses/RamsesV2DTL.sol | 96 +++++++++++-------- .../liquidity/Ramses/lib/IVotingEscrow.sol | 68 +++++++++++++ 2 files changed, 125 insertions(+), 39 deletions(-) create mode 100644 src/callbacks/liquidity/Ramses/lib/IVotingEscrow.sol diff --git a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol index e6142d49..98ab2c2a 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol +++ b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol @@ -11,6 +11,7 @@ import {BaseDirectToLiquidity} from "../BaseDTL.sol"; import {IRamsesV2Pool} from "./lib/IRamsesV2Pool.sol"; import {IRamsesV2Factory} from "./lib/IRamsesV2Factory.sol"; import {IRamsesV2PositionManager} from "./lib/IRamsesV2PositionManager.sol"; +import {IVotingEscrow} from "./lib/IVotingEscrow.sol"; // Uniswap import {TickMath} from "@uniswap-v3-core-1.0.1-solc-0.8-simulate/libraries/TickMath.sol"; @@ -35,12 +36,13 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { // ========== STRUCTS ========== // - /// @notice Parameters for the onSettle callback + /// @notice Parameters for the onCreate callback /// @dev This will be encoded in the `callbackData_` parameter /// - /// @param maxSlippage The maximum slippage allowed when adding liquidity (in terms of `ONE_HUNDRED_PERCENT`) - /// @param veRamTokenId The token ID of the veRAM token to use for the position (optional) - struct OnSettleParams { + /// @param maxSlippage The maximum slippage allowed when adding liquidity (in terms of basis points, where 1% = 1e2) + /// @param veRamTokenId The token ID of the veRAM token to use for the position (optional) + struct RamsesV2OnCreateParams { + uint24 poolFee; uint24 maxSlippage; uint256 veRamTokenId; } @@ -53,6 +55,9 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { /// @notice The Ramses V2 position manager IRamsesV2PositionManager public ramsesV2PositionManager; + /// @notice Mapping of lot ID to configuration parameters + mapping(uint96 => RamsesV2OnCreateParams) public lotParameters; + // ========== CONSTRUCTOR ========== // constructor( @@ -78,31 +83,56 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { /// - Validates the input data /// /// This function reverts if: - /// - OnCreateParams.implParams.poolFee is not enabled - /// - The pool for the token and fee combination already exists + /// - `RamsesV1OnCreateParams.poolFee` is not enabled + /// - `RamsesV1OnCreateParams.maxSlippage` is out of bounds + /// - This contract does not have permission to use the veRamTokenId function __onCreate( - uint96, + uint96 lotId_, + address, + address, address, - address baseToken_, - address quoteToken_, uint256, bool, bytes calldata callbackData_ ) internal virtual override { - OnCreateParams memory params = abi.decode(callbackData_, (OnCreateParams)); - uint24 poolFee = abi.decode(params.implParams, (uint24)); + RamsesV2OnCreateParams memory params; + { + OnCreateParams memory onCreateParams = abi.decode(callbackData_, (OnCreateParams)); + + // Validate that the callback data is of the correct length + if (onCreateParams.implParams.length != 96) { + revert Callback_InvalidParams(); + } + + // Decode the callback data + params = abi.decode(onCreateParams.implParams, (RamsesV2OnCreateParams)); + } // Validate the parameters // Pool fee // Fee not enabled - if (ramsesV2Factory.feeAmountTickSpacing(poolFee) == 0) { + if (ramsesV2Factory.feeAmountTickSpacing(params.poolFee) == 0) { revert Callback_Params_PoolFeeNotEnabled(); } - // Check that the pool does not exist - if (ramsesV2Factory.getPool(baseToken_, quoteToken_, poolFee) != address(0)) { - revert Callback_Params_PoolExists(); + // Check that the maxSlippage is in bounds + // The maxSlippage is stored during onCreate, as the callback data is passed in by the auction seller. + // As AuctionHouse.settle() can be called by anyone, a value for maxSlippage could be passed that would result in a loss for the auction seller. + if (params.maxSlippage > ONE_HUNDRED_PERCENT) { + revert Callback_Params_PercentOutOfBounds(params.maxSlippage, 0, ONE_HUNDRED_PERCENT); } + + // Check that the callback has been given permission to use the veRamTokenId + if ( + params.veRamTokenId > 0 + && !IVotingEscrow(ramsesV2PositionManager.veRam()).isApprovedOrOwner( + address(this), params.veRamTokenId + ) + ) { + revert Callback_InvalidParams(); + } + + lotParameters[lotId_] = params; } /// @inheritdoc BaseDirectToLiquidity @@ -120,14 +150,8 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { uint256 quoteTokenAmount_, address baseToken_, uint256 baseTokenAmount_, - bytes memory callbackData_ + bytes memory ) internal virtual override { - // Decode the callback data - OnSettleParams memory params = abi.decode(callbackData_, (OnSettleParams)); - - // Extract the pool fee from the implParams - uint24 poolFee = abi.decode(lotConfiguration[lotId_].implParams, (uint24)); - // Determine the ordering of tokens bool quoteTokenIsToken0 = quoteToken_ < baseToken_; @@ -143,16 +167,15 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { _createAndInitializePoolIfNecessary( quoteTokenIsToken0 ? quoteToken_ : baseToken_, quoteTokenIsToken0 ? baseToken_ : quoteToken_, - poolFee, + lotParameters[lotId_].poolFee, sqrtPriceX96 ); } // Mint the position and add liquidity { - IRamsesV2PositionManager.MintParams memory mintParams = _getMintParams( - lotId_, quoteToken_, quoteTokenAmount_, baseToken_, baseTokenAmount_, params - ); + IRamsesV2PositionManager.MintParams memory mintParams = + _getMintParams(lotId_, quoteToken_, quoteTokenAmount_, baseToken_, baseTokenAmount_); // Approve spending ERC20(quoteToken_).approve(address(ramsesV2PositionManager), quoteTokenAmount_); @@ -203,16 +226,11 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { address quoteToken_, uint256 quoteTokenAmount_, address baseToken_, - uint256 baseTokenAmount_, - OnSettleParams memory onSettleParams_ + uint256 baseTokenAmount_ ) internal view returns (IRamsesV2PositionManager.MintParams memory) { - // Extract the pool fee from the implParams - uint24 poolFee; - int24 tickSpacing; - { - poolFee = abi.decode(lotConfiguration[lotId_].implParams, (uint24)); - tickSpacing = ramsesV2Factory.feeAmountTickSpacing(poolFee); - } + RamsesV2OnCreateParams memory params = lotParameters[lotId_]; + + int24 tickSpacing = ramsesV2Factory.feeAmountTickSpacing(params.poolFee); // Determine the ordering of tokens bool quoteTokenIsToken0 = quoteToken_ < baseToken_; @@ -224,16 +242,16 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { return IRamsesV2PositionManager.MintParams({ token0: quoteTokenIsToken0 ? quoteToken_ : baseToken_, token1: quoteTokenIsToken0 ? baseToken_ : quoteToken_, - fee: poolFee, + fee: params.poolFee, tickLower: (TickMath.MIN_TICK / tickSpacing) * tickSpacing, tickUpper: (TickMath.MAX_TICK / tickSpacing) * tickSpacing, amount0Desired: amount0, amount1Desired: amount1, - amount0Min: _getAmountWithSlippage(amount0, onSettleParams_.maxSlippage), - amount1Min: _getAmountWithSlippage(amount1, onSettleParams_.maxSlippage), + amount0Min: _getAmountWithSlippage(amount0, params.maxSlippage), + amount1Min: _getAmountWithSlippage(amount1, params.maxSlippage), recipient: lotConfiguration[lotId_].recipient, deadline: block.timestamp, - veRamTokenId: onSettleParams_.veRamTokenId + veRamTokenId: params.veRamTokenId }); } } diff --git a/src/callbacks/liquidity/Ramses/lib/IVotingEscrow.sol b/src/callbacks/liquidity/Ramses/lib/IVotingEscrow.sol new file mode 100644 index 00000000..292169c8 --- /dev/null +++ b/src/callbacks/liquidity/Ramses/lib/IVotingEscrow.sol @@ -0,0 +1,68 @@ +// SPDX-License-Identifier: MIT +pragma solidity =0.7.6 || ^0.8.13; +pragma abicoder v2; + +interface IVotingEscrow { + struct Point { + int128 bias; + int128 slope; // # -dweight / dt + uint256 ts; + uint256 blk; // block + } + + struct LockedBalance { + int128 amount; + uint256 end; + } + + function token() external view returns (address); + + function team() external returns (address); + + function epoch() external view returns (uint256); + + function point_history(uint256 loc) external view returns (Point memory); + + function user_point_history( + uint256 tokenId, + uint256 loc + ) external view returns (Point memory); + + function user_point_epoch(uint256 tokenId) external view returns (uint256); + + function ownerOf(uint256) external view returns (address); + + function isApprovedOrOwner(address, uint256) external view returns (bool); + + function transferFrom(address, address, uint256) external; + + function voting(uint256 tokenId) external; + + function abstain(uint256 tokenId) external; + + function attach(uint256 tokenId) external; + + function detach(uint256 tokenId) external; + + function checkpoint() external; + + function deposit_for(uint256 tokenId, uint256 value) external; + + function create_lock_for(uint256, uint256, address) external returns (uint256); + + function balanceOfNFT(uint256) external view returns (uint256); + + function balanceOfNFTAt(uint256, uint256) external view returns (uint256); + + function totalSupply() external view returns (uint256); + + function locked__end(uint256) external view returns (uint256); + + function balanceOf(address) external view returns (uint256); + + function tokenOfOwnerByIndex(address, uint256) external view returns (uint256); + + function locked(uint256) external view returns (LockedBalance memory); + + function isDelegate(address _operator, uint256 _tokenId) external view returns (bool); +} From 0fe01580a2632d88544cd66657de6c0112029eb7 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 25 Jul 2024 15:37:07 +0400 Subject: [PATCH 16/47] Add note --- src/callbacks/liquidity/Ramses/RamsesV2DTL.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol index 98ab2c2a..4bdb4dc7 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol +++ b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol @@ -123,6 +123,7 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { } // Check that the callback has been given permission to use the veRamTokenId + // Similar to maxSlippage, this needs to be stored during onCreate if ( params.veRamTokenId > 0 && !IVotingEscrow(ramsesV2PositionManager.veRam()).isApprovedOrOwner( From c2bbf8bf9f4692044d3ff2eaceb32890387df823 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 25 Jul 2024 15:53:54 +0400 Subject: [PATCH 17/47] Add salt and deploy scripts --- script/deploy/Deploy.s.sol | 96 +++++++++++++++ script/salts/dtl-ramses/RamsesDTLSalts.s.sol | 120 +++++++++++++++++++ script/salts/dtl-ramses/ramses_dtl_salts.sh | 49 ++++++++ 3 files changed, 265 insertions(+) create mode 100644 script/salts/dtl-ramses/RamsesDTLSalts.s.sol create mode 100755 script/salts/dtl-ramses/ramses_dtl_salts.sh diff --git a/script/deploy/Deploy.s.sol b/script/deploy/Deploy.s.sol index 4360e1fc..82f604f7 100644 --- a/script/deploy/Deploy.s.sol +++ b/script/deploy/Deploy.s.sol @@ -33,6 +33,13 @@ import {BALwithCappedAllowlist} from "../../src/callbacks/liquidity/BaselineV2/BALwithCappedAllowlist.sol"; import {BALwithTokenAllowlist} from "../../src/callbacks/liquidity/BaselineV2/BALwithTokenAllowlist.sol"; +import {RamsesV1DirectToLiquidity} from "../../src/callbacks/liquidity/Ramses/RamsesV1DTL.sol"; +import {RamsesV2DirectToLiquidity} from "../../src/callbacks/liquidity/Ramses/RamsesV2DTL.sol"; + +// Ramses +import {IRamsesV1Router} from "../../src/callbacks/liquidity/Ramses/lib/IRamsesV1Router.sol"; +import {IRamsesV2PositionManager} from + "../../src/callbacks/liquidity/Ramses/lib/IRamsesV2PositionManager.sol"; // Baseline import { @@ -972,6 +979,95 @@ contract Deploy is Script, WithDeploySequence, WithSalts { return (address(batchAllowlist), _PREFIX_CALLBACKS, deploymentKey); } + function deployBatchRamsesV1DirectToLiquidity(string memory sequenceName_) + public + returns (address, string memory, string memory) + { + // Get configuration variables + address batchAuctionHouse = _getAddressNotZero("deployments.BatchAuctionHouse"); + address ramsesV1PairFactory = _getEnvAddressOrOverride( + "constants.ramsesV1.pairFactory", sequenceName_, "args.pairFactory" + ); + address payable ramsesV1Router = payable( + _getEnvAddressOrOverride("constants.ramsesV1.router", sequenceName_, "args.router") + ); + string memory deploymentKey = _getDeploymentKey(sequenceName_); + console2.log(" deploymentKey:", deploymentKey); + + // Check that the router and factory match + require( + IRamsesV1Router(ramsesV1Router).factory() == ramsesV1PairFactory, + "IRamsesV1Router.factory() does not match given Ramses V1 pair factory address" + ); + + // Get the salt + bytes32 salt_ = _getSalt( + deploymentKey, + type(RamsesV1DirectToLiquidity).creationCode, + abi.encode(batchAuctionHouse, ramsesV1PairFactory, ramsesV1Router) + ); + + // Revert if the salt is not set + require(salt_ != bytes32(0), "Salt not set"); + + // Deploy the module + console2.log(" salt:", vm.toString(salt_)); + + vm.broadcast(); + RamsesV1DirectToLiquidity dtl = new RamsesV1DirectToLiquidity{salt: salt_}( + batchAuctionHouse, ramsesV1PairFactory, ramsesV1Router + ); + console2.log(""); + console2.log(" deployed at:", address(dtl)); + + return (address(dtl), _PREFIX_CALLBACKS, deploymentKey); + } + + function deployBatchRamsesV2DirectToLiquidity(string memory sequenceName_) + public + returns (address, string memory, string memory) + { + // Get configuration variables + address batchAuctionHouse = _getAddressNotZero("deployments.BatchAuctionHouse"); + address ramsesV2Factory = + _getEnvAddressOrOverride("constants.ramsesV2.factory", sequenceName_, "args.factory"); + address payable ramsesV2PositionManager = payable( + _getEnvAddressOrOverride( + "constants.ramsesV2.positionManager", sequenceName_, "args.positionManager" + ) + ); + string memory deploymentKey = _getDeploymentKey(sequenceName_); + console2.log(" deploymentKey:", deploymentKey); + + // Check that the router and factory match + require( + IRamsesV2PositionManager(ramsesV2PositionManager).factory() == ramsesV2Factory, + "IRamsesV2PositionManager.factory() does not match given Ramses V2 factory address" + ); + + // Get the salt + bytes32 salt_ = _getSalt( + deploymentKey, + type(RamsesV1DirectToLiquidity).creationCode, + abi.encode(batchAuctionHouse, ramsesV2Factory, ramsesV2PositionManager) + ); + + // Revert if the salt is not set + require(salt_ != bytes32(0), "Salt not set"); + + // Deploy the module + console2.log(" salt:", vm.toString(salt_)); + + vm.broadcast(); + RamsesV2DirectToLiquidity dtl = new RamsesV2DirectToLiquidity{salt: salt_}( + batchAuctionHouse, ramsesV2Factory, ramsesV2PositionManager + ); + console2.log(""); + console2.log(" deployed at:", address(dtl)); + + return (address(dtl), _PREFIX_CALLBACKS, deploymentKey); + } + // ========== HELPER FUNCTIONS ========== // function _configureDeployment(string memory data_, string memory name_) internal { diff --git a/script/salts/dtl-ramses/RamsesDTLSalts.s.sol b/script/salts/dtl-ramses/RamsesDTLSalts.s.sol new file mode 100644 index 00000000..18ee4d93 --- /dev/null +++ b/script/salts/dtl-ramses/RamsesDTLSalts.s.sol @@ -0,0 +1,120 @@ +/// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +// Scripting libraries +import {Script, console2} from "@forge-std-1.9.1/Script.sol"; +import {WithSalts} from "../WithSalts.s.sol"; +import {WithDeploySequence} from "../../deploy/WithDeploySequence.s.sol"; + +// Ramses +import {RamsesV1DirectToLiquidity} from "../../../src/callbacks/liquidity/Ramses/RamsesV1DTL.sol"; +import {RamsesV2DirectToLiquidity} from "../../../src/callbacks/liquidity/Ramses/RamsesV2DTL.sol"; + +contract RamsesDTLSalts is Script, WithDeploySequence, WithSalts { + string internal constant _ADDRESS_PREFIX = "E6"; + + function _setUp(string calldata chain_, string calldata sequenceFilePath_) internal { + _loadSequence(chain_, sequenceFilePath_); + _createBytecodeDirectory(); + } + + function generate(string calldata chain_, string calldata deployFilePath_) public { + _setUp(chain_, deployFilePath_); + + // Iterate over the deployment sequence + string[] memory sequenceNames = _getSequenceNames(); + for (uint256 i; i < sequenceNames.length; i++) { + string memory sequenceName = sequenceNames[i]; + console2.log(""); + console2.log("Generating salt for :", sequenceName); + + string memory deploymentKey = _getDeploymentKey(sequenceName); + console2.log(" deploymentKey: %s", deploymentKey); + + // Atomic Ramses V1 + if ( + keccak256(abi.encodePacked(sequenceName)) + == keccak256(abi.encodePacked("AtomicRamsesV1DirectToLiquidity")) + ) { + address auctionHouse = _envAddressNotZero("deployments.AtomicAuctionHouse"); + + _generateV1(sequenceName, auctionHouse, deploymentKey); + } + // Batch Ramses V1 + else if ( + keccak256(abi.encodePacked(sequenceName)) + == keccak256(abi.encodePacked("BatchRamsesV1DirectToLiquidity")) + ) { + address auctionHouse = _envAddressNotZero("deployments.BatchAuctionHouse"); + + _generateV1(sequenceName, auctionHouse, deploymentKey); + } + // Atomic Ramses V2 + else if ( + keccak256(abi.encodePacked(sequenceName)) + == keccak256(abi.encodePacked("AtomicRamsesV2DirectToLiquidity")) + ) { + address auctionHouse = _envAddressNotZero("deployments.AtomicAuctionHouse"); + + _generateV2(sequenceName, auctionHouse, deploymentKey); + } + // Batch Ramses V2 + else if ( + keccak256(abi.encodePacked(sequenceName)) + == keccak256(abi.encodePacked("BatchRamsesV2DirectToLiquidity")) + ) { + address auctionHouse = _envAddressNotZero("deployments.BatchAuctionHouse"); + + _generateV2(sequenceName, auctionHouse, deploymentKey); + } + // Something else + else { + console2.log(" Skipping unknown sequence: %s", sequenceName); + } + } + } + + function _generateV1( + string memory sequenceName_, + address auctionHouse_, + string memory deploymentKey_ + ) internal { + // Get input variables or overrides + address envRamsesV1PairFactory = _getEnvAddressOrOverride( + "constants.ramsesV1.pairFactory", sequenceName_, "args.pairFactory" + ); + address envRamsesV1Router = + _getEnvAddressOrOverride("constants.ramsesV1.router", sequenceName_, "args.router"); + + // Calculate salt for the RamsesV1DirectToLiquidity + bytes memory contractCode = type(RamsesV1DirectToLiquidity).creationCode; + (string memory bytecodePath, bytes32 bytecodeHash) = _writeBytecode( + deploymentKey_, + contractCode, + abi.encode(auctionHouse_, envRamsesV1PairFactory, envRamsesV1Router) + ); + _setSalt(bytecodePath, _ADDRESS_PREFIX, deploymentKey_, bytecodeHash); + } + + function _generateV2( + string memory sequenceName_, + address auctionHouse_, + string memory deploymentKey_ + ) internal { + // Get input variables or overrides + address envRamsesV2Factory = + _getEnvAddressOrOverride("constants.ramsesV2.factory", sequenceName_, "args.factory"); + address envRamsesV2PositionManager = _getEnvAddressOrOverride( + "constants.ramsesV2.positionManager", sequenceName_, "args.positionManager" + ); + + // Calculate salt for the RamsesV2DirectToLiquidity + bytes memory contractCode = type(RamsesV2DirectToLiquidity).creationCode; + (string memory bytecodePath, bytes32 bytecodeHash) = _writeBytecode( + deploymentKey_, + contractCode, + abi.encode(auctionHouse_, envRamsesV2Factory, envRamsesV2PositionManager) + ); + _setSalt(bytecodePath, _ADDRESS_PREFIX, deploymentKey_, bytecodeHash); + } +} diff --git a/script/salts/dtl-ramses/ramses_dtl_salts.sh b/script/salts/dtl-ramses/ramses_dtl_salts.sh new file mode 100755 index 00000000..b768db03 --- /dev/null +++ b/script/salts/dtl-ramses/ramses_dtl_salts.sh @@ -0,0 +1,49 @@ +#!/bin/bash + +# Usage: +# ./ramses_dtl_salts.sh --deployFile --envFile <.env> +# +# Expects the following environment variables: +# CHAIN: The chain to deploy to, based on values from the ./script/env.json file. + +# Iterate through named arguments +# Source: https://unix.stackexchange.com/a/388038 +while [ $# -gt 0 ]; do + if [[ $1 == *"--"* ]]; then + v="${1/--/}" + declare $v="$2" + fi + + shift +done + +DEPLOY_FILE=$deployFile + +# Get the name of the .env file or use the default +ENV_FILE=${envFile:-".env"} +echo "Sourcing environment variables from $ENV_FILE" + +# Load environment file +set -a # Automatically export all variables +source $ENV_FILE +set +a # Disable automatic export + +# Check that the CHAIN environment variable is set +if [ -z "$CHAIN" ] +then + echo "CHAIN environment variable is not set. Please set it in the .env file or provide it as an environment variable." + exit 1 +fi + +# Check if DEPLOY_FILE is set +if [ -z "$DEPLOY_FILE" ] +then + echo "No deploy file specified. Provide the relative path after the --deployFile flag." + exit 1 +fi + +echo "Using chain: $CHAIN" +echo "Using RPC at URL: $RPC_URL" +echo "Using deploy file: $DEPLOY_FILE" + +forge script ./script/salts/dtl-ramses/RamsesDTLSalts.s.sol:RamsesDTLSalts --sig "generate(string,string)()" $CHAIN $DEPLOY_FILE From ed147e2fde39d28467e9ff78bd71f97aaa5b9131 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 25 Jul 2024 16:13:35 +0400 Subject: [PATCH 18/47] Test salts for Ramses DTL contracts --- script/salts/test/TestSalts.s.sol | 19 +++++++++++++++++++ test/Constants.sol | 10 ++++++++++ 2 files changed, 29 insertions(+) diff --git a/script/salts/test/TestSalts.s.sol b/script/salts/test/TestSalts.s.sol index b451a678..13a8d9da 100644 --- a/script/salts/test/TestSalts.s.sol +++ b/script/salts/test/TestSalts.s.sol @@ -34,6 +34,8 @@ import {BALwithCappedAllowlist} from "../../../src/callbacks/liquidity/BaselineV2/BALwithCappedAllowlist.sol"; import {BALwithTokenAllowlist} from "../../../src/callbacks/liquidity/BaselineV2/BALwithTokenAllowlist.sol"; +import {RamsesV1DirectToLiquidity} from "../../../src/callbacks/liquidity/Ramses/RamsesV1DTL.sol"; +import {RamsesV2DirectToLiquidity} from "../../../src/callbacks/liquidity/Ramses/RamsesV2DTL.sol"; contract TestSalts is Script, WithEnvironment, Permit2User, WithSalts, TestConstants { string internal constant _CAPPED_MERKLE_ALLOWLIST = "CappedMerkleAllowlist"; @@ -312,4 +314,21 @@ contract TestSalts is Script, WithEnvironment, Permit2User, WithSalts, TestConst ); _setTestSalt(callbackBytecodePath, "EF", "BaselineTokenAllowlist", callbackBytecodeHash); } + + function generateRamsesV1DirectToLiquidity() public { + bytes memory args = abi.encode(_AUCTION_HOUSE, _RAMSES_V1_FACTORY, _RAMSES_V1_ROUTER); + bytes memory contractCode = type(RamsesV1DirectToLiquidity).creationCode; + (string memory bytecodePath, bytes32 bytecodeHash) = + _writeBytecode("RamsesV1DirectToLiquidity", contractCode, args); + _setTestSalt(bytecodePath, "E6", "RamsesV1DirectToLiquidity", bytecodeHash); + } + + function generateRamsesV2DirectToLiquidity() public { + bytes memory args = + abi.encode(_AUCTION_HOUSE, _RAMSES_V2_FACTORY, _RAMSES_V2_POSITION_MANAGER); + bytes memory contractCode = type(RamsesV2DirectToLiquidity).creationCode; + (string memory bytecodePath, bytes32 bytecodeHash) = + _writeBytecode("RamsesV2DirectToLiquidity", contractCode, args); + _setTestSalt(bytecodePath, "E6", "RamsesV2DirectToLiquidity", bytecodeHash); + } } diff --git a/test/Constants.sol b/test/Constants.sol index 1f37435d..79f9d715 100644 --- a/test/Constants.sol +++ b/test/Constants.sol @@ -16,4 +16,14 @@ abstract contract TestConstants is TestConstantsCore { address(0xAA22883d39ea4e42f7033e3e931aA476DEe30b73); address internal constant _CREATE2_DEPLOYER = address(0x4e59b44847b379578588920cA78FbF26c0B4956C); + + // Ramses addresses on Arbitrum + address internal constant _RAMSES_V1_FACTORY = + address(0xAAA20D08e59F6561f242b08513D36266C5A29415); + address internal constant _RAMSES_V1_ROUTER = + address(0xAAA87963EFeB6f7E0a2711F397663105Acb1805e); + address internal constant _RAMSES_V2_FACTORY = + address(0xAA2cd7477c451E703f3B9Ba5663334914763edF8); + address internal constant _RAMSES_V2_POSITION_MANAGER = + address(0xAA277CB7914b7e5514946Da92cb9De332Ce610EF); } From 8f64c101e1ce54c59d36e0a3e4afec9701036c5a Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 25 Jul 2024 17:03:42 +0400 Subject: [PATCH 19/47] Add initial (copied) version of Ramses V1 tests --- .../liquidity/Ramses/RamsesV1DTL.sol | 1 + .../liquidity/Ramses/lib/IRamsesV1Pool.sol | 116 +++ .../liquidity/RamsesV1/RamsesV1DTLTest.sol | 264 +++++++ .../liquidity/RamsesV1/onCancel.t.sol | 73 ++ .../liquidity/RamsesV1/onCreate.t.sol | 355 +++++++++ .../liquidity/RamsesV1/onCurate.t.sol | 85 +++ .../liquidity/RamsesV1/onSettle.t.sol | 692 ++++++++++++++++++ 7 files changed, 1586 insertions(+) create mode 100644 src/callbacks/liquidity/Ramses/lib/IRamsesV1Pool.sol create mode 100644 test/callbacks/liquidity/RamsesV1/RamsesV1DTLTest.sol create mode 100644 test/callbacks/liquidity/RamsesV1/onCancel.t.sol create mode 100644 test/callbacks/liquidity/RamsesV1/onCreate.t.sol create mode 100644 test/callbacks/liquidity/RamsesV1/onCurate.t.sol create mode 100644 test/callbacks/liquidity/RamsesV1/onSettle.t.sol diff --git a/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol index a1a25122..295075f3 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol +++ b/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol @@ -79,6 +79,7 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { /// - Validates the parameters /// /// This function reverts if: + /// - The callback data is of the incorrect length /// - `RamsesV1OnCreateParams.maxSlippage` is out of bounds function __onCreate( uint96 lotId_, diff --git a/src/callbacks/liquidity/Ramses/lib/IRamsesV1Pool.sol b/src/callbacks/liquidity/Ramses/lib/IRamsesV1Pool.sol new file mode 100644 index 00000000..2956050c --- /dev/null +++ b/src/callbacks/liquidity/Ramses/lib/IRamsesV1Pool.sol @@ -0,0 +1,116 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +interface IRamsesV1Pool { + struct Observation { + uint256 timestamp; + uint256 reserve0Cumulative; + uint256 reserve1Cumulative; + } + + event Approval(address indexed owner, address indexed spender, uint256 amount); + event Burn(address indexed sender, uint256 amount0, uint256 amount1, address indexed to); + event Claim( + address indexed sender, address indexed recipient, uint256 amount0, uint256 amount1 + ); + event Fees(address indexed sender, uint256 amount0, uint256 amount1); + event Initialized(uint8 version); + event Mint(address indexed sender, uint256 amount0, uint256 amount1); + event SetFeeSplit(uint8 toFeesOld, uint8 toTreasuryOld, uint8 toFeesNew, uint8 toTreasuryNew); + event Swap( + address indexed sender, + uint256 amount0In, + uint256 amount1In, + uint256 amount0Out, + uint256 amount1Out, + address indexed to + ); + event Sync(uint256 reserve0, uint256 reserve1); + event Transfer(address indexed from, address indexed to, uint256 amount); + + function allowance(address, address) external view returns (uint256); + function approve(address spender, uint256 amount) external returns (bool); + function balanceOf(address) external view returns (uint256); + function blockTimestampLast() external view returns (uint256); + function burn(address to) external returns (uint256 amount0, uint256 amount1); + function claimFees() external returns (uint256, uint256); + function current(address tokenIn, uint256 amountIn) external view returns (uint256 amountOut); + function currentCumulativePrices() + external + view + returns (uint256 reserve0Cumulative, uint256 reserve1Cumulative, uint256 blockTimestamp); + function decimals() external view returns (uint8); + function feeSplit() external view returns (uint8); + function fees() external view returns (address); + function getAmountOut(uint256 amountIn, address tokenIn) external view returns (uint256); + function getReserves() + external + view + returns (uint256 _reserve0, uint256 _reserve1, uint256 _blockTimestampLast); + function initialize(address _token0, address _token1, bool _stable) external; + function initializeVoter() external; + function lastObservation() external view returns (Observation memory); + function metadata() + external + view + returns ( + uint256 dec0, + uint256 dec1, + uint256 r0, + uint256 r1, + bool st, + address t0, + address t1 + ); + function mint(address to) external returns (uint256 liquidity); + function name() external view returns (string memory); + function nonces(address) external view returns (uint256); + function observationLength() external view returns (uint256); + function observations(uint256) + external + view + returns (uint256 timestamp, uint256 reserve0Cumulative, uint256 reserve1Cumulative); + function permit( + address owner, + address spender, + uint256 value, + uint256 deadline, + uint8 v, + bytes32 r, + bytes32 s + ) external; + function prices( + address tokenIn, + uint256 amountIn, + uint256 points + ) external view returns (uint256[] memory); + function quote( + address tokenIn, + uint256 amountIn, + uint256 granularity + ) external view returns (uint256 amountOut); + function reserve0() external view returns (uint256); + function reserve0CumulativeLast() external view returns (uint256); + function reserve1() external view returns (uint256); + function reserve1CumulativeLast() external view returns (uint256); + function sample( + address tokenIn, + uint256 amountIn, + uint256 points, + uint256 window + ) external view returns (uint256[] memory); + function setActiveGauge(bool isActive) external; + function setFeeSplit() external; + function skim(address to) external; + function stable() external view returns (bool); + function swap(uint256 amount0Out, uint256 amount1Out, address to, bytes memory data) external; + function symbol() external view returns (string memory); + function sync() external; + function token0() external view returns (address); + function token1() external view returns (address); + function tokens() external view returns (address, address); + function totalSupply() external view returns (uint256); + function transfer(address dst, uint256 amount) external returns (bool); + function transferFrom(address src, address dst, uint256 amount) external returns (bool); + function voter() external view returns (address); +} diff --git a/test/callbacks/liquidity/RamsesV1/RamsesV1DTLTest.sol b/test/callbacks/liquidity/RamsesV1/RamsesV1DTLTest.sol new file mode 100644 index 00000000..3b6e879b --- /dev/null +++ b/test/callbacks/liquidity/RamsesV1/RamsesV1DTLTest.sol @@ -0,0 +1,264 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +// Test scaffolding +import {Test} from "@forge-std-1.9.1/Test.sol"; +import {Callbacks} from "@axis-core-1.0.0/lib/Callbacks.sol"; +import {Permit2User} from "@axis-core-1.0.0-test/lib/permit2/Permit2User.sol"; +import {WithSalts} from "../../../lib/WithSalts.sol"; +import {TestConstants} from "../../../Constants.sol"; +import {console2} from "@forge-std-1.9.1/console2.sol"; + +// Mocks +import {MockERC20} from "@solmate-6.7.0/test/utils/mocks/MockERC20.sol"; +import {MockBatchAuctionModule} from + "@axis-core-1.0.0-test/modules/Auction/MockBatchAuctionModule.sol"; + +// Callbacks +import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; + +// Ramses +import {RamsesV1DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ramses/RamsesV1DTL.sol"; +import {IRamsesV1Factory} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV1Factory.sol"; +import {IRamsesV1Router} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV1Router.sol"; + +// Axis core +import {keycodeFromVeecode, toKeycode} from "@axis-core-1.0.0/modules/Keycode.sol"; +import {IAuction} from "@axis-core-1.0.0/interfaces/modules/IAuction.sol"; +import {IAuctionHouse} from "@axis-core-1.0.0/interfaces/IAuctionHouse.sol"; +import {BatchAuctionHouse} from "@axis-core-1.0.0/BatchAuctionHouse.sol"; +import {LinearVesting} from "@axis-core-1.0.0/modules/derivatives/LinearVesting.sol"; + +abstract contract RamsesV1DirectToLiquidityTest is Test, Permit2User, WithSalts, TestConstants { + using Callbacks for RamsesV1DirectToLiquidity; + + address internal constant _SELLER = address(0x2); + address internal constant _PROTOCOL = address(0x3); + address internal constant _BUYER = address(0x4); + address internal constant _NOT_SELLER = address(0x20); + + uint96 internal constant _LOT_CAPACITY = 10e18; + + uint48 internal _initialTimestamp; + + uint96 internal _lotId = 1; + + BatchAuctionHouse internal _auctionHouse; + RamsesV1DirectToLiquidity internal _dtl; + address internal _dtlAddress; + + IRamsesV1Factory internal _factory; + IRamsesV1Router internal _router; + LinearVesting internal _linearVesting; + MockBatchAuctionModule internal _batchAuctionModule; + + MockERC20 internal _quoteToken; + MockERC20 internal _baseToken; + + // Inputs + RamsesV1DirectToLiquidity.RamsesV1OnCreateParams internal _ramsesCreateParams = + RamsesV1DirectToLiquidity.RamsesV1OnCreateParams({stable: false, maxSlippage: uint24(0)}); + BaseDirectToLiquidity.OnCreateParams internal _dtlCreateParams = BaseDirectToLiquidity + .OnCreateParams({ + proceedsUtilisationPercent: 100e2, + vestingStart: 0, + vestingExpiry: 0, + recipient: _SELLER, + implParams: abi.encode(_ramsesCreateParams) + }); + + function setUp() public { + // Create a fork on Arbitrum + string memory arbitrumRpcUrl = vm.envString("ARBITRUM_RPC_URL"); + vm.createSelectFork(arbitrumRpcUrl); + require(block.chainid == 42_161, "Must be on Arbitrum"); + + _initialTimestamp = uint48(block.timestamp); + + // Create an BatchAuctionHouse at a deterministic address, since it is used as input to callbacks + BatchAuctionHouse auctionHouse = new BatchAuctionHouse(_OWNER, _PROTOCOL, _permit2Address); + _auctionHouse = BatchAuctionHouse(_AUCTION_HOUSE); + vm.etch(address(_auctionHouse), address(auctionHouse).code); + vm.store(address(_auctionHouse), bytes32(uint256(0)), bytes32(abi.encode(_OWNER))); // Owner + vm.store(address(_auctionHouse), bytes32(uint256(6)), bytes32(abi.encode(1))); // Reentrancy + vm.store(address(_auctionHouse), bytes32(uint256(10)), bytes32(abi.encode(_PROTOCOL))); // Protocol + + _factory = IRamsesV1Factory(_RAMSES_V1_FACTORY); + _router = IRamsesV1Router(payable(_RAMSES_V1_ROUTER)); + + _linearVesting = new LinearVesting(address(_auctionHouse)); + _batchAuctionModule = new MockBatchAuctionModule(address(_auctionHouse)); + + // Install a mock batch auction module + vm.prank(_OWNER); + _auctionHouse.installModule(_batchAuctionModule); + + _quoteToken = new MockERC20("Quote Token", "QT", 18); + _baseToken = new MockERC20("Base Token", "BT", 18); + } + + // ========== MODIFIERS ========== // + + modifier givenLinearVestingModuleIsInstalled() { + vm.prank(_OWNER); + _auctionHouse.installModule(_linearVesting); + _; + } + + modifier givenCallbackIsCreated() { + // Get the salt + bytes memory args = abi.encode(address(_auctionHouse), address(_factory), address(_router)); + bytes32 salt = _getTestSalt( + "RamsesV1DirectToLiquidity", type(RamsesV1DirectToLiquidity).creationCode, args + ); + + // Required for CREATE2 address to work correctly. doesn't do anything in a test + // Source: https://github.com/foundry-rs/foundry/issues/6402 + vm.startBroadcast(); + _dtl = new RamsesV1DirectToLiquidity{salt: salt}( + address(_auctionHouse), address(_factory), payable(_router) + ); + vm.stopBroadcast(); + + _dtlAddress = address(_dtl); + _; + } + + modifier givenAddressHasQuoteTokenBalance(address address_, uint256 amount_) { + _quoteToken.mint(address_, amount_); + _; + } + + modifier givenAddressHasBaseTokenBalance(address address_, uint256 amount_) { + _baseToken.mint(address_, amount_); + _; + } + + modifier givenAddressHasQuoteTokenAllowance(address owner_, address spender_, uint256 amount_) { + vm.prank(owner_); + _quoteToken.approve(spender_, amount_); + _; + } + + modifier givenAddressHasBaseTokenAllowance(address owner_, address spender_, uint256 amount_) { + vm.prank(owner_); + _baseToken.approve(spender_, amount_); + _; + } + + function _createLot(address seller_) internal returns (uint96 lotId) { + // Mint and approve the capacity to the owner + _baseToken.mint(seller_, _LOT_CAPACITY); + vm.prank(seller_); + _baseToken.approve(address(_auctionHouse), _LOT_CAPACITY); + + // Prep the lot arguments + IAuctionHouse.RoutingParams memory routingParams = IAuctionHouse.RoutingParams({ + auctionType: keycodeFromVeecode(_batchAuctionModule.VEECODE()), + baseToken: address(_baseToken), + quoteToken: address(_quoteToken), + referrerFee: 0, // No referrer fee + curator: address(0), + callbacks: _dtl, + callbackData: abi.encode(_dtlCreateParams), + derivativeType: toKeycode(""), + derivativeParams: abi.encode(""), + wrapDerivative: false + }); + + IAuction.AuctionParams memory auctionParams = IAuction.AuctionParams({ + start: uint48(block.timestamp) + 1, + duration: 1 days, + capacityInQuote: false, + capacity: _LOT_CAPACITY, + implParams: abi.encode("") + }); + + // Create a new lot + vm.prank(seller_); + return _auctionHouse.auction(routingParams, auctionParams, ""); + } + + modifier givenOnCreate() { + _lotId = _createLot(_SELLER); + _; + } + + function _performOnCurate(uint96 curatorPayout_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onCurate(_lotId, curatorPayout_, false, abi.encode("")); + } + + modifier givenOnCurate(uint96 curatorPayout_) { + _performOnCurate(curatorPayout_); + _; + } + + modifier givenProceedsUtilisationPercent(uint24 percent_) { + _dtlCreateParams.proceedsUtilisationPercent = percent_; + _; + } + + modifier givenVestingStart(uint48 start_) { + _dtlCreateParams.vestingStart = start_; + _; + } + + modifier givenVestingExpiry(uint48 end_) { + _dtlCreateParams.vestingExpiry = end_; + _; + } + + modifier whenRecipientIsNotSeller() { + _dtlCreateParams.recipient = _NOT_SELLER; + _; + } + + modifier givenStable(bool stable_) { + _ramsesCreateParams.stable = stable_; + + // Update the callback data + _dtlCreateParams.implParams = abi.encode(_ramsesCreateParams); + _; + } + + modifier givenMaxSlippage(uint24 maxSlippage_) { + _ramsesCreateParams.maxSlippage = maxSlippage_; + + // Update the callback data + _dtlCreateParams.implParams = abi.encode(_ramsesCreateParams); + _; + } + + // ========== FUNCTIONS ========== // + + function _getDTLConfiguration(uint96 lotId_) + internal + view + returns (BaseDirectToLiquidity.DTLConfiguration memory) + { + ( + address recipient_, + uint256 lotCapacity_, + uint256 lotCuratorPayout_, + uint24 proceedsUtilisationPercent_, + uint48 vestingStart_, + uint48 vestingExpiry_, + LinearVesting linearVestingModule_, + bool active_, + bytes memory implParams_ + ) = _dtl.lotConfiguration(lotId_); + + return BaseDirectToLiquidity.DTLConfiguration({ + recipient: recipient_, + lotCapacity: lotCapacity_, + lotCuratorPayout: lotCuratorPayout_, + proceedsUtilisationPercent: proceedsUtilisationPercent_, + vestingStart: vestingStart_, + vestingExpiry: vestingExpiry_, + linearVestingModule: linearVestingModule_, + active: active_, + implParams: implParams_ + }); + } +} diff --git a/test/callbacks/liquidity/RamsesV1/onCancel.t.sol b/test/callbacks/liquidity/RamsesV1/onCancel.t.sol new file mode 100644 index 00000000..5e2fb471 --- /dev/null +++ b/test/callbacks/liquidity/RamsesV1/onCancel.t.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {RamsesV1DirectToLiquidityTest} from "./RamsesV1DTLTest.sol"; + +import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; +import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; + +contract RamsesV1DTLOnCancelForkTest is RamsesV1DirectToLiquidityTest { + uint96 internal constant _REFUND_AMOUNT = 2e18; + + // ============ Modifiers ============ // + + function _performCallback(uint96 lotId_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onCancel(lotId_, _REFUND_AMOUNT, false, abi.encode("")); + } + + // ============ Tests ============ // + + // [ ] when the lot has not been registered + // [ ] it reverts + // [ ] when multiple lots are created + // [ ] it marks the correct lot as inactive + // [ ] when the lot has already been completed + // [ ] it reverts + // [ ] it marks the lot as inactive + + function test_whenLotNotRegistered_reverts() public givenCallbackIsCreated { + // Expect revert + bytes memory err = abi.encodeWithSelector(BaseCallback.Callback_NotAuthorized.selector); + vm.expectRevert(err); + + // Call the function + _performCallback(_lotId); + } + + function test_success() public givenCallbackIsCreated givenOnCreate { + // Call the function + _performCallback(_lotId); + + // Check the values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.active, false, "active"); + + // Check the balances + assertEq(_baseToken.balanceOf(_dtlAddress), 0, "base token balance"); + assertEq(_baseToken.balanceOf(_SELLER), 0, "seller base token balance"); + assertEq(_baseToken.balanceOf(_NOT_SELLER), 0, "not seller base token balance"); + } + + function test_success_multiple() public givenCallbackIsCreated givenOnCreate { + uint96 lotIdOne = _lotId; + + // Create a second lot and cancel it + uint96 lotIdTwo = _createLot(_NOT_SELLER); + _performCallback(lotIdTwo); + + // Check the values + BaseDirectToLiquidity.DTLConfiguration memory configurationOne = + _getDTLConfiguration(lotIdOne); + assertEq(configurationOne.active, true, "lot one: active"); + + BaseDirectToLiquidity.DTLConfiguration memory configurationTwo = + _getDTLConfiguration(lotIdTwo); + assertEq(configurationTwo.active, false, "lot two: active"); + + // Check the balances + assertEq(_baseToken.balanceOf(_dtlAddress), 0, "base token balance"); + assertEq(_baseToken.balanceOf(_SELLER), 0, "seller base token balance"); + assertEq(_baseToken.balanceOf(_NOT_SELLER), 0, "not seller base token balance"); + } +} diff --git a/test/callbacks/liquidity/RamsesV1/onCreate.t.sol b/test/callbacks/liquidity/RamsesV1/onCreate.t.sol new file mode 100644 index 00000000..84b14689 --- /dev/null +++ b/test/callbacks/liquidity/RamsesV1/onCreate.t.sol @@ -0,0 +1,355 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {RamsesV1DirectToLiquidityTest} from "./RamsesV1DTLTest.sol"; + +import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; +import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; + +contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { + // ============ Modifiers ============ // + + function _performCallback(address seller_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onCreate( + _lotId, + seller_, + address(_baseToken), + address(_quoteToken), + _LOT_CAPACITY, + false, + abi.encode(_dtlCreateParams) + ); + } + + function _performCallback() internal { + _performCallback(_SELLER); + } + + // ============ Assertions ============ // + + function _expectTransferFrom() internal { + vm.expectRevert("TRANSFER_FROM_FAILED"); + } + + function _expectInvalidParams() internal { + bytes memory err = abi.encodeWithSelector(BaseCallback.Callback_InvalidParams.selector); + vm.expectRevert(err); + } + + function _expectNotAuthorized() internal { + bytes memory err = abi.encodeWithSelector(BaseCallback.Callback_NotAuthorized.selector); + vm.expectRevert(err); + } + + function _assertBaseTokenBalances() internal view { + assertEq(_baseToken.balanceOf(_SELLER), 0, "seller balance"); + assertEq(_baseToken.balanceOf(_NOT_SELLER), 0, "not seller balance"); + assertEq(_baseToken.balanceOf(_dtlAddress), 0, "dtl balance"); + } + + // ============ Tests ============ // + + // [ ] when the callback data is incorrect + // [ ] it reverts + // [ ] when the callback is not called by the auction house + // [ ] it reverts + // [ ] when the lot has already been registered + // [ ] it reverts + // [ ] when the lot has already been completed + // [ ] it reverts + // [ ] when the proceeds utilisation is 0 + // [ ] it reverts + // [ ] when the proceeds utilisation is greater than 100% + // [ ] it reverts + // [ ] when the implParams is not the correct length + // [ ] it reverts + // [ ] when the max slippage is greater than 100% + // [ ] it reverts + // [ ] given ramses v1 pool already exists + // [ ] it succeeds + // [ ] when the start and expiry timestamps are the same + // [ ] it reverts + // [ ] when the start timestamp is after the expiry timestamp + // [ ] it reverts + // [ ] when the start timestamp is before the current timestamp + // [ ] it succeeds + // [ ] when the expiry timestamp is before the current timestamp + // [ ] it reverts + // [ ] when the start timestamp and expiry timestamp are specified + // [ ] given the linear vesting module is not installed + // [ ] it reverts + // [ ] it records the address of the linear vesting module + // [ ] when the recipient is the zero address + // [ ] it reverts + // [ ] when the recipient is not the seller + // [ ] it records the recipient + // [ ] when multiple lots are created + // [ ] it registers each lot + // [ ] it registers the lot, stores the parameters + + function test_whenCallbackDataIsIncorrect_reverts() public givenCallbackIsCreated { + // Expect revert + vm.expectRevert(); + + vm.prank(address(_auctionHouse)); + _dtl.onCreate( + _lotId, + _SELLER, + address(_baseToken), + address(_quoteToken), + _LOT_CAPACITY, + false, + abi.encode(uint256(10)) + ); + } + + function test_whenCallbackIsNotCalledByAuctionHouse_reverts() public givenCallbackIsCreated { + _expectNotAuthorized(); + + _dtl.onCreate( + _lotId, + _SELLER, + address(_baseToken), + address(_quoteToken), + _LOT_CAPACITY, + false, + abi.encode(_dtlCreateParams) + ); + } + + function test_whenLotHasAlreadyBeenRegistered_reverts() public givenCallbackIsCreated { + _performCallback(); + + _expectInvalidParams(); + + _performCallback(); + } + + function test_whenProceedsUtilisationIs0_reverts() + public + givenCallbackIsCreated + givenProceedsUtilisationPercent(0) + { + // Expect revert + bytes memory err = abi.encodeWithSelector( + BaseDirectToLiquidity.Callback_Params_PercentOutOfBounds.selector, 0, 1, 100e2 + ); + vm.expectRevert(err); + + _performCallback(); + } + + function test_whenProceedsUtilisationIsGreaterThan100Percent_reverts() + public + givenCallbackIsCreated + givenProceedsUtilisationPercent(100e2 + 1) + { + // Expect revert + bytes memory err = abi.encodeWithSelector( + BaseDirectToLiquidity.Callback_Params_PercentOutOfBounds.selector, 100e2 + 1, 1, 100e2 + ); + vm.expectRevert(err); + + _performCallback(); + } + + function test_givenUniswapV2PoolAlreadyExists_reverts() public givenCallbackIsCreated { + // Create the pool + _factory.createPair(address(_baseToken), address(_quoteToken), false); + + // Expect revert + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_Params_PoolExists.selector); + vm.expectRevert(err); + + _performCallback(); + } + + function test_whenStartAndExpiryTimestampsAreTheSame_reverts() + public + givenCallbackIsCreated + givenLinearVestingModuleIsInstalled + givenVestingStart(_initialTimestamp + 1) + givenVestingExpiry(_initialTimestamp + 1) + { + // Expect revert + bytes memory err = abi.encodeWithSelector( + BaseDirectToLiquidity.Callback_Params_InvalidVestingParams.selector + ); + vm.expectRevert(err); + + _performCallback(); + } + + function test_whenStartTimestampIsAfterExpiryTimestamp_reverts() + public + givenCallbackIsCreated + givenLinearVestingModuleIsInstalled + givenVestingStart(_initialTimestamp + 2) + givenVestingExpiry(_initialTimestamp + 1) + { + // Expect revert + bytes memory err = abi.encodeWithSelector( + BaseDirectToLiquidity.Callback_Params_InvalidVestingParams.selector + ); + vm.expectRevert(err); + + _performCallback(); + } + + function test_whenStartTimestampIsBeforeCurrentTimestamp_succeeds() + public + givenCallbackIsCreated + givenLinearVestingModuleIsInstalled + givenVestingStart(_initialTimestamp - 1) + givenVestingExpiry(_initialTimestamp + 1) + { + _performCallback(); + + // Assert values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.vestingStart, _initialTimestamp - 1, "vestingStart"); + assertEq(configuration.vestingExpiry, _initialTimestamp + 1, "vestingExpiry"); + assertEq( + address(configuration.linearVestingModule), + address(_linearVesting), + "linearVestingModule" + ); + + // Assert balances + _assertBaseTokenBalances(); + } + + function test_whenExpiryTimestampIsBeforeCurrentTimestamp_reverts() + public + givenCallbackIsCreated + givenLinearVestingModuleIsInstalled + givenVestingStart(_initialTimestamp + 1) + givenVestingExpiry(_initialTimestamp - 1) + { + // Expect revert + bytes memory err = abi.encodeWithSelector( + BaseDirectToLiquidity.Callback_Params_InvalidVestingParams.selector + ); + vm.expectRevert(err); + + _performCallback(); + } + + function test_whenVestingSpecified_givenLinearVestingModuleNotInstalled_reverts() + public + givenCallbackIsCreated + givenVestingStart(_initialTimestamp + 1) + givenVestingExpiry(_initialTimestamp + 2) + { + // Expect revert + bytes memory err = abi.encodeWithSelector( + BaseDirectToLiquidity.Callback_LinearVestingModuleNotFound.selector + ); + vm.expectRevert(err); + + _performCallback(); + } + + function test_whenVestingSpecified() + public + givenCallbackIsCreated + givenLinearVestingModuleIsInstalled + givenVestingStart(_initialTimestamp + 1) + givenVestingExpiry(_initialTimestamp + 2) + { + _performCallback(); + + // Assert values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.vestingStart, _initialTimestamp + 1, "vestingStart"); + assertEq(configuration.vestingExpiry, _initialTimestamp + 2, "vestingExpiry"); + assertEq( + address(configuration.linearVestingModule), + address(_linearVesting), + "linearVestingModule" + ); + + // Assert balances + _assertBaseTokenBalances(); + } + + function test_whenRecipientIsZeroAddress_reverts() public givenCallbackIsCreated { + _dtlCreateParams.recipient = address(0); + + // Expect revert + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_Params_InvalidAddress.selector); + vm.expectRevert(err); + + _performCallback(); + } + + function test_whenRecipientIsNotSeller_succeeds() + public + givenCallbackIsCreated + whenRecipientIsNotSeller + { + _performCallback(); + + // Assert values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.recipient, _NOT_SELLER, "recipient"); + + // Assert balances + _assertBaseTokenBalances(); + } + + function test_succeeds() public givenCallbackIsCreated { + _performCallback(); + + // Assert values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.recipient, _SELLER, "recipient"); + assertEq(configuration.lotCapacity, _LOT_CAPACITY, "lotCapacity"); + assertEq(configuration.lotCuratorPayout, 0, "lotCuratorPayout"); + assertEq( + configuration.proceedsUtilisationPercent, + _dtlCreateParams.proceedsUtilisationPercent, + "proceedsUtilisationPercent" + ); + assertEq(configuration.vestingStart, 0, "vestingStart"); + assertEq(configuration.vestingExpiry, 0, "vestingExpiry"); + assertEq(address(configuration.linearVestingModule), address(0), "linearVestingModule"); + assertEq(configuration.active, true, "active"); + assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); + + // Assert balances + _assertBaseTokenBalances(); + } + + function test_succeeds_multiple() public givenCallbackIsCreated { + // Lot one + _performCallback(); + + // Lot two + _dtlCreateParams.recipient = _NOT_SELLER; + _lotId = 2; + _performCallback(_NOT_SELLER); + + // Assert values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.recipient, _NOT_SELLER, "recipient"); + assertEq(configuration.lotCapacity, _LOT_CAPACITY, "lotCapacity"); + assertEq(configuration.lotCuratorPayout, 0, "lotCuratorPayout"); + assertEq( + configuration.proceedsUtilisationPercent, + _dtlCreateParams.proceedsUtilisationPercent, + "proceedsUtilisationPercent" + ); + assertEq(configuration.vestingStart, 0, "vestingStart"); + assertEq(configuration.vestingExpiry, 0, "vestingExpiry"); + assertEq(address(configuration.linearVestingModule), address(0), "linearVestingModule"); + assertEq(configuration.active, true, "active"); + assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); + + // Assert balances + _assertBaseTokenBalances(); + } +} diff --git a/test/callbacks/liquidity/RamsesV1/onCurate.t.sol b/test/callbacks/liquidity/RamsesV1/onCurate.t.sol new file mode 100644 index 00000000..1279193b --- /dev/null +++ b/test/callbacks/liquidity/RamsesV1/onCurate.t.sol @@ -0,0 +1,85 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {RamsesV1DirectToLiquidityTest} from "./RamsesV1DTLTest.sol"; + +import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; +import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; + +contract RamsesV1DTLOnCurateForkTest is RamsesV1DirectToLiquidityTest { + uint96 internal constant _PAYOUT_AMOUNT = 1e18; + + // ============ Modifiers ============ // + + function _performCallback(uint96 lotId_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onCurate(lotId_, _PAYOUT_AMOUNT, false, abi.encode("")); + } + + // ============ Tests ============ // + + // [X] when the lot has not been registered + // [X] it reverts + // [X] when multiple lots are created + // [X] it marks the correct lot as inactive + // [ ] when the auction lot has already been completed + // [ ] it reverts + // [X] it registers the curator payout + + function test_whenLotNotRegistered_reverts() public givenCallbackIsCreated { + // Expect revert + bytes memory err = abi.encodeWithSelector(BaseCallback.Callback_NotAuthorized.selector); + vm.expectRevert(err); + + // Call the function + _performCallback(_lotId); + } + + function test_success() public givenCallbackIsCreated givenOnCreate { + // Call the function + _performCallback(_lotId); + + // Check the values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.lotCuratorPayout, _PAYOUT_AMOUNT, "lotCuratorPayout"); + + // Check the balances + assertEq(_baseToken.balanceOf(_dtlAddress), 0, "base token balance"); + assertEq(_baseToken.balanceOf(_SELLER), 0, "seller base token balance"); + assertEq(_baseToken.balanceOf(_NOT_SELLER), 0, "not seller base token balance"); + assertEq( + _baseToken.balanceOf(address(_auctionHouse)), + _LOT_CAPACITY, + "auction house base token balance" + ); + } + + function test_success_multiple() public givenCallbackIsCreated givenOnCreate { + uint96 lotIdOne = _lotId; + + // Create a second lot + uint96 lotIdTwo = _createLot(_NOT_SELLER); + + // Call the function + _performCallback(lotIdTwo); + + // Check the values + BaseDirectToLiquidity.DTLConfiguration memory configurationOne = + _getDTLConfiguration(lotIdOne); + assertEq(configurationOne.lotCuratorPayout, 0, "lot one: lotCuratorPayout"); + + BaseDirectToLiquidity.DTLConfiguration memory configurationTwo = + _getDTLConfiguration(lotIdTwo); + assertEq(configurationTwo.lotCuratorPayout, _PAYOUT_AMOUNT, "lot two: lotCuratorPayout"); + + // Check the balances + assertEq(_baseToken.balanceOf(_dtlAddress), 0, "base token balance"); + assertEq(_baseToken.balanceOf(_SELLER), 0, "seller base token balance"); + assertEq(_baseToken.balanceOf(_NOT_SELLER), 0, "not seller base token balance"); + assertEq( + _baseToken.balanceOf(address(_auctionHouse)), + _LOT_CAPACITY * 2, + "auction house base token balance" + ); + } +} diff --git a/test/callbacks/liquidity/RamsesV1/onSettle.t.sol b/test/callbacks/liquidity/RamsesV1/onSettle.t.sol new file mode 100644 index 00000000..6fcd09f8 --- /dev/null +++ b/test/callbacks/liquidity/RamsesV1/onSettle.t.sol @@ -0,0 +1,692 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {RamsesV1DirectToLiquidityTest} from "./RamsesV1DTLTest.sol"; + +// Libraries +import {FixedPointMathLib} from "@solmate-6.7.0/utils/FixedPointMathLib.sol"; +import {ERC20} from "@solmate-6.7.0/tokens/ERC20.sol"; + +// Rames +import {IRamsesV1Pool} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV1Pool.sol"; + +// AuctionHouse +import {ILinearVesting} from "@axis-core-1.0.0/interfaces/modules/derivatives/ILinearVesting.sol"; +import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; +import {RamsesV1DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ramses/RamsesV1DTL.sol"; + +contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { + uint96 internal constant _PROCEEDS = 20e18; + uint96 internal constant _REFUND = 0; + + /// @dev The minimum amount of liquidity retained in the pool + uint256 internal constant _MINIMUM_LIQUIDITY = 10 ** 3; + + uint96 internal _proceeds; + uint96 internal _refund; + uint96 internal _capacityUtilised; + uint96 internal _quoteTokensToDeposit; + uint96 internal _baseTokensToDeposit; + uint96 internal _curatorPayout; + + uint24 internal _maxSlippage = 1; // 0.01% + + // ========== Internal functions ========== // + + function _getRamsesV1Pool() internal view returns (IRamsesV1Pool) { + return IRamsesV1Pool( + _factory.getPair(address(_quoteToken), address(_baseToken), _ramsesCreateParams.stable) + ); + } + + function _getVestingTokenId() internal view returns (uint256) { + // Get the pools deployed by the DTL callback + address pool = address(_getRamsesV1Pool()); + + return _linearVesting.computeId( + pool, + abi.encode( + ILinearVesting.VestingParams({ + start: _dtlCreateParams.vestingStart, + expiry: _dtlCreateParams.vestingExpiry + }) + ) + ); + } + + // ========== Assertions ========== // + + function _assertLpTokenBalance() internal view { + // Get the pools deployed by the DTL callback + IRamsesV1Pool pool = _getRamsesV1Pool(); + + // Exclude the LP token balance on this contract + uint256 testBalance = pool.balanceOf(address(this)); + + uint256 sellerExpectedBalance; + uint256 linearVestingExpectedBalance; + // Only has a balance if not vesting + if (_dtlCreateParams.vestingStart == 0) { + sellerExpectedBalance = pool.totalSupply() - testBalance - _MINIMUM_LIQUIDITY; + } else { + linearVestingExpectedBalance = pool.totalSupply() - testBalance - _MINIMUM_LIQUIDITY; + } + + assertEq( + pool.balanceOf(_SELLER), + _dtlCreateParams.recipient == _SELLER ? sellerExpectedBalance : 0, + "seller: LP token balance" + ); + assertEq( + pool.balanceOf(_NOT_SELLER), + _dtlCreateParams.recipient == _NOT_SELLER ? sellerExpectedBalance : 0, + "not seller: LP token balance" + ); + assertEq( + pool.balanceOf(address(_linearVesting)), + linearVestingExpectedBalance, + "linear vesting: LP token balance" + ); + } + + function _assertVestingTokenBalance() internal { + // Exit if not vesting + if (_dtlCreateParams.vestingStart == 0) { + return; + } + + // Get the pools deployed by the DTL callback + address pool = address(_getRamsesV1Pool()); + + // Get the wrapped address + (, address wrappedVestingTokenAddress) = _linearVesting.deploy( + pool, + abi.encode( + ILinearVesting.VestingParams({ + start: _dtlCreateParams.vestingStart, + expiry: _dtlCreateParams.vestingExpiry + }) + ), + true + ); + ERC20 wrappedVestingToken = ERC20(wrappedVestingTokenAddress); + uint256 sellerExpectedBalance = wrappedVestingToken.totalSupply(); + + assertEq( + wrappedVestingToken.balanceOf(_SELLER), + _dtlCreateParams.recipient == _SELLER ? sellerExpectedBalance : 0, + "seller: vesting token balance" + ); + assertEq( + wrappedVestingToken.balanceOf(_NOT_SELLER), + _dtlCreateParams.recipient == _NOT_SELLER ? sellerExpectedBalance : 0, + "not seller: vesting token balance" + ); + } + + function _assertQuoteTokenBalance() internal view { + assertEq(_quoteToken.balanceOf(_dtlAddress), 0, "DTL: quote token balance"); + } + + function _assertBaseTokenBalance() internal view { + assertEq(_baseToken.balanceOf(_dtlAddress), 0, "DTL: base token balance"); + } + + function _assertApprovals() internal view { + // Ensure there are no dangling approvals + assertEq( + _quoteToken.allowance(_dtlAddress, address(_router)), 0, "DTL: quote token allowance" + ); + assertEq( + _baseToken.allowance(_dtlAddress, address(_router)), 0, "DTL: base token allowance" + ); + } + + // ========== Modifiers ========== // + + function _performCallback(uint96 lotId_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onSettle(lotId_, _proceeds, _refund, abi.encode("")); + } + + function _performCallback() internal { + _performCallback(_lotId); + } + + function _createPool() internal returns (address) { + return _factory.createPair( + address(_quoteToken), address(_baseToken), _ramsesCreateParams.stable + ); + } + + modifier givenPoolIsCreated() { + _createPool(); + _; + } + + modifier setCallbackParameters(uint96 proceeds_, uint96 refund_) { + _proceeds = proceeds_; + _refund = refund_; + + // Calculate the capacity utilised + // Any unspent curator payout is included in the refund + // However, curator payouts are linear to the capacity utilised + // Calculate the percent utilisation + uint96 capacityUtilisationPercent = 100e2 + - uint96(FixedPointMathLib.mulDivDown(_refund, 100e2, _LOT_CAPACITY + _curatorPayout)); + _capacityUtilised = _LOT_CAPACITY * capacityUtilisationPercent / 100e2; + + // The proceeds utilisation percent scales the quote tokens and base tokens linearly + _quoteTokensToDeposit = _proceeds * _dtlCreateParams.proceedsUtilisationPercent / 100e2; + _baseTokensToDeposit = + _capacityUtilised * _dtlCreateParams.proceedsUtilisationPercent / 100e2; + _; + } + + modifier givenUnboundedProceedsUtilisationPercent(uint24 percent_) { + // Bound the percent + uint24 percent = uint24(bound(percent_, 1, 100e2)); + + // Set the value on the DTL + _dtlCreateParams.proceedsUtilisationPercent = percent; + _; + } + + modifier givenUnboundedOnCurate(uint96 curationPayout_) { + // Bound the value + _curatorPayout = uint96(bound(curationPayout_, 1e17, _LOT_CAPACITY)); + + // Call the onCurate callback + _performOnCurate(_curatorPayout); + _; + } + + modifier whenRefundIsBounded(uint96 refund_) { + // Bound the refund + _refund = uint96(bound(refund_, 1e17, 5e18)); + _; + } + + modifier givenPoolHasDepositLowerPrice() { + uint256 quoteTokensToDeposit = _quoteTokensToDeposit * 95 / 100; + uint256 baseTokensToDeposit = _baseTokensToDeposit; + + // Mint additional tokens + _quoteToken.mint(address(this), quoteTokensToDeposit); + _baseToken.mint(address(this), baseTokensToDeposit); + + // Approve spending + _quoteToken.approve(address(_router), quoteTokensToDeposit); + _baseToken.approve(address(_router), baseTokensToDeposit); + + // Deposit tokens into the pool + _router.addLiquidity( + address(_quoteToken), + address(_baseToken), + _ramsesCreateParams.stable, + quoteTokensToDeposit, + baseTokensToDeposit, + quoteTokensToDeposit, + baseTokensToDeposit, + address(this), + block.timestamp + ); + _; + } + + modifier givenPoolHasDepositHigherPrice() { + uint256 quoteTokensToDeposit = _quoteTokensToDeposit; + uint256 baseTokensToDeposit = _baseTokensToDeposit * 95 / 100; + + // Mint additional tokens + _quoteToken.mint(address(this), quoteTokensToDeposit); + _baseToken.mint(address(this), baseTokensToDeposit); + + // Approve spending + _quoteToken.approve(address(_router), quoteTokensToDeposit); + _baseToken.approve(address(_router), baseTokensToDeposit); + + // Deposit tokens into the pool + _router.addLiquidity( + address(_quoteToken), + address(_baseToken), + _ramsesCreateParams.stable, + quoteTokensToDeposit, + baseTokensToDeposit, + quoteTokensToDeposit, + baseTokensToDeposit, + address(this), + block.timestamp + ); + _; + } + + // ========== Tests ========== // + + // [ ] given the onSettle callback has already been called + // [ ] it reverts + // [X] given the pool is created + // [X] it initializes the pool + // [X] given the pool is created and initialized + // [X] it succeeds + // [X] given the proceeds utilisation percent is set + // [X] it calculates the deposit amount correctly + // [X] given curation is enabled + // [X] the utilisation percent considers this + // [X] when the refund amount changes + // [X] the utilisation percent considers this + // [X] given minting pool tokens utilises less than the available amount of base tokens + // [X] the excess base tokens are returned + // [X] given minting pool tokens utilises less than the available amount of quote tokens + // [X] the excess quote tokens are returned + // [X] given the send base tokens flag is false + // [X] it transfers the base tokens from the seller + // [X] given vesting is enabled + // [X] given the recipient is not the seller + // [X] it mints the vesting tokens to the seller + // [X] it mints the vesting tokens to the seller + // [X] given the recipient is not the seller + // [X] it mints the LP token to the recipient + // [X] when multiple lots are created + // [X] it performs actions on the correct pool + // [ ] given the stable parameter is true + // [ ] it creates a stable pool + // [X] it creates and initializes the pool, creates a pool token, deposits into the pool token, transfers the LP token to the seller and transfers any excess back to the seller + + function test_givenPoolIsCreated() + public + givenCallbackIsCreated + givenOnCreate + givenPoolIsCreated + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performCallback(); + + _assertLpTokenBalance(); + _assertVestingTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_givenProceedsUtilisationPercent_fuzz(uint24 percent_) + public + givenCallbackIsCreated + givenUnboundedProceedsUtilisationPercent(percent_) + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performCallback(); + + _assertLpTokenBalance(); + _assertVestingTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_givenCurationPayout_fuzz(uint96 curationPayout_) + public + givenCallbackIsCreated + givenOnCreate + givenUnboundedOnCurate(curationPayout_) + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performCallback(); + + _assertLpTokenBalance(); + _assertVestingTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_givenProceedsUtilisationPercent_givenCurationPayout_fuzz( + uint24 percent_, + uint96 curationPayout_ + ) + public + givenCallbackIsCreated + givenUnboundedProceedsUtilisationPercent(percent_) + givenOnCreate + givenUnboundedOnCurate(curationPayout_) + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) + { + _performCallback(); + + _assertLpTokenBalance(); + _assertVestingTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_whenRefund_fuzz(uint96 refund_) + public + givenCallbackIsCreated + givenOnCreate + whenRefundIsBounded(refund_) + setCallbackParameters(_PROCEEDS, _refund) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) + { + _performCallback(); + + _assertLpTokenBalance(); + _assertVestingTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_givenPoolHasDepositWithLowerPrice_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenPoolIsCreated + givenPoolHasDepositLowerPrice + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) + { + // Expect revert + vm.expectRevert("UniswapV2Router: INSUFFICIENT_A_AMOUNT"); + + _performCallback(); + } + + function test_givenPoolHasDepositWithLowerPrice_whenMaxSlippageIsSet() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenPoolIsCreated + givenPoolHasDepositLowerPrice + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) + givenMaxSlippage(500) // 5% + { + _performCallback(); + + _assertLpTokenBalance(); + _assertVestingTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_givenPoolHasDepositWithHigherPrice_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenPoolIsCreated + givenPoolHasDepositHigherPrice + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) + { + // Expect revert + vm.expectRevert("UniswapV2Router: INSUFFICIENT_B_AMOUNT"); + + _performCallback(); + } + + function test_givenPoolHasDepositWithHigherPrice_whenMaxSlippageIsSet() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenPoolIsCreated + givenPoolHasDepositHigherPrice + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) + givenMaxSlippage(500) // 5% + { + _performCallback(); + + _assertLpTokenBalance(); + _assertVestingTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_givenVesting() + public + givenLinearVestingModuleIsInstalled + givenCallbackIsCreated + givenVestingStart(_initialTimestamp + 1) + givenVestingExpiry(_initialTimestamp + 2) + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) + { + _performCallback(); + + _assertLpTokenBalance(); + _assertVestingTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_givenVesting_whenRecipientIsNotSeller() + public + givenLinearVestingModuleIsInstalled + givenCallbackIsCreated + givenVestingStart(_initialTimestamp + 1) + givenVestingExpiry(_initialTimestamp + 2) + whenRecipientIsNotSeller + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) + { + _performCallback(); + + _assertLpTokenBalance(); + _assertVestingTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_givenVesting_redemption() + public + givenLinearVestingModuleIsInstalled + givenCallbackIsCreated + givenVestingStart(_initialTimestamp + 1) + givenVestingExpiry(_initialTimestamp + 2) + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) + { + _performCallback(); + + // Warp to the end of the vesting period + vm.warp(_initialTimestamp + 3); + + // Redeem the vesting tokens + uint256 tokenId = _getVestingTokenId(); + vm.prank(_SELLER); + _linearVesting.redeemMax(tokenId); + + // Assert that the LP token has been transferred to the seller + IRamsesV1Pool pool = _getRamsesV1Pool(); + assertEq( + pool.balanceOf(_SELLER), + pool.totalSupply() - _MINIMUM_LIQUIDITY, + "seller: LP token balance" + ); + } + + function test_withdrawLpToken() + public + givenCallbackIsCreated + givenOnCreate + givenPoolIsCreated + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performCallback(); + + // Get the pools deployed by the DTL callback + IRamsesV1Pool pool = _getRamsesV1Pool(); + + // Approve the spending of the LP token + uint256 lpTokenAmount = pool.balanceOf(_SELLER); + vm.prank(_SELLER); + pool.approve(address(_router), lpTokenAmount); + + // Withdraw the LP token + vm.prank(_SELLER); + _router.removeLiquidity( + address(_quoteToken), + address(_baseToken), + _ramsesCreateParams.stable, + lpTokenAmount, + _quoteTokensToDeposit * 99 / 100, + _baseTokensToDeposit * 99 / 100, + _SELLER, + block.timestamp + ); + + // Get the minimum liquidity retained in the pool + uint256 quoteTokenPoolAmount = _quoteToken.balanceOf(address(pool)); + uint256 baseTokenPoolAmount = _baseToken.balanceOf(address(pool)); + + // Check the balances + assertEq(pool.balanceOf(_SELLER), 0, "seller: LP token balance"); + assertEq( + _quoteToken.balanceOf(_SELLER), + _proceeds - quoteTokenPoolAmount, + "seller: quote token balance" + ); + assertEq( + _baseToken.balanceOf(_SELLER), + _capacityUtilised - baseTokenPoolAmount, + "seller: base token balance" + ); + assertEq(_quoteToken.balanceOf(_dtlAddress), 0, "DTL: quote token balance"); + assertEq(_baseToken.balanceOf(_dtlAddress), 0, "DTL: base token balance"); + } + + function test_givenInsufficientBaseTokenBalance_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised - 1) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + // Expect revert + bytes memory err = abi.encodeWithSelector( + BaseDirectToLiquidity.Callback_InsufficientBalance.selector, + address(_baseToken), + _SELLER, + _baseTokensToDeposit, + _baseTokensToDeposit - 1 + ); + vm.expectRevert(err); + + _performCallback(); + } + + function test_givenInsufficientBaseTokenAllowance_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised - 1) + { + // Expect revert + vm.expectRevert("TRANSFER_FROM_FAILED"); + + _performCallback(); + } + + function test_success() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performCallback(); + + _assertLpTokenBalance(); + _assertVestingTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_success_multiple() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_NOT_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_NOT_SELLER, _dtlAddress, _capacityUtilised) + { + // Create second lot + uint96 lotIdTwo = _createLot(_NOT_SELLER); + + _performCallback(lotIdTwo); + + _assertLpTokenBalance(); + _assertVestingTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_whenRecipientIsNotSeller() + public + givenCallbackIsCreated + whenRecipientIsNotSeller + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performCallback(); + + _assertLpTokenBalance(); + _assertVestingTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } +} From 0a1aeca07240df74b6ed6ed3f40bfe49fdc1e7fe Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 25 Jul 2024 17:03:55 +0400 Subject: [PATCH 20/47] TODOs for other DTL tests --- test/callbacks/liquidity/UniswapV2DTL/onCancel.t.sol | 2 ++ test/callbacks/liquidity/UniswapV2DTL/onCreate.t.sol | 2 ++ test/callbacks/liquidity/UniswapV2DTL/onCurate.t.sol | 2 ++ test/callbacks/liquidity/UniswapV3DTL/onCancel.t.sol | 2 ++ test/callbacks/liquidity/UniswapV3DTL/onCreate.t.sol | 2 ++ test/callbacks/liquidity/UniswapV3DTL/onCurate.t.sol | 2 ++ 6 files changed, 12 insertions(+) diff --git a/test/callbacks/liquidity/UniswapV2DTL/onCancel.t.sol b/test/callbacks/liquidity/UniswapV2DTL/onCancel.t.sol index c2ccd09e..36139308 100644 --- a/test/callbacks/liquidity/UniswapV2DTL/onCancel.t.sol +++ b/test/callbacks/liquidity/UniswapV2DTL/onCancel.t.sol @@ -20,6 +20,8 @@ contract UniswapV2DirectToLiquidityOnCancelTest is UniswapV2DirectToLiquidityTes // [X] when the lot has not been registered // [X] it reverts + // [ ] when the auction lot has already been completed + // [ ] it reverts // [X] when multiple lots are created // [X] it marks the correct lot as inactive // [X] it marks the lot as inactive diff --git a/test/callbacks/liquidity/UniswapV2DTL/onCreate.t.sol b/test/callbacks/liquidity/UniswapV2DTL/onCreate.t.sol index ca3294cc..5b31664f 100644 --- a/test/callbacks/liquidity/UniswapV2DTL/onCreate.t.sol +++ b/test/callbacks/liquidity/UniswapV2DTL/onCreate.t.sol @@ -50,6 +50,8 @@ contract UniswapV2DirectToLiquidityOnCreateTest is UniswapV2DirectToLiquidityTes // ============ Tests ============ // + // [ ] when the auction lot has already been completed + // [ ] it reverts // [X] when the callback data is incorrect // [X] it reverts // [X] when the callback is not called by the auction house diff --git a/test/callbacks/liquidity/UniswapV2DTL/onCurate.t.sol b/test/callbacks/liquidity/UniswapV2DTL/onCurate.t.sol index 7078fc5e..8d43ced2 100644 --- a/test/callbacks/liquidity/UniswapV2DTL/onCurate.t.sol +++ b/test/callbacks/liquidity/UniswapV2DTL/onCurate.t.sol @@ -18,6 +18,8 @@ contract UniswapV2DirectToLiquidityOnCurateTest is UniswapV2DirectToLiquidityTes // ============ Tests ============ // + // [ ] when the auction lot has already been completed + // [ ] it reverts // [X] when the lot has not been registered // [X] it reverts // [X] when multiple lots are created diff --git a/test/callbacks/liquidity/UniswapV3DTL/onCancel.t.sol b/test/callbacks/liquidity/UniswapV3DTL/onCancel.t.sol index 1383af27..267f580d 100644 --- a/test/callbacks/liquidity/UniswapV3DTL/onCancel.t.sol +++ b/test/callbacks/liquidity/UniswapV3DTL/onCancel.t.sol @@ -18,6 +18,8 @@ contract UniswapV3DirectToLiquidityOnCancelTest is UniswapV3DirectToLiquidityTes // ============ Tests ============ // + // [ ] when the auction lot has already been completed + // [ ] it reverts // [X] when the lot has not been registered // [X] it reverts // [X] when multiple lots are created diff --git a/test/callbacks/liquidity/UniswapV3DTL/onCreate.t.sol b/test/callbacks/liquidity/UniswapV3DTL/onCreate.t.sol index 097d2dcb..a23ad8b4 100644 --- a/test/callbacks/liquidity/UniswapV3DTL/onCreate.t.sol +++ b/test/callbacks/liquidity/UniswapV3DTL/onCreate.t.sol @@ -57,6 +57,8 @@ contract UniswapV3DirectToLiquidityOnCreateTest is UniswapV3DirectToLiquidityTes // [X] it reverts // [X] when the lot has already been registered // [X] it reverts + // [ ] when the auction lot has already been completed + // [ ] it reverts // [X] when the proceeds utilisation is 0 // [X] it reverts // [X] when the proceeds utilisation is greater than 100% diff --git a/test/callbacks/liquidity/UniswapV3DTL/onCurate.t.sol b/test/callbacks/liquidity/UniswapV3DTL/onCurate.t.sol index 79308ef2..0140d62f 100644 --- a/test/callbacks/liquidity/UniswapV3DTL/onCurate.t.sol +++ b/test/callbacks/liquidity/UniswapV3DTL/onCurate.t.sol @@ -20,6 +20,8 @@ contract UniswapV3DirectToLiquidityOnCurateTest is UniswapV3DirectToLiquidityTes // [X] when the lot has not been registered // [X] it reverts + // [ ] when the auction lot has already been completed + // [ ] it reverts // [X] when multiple lots are created // [X] it marks the correct lot as inactive // [X] it registers the curator payout From 2ab22575c7ac561cc67f32dfcb758c41fa527c31 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 25 Jul 2024 17:26:16 +0400 Subject: [PATCH 21/47] Remove redundant storage in Ramses DTL contracts --- .../liquidity/Ramses/RamsesV1DTL.sol | 36 +++++++++--------- .../liquidity/Ramses/RamsesV2DTL.sol | 38 +++++++++---------- 2 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol index 295075f3..d6097f2c 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol +++ b/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol @@ -49,9 +49,6 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { /// @dev This contract is used to add liquidity to Ramses pairs IRamsesV1Router public router; - /// @notice Mapping of lot ID to configuration parameters - mapping(uint96 => RamsesV1OnCreateParams) public lotParameters; - /// @notice Mapping of lot ID to pool token mapping(uint96 => address) public lotIdToPoolToken; @@ -88,20 +85,9 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { address, uint256, bool, - bytes calldata callbackData_ + bytes calldata ) internal virtual override { - RamsesV1OnCreateParams memory params; - { - OnCreateParams memory onCreateParams = abi.decode(callbackData_, (OnCreateParams)); - - // Validate that the callback data is of the correct length - if (onCreateParams.implParams.length != 64) { - revert Callback_InvalidParams(); - } - - // Decode the callback data - params = abi.decode(onCreateParams.implParams, (RamsesV1OnCreateParams)); - } + RamsesV1OnCreateParams memory params = _decodeParameters(lotId_); // Check that the slippage amount is within bounds // The maxSlippage is stored during onCreate, as the callback data is passed in by the auction seller. @@ -109,8 +95,6 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { if (params.maxSlippage > ONE_HUNDRED_PERCENT) { revert Callback_Params_PercentOutOfBounds(params.maxSlippage, 0, ONE_HUNDRED_PERCENT); } - - lotParameters[lotId_] = params; } /// @inheritdoc BaseDirectToLiquidity @@ -126,7 +110,7 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { uint256 baseTokenAmount_, bytes memory ) internal virtual override { - RamsesV1OnCreateParams memory params = lotParameters[lotId_]; + RamsesV1OnCreateParams memory params = _decodeParameters(lotId_); // Create and initialize the pool if necessary // Token orientation is irrelevant @@ -198,4 +182,18 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { poolToken.safeTransfer(config.recipient, poolTokenQuantity); } } + + function _decodeParameters(uint96 lotId_) + internal + view + returns (RamsesV1OnCreateParams memory) + { + DTLConfiguration memory lotConfig = lotConfiguration[lotId_]; + // Validate that the callback data is of the correct length + if (lotConfig.implParams.length != 64) { + revert Callback_InvalidParams(); + } + + return abi.decode(lotConfig.implParams, (RamsesV1OnCreateParams)); + } } diff --git a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol index 4bdb4dc7..b257cd3d 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol +++ b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol @@ -55,9 +55,6 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { /// @notice The Ramses V2 position manager IRamsesV2PositionManager public ramsesV2PositionManager; - /// @notice Mapping of lot ID to configuration parameters - mapping(uint96 => RamsesV2OnCreateParams) public lotParameters; - // ========== CONSTRUCTOR ========== // constructor( @@ -93,20 +90,9 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { address, uint256, bool, - bytes calldata callbackData_ + bytes calldata ) internal virtual override { - RamsesV2OnCreateParams memory params; - { - OnCreateParams memory onCreateParams = abi.decode(callbackData_, (OnCreateParams)); - - // Validate that the callback data is of the correct length - if (onCreateParams.implParams.length != 96) { - revert Callback_InvalidParams(); - } - - // Decode the callback data - params = abi.decode(onCreateParams.implParams, (RamsesV2OnCreateParams)); - } + RamsesV2OnCreateParams memory params = _decodeParameters(lotId_); // Validate the parameters // Pool fee @@ -132,8 +118,6 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { ) { revert Callback_InvalidParams(); } - - lotParameters[lotId_] = params; } /// @inheritdoc BaseDirectToLiquidity @@ -168,7 +152,7 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { _createAndInitializePoolIfNecessary( quoteTokenIsToken0 ? quoteToken_ : baseToken_, quoteTokenIsToken0 ? baseToken_ : quoteToken_, - lotParameters[lotId_].poolFee, + _decodeParameters(lotId_).poolFee, sqrtPriceX96 ); } @@ -229,7 +213,7 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { address baseToken_, uint256 baseTokenAmount_ ) internal view returns (IRamsesV2PositionManager.MintParams memory) { - RamsesV2OnCreateParams memory params = lotParameters[lotId_]; + RamsesV2OnCreateParams memory params = _decodeParameters(lotId_); int24 tickSpacing = ramsesV2Factory.feeAmountTickSpacing(params.poolFee); @@ -255,4 +239,18 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { veRamTokenId: params.veRamTokenId }); } + + function _decodeParameters(uint96 lotId_) + internal + view + returns (RamsesV2OnCreateParams memory) + { + DTLConfiguration memory lotConfig = lotConfiguration[lotId_]; + // Validate that the callback data is of the correct length + if (lotConfig.implParams.length != 96) { + revert Callback_InvalidParams(); + } + + return abi.decode(lotConfig.implParams, (RamsesV2OnCreateParams)); + } } From de73a9de551b68cca6bf0a97a8675330b109acf4 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 25 Jul 2024 17:29:12 +0400 Subject: [PATCH 22/47] Update test TODOs --- .../liquidity/RamsesV1/onCreate.t.sol | 73 +++++++++++-------- 1 file changed, 43 insertions(+), 30 deletions(-) diff --git a/test/callbacks/liquidity/RamsesV1/onCreate.t.sol b/test/callbacks/liquidity/RamsesV1/onCreate.t.sol index 84b14689..beeba0fc 100644 --- a/test/callbacks/liquidity/RamsesV1/onCreate.t.sol +++ b/test/callbacks/liquidity/RamsesV1/onCreate.t.sol @@ -5,6 +5,7 @@ import {RamsesV1DirectToLiquidityTest} from "./RamsesV1DTLTest.sol"; import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; +import {RamsesV1DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ramses/RamsesV1DTL.sol"; contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { // ============ Modifiers ============ // @@ -50,43 +51,43 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { // ============ Tests ============ // - // [ ] when the callback data is incorrect - // [ ] it reverts - // [ ] when the callback is not called by the auction house - // [ ] it reverts - // [ ] when the lot has already been registered - // [ ] it reverts + // [X] when the callback data is incorrect + // [ X] it reverts + // [X] when the callback is not called by the auction house + // [X] it reverts + // [X] when the lot has already been registered + // [X] it reverts // [ ] when the lot has already been completed // [ ] it reverts - // [ ] when the proceeds utilisation is 0 - // [ ] it reverts - // [ ] when the proceeds utilisation is greater than 100% - // [ ] it reverts + // [X] when the proceeds utilisation is 0 + // [X] it reverts + // [X] when the proceeds utilisation is greater than 100% + // [X] it reverts // [ ] when the implParams is not the correct length // [ ] it reverts // [ ] when the max slippage is greater than 100% // [ ] it reverts // [ ] given ramses v1 pool already exists // [ ] it succeeds - // [ ] when the start and expiry timestamps are the same - // [ ] it reverts - // [ ] when the start timestamp is after the expiry timestamp - // [ ] it reverts - // [ ] when the start timestamp is before the current timestamp - // [ ] it succeeds - // [ ] when the expiry timestamp is before the current timestamp - // [ ] it reverts - // [ ] when the start timestamp and expiry timestamp are specified - // [ ] given the linear vesting module is not installed - // [ ] it reverts - // [ ] it records the address of the linear vesting module - // [ ] when the recipient is the zero address - // [ ] it reverts - // [ ] when the recipient is not the seller - // [ ] it records the recipient - // [ ] when multiple lots are created - // [ ] it registers each lot - // [ ] it registers the lot, stores the parameters + // [X] when the start and expiry timestamps are the same + // [X] it reverts + // [X] when the start timestamp is after the expiry timestamp + // [X] it reverts + // [X] when the start timestamp is before the current timestamp + // [X] it succeeds + // [X] when the expiry timestamp is before the current timestamp + // [X] it reverts + // [X] when the start timestamp and expiry timestamp are specified + // [X] given the linear vesting module is not installed + // [X] it reverts + // [X] it records the address of the linear vesting module + // [X] when the recipient is the zero address + // [X] it reverts + // [X] when the recipient is not the seller + // [X] it records the recipient + // [X] when multiple lots are created + // [X] it registers each lot + // [X] it registers the lot, stores the parameters function test_whenCallbackDataIsIncorrect_reverts() public givenCallbackIsCreated { // Expect revert @@ -154,7 +155,7 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { _performCallback(); } - function test_givenUniswapV2PoolAlreadyExists_reverts() public givenCallbackIsCreated { + function test_givenRamsesV1PoolAlreadyExists_reverts() public givenCallbackIsCreated { // Create the pool _factory.createPair(address(_baseToken), address(_quoteToken), false); @@ -320,6 +321,12 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { assertEq(configuration.active, true, "active"); assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); + RamsesV1DirectToLiquidity.RamsesV1OnCreateParams memory ramsesCreateParams = abi.decode( + _dtlCreateParams.implParams, (RamsesV1DirectToLiquidity.RamsesV1OnCreateParams) + ); + assertEq(ramsesCreateParams.stable, _ramsesCreateParams.stable, "stable"); + assertEq(ramsesCreateParams.maxSlippage, _ramsesCreateParams.maxSlippage, "maxSlippage"); + // Assert balances _assertBaseTokenBalances(); } @@ -349,6 +356,12 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { assertEq(configuration.active, true, "active"); assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); + RamsesV1DirectToLiquidity.RamsesV1OnCreateParams memory ramsesCreateParams = abi.decode( + _dtlCreateParams.implParams, (RamsesV1DirectToLiquidity.RamsesV1OnCreateParams) + ); + assertEq(ramsesCreateParams.stable, _ramsesCreateParams.stable, "stable"); + assertEq(ramsesCreateParams.maxSlippage, _ramsesCreateParams.maxSlippage, "maxSlippage"); + // Assert balances _assertBaseTokenBalances(); } From 6de62eb54a52b8eaf16fee333a981c457e3e77b4 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Thu, 25 Jul 2024 18:03:42 +0400 Subject: [PATCH 23/47] Update salts --- script/salts/salts.json | 3 +++ 1 file changed, 3 insertions(+) diff --git a/script/salts/salts.json b/script/salts/salts.json index 74649f91..adc95b6a 100644 --- a/script/salts/salts.json +++ b/script/salts/salts.json @@ -97,6 +97,9 @@ "Test_QuoteToken": { "0x73857a6649000d8d1c6d627e9eda2a6079605eda4cd860cffe3bb939dfe3e3ef": "0xe9bd28f6c7d9ac5ecc0f59b51855093220efab06f25f16c4d0e82f04eabaf13c" }, + "Test_RamsesV1DirectToLiquidity": { + "0x1b05950659933a441b005fe8073f879dda2c6e58863f23a11bc2b31c5d0251ff": "0x01273217bbc7f1614ca8ef3a01711b1af6a0aee1a6b58e6d3061b4d12f4f491a" + }, "Test_TokenAllowlist": { "0x3cf6404d9502da98e1ed6560fce585a0a87f56f598397915ef5a6207eb9b439f": "0xd8172a08ee7909a096d99b3ef34fe0d4212c85e77206518e9b5e1640926dde85", "0xdb5689fd93b97ad35ee469bbcb38d4472c005f1ae951b154f2437bd1207a03f3": "0xf35c7565f04f89db7d1783343067728c4db35f70d7aea4d7dbd732fa462bb956" From a0b036d84ac219bb551876c630a8c451acceabeb Mon Sep 17 00:00:00 2001 From: Oighty Date: Thu, 25 Jul 2024 15:22:28 -0500 Subject: [PATCH 24/47] test: remove leftover onCreate test is pool is created already --- test/callbacks/liquidity/RamsesV1/onCreate.t.sol | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/test/callbacks/liquidity/RamsesV1/onCreate.t.sol b/test/callbacks/liquidity/RamsesV1/onCreate.t.sol index beeba0fc..4490a474 100644 --- a/test/callbacks/liquidity/RamsesV1/onCreate.t.sol +++ b/test/callbacks/liquidity/RamsesV1/onCreate.t.sol @@ -155,18 +155,6 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { _performCallback(); } - function test_givenRamsesV1PoolAlreadyExists_reverts() public givenCallbackIsCreated { - // Create the pool - _factory.createPair(address(_baseToken), address(_quoteToken), false); - - // Expect revert - bytes memory err = - abi.encodeWithSelector(BaseDirectToLiquidity.Callback_Params_PoolExists.selector); - vm.expectRevert(err); - - _performCallback(); - } - function test_whenStartAndExpiryTimestampsAreTheSame_reverts() public givenCallbackIsCreated From 13f0c0187e0d317d41714bb5899b5f3615344120 Mon Sep 17 00:00:00 2001 From: Oighty Date: Thu, 25 Jul 2024 15:30:59 -0500 Subject: [PATCH 25/47] test: fix ramses v1 settle tests --- .../liquidity/RamsesV1/RamsesV1DTLTest.sol | 1 - test/callbacks/liquidity/RamsesV1/onSettle.t.sol | 15 +++++++-------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/test/callbacks/liquidity/RamsesV1/RamsesV1DTLTest.sol b/test/callbacks/liquidity/RamsesV1/RamsesV1DTLTest.sol index 3b6e879b..cb8de36a 100644 --- a/test/callbacks/liquidity/RamsesV1/RamsesV1DTLTest.sol +++ b/test/callbacks/liquidity/RamsesV1/RamsesV1DTLTest.sol @@ -7,7 +7,6 @@ import {Callbacks} from "@axis-core-1.0.0/lib/Callbacks.sol"; import {Permit2User} from "@axis-core-1.0.0-test/lib/permit2/Permit2User.sol"; import {WithSalts} from "../../../lib/WithSalts.sol"; import {TestConstants} from "../../../Constants.sol"; -import {console2} from "@forge-std-1.9.1/console2.sol"; // Mocks import {MockERC20} from "@solmate-6.7.0/test/utils/mocks/MockERC20.sol"; diff --git a/test/callbacks/liquidity/RamsesV1/onSettle.t.sol b/test/callbacks/liquidity/RamsesV1/onSettle.t.sol index 6fcd09f8..26cda8db 100644 --- a/test/callbacks/liquidity/RamsesV1/onSettle.t.sol +++ b/test/callbacks/liquidity/RamsesV1/onSettle.t.sol @@ -13,7 +13,6 @@ import {IRamsesV1Pool} from "../../../../src/callbacks/liquidity/Ramses/lib/IRam // AuctionHouse import {ILinearVesting} from "@axis-core-1.0.0/interfaces/modules/derivatives/ILinearVesting.sol"; import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; -import {RamsesV1DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ramses/RamsesV1DTL.sol"; contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { uint96 internal constant _PROCEEDS = 20e18; @@ -208,7 +207,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { } modifier givenPoolHasDepositLowerPrice() { - uint256 quoteTokensToDeposit = _quoteTokensToDeposit * 95 / 100; + uint256 quoteTokensToDeposit = _quoteTokensToDeposit * 105 / 100; uint256 baseTokensToDeposit = _baseTokensToDeposit; // Mint additional tokens @@ -235,8 +234,8 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { } modifier givenPoolHasDepositHigherPrice() { - uint256 quoteTokensToDeposit = _quoteTokensToDeposit; - uint256 baseTokensToDeposit = _baseTokensToDeposit * 95 / 100; + uint256 quoteTokensToDeposit = _quoteTokensToDeposit * 95 / 100; + uint256 baseTokensToDeposit = _baseTokensToDeposit; // Mint additional tokens _quoteToken.mint(address(this), quoteTokensToDeposit); @@ -404,7 +403,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { // Expect revert - vm.expectRevert("UniswapV2Router: INSUFFICIENT_A_AMOUNT"); + vm.expectRevert("Router: INSUFFICIENT_B_AMOUNT"); _performCallback(); } @@ -412,6 +411,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { function test_givenPoolHasDepositWithLowerPrice_whenMaxSlippageIsSet() public givenCallbackIsCreated + givenMaxSlippage(500) // 5% givenOnCreate setCallbackParameters(_PROCEEDS, _REFUND) givenPoolIsCreated @@ -419,7 +419,6 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) - givenMaxSlippage(500) // 5% { _performCallback(); @@ -442,7 +441,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { // Expect revert - vm.expectRevert("UniswapV2Router: INSUFFICIENT_B_AMOUNT"); + vm.expectRevert("Router: INSUFFICIENT_A_AMOUNT"); _performCallback(); } @@ -450,6 +449,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { function test_givenPoolHasDepositWithHigherPrice_whenMaxSlippageIsSet() public givenCallbackIsCreated + givenMaxSlippage(500) // 5% givenOnCreate setCallbackParameters(_PROCEEDS, _REFUND) givenPoolIsCreated @@ -457,7 +457,6 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) - givenMaxSlippage(500) // 5% { _performCallback(); From 5bfe27b4756abd6ec7f978e83cd36e8e5c3d99d7 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 26 Jul 2024 12:08:03 +0400 Subject: [PATCH 26/47] Complete tests for Ramses V1 DTL --- .../liquidity/RamsesV1/RamsesV1DTLTest.sol | 74 ++++++- .../liquidity/RamsesV1/onCancel.t.sol | 75 ++++++- .../liquidity/RamsesV1/onCreate.t.sol | 137 ++++++++---- .../liquidity/RamsesV1/onCurate.t.sol | 6 +- .../liquidity/RamsesV1/onSettle.t.sol | 206 ++++++++++++++---- 5 files changed, 397 insertions(+), 101 deletions(-) diff --git a/test/callbacks/liquidity/RamsesV1/RamsesV1DTLTest.sol b/test/callbacks/liquidity/RamsesV1/RamsesV1DTLTest.sol index cb8de36a..019c7185 100644 --- a/test/callbacks/liquidity/RamsesV1/RamsesV1DTLTest.sol +++ b/test/callbacks/liquidity/RamsesV1/RamsesV1DTLTest.sol @@ -54,6 +54,9 @@ abstract contract RamsesV1DirectToLiquidityTest is Test, Permit2User, WithSalts, MockERC20 internal _quoteToken; MockERC20 internal _baseToken; + uint96 internal _proceeds; + uint96 internal _refund; + // Inputs RamsesV1DirectToLiquidity.RamsesV1OnCreateParams internal _ramsesCreateParams = RamsesV1DirectToLiquidity.RamsesV1OnCreateParams({stable: false, maxSlippage: uint24(0)}); @@ -183,6 +186,32 @@ abstract contract RamsesV1DirectToLiquidityTest is Test, Permit2User, WithSalts, _; } + function _performOnCreate(address seller_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onCreate( + _lotId, + seller_, + address(_baseToken), + address(_quoteToken), + _LOT_CAPACITY, + false, + abi.encode(_dtlCreateParams) + ); + } + + function _performOnCreate() internal { + _performOnCreate(_SELLER); + } + + function _performOnCancel(uint96 lotId_, uint256 refundAmount_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onCancel(lotId_, refundAmount_, false, abi.encode("")); + } + + function _performOnCancel() internal { + _performOnCancel(_lotId, 0); + } + function _performOnCurate(uint96 curatorPayout_) internal { vm.prank(address(_auctionHouse)); _dtl.onCurate(_lotId, curatorPayout_, false, abi.encode("")); @@ -193,6 +222,15 @@ abstract contract RamsesV1DirectToLiquidityTest is Test, Permit2User, WithSalts, _; } + function _performOnSettle(uint96 lotId_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onSettle(lotId_, _proceeds, _refund, abi.encode("")); + } + + function _performOnSettle() internal { + _performOnSettle(_lotId); + } + modifier givenProceedsUtilisationPercent(uint24 percent_) { _dtlCreateParams.proceedsUtilisationPercent = percent_; _; @@ -221,11 +259,13 @@ abstract contract RamsesV1DirectToLiquidityTest is Test, Permit2User, WithSalts, _; } - modifier givenMaxSlippage(uint24 maxSlippage_) { + function _setMaxSlippage(uint24 maxSlippage_) internal { _ramsesCreateParams.maxSlippage = maxSlippage_; - - // Update the callback data _dtlCreateParams.implParams = abi.encode(_ramsesCreateParams); + } + + modifier givenMaxSlippage(uint24 maxSlippage_) { + _setMaxSlippage(maxSlippage_); _; } @@ -260,4 +300,32 @@ abstract contract RamsesV1DirectToLiquidityTest is Test, Permit2User, WithSalts, implParams: implParams_ }); } + + // ========== ASSERTIONS ========== // + + function _assertApprovals() internal view { + // Router + assertEq( + _quoteToken.allowance(address(_dtl), address(_router)), + 0, + "allowance: quote token: router" + ); + assertEq( + _baseToken.allowance(address(_dtl), address(_router)), + 0, + "allowance: base token: router" + ); + + // LinearVesting + assertEq( + _quoteToken.allowance(address(_dtl), address(_linearVesting)), + 0, + "allowance: quote token: linear vesting" + ); + assertEq( + _baseToken.allowance(address(_dtl), address(_linearVesting)), + 0, + "allowance: base token: linear vesting" + ); + } } diff --git a/test/callbacks/liquidity/RamsesV1/onCancel.t.sol b/test/callbacks/liquidity/RamsesV1/onCancel.t.sol index 5e2fb471..61b33335 100644 --- a/test/callbacks/liquidity/RamsesV1/onCancel.t.sol +++ b/test/callbacks/liquidity/RamsesV1/onCancel.t.sol @@ -12,19 +12,25 @@ contract RamsesV1DTLOnCancelForkTest is RamsesV1DirectToLiquidityTest { // ============ Modifiers ============ // function _performCallback(uint96 lotId_) internal { - vm.prank(address(_auctionHouse)); - _dtl.onCancel(lotId_, _REFUND_AMOUNT, false, abi.encode("")); + _performOnCancel(lotId_, _REFUND_AMOUNT); } // ============ Tests ============ // - // [ ] when the lot has not been registered - // [ ] it reverts - // [ ] when multiple lots are created - // [ ] it marks the correct lot as inactive - // [ ] when the lot has already been completed - // [ ] it reverts - // [ ] it marks the lot as inactive + // [X] given the onCancel callback has already been called + // [X] when onSettle is called + // [X] it reverts + // [X] when onCancel is called + // [X] it reverts + // [X] when onCurate is called + // [X] it reverts + // [X] when onCreate is called + // [X] it reverts + // [X] when the lot has not been registered + // [X] it reverts + // [X] when multiple lots are created + // [X] it marks the correct lot as inactive + // [X] it marks the lot as inactive function test_whenLotNotRegistered_reverts() public givenCallbackIsCreated { // Expect revert @@ -70,4 +76,55 @@ contract RamsesV1DTLOnCancelForkTest is RamsesV1DirectToLiquidityTest { assertEq(_baseToken.balanceOf(_SELLER), 0, "seller base token balance"); assertEq(_baseToken.balanceOf(_NOT_SELLER), 0, "not seller base token balance"); } + + function test_auctionCancelled_onCreate_reverts() public givenCallbackIsCreated givenOnCreate { + // Call the function + _performCallback(_lotId); + + // Expect revert + // BaseCallback determines if the lot has already been registered + bytes memory err = abi.encodeWithSelector(BaseCallback.Callback_InvalidParams.selector); + vm.expectRevert(err); + + _performOnCreate(); + } + + function test_auctionCancelled_onCurate_reverts() public givenCallbackIsCreated givenOnCreate { + // Call the function + _performCallback(_lotId); + + // Expect revert + // BaseDirectToLiquidity determines if the lot has already been completed + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_AlreadyComplete.selector); + vm.expectRevert(err); + + _performOnCurate(0); + } + + function test_auctionCancelled_onCancel_reverts() public givenCallbackIsCreated givenOnCreate { + // Call the function + _performCallback(_lotId); + + // Expect revert + // BaseDirectToLiquidity determines if the lot has already been completed + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_AlreadyComplete.selector); + vm.expectRevert(err); + + _performOnCancel(); + } + + function test_auctionCancelled_onSettle_reverts() public givenCallbackIsCreated givenOnCreate { + // Call the function + _performCallback(_lotId); + + // Expect revert + // BaseDirectToLiquidity determines if the lot has already been completed + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_AlreadyComplete.selector); + vm.expectRevert(err); + + _performOnSettle(); + } } diff --git a/test/callbacks/liquidity/RamsesV1/onCreate.t.sol b/test/callbacks/liquidity/RamsesV1/onCreate.t.sol index 4490a474..fb6ed572 100644 --- a/test/callbacks/liquidity/RamsesV1/onCreate.t.sol +++ b/test/callbacks/liquidity/RamsesV1/onCreate.t.sol @@ -10,23 +10,6 @@ import {RamsesV1DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ram contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { // ============ Modifiers ============ // - function _performCallback(address seller_) internal { - vm.prank(address(_auctionHouse)); - _dtl.onCreate( - _lotId, - seller_, - address(_baseToken), - address(_quoteToken), - _LOT_CAPACITY, - false, - abi.encode(_dtlCreateParams) - ); - } - - function _performCallback() internal { - _performCallback(_SELLER); - } - // ============ Assertions ============ // function _expectTransferFrom() internal { @@ -57,18 +40,20 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { // [X] it reverts // [X] when the lot has already been registered // [X] it reverts - // [ ] when the lot has already been completed - // [ ] it reverts // [X] when the proceeds utilisation is 0 // [X] it reverts // [X] when the proceeds utilisation is greater than 100% // [X] it reverts - // [ ] when the implParams is not the correct length - // [ ] it reverts - // [ ] when the max slippage is greater than 100% - // [ ] it reverts - // [ ] given ramses v1 pool already exists - // [ ] it succeeds + // [X] when the implParams is not the correct length + // [X] it reverts + // [X] when the max slippage is between 0 and 100% + // [X] it succeeds + // [X] when the max slippage is greater than 100% + // [X] it reverts + // [X] given ramses v1 pool stable already exists + // [X] it succeeds + // [X] given ramses v1 pool volatile already exists + // [X] it succeeds // [X] when the start and expiry timestamps are the same // [X] it reverts // [X] when the start timestamp is after the expiry timestamp @@ -120,11 +105,11 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { } function test_whenLotHasAlreadyBeenRegistered_reverts() public givenCallbackIsCreated { - _performCallback(); + _performOnCreate(); _expectInvalidParams(); - _performCallback(); + _performOnCreate(); } function test_whenProceedsUtilisationIs0_reverts() @@ -138,7 +123,7 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { ); vm.expectRevert(err); - _performCallback(); + _performOnCreate(); } function test_whenProceedsUtilisationIsGreaterThan100Percent_reverts() @@ -152,7 +137,34 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { ); vm.expectRevert(err); - _performCallback(); + _performOnCreate(); + } + + function test_paramsIncorrectLength_reverts() public givenCallbackIsCreated { + // Set the implParams to an incorrect length + _dtlCreateParams.implParams = abi.encode(uint256(10)); + + // Expect revert + bytes memory err = abi.encodeWithSelector(BaseCallback.Callback_InvalidParams.selector); + vm.expectRevert(err); + + _performOnCreate(); + } + + function test_maxSlippageGreaterThan100Percent_reverts(uint24 maxSlippage_) + public + givenCallbackIsCreated + { + uint24 maxSlippage = uint24(bound(maxSlippage_, 100e2 + 1, type(uint24).max)); + _setMaxSlippage(maxSlippage); + + // Expect revert + bytes memory err = abi.encodeWithSelector( + BaseDirectToLiquidity.Callback_Params_PercentOutOfBounds.selector, maxSlippage, 0, 100e2 + ); + vm.expectRevert(err); + + _performOnCreate(); } function test_whenStartAndExpiryTimestampsAreTheSame_reverts() @@ -168,7 +180,7 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { ); vm.expectRevert(err); - _performCallback(); + _performOnCreate(); } function test_whenStartTimestampIsAfterExpiryTimestamp_reverts() @@ -184,7 +196,7 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { ); vm.expectRevert(err); - _performCallback(); + _performOnCreate(); } function test_whenStartTimestampIsBeforeCurrentTimestamp_succeeds() @@ -194,7 +206,7 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { givenVestingStart(_initialTimestamp - 1) givenVestingExpiry(_initialTimestamp + 1) { - _performCallback(); + _performOnCreate(); // Assert values BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); @@ -223,7 +235,7 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { ); vm.expectRevert(err); - _performCallback(); + _performOnCreate(); } function test_whenVestingSpecified_givenLinearVestingModuleNotInstalled_reverts() @@ -238,7 +250,7 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { ); vm.expectRevert(err); - _performCallback(); + _performOnCreate(); } function test_whenVestingSpecified() @@ -248,7 +260,7 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { givenVestingStart(_initialTimestamp + 1) givenVestingExpiry(_initialTimestamp + 2) { - _performCallback(); + _performOnCreate(); // Assert values BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); @@ -262,6 +274,8 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { // Assert balances _assertBaseTokenBalances(); + + _assertApprovals(); } function test_whenRecipientIsZeroAddress_reverts() public givenCallbackIsCreated { @@ -272,7 +286,7 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { abi.encodeWithSelector(BaseDirectToLiquidity.Callback_Params_InvalidAddress.selector); vm.expectRevert(err); - _performCallback(); + _performOnCreate(); } function test_whenRecipientIsNotSeller_succeeds() @@ -280,7 +294,7 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { givenCallbackIsCreated whenRecipientIsNotSeller { - _performCallback(); + _performOnCreate(); // Assert values BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); @@ -291,7 +305,7 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { } function test_succeeds() public givenCallbackIsCreated { - _performCallback(); + _performOnCreate(); // Assert values BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); @@ -317,16 +331,18 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { // Assert balances _assertBaseTokenBalances(); + + _assertApprovals(); } function test_succeeds_multiple() public givenCallbackIsCreated { // Lot one - _performCallback(); + _performOnCreate(); // Lot two _dtlCreateParams.recipient = _NOT_SELLER; _lotId = 2; - _performCallback(_NOT_SELLER); + _performOnCreate(_NOT_SELLER); // Assert values BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); @@ -352,5 +368,46 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { // Assert balances _assertBaseTokenBalances(); + + _assertApprovals(); + } + + function test_maxSlippage_fuzz(uint24 maxSlippage_) public givenCallbackIsCreated { + uint24 maxSlippage = uint24(bound(maxSlippage_, 0, 100e2)); + _setMaxSlippage(maxSlippage); + + _performOnCreate(); + + // Assert values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); + + RamsesV1DirectToLiquidity.RamsesV1OnCreateParams memory ramsesCreateParams = abi.decode( + _dtlCreateParams.implParams, (RamsesV1DirectToLiquidity.RamsesV1OnCreateParams) + ); + assertEq(ramsesCreateParams.stable, _ramsesCreateParams.stable, "stable"); + assertEq(ramsesCreateParams.maxSlippage, maxSlippage, "maxSlippage"); + } + + function test_givenStablePoolExists() public givenCallbackIsCreated { + // Create the pool + _factory.createPair(address(_baseToken), address(_quoteToken), true); + + _performOnCreate(); + + // Assert values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.active, true, "active"); + } + + function test_givenVolatilePoolExists() public givenCallbackIsCreated { + // Create the pool + _factory.createPair(address(_baseToken), address(_quoteToken), false); + + _performOnCreate(); + + // Assert values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.active, true, "active"); } } diff --git a/test/callbacks/liquidity/RamsesV1/onCurate.t.sol b/test/callbacks/liquidity/RamsesV1/onCurate.t.sol index 1279193b..efb8dde9 100644 --- a/test/callbacks/liquidity/RamsesV1/onCurate.t.sol +++ b/test/callbacks/liquidity/RamsesV1/onCurate.t.sol @@ -22,8 +22,6 @@ contract RamsesV1DTLOnCurateForkTest is RamsesV1DirectToLiquidityTest { // [X] it reverts // [X] when multiple lots are created // [X] it marks the correct lot as inactive - // [ ] when the auction lot has already been completed - // [ ] it reverts // [X] it registers the curator payout function test_whenLotNotRegistered_reverts() public givenCallbackIsCreated { @@ -52,6 +50,8 @@ contract RamsesV1DTLOnCurateForkTest is RamsesV1DirectToLiquidityTest { _LOT_CAPACITY, "auction house base token balance" ); + + _assertApprovals(); } function test_success_multiple() public givenCallbackIsCreated givenOnCreate { @@ -81,5 +81,7 @@ contract RamsesV1DTLOnCurateForkTest is RamsesV1DirectToLiquidityTest { _LOT_CAPACITY * 2, "auction house base token balance" ); + + _assertApprovals(); } } diff --git a/test/callbacks/liquidity/RamsesV1/onSettle.t.sol b/test/callbacks/liquidity/RamsesV1/onSettle.t.sol index 26cda8db..e7f8aafc 100644 --- a/test/callbacks/liquidity/RamsesV1/onSettle.t.sol +++ b/test/callbacks/liquidity/RamsesV1/onSettle.t.sol @@ -7,12 +7,13 @@ import {RamsesV1DirectToLiquidityTest} from "./RamsesV1DTLTest.sol"; import {FixedPointMathLib} from "@solmate-6.7.0/utils/FixedPointMathLib.sol"; import {ERC20} from "@solmate-6.7.0/tokens/ERC20.sol"; -// Rames +// Ramses import {IRamsesV1Pool} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV1Pool.sol"; // AuctionHouse import {ILinearVesting} from "@axis-core-1.0.0/interfaces/modules/derivatives/ILinearVesting.sol"; import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; +import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { uint96 internal constant _PROCEEDS = 20e18; @@ -21,8 +22,6 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { /// @dev The minimum amount of liquidity retained in the pool uint256 internal constant _MINIMUM_LIQUIDITY = 10 ** 3; - uint96 internal _proceeds; - uint96 internal _refund; uint96 internal _capacityUtilised; uint96 internal _quoteTokensToDeposit; uint96 internal _baseTokensToDeposit; @@ -32,10 +31,12 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { // ========== Internal functions ========== // + function _getRamsesV1Pool(bool stable_) internal view returns (IRamsesV1Pool) { + return IRamsesV1Pool(_factory.getPair(address(_quoteToken), address(_baseToken), stable_)); + } + function _getRamsesV1Pool() internal view returns (IRamsesV1Pool) { - return IRamsesV1Pool( - _factory.getPair(address(_quoteToken), address(_baseToken), _ramsesCreateParams.stable) - ); + return _getRamsesV1Pool(_ramsesCreateParams.stable); } function _getVestingTokenId() internal view returns (uint256) { @@ -131,27 +132,8 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { assertEq(_baseToken.balanceOf(_dtlAddress), 0, "DTL: base token balance"); } - function _assertApprovals() internal view { - // Ensure there are no dangling approvals - assertEq( - _quoteToken.allowance(_dtlAddress, address(_router)), 0, "DTL: quote token allowance" - ); - assertEq( - _baseToken.allowance(_dtlAddress, address(_router)), 0, "DTL: base token allowance" - ); - } - // ========== Modifiers ========== // - function _performCallback(uint96 lotId_) internal { - vm.prank(address(_auctionHouse)); - _dtl.onSettle(lotId_, _proceeds, _refund, abi.encode("")); - } - - function _performCallback() internal { - _performCallback(_lotId); - } - function _createPool() internal returns (address) { return _factory.createPair( address(_quoteToken), address(_baseToken), _ramsesCreateParams.stable @@ -262,8 +244,15 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { // ========== Tests ========== // - // [ ] given the onSettle callback has already been called - // [ ] it reverts + // [X] given the onSettle callback has already been called + // [X] when onSettle is called + // [X] it reverts + // [X] when onCancel is called + // [X] it reverts + // [X] when onCreate is called + // [X] it reverts + // [X] when onCurate is called + // [X] it reverts // [X] given the pool is created // [X] it initializes the pool // [X] given the pool is created and initialized @@ -288,8 +277,10 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { // [X] it mints the LP token to the recipient // [X] when multiple lots are created // [X] it performs actions on the correct pool - // [ ] given the stable parameter is true - // [ ] it creates a stable pool + // [X] given the stable parameter is true + // [X] it creates a stable pool + // [X] given the stable parameter is false + // [X] it creates a volatile pool // [X] it creates and initializes the pool, creates a pool token, deposits into the pool token, transfers the LP token to the seller and transfers any excess back to the seller function test_givenPoolIsCreated() @@ -302,7 +293,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) { - _performCallback(); + _performOnSettle(); _assertLpTokenBalance(); _assertVestingTokenBalance(); @@ -321,7 +312,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) { - _performCallback(); + _performOnSettle(); _assertLpTokenBalance(); _assertVestingTokenBalance(); @@ -340,7 +331,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) { - _performCallback(); + _performOnSettle(); _assertLpTokenBalance(); _assertVestingTokenBalance(); @@ -363,7 +354,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { - _performCallback(); + _performOnSettle(); _assertLpTokenBalance(); _assertVestingTokenBalance(); @@ -382,7 +373,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { - _performCallback(); + _performOnSettle(); _assertLpTokenBalance(); _assertVestingTokenBalance(); @@ -405,7 +396,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { // Expect revert vm.expectRevert("Router: INSUFFICIENT_B_AMOUNT"); - _performCallback(); + _performOnSettle(); } function test_givenPoolHasDepositWithLowerPrice_whenMaxSlippageIsSet() @@ -420,7 +411,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { - _performCallback(); + _performOnSettle(); _assertLpTokenBalance(); _assertVestingTokenBalance(); @@ -443,7 +434,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { // Expect revert vm.expectRevert("Router: INSUFFICIENT_A_AMOUNT"); - _performCallback(); + _performOnSettle(); } function test_givenPoolHasDepositWithHigherPrice_whenMaxSlippageIsSet() @@ -458,7 +449,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { - _performCallback(); + _performOnSettle(); _assertLpTokenBalance(); _assertVestingTokenBalance(); @@ -479,7 +470,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { - _performCallback(); + _performOnSettle(); _assertLpTokenBalance(); _assertVestingTokenBalance(); @@ -501,7 +492,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { - _performCallback(); + _performOnSettle(); _assertLpTokenBalance(); _assertVestingTokenBalance(); @@ -522,7 +513,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { - _performCallback(); + _performOnSettle(); // Warp to the end of the vesting period vm.warp(_initialTimestamp + 3); @@ -551,7 +542,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) { - _performCallback(); + _performOnSettle(); // Get the pools deployed by the DTL callback IRamsesV1Pool pool = _getRamsesV1Pool(); @@ -613,7 +604,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { ); vm.expectRevert(err); - _performCallback(); + _performOnSettle(); } function test_givenInsufficientBaseTokenAllowance_reverts() @@ -628,7 +619,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { // Expect revert vm.expectRevert("TRANSFER_FROM_FAILED"); - _performCallback(); + _performOnSettle(); } function test_success() @@ -640,7 +631,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) { - _performCallback(); + _performOnSettle(); _assertLpTokenBalance(); _assertVestingTokenBalance(); @@ -661,7 +652,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { // Create second lot uint96 lotIdTwo = _createLot(_NOT_SELLER); - _performCallback(lotIdTwo); + _performOnSettle(lotIdTwo); _assertLpTokenBalance(); _assertVestingTokenBalance(); @@ -670,6 +661,44 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { _assertApprovals(); } + function test_stablePool() + public + givenCallbackIsCreated + givenStable(true) + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performOnSettle(); + + address stablePool = address(_getRamsesV1Pool(true)); + address volatilePool = address(_getRamsesV1Pool(false)); + + assertNotEq(stablePool, address(0), "stable pool address"); + assertEq(volatilePool, address(0), "volatile pool address"); + } + + function test_volatilePool() + public + givenCallbackIsCreated + givenStable(false) + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performOnSettle(); + + address stablePool = address(_getRamsesV1Pool(true)); + address volatilePool = address(_getRamsesV1Pool(false)); + + assertEq(stablePool, address(0), "stable pool address"); + assertNotEq(volatilePool, address(0), "volatile pool address"); + } + function test_whenRecipientIsNotSeller() public givenCallbackIsCreated @@ -680,7 +709,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) { - _performCallback(); + _performOnSettle(); _assertLpTokenBalance(); _assertVestingTokenBalance(); @@ -688,4 +717,87 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { _assertBaseTokenBalance(); _assertApprovals(); } + + function test_auctionCompleted_onCreate_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performOnSettle(); + + // Expect revert + // BaseCallback determines if the lot has already been registered + bytes memory err = abi.encodeWithSelector(BaseCallback.Callback_InvalidParams.selector); + vm.expectRevert(err); + + // Try to call onCreate again + _performOnCreate(); + } + + function test_auctionCompleted_onCurate_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performOnSettle(); + + // Expect revert + // BaseDirectToLiquidity determines if the lot has already been completed + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_AlreadyComplete.selector); + vm.expectRevert(err); + + // Try to call onCurate + _performOnCurate(_curatorPayout); + } + + function test_auctionCompleted_onCancel_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performOnSettle(); + + // Expect revert + // BaseDirectToLiquidity determines if the lot has already been completed + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_AlreadyComplete.selector); + vm.expectRevert(err); + + // Try to call onCancel + _performOnCancel(); + } + + function test_auctionCompleted_onSettle_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performOnSettle(); + + // Expect revert + // BaseDirectToLiquidity determines if the lot has already been completed + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_AlreadyComplete.selector); + vm.expectRevert(err); + + // Try to call onSettle + _performOnSettle(); + } } From b883788e6cfce1dd37487033cc2f144fac91cfad Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 26 Jul 2024 12:13:24 +0400 Subject: [PATCH 27/47] Check for completed auction lots in onCancel and onCurate callback functions --- script/salts/salts.json | 16 ++++++++-------- src/callbacks/liquidity/BaseDTL.sol | 12 ++++++++++++ 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/script/salts/salts.json b/script/salts/salts.json index adc95b6a..b6c514a0 100644 --- a/script/salts/salts.json +++ b/script/salts/salts.json @@ -53,19 +53,19 @@ "0x3e786da8306fcbed7cac84942338a1270f92283628b6c00f68be021274a9398c": "0x0f25886775a191f8793d3da1493fe07c98943f0d34227e3ceebae3dfb555b56a" }, "Test_BaselineAllocatedAllowlist": { - "0x5d746474bd6f58dea871b468d53e3ab576b19043d224904750600a2d128829a1": "0x8452e66de7fac583f0fc44105edcef467d77d8edcf4c938a71aa76596ecfb67c" + "0x5d746474bd6f58dea871b468d53e3ab576b19043d224904750600a2d128829a1": "0x01e8a1097ae9c6b111d41a7083a3a951186ae7e57740c033f8b1ee1f65d75a69" }, "Test_BaselineAllowlist": { - "0x5994dd0cafe772dd48a75ac3eba409d0654bf2cac96c9f15c8096296038e2a00": "0xf6689b20de39181dd95dcd925b9bc3b10e545bf381bedaaa54dbac2d54a8334b" + "0x5994dd0cafe772dd48a75ac3eba409d0654bf2cac96c9f15c8096296038e2a00": "0x46385df55b069cfb03a79134ed0238753451d5c759ec65ec1862c46696aa74a0" }, "Test_BaselineAxisLaunch": { - "0x52496df1c08f8ccb2ff3d5890297b3619ae3d670581df798367699ac94530d12": "0x9a04d55297d9a3348f8fe51141f2c75825152eb31e306f1411ddbd741d4d14dc" + "0x52496df1c08f8ccb2ff3d5890297b3619ae3d670581df798367699ac94530d12": "0x7bb31290b0bb64c5cf5f07d5a6a805e8b90546a54cd74216908eccfef0f2731a" }, "Test_BaselineCappedAllowlist": { - "0x1ed7b5269875d6fd24f19a8b6a1906cca2ca94bba1d23cf1fb3e95877b8b0338": "0x6f2e4acbd33ef40152b0e5679bd694d0f60f4a9af611cded049d04f3598d8f10" + "0x1ed7b5269875d6fd24f19a8b6a1906cca2ca94bba1d23cf1fb3e95877b8b0338": "0xbd4d24db803bc5f1c5dc27826b7e2bd8e4b52dd8854225ac8278bb280ae24b92" }, "Test_BaselineTokenAllowlist": { - "0x3f8ca4e10bd4e9daa2aee9c323586dc24d4a3358d7599a3668eed1dd1860a829": "0xee3f8d4ffa9ffed5fa846f99d80c6080722f07cca9e388c72bfd5c49b007d8b4" + "0x3f8ca4e10bd4e9daa2aee9c323586dc24d4a3358d7599a3668eed1dd1860a829": "0xd513ec98769f0961837fec2fad1c59af4b94d69e322bc78fc3672ebc6049c2c0" }, "Test_CappedMerkleAllowlist": { "0x1dc038ba91f15889eedf059525478d760c3b01dbf5288c0c995ef5c8f1395e8b": "0x6bfac83b21063468377650c02a19f8b39750ffa72f1763596c48d62833e54e12", @@ -98,20 +98,20 @@ "0x73857a6649000d8d1c6d627e9eda2a6079605eda4cd860cffe3bb939dfe3e3ef": "0xe9bd28f6c7d9ac5ecc0f59b51855093220efab06f25f16c4d0e82f04eabaf13c" }, "Test_RamsesV1DirectToLiquidity": { - "0x1b05950659933a441b005fe8073f879dda2c6e58863f23a11bc2b31c5d0251ff": "0x01273217bbc7f1614ca8ef3a01711b1af6a0aee1a6b58e6d3061b4d12f4f491a" + "0x53bbd9c8a867624e56d19bf63d184f39112c1ad047c11555758b6e6d5e153499": "0x03349021186521ece9eaa54f521a1cb4c378198c47cbca9020936993c02a23c0" }, "Test_TokenAllowlist": { "0x3cf6404d9502da98e1ed6560fce585a0a87f56f598397915ef5a6207eb9b439f": "0xd8172a08ee7909a096d99b3ef34fe0d4212c85e77206518e9b5e1640926dde85", "0xdb5689fd93b97ad35ee469bbcb38d4472c005f1ae951b154f2437bd1207a03f3": "0xf35c7565f04f89db7d1783343067728c4db35f70d7aea4d7dbd732fa462bb956" }, "Test_UniswapV2DirectToLiquidity": { - "0xf3cf5a017c393b255bc51f7bc4327a42384669b656c57e12841c3f45e8f261c7": "0x4a019a12c26020c0311e6f902b4cd4774e042aaba59fdeedf186a7e644b26827" + "0x3b9597ed8d70c8fe0f4beda2a2c5d3f99be1daf59b5d28221d2f1609d53730e6": "0x777f030735cde785ecbdc7a0f3d5984e7e0f5cb385b37c47622960af95347464" }, "Test_UniswapV2Router": { "0x3aeb5c743a058e3c9d871533d2537013b819c4e401acaca3619f046cd9422258": "0x4c096423447dffd4ffce23f8e15e2d1b08619f72abc81536b5492d95b7c8f03f" }, "Test_UniswapV3DirectToLiquidity": { - "0xd87004414e9285e9c723ac4d3311b9b9540dd5390e4c74bd1aa3bdb13f3e9cf0": "0x307f1610a69d585a53a3692ad1f188d88a710be5e0def4247c4bef89dea49c9e" + "0x177a207e41b48a7ff6d561046056b1dacbbe01ef97bf6381a7cda6d781d04e02": "0x2ab7082b39720b3cd945b25a712c093f7ab7924c9db5095a80099d9b3746604c" }, "Test_UniswapV3Factory": { "0x33479a3955b5801da918af0aa61f4501368d483ac02e8ae036d4e6f9c56d2038": "0x715638753ed78c13d0d385c1758204daad3b308083604fb0d555eb285199ed30" diff --git a/src/callbacks/liquidity/BaseDTL.sol b/src/callbacks/liquidity/BaseDTL.sol index 2eec9d2a..08a6ea36 100644 --- a/src/callbacks/liquidity/BaseDTL.sol +++ b/src/callbacks/liquidity/BaseDTL.sol @@ -228,9 +228,15 @@ abstract contract BaseDirectToLiquidity is BaseCallback { /// /// This function reverts if: /// - The lot is not registered + /// - The lot has already been completed /// /// @param lotId_ The lot ID function _onCancel(uint96 lotId_, uint256, bool, bytes calldata) internal override { + // Check that the lot is active + if (!lotConfiguration[lotId_].active) { + revert Callback_AlreadyComplete(); + } + // Mark the lot as inactive to prevent further actions DTLConfiguration storage config = lotConfiguration[lotId_]; config.active = false; @@ -242,6 +248,7 @@ abstract contract BaseDirectToLiquidity is BaseCallback { /// /// This function reverts if: /// - The lot is not registered + /// - The lot has already been completed /// /// @param lotId_ The lot ID /// @param curatorPayout_ The maximum curator payout @@ -251,6 +258,11 @@ abstract contract BaseDirectToLiquidity is BaseCallback { bool, bytes calldata ) internal override { + // Check that the lot is active + if (!lotConfiguration[lotId_].active) { + revert Callback_AlreadyComplete(); + } + // Update the funding DTLConfiguration storage config = lotConfiguration[lotId_]; config.lotCuratorPayout = curatorPayout_; From 98c5c11c1fbf10b183f37dcbbaf968a1cea83350 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 26 Jul 2024 12:13:50 +0400 Subject: [PATCH 28/47] Add ARBITRUM_RPC_URL to environment in CI, required for fork test --- .github/workflows/test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1c3dbf7d..8d004d0b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -52,6 +52,8 @@ jobs: run: | forge test -vvv id: test + env: + ARBITRUM_RPC_URL: ${{ secrets.ARBITRUM_RPC_URL }} - name: Contract Sizes run: | From 76645754d2a20823250ea1668693eeab64f0bc15 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 26 Jul 2024 12:19:28 +0400 Subject: [PATCH 29/47] Add storage of tokenId to Ramses V2 DTL --- script/salts/salts.json | 3 +++ src/callbacks/liquidity/Ramses/RamsesV2DTL.sol | 12 ++++++++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/script/salts/salts.json b/script/salts/salts.json index b6c514a0..e5ca2e4a 100644 --- a/script/salts/salts.json +++ b/script/salts/salts.json @@ -100,6 +100,9 @@ "Test_RamsesV1DirectToLiquidity": { "0x53bbd9c8a867624e56d19bf63d184f39112c1ad047c11555758b6e6d5e153499": "0x03349021186521ece9eaa54f521a1cb4c378198c47cbca9020936993c02a23c0" }, + "Test_RamsesV2DirectToLiquidity": { + "0x0b17a4c7f488dbaa202bc1e5b4375746b93e5627e3a55fedd3359e3c8c343f6f": "0x3208e391ac10f52ded48bb10d3d1de808c85a00e1d99dbb64f83cb7f9beb8d98" + }, "Test_TokenAllowlist": { "0x3cf6404d9502da98e1ed6560fce585a0a87f56f598397915ef5a6207eb9b439f": "0xd8172a08ee7909a096d99b3ef34fe0d4212c85e77206518e9b5e1640926dde85", "0xdb5689fd93b97ad35ee469bbcb38d4472c005f1ae951b154f2437bd1207a03f3": "0xf35c7565f04f89db7d1783343067728c4db35f70d7aea4d7dbd732fa462bb956" diff --git a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol index b257cd3d..f1cb8129 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol +++ b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol @@ -55,6 +55,9 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { /// @notice The Ramses V2 position manager IRamsesV2PositionManager public ramsesV2PositionManager; + /// @notice Mapping of lot ID to Ramses V2 token ID + mapping(uint96 => uint256) public lotIdToTokenId; + // ========== CONSTRUCTOR ========== // constructor( @@ -80,9 +83,9 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { /// - Validates the input data /// /// This function reverts if: - /// - `RamsesV1OnCreateParams.poolFee` is not enabled - /// - `RamsesV1OnCreateParams.maxSlippage` is out of bounds - /// - This contract does not have permission to use the veRamTokenId + /// - `RamsesV2OnCreateParams.poolFee` is not enabled + /// - `RamsesV2OnCreateParams.maxSlippage` is out of bounds + /// - This contract does not have permission to use `RamsesV2OnCreateParams.veRamTokenId` function __onCreate( uint96 lotId_, address, @@ -167,7 +170,8 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { ERC20(baseToken_).approve(address(ramsesV2PositionManager), baseTokenAmount_); // Mint the position - ramsesV2PositionManager.mint(mintParams); + (uint256 tokenId,,,) = ramsesV2PositionManager.mint(mintParams); + lotIdToTokenId[lotId_] = tokenId; // Reset dangling approvals // The position manager may not spend all tokens From 30c4c65af1ea886e38d84943ca72589626cd974d Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 26 Jul 2024 14:23:16 +0400 Subject: [PATCH 30/47] Initial version of Ramses V2 tests --- .../liquidity/RamsesV2/RamsesV2DTLTest.sol | 274 +++++++++ .../liquidity/RamsesV2/onCancel.t.sol | 80 +++ .../liquidity/RamsesV2/onCreate.t.sol | 267 +++++++++ .../liquidity/RamsesV2/onCurate.t.sol | 83 +++ .../liquidity/RamsesV2/onSettle.t.sol | 531 ++++++++++++++++++ 5 files changed, 1235 insertions(+) create mode 100644 test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol create mode 100644 test/callbacks/liquidity/RamsesV2/onCancel.t.sol create mode 100644 test/callbacks/liquidity/RamsesV2/onCreate.t.sol create mode 100644 test/callbacks/liquidity/RamsesV2/onCurate.t.sol create mode 100644 test/callbacks/liquidity/RamsesV2/onSettle.t.sol diff --git a/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol b/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol new file mode 100644 index 00000000..bd64f8d0 --- /dev/null +++ b/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol @@ -0,0 +1,274 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +// Test scaffolding +import {Test} from "@forge-std-1.9.1/Test.sol"; +import {Callbacks} from "@axis-core-1.0.0/lib/Callbacks.sol"; +import {Permit2User} from "@axis-core-1.0.0-test/lib/permit2/Permit2User.sol"; +import {WithSalts} from "../../../lib/WithSalts.sol"; +import {console2} from "@forge-std-1.9.1/console2.sol"; +import {TestConstants} from "../../../Constants.sol"; + +// Mocks +import {MockERC20} from "@solmate-6.7.0/test/utils/mocks/MockERC20.sol"; +import {MockBatchAuctionModule} from + "@axis-core-1.0.0-test/modules/Auction/MockBatchAuctionModule.sol"; + +// Callbacks +import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; + +// Ramses +import {RamsesV2DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ramses/RamsesV2DTL.sol"; +import {IRamsesV2Factory} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV2Factory.sol"; +import {IRamsesV2Pool} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV2Pool.sol"; +import {IRamsesV2PositionManager} from + "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV2PositionManager.sol"; + +// Axis core +import {keycodeFromVeecode, toKeycode} from "@axis-core-1.0.0/modules/Keycode.sol"; +import {IAuction} from "@axis-core-1.0.0/interfaces/modules/IAuction.sol"; +import {IAuctionHouse} from "@axis-core-1.0.0/interfaces/IAuctionHouse.sol"; +import {BatchAuctionHouse} from "@axis-core-1.0.0/BatchAuctionHouse.sol"; +import {LinearVesting} from "@axis-core-1.0.0/modules/derivatives/LinearVesting.sol"; + +abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, TestConstants { + using Callbacks for RamsesV2DirectToLiquidity; + + address internal constant _SELLER = address(0x2); + address internal constant _PROTOCOL = address(0x3); + address internal constant _BUYER = address(0x4); + address internal constant _NOT_SELLER = address(0x20); + + uint96 internal constant _LOT_CAPACITY = 10e18; + + uint48 internal _initialTimestamp; + + uint96 internal _lotId = 1; + + BatchAuctionHouse internal _auctionHouse; + RamsesV2DirectToLiquidity internal _dtl; + address internal _dtlAddress; + IRamsesV2Factory internal _factory; + IRamsesV2PositionManager internal _positionManager; + MockBatchAuctionModule internal _batchAuctionModule; + + MockERC20 internal _quoteToken; + MockERC20 internal _baseToken; + + // Inputs + RamsesV2DirectToLiquidity.RamsesV2OnCreateParams internal _ramsesCreateParams = RamsesV2DirectToLiquidity + .RamsesV2OnCreateParams({ + poolFee: 500, + maxSlippage: 0, + veRamTokenId: 0 + }); + BaseDirectToLiquidity.OnCreateParams internal _dtlCreateParams = BaseDirectToLiquidity + .OnCreateParams({ + proceedsUtilisationPercent: 100e2, + vestingStart: 0, + vestingExpiry: 0, + recipient: _SELLER, + implParams: abi.encode(_ramsesCreateParams) + }); + + function setUp() public { + // Create a fork on Arbitrum + string memory arbitrumRpcUrl = vm.envString("ARBITRUM_RPC_URL"); + vm.createSelectFork(arbitrumRpcUrl); + require(block.chainid == 42_161, "Must be on Arbitrum"); + + _initialTimestamp = uint48(block.timestamp); + + // Create an AuctionHouse at a deterministic address, since it is used as input to callbacks + BatchAuctionHouse auctionHouse = new BatchAuctionHouse(_OWNER, _PROTOCOL, _permit2Address); + _auctionHouse = BatchAuctionHouse(_AUCTION_HOUSE); + vm.etch(address(_auctionHouse), address(auctionHouse).code); + vm.store(address(_auctionHouse), bytes32(uint256(0)), bytes32(abi.encode(_OWNER))); // Owner + vm.store(address(_auctionHouse), bytes32(uint256(6)), bytes32(abi.encode(1))); // Reentrancy + vm.store(address(_auctionHouse), bytes32(uint256(10)), bytes32(abi.encode(_PROTOCOL))); // Protocol + + _factory = IRamsesV2Factory(_RAMSES_V2_FACTORY); + _positionManager = IRamsesV2PositionManager(payable(_RAMSES_V2_POSITION_MANAGER)); + + _batchAuctionModule = new MockBatchAuctionModule(address(_auctionHouse)); + + // Install a mock batch auction module + vm.prank(_OWNER); + _auctionHouse.installModule(_batchAuctionModule); + + _quoteToken = new MockERC20("Quote Token", "QT", 18); + _baseToken = new MockERC20("Base Token", "BT", 18); + } + + // ========== MODIFIERS ========== // + + modifier givenCallbackIsCreated() { + // Get the salt + bytes memory args = + abi.encode(address(_auctionHouse), address(_factory), address(_positionManager)); + bytes32 salt = _getTestSalt( + "RamsesV2DirectToLiquidity", type(RamsesV2DirectToLiquidity).creationCode, args + ); + + // Required for CREATE2 address to work correctly. doesn't do anything in a test + // Source: https://github.com/foundry-rs/foundry/issues/6402 + vm.startBroadcast(); + _dtl = new RamsesV2DirectToLiquidity{salt: salt}( + address(_auctionHouse), address(_factory), payable(_positionManager) + ); + vm.stopBroadcast(); + + _dtlAddress = address(_dtl); + _; + } + + modifier givenAddressHasQuoteTokenBalance(address address_, uint256 amount_) { + _quoteToken.mint(address_, amount_); + _; + } + + modifier givenAddressHasBaseTokenBalance(address address_, uint256 amount_) { + _baseToken.mint(address_, amount_); + _; + } + + modifier givenAddressHasQuoteTokenAllowance(address owner_, address spender_, uint256 amount_) { + vm.prank(owner_); + _quoteToken.approve(spender_, amount_); + _; + } + + modifier givenAddressHasBaseTokenAllowance(address owner_, address spender_, uint256 amount_) { + vm.prank(owner_); + _baseToken.approve(spender_, amount_); + _; + } + + function _createLot(address seller_) internal returns (uint96 lotId) { + // Mint and approve the capacity to the owner + _baseToken.mint(seller_, _LOT_CAPACITY); + vm.prank(seller_); + _baseToken.approve(address(_auctionHouse), _LOT_CAPACITY); + + // Prep the lot arguments + IAuctionHouse.RoutingParams memory routingParams = IAuctionHouse.RoutingParams({ + auctionType: keycodeFromVeecode(_batchAuctionModule.VEECODE()), + baseToken: address(_baseToken), + quoteToken: address(_quoteToken), + referrerFee: 0, // No referrer fee + curator: address(0), + callbacks: _dtl, + callbackData: abi.encode(_dtlCreateParams), + derivativeType: toKeycode(""), + derivativeParams: abi.encode(""), + wrapDerivative: false + }); + + IAuction.AuctionParams memory auctionParams = IAuction.AuctionParams({ + start: uint48(block.timestamp) + 1, + duration: 1 days, + capacityInQuote: false, + capacity: _LOT_CAPACITY, + implParams: abi.encode("") + }); + + // Create a new lot + vm.prank(seller_); + return _auctionHouse.auction(routingParams, auctionParams, ""); + } + + modifier givenOnCreate() { + _lotId = _createLot(_SELLER); + _; + } + + function _performOnCurate(uint96 curatorPayout_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onCurate(_lotId, curatorPayout_, false, abi.encode("")); + } + + modifier givenOnCurate(uint96 curatorPayout_) { + _performOnCurate(curatorPayout_); + _; + } + + modifier givenProceedsUtilisationPercent(uint24 percent_) { + _dtlCreateParams.proceedsUtilisationPercent = percent_; + _; + } + + modifier givenPoolFee(uint24 fee_) { + _ramsesCreateParams.poolFee = fee_; + + // Update the callback data + _dtlCreateParams.implParams = abi.encode(_ramsesCreateParams); + _; + } + + function _setMaxSlippage(uint24 maxSlippage_) internal { + _ramsesCreateParams.maxSlippage = maxSlippage_; + _dtlCreateParams.implParams = abi.encode(_ramsesCreateParams); + } + + modifier givenMaxSlippage(uint24 maxSlippage_) { + _setMaxSlippage(maxSlippage_); + _; + } + + function _setVmRamTokenId(uint24 vmRamTokenId_) internal { + _ramsesCreateParams.veRamTokenId = vmRamTokenId_; + _dtlCreateParams.implParams = abi.encode(_ramsesCreateParams); + } + + modifier givenVmRamTokenId(uint24 vmRamTokenId_) { + _setVmRamTokenId(vmRamTokenId_); + _; + } + + modifier givenVestingStart(uint48 start_) { + _dtlCreateParams.vestingStart = start_; + _; + } + + modifier givenVestingExpiry(uint48 end_) { + _dtlCreateParams.vestingExpiry = end_; + _; + } + + modifier whenRecipientIsNotSeller() { + _dtlCreateParams.recipient = _NOT_SELLER; + _; + } + + // ========== FUNCTIONS ========== // + + function _getDTLConfiguration(uint96 lotId_) + internal + view + returns (BaseDirectToLiquidity.DTLConfiguration memory) + { + ( + address recipient_, + uint256 lotCapacity_, + uint256 lotCuratorPayout_, + uint24 proceedsUtilisationPercent_, + uint48 vestingStart_, + uint48 vestingExpiry_, + LinearVesting linearVestingModule_, + bool active_, + bytes memory implParams_ + ) = _dtl.lotConfiguration(lotId_); + + return BaseDirectToLiquidity.DTLConfiguration({ + recipient: recipient_, + lotCapacity: lotCapacity_, + lotCuratorPayout: lotCuratorPayout_, + proceedsUtilisationPercent: proceedsUtilisationPercent_, + vestingStart: vestingStart_, + vestingExpiry: vestingExpiry_, + linearVestingModule: linearVestingModule_, + active: active_, + implParams: implParams_ + }); + } +} diff --git a/test/callbacks/liquidity/RamsesV2/onCancel.t.sol b/test/callbacks/liquidity/RamsesV2/onCancel.t.sol new file mode 100644 index 00000000..eaf0bd0e --- /dev/null +++ b/test/callbacks/liquidity/RamsesV2/onCancel.t.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {RamsesV2DirectToLiquidityTest} from "./RamsesV2DTLTest.sol"; + +import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; +import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; + +contract RamsesV2DTLOnCancelForkTest is RamsesV2DirectToLiquidityTest { + uint96 internal constant _REFUND_AMOUNT = 2e18; + + // ============ Modifiers ============ // + + function _performCallback(uint96 lotId_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onCancel(lotId_, _REFUND_AMOUNT, false, abi.encode("")); + } + + // ============ Tests ============ // + + // [X] given the onCancel callback has already been called + // [X] when onSettle is called + // [ ] it reverts + // [X] when onCancel is called + // [ ] it reverts + // [X] when onCurate is called + // [ ] it reverts + // [X] when onCreate is called + // [ ] it reverts + // [X] when the lot has not been registered + // [X] it reverts + // [X] when multiple lots are created + // [X] it marks the correct lot as inactive + // [X] it marks the lot as inactive + + function test_whenLotNotRegistered_reverts() public givenCallbackIsCreated { + // Expect revert + bytes memory err = abi.encodeWithSelector(BaseCallback.Callback_NotAuthorized.selector); + vm.expectRevert(err); + + // Call the function + _performCallback(_lotId); + } + + function test_success() public givenCallbackIsCreated givenOnCreate { + // Call the function + _performCallback(_lotId); + + // Check the values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.active, false, "active"); + + // Check the balances + assertEq(_baseToken.balanceOf(_dtlAddress), 0, "base token balance"); + assertEq(_baseToken.balanceOf(_SELLER), 0, "seller base token balance"); + assertEq(_baseToken.balanceOf(_NOT_SELLER), 0, "not seller base token balance"); + } + + function test_success_multiple() public givenCallbackIsCreated givenOnCreate { + uint96 lotIdOne = _lotId; + + // Create a second lot and cancel it + uint96 lotIdTwo = _createLot(_NOT_SELLER); + _performCallback(lotIdTwo); + + // Check the values + BaseDirectToLiquidity.DTLConfiguration memory configurationOne = + _getDTLConfiguration(lotIdOne); + assertEq(configurationOne.active, true, "lot one: active"); + + BaseDirectToLiquidity.DTLConfiguration memory configurationTwo = + _getDTLConfiguration(lotIdTwo); + assertEq(configurationTwo.active, false, "lot two: active"); + + // Check the balances + assertEq(_baseToken.balanceOf(_dtlAddress), 0, "base token balance"); + assertEq(_baseToken.balanceOf(_SELLER), 0, "seller base token balance"); + assertEq(_baseToken.balanceOf(_NOT_SELLER), 0, "not seller base token balance"); + } +} diff --git a/test/callbacks/liquidity/RamsesV2/onCreate.t.sol b/test/callbacks/liquidity/RamsesV2/onCreate.t.sol new file mode 100644 index 00000000..44ed8c39 --- /dev/null +++ b/test/callbacks/liquidity/RamsesV2/onCreate.t.sol @@ -0,0 +1,267 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {RamsesV2DirectToLiquidityTest} from "./RamsesV2DTLTest.sol"; + +import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; +import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; +import {RamsesV2DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ramses/RamsesV2DTL.sol"; + +contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { + // ============ Modifiers ============ // + + function _performCallback(address seller_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onCreate( + _lotId, + seller_, + address(_baseToken), + address(_quoteToken), + _LOT_CAPACITY, + false, + abi.encode(_dtlCreateParams) + ); + } + + function _performCallback() internal { + _performCallback(_SELLER); + } + + // ============ Assertions ============ // + + function _expectTransferFrom() internal { + vm.expectRevert("TRANSFER_FROM_FAILED"); + } + + function _expectInvalidParams() internal { + bytes memory err = abi.encodeWithSelector(BaseCallback.Callback_InvalidParams.selector); + vm.expectRevert(err); + } + + function _expectNotAuthorized() internal { + bytes memory err = abi.encodeWithSelector(BaseCallback.Callback_NotAuthorized.selector); + vm.expectRevert(err); + } + + function _assertBaseTokenBalances() internal view { + assertEq(_baseToken.balanceOf(_SELLER), 0, "seller balance"); + assertEq(_baseToken.balanceOf(_NOT_SELLER), 0, "not seller balance"); + assertEq(_baseToken.balanceOf(_dtlAddress), 0, "dtl balance"); + } + + // ============ Tests ============ // + + // [X] when the callback data is incorrect + // [X] it reverts + // [X] when the callback is not called by the auction house + // [X] it reverts + // [X] when the lot has already been registered + // [X] it reverts + // [X] when the proceeds utilisation is 0 + // [X] it reverts + // [X] when the proceeds utilisation is greater than 100% + // [X] it reverts + // [ ] when the implParams is not the correct length + // [ ] it reverts + // [ ] when the max slippage is between 0 and 100% + // [ ] it succeeds + // [ ] when the max slippage is greater than 100% + // [ ] it reverts + // [X] given the pool fee is not enabled + // [X] it reverts + // [ ] given ramses v2 pool already exists + // [ ] it succeeds + // [ ] when the vesting start timestamp is set + // [ ] it reverts + // [ ] when the vesting expiry timestamp is set + // [ ] it reverts + // [X] when the recipient is the zero address + // [X] it reverts + // [X] when the recipient is not the seller + // [X] it records the recipient + // [X] when multiple lots are created + // [X] it registers each lot + // [X] it registers the lot + + function test_whenCallbackDataIsIncorrect_reverts() public givenCallbackIsCreated { + // Expect revert + vm.expectRevert(); + + vm.prank(address(_auctionHouse)); + _dtl.onCreate( + _lotId, + _SELLER, + address(_baseToken), + address(_quoteToken), + _LOT_CAPACITY, + false, + abi.encode(uint256(10)) + ); + } + + function test_whenCallbackIsNotCalledByAuctionHouse_reverts() public givenCallbackIsCreated { + _expectNotAuthorized(); + + _dtl.onCreate( + _lotId, + _SELLER, + address(_baseToken), + address(_quoteToken), + _LOT_CAPACITY, + false, + abi.encode(_dtlCreateParams) + ); + } + + function test_whenLotHasAlreadyBeenRegistered_reverts() public givenCallbackIsCreated { + _performCallback(); + + _expectInvalidParams(); + + _performCallback(); + } + + function test_whenProceedsUtilisationIs0_reverts() + public + givenCallbackIsCreated + givenProceedsUtilisationPercent(0) + { + // Expect revert + bytes memory err = abi.encodeWithSelector( + BaseDirectToLiquidity.Callback_Params_PercentOutOfBounds.selector, 0, 1, 100e2 + ); + vm.expectRevert(err); + + _performCallback(); + } + + function test_whenProceedsUtilisationIsGreaterThan100Percent_reverts() + public + givenCallbackIsCreated + givenProceedsUtilisationPercent(100e2 + 1) + { + // Expect revert + bytes memory err = abi.encodeWithSelector( + BaseDirectToLiquidity.Callback_Params_PercentOutOfBounds.selector, 100e2 + 1, 1, 100e2 + ); + vm.expectRevert(err); + + _performCallback(); + } + + function test_givenPoolFeeIsNotEnabled_reverts() + public + givenCallbackIsCreated + givenPoolFee(0) + { + // Expect revert + bytes memory err = abi.encodeWithSelector( + RamsesV2DirectToLiquidity.Callback_Params_PoolFeeNotEnabled.selector + ); + vm.expectRevert(err); + + _performCallback(); + } + + function test_givenRamsesV2PoolAlreadyExists_reverts() + public + givenCallbackIsCreated + givenPoolFee(500) + { + // Create the pool + _factory.createPool(address(_baseToken), address(_quoteToken), 500); + + // Expect revert + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_Params_PoolExists.selector); + vm.expectRevert(err); + + _performCallback(); + } + + function test_whenRecipientIsZeroAddress_reverts() public givenCallbackIsCreated { + _dtlCreateParams.recipient = address(0); + + // Expect revert + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_Params_InvalidAddress.selector); + vm.expectRevert(err); + + _performCallback(); + } + + function test_whenRecipientIsNotSeller_succeeds() + public + givenCallbackIsCreated + whenRecipientIsNotSeller + { + _performCallback(); + + // Assert values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.recipient, _NOT_SELLER, "recipient"); + + // Assert balances + _assertBaseTokenBalances(); + } + + function test_succeeds() public givenCallbackIsCreated { + _performCallback(); + + // Assert values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.recipient, _SELLER, "recipient"); + assertEq(configuration.lotCapacity, _LOT_CAPACITY, "lotCapacity"); + assertEq(configuration.lotCuratorPayout, 0, "lotCuratorPayout"); + assertEq( + configuration.proceedsUtilisationPercent, + _dtlCreateParams.proceedsUtilisationPercent, + "proceedsUtilisationPercent" + ); + assertEq(configuration.vestingStart, 0, "vestingStart"); + assertEq(configuration.vestingExpiry, 0, "vestingExpiry"); + assertEq(address(configuration.linearVestingModule), address(0), "linearVestingModule"); + assertEq(configuration.active, true, "active"); + assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); + + RamsesV2DirectToLiquidity.RamsesV2OnCreateParams memory _ramsesCreateParams = abi.decode( + _dtlCreateParams.implParams, + (RamsesV2DirectToLiquidity.RamsesV2OnCreateParams) + ); + assertEq(_ramsesCreateParams.poolFee, _ramsesCreateParams.poolFee, "poolFee"); + assertEq(_ramsesCreateParams.maxSlippage, _ramsesCreateParams.maxSlippage, "maxSlippage"); + assertEq(_ramsesCreateParams.veRamTokenId, _ramsesCreateParams.veRamTokenId, "veRamTokenId"); + + // Assert balances + _assertBaseTokenBalances(); + } + + function test_succeeds_multiple() public givenCallbackIsCreated { + // Lot one + _performCallback(); + + // Lot two + _dtlCreateParams.recipient = _NOT_SELLER; + _lotId = 2; + _performCallback(_NOT_SELLER); + + // Assert values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.recipient, _NOT_SELLER, "recipient"); + assertEq(configuration.lotCapacity, _LOT_CAPACITY, "lotCapacity"); + assertEq(configuration.lotCuratorPayout, 0, "lotCuratorPayout"); + assertEq( + configuration.proceedsUtilisationPercent, + _dtlCreateParams.proceedsUtilisationPercent, + "proceedsUtilisationPercent" + ); + assertEq(configuration.vestingStart, 0, "vestingStart"); + assertEq(configuration.vestingExpiry, 0, "vestingExpiry"); + assertEq(address(configuration.linearVestingModule), address(0), "linearVestingModule"); + assertEq(configuration.active, true, "active"); + assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); + + // Assert balances + _assertBaseTokenBalances(); + } +} diff --git a/test/callbacks/liquidity/RamsesV2/onCurate.t.sol b/test/callbacks/liquidity/RamsesV2/onCurate.t.sol new file mode 100644 index 00000000..8dd1ea9f --- /dev/null +++ b/test/callbacks/liquidity/RamsesV2/onCurate.t.sol @@ -0,0 +1,83 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {RamsesV2DirectToLiquidityTest} from "./RamsesV2DTLTest.sol"; + +import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; +import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; + +contract RamsesV2DTLOnCurateForkTest is RamsesV2DirectToLiquidityTest { + uint96 internal constant _PAYOUT_AMOUNT = 1e18; + + // ============ Modifiers ============ // + + function _performCallback(uint96 lotId_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onCurate(lotId_, _PAYOUT_AMOUNT, false, abi.encode("")); + } + + // ============ Tests ============ // + + // [X] when the lot has not been registered + // [X] it reverts + // [X] when multiple lots are created + // [X] it marks the correct lot as inactive + // [X] it registers the curator payout + + function test_whenLotNotRegistered_reverts() public givenCallbackIsCreated { + // Expect revert + bytes memory err = abi.encodeWithSelector(BaseCallback.Callback_NotAuthorized.selector); + vm.expectRevert(err); + + // Call the function + _performCallback(_lotId); + } + + function test_success() public givenCallbackIsCreated givenOnCreate { + // Call the function + _performCallback(_lotId); + + // Check the values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.lotCuratorPayout, _PAYOUT_AMOUNT, "lotCuratorPayout"); + + // Check the balances + assertEq(_baseToken.balanceOf(_dtlAddress), 0, "base token balance"); + assertEq(_baseToken.balanceOf(_SELLER), 0, "seller base token balance"); + assertEq(_baseToken.balanceOf(_NOT_SELLER), 0, "not seller base token balance"); + assertEq( + _baseToken.balanceOf(address(_auctionHouse)), + _LOT_CAPACITY, + "auction house base token balance" + ); + } + + function test_success_multiple() public givenCallbackIsCreated givenOnCreate { + uint96 lotIdOne = _lotId; + + // Create a second lot + uint96 lotIdTwo = _createLot(_NOT_SELLER); + + // Call the function + _performCallback(lotIdTwo); + + // Check the values + BaseDirectToLiquidity.DTLConfiguration memory configurationOne = + _getDTLConfiguration(lotIdOne); + assertEq(configurationOne.lotCuratorPayout, 0, "lot one: lotCuratorPayout"); + + BaseDirectToLiquidity.DTLConfiguration memory configurationTwo = + _getDTLConfiguration(lotIdTwo); + assertEq(configurationTwo.lotCuratorPayout, _PAYOUT_AMOUNT, "lot two: lotCuratorPayout"); + + // Check the balances + assertEq(_baseToken.balanceOf(_dtlAddress), 0, "base token balance"); + assertEq(_baseToken.balanceOf(_SELLER), 0, "seller base token balance"); + assertEq(_baseToken.balanceOf(_NOT_SELLER), 0, "not seller base token balance"); + assertEq( + _baseToken.balanceOf(address(_auctionHouse)), + _LOT_CAPACITY * 2, + "auction house base token balance" + ); + } +} diff --git a/test/callbacks/liquidity/RamsesV2/onSettle.t.sol b/test/callbacks/liquidity/RamsesV2/onSettle.t.sol new file mode 100644 index 00000000..30db7f52 --- /dev/null +++ b/test/callbacks/liquidity/RamsesV2/onSettle.t.sol @@ -0,0 +1,531 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity 0.8.19; + +import {RamsesV2DirectToLiquidityTest} from "./RamsesV2DTLTest.sol"; + +// Libraries +import {FixedPointMathLib} from "@solmate-6.7.0/utils/FixedPointMathLib.sol"; +import {ERC20} from "@solmate-6.7.0/tokens/ERC20.sol"; + +// Uniswap +import {IUniswapV3Pool} from + "@uniswap-v3-core-1.0.1-solc-0.8-simulate/interfaces/IUniswapV3Pool.sol"; +import {SqrtPriceMath} from "../../../../src/lib/uniswap-v3/SqrtPriceMath.sol"; + +// G-UNI +import {GUniPool} from "@g-uni-v1-core-0.9.9/GUniPool.sol"; + +// AuctionHouse +import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; +import {RamsesV2DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ramses/RamsesV2DTL.sol"; + +contract RamsesV2OnSettleForkTest is RamsesV2DirectToLiquidityTest { + uint96 internal constant _PROCEEDS = 20e18; + uint96 internal constant _REFUND = 0; + + uint96 internal _proceeds; + uint96 internal _refund; + uint96 internal _capacityUtilised; + uint96 internal _quoteTokensToDeposit; + uint96 internal _baseTokensToDeposit; + uint96 internal _curatorPayout; + uint24 internal _maxSlippage = 1; // 0.01% + + uint160 internal constant _SQRT_PRICE_X96_OVERRIDE = 125_270_724_187_523_965_593_206_000_000; // Different to what is normally calculated + + /// @dev Set via `setCallbackParameters` modifier + uint160 internal _sqrtPriceX96; + + // ========== Internal functions ========== // + + function _getTokenId() internal view returns (uint256) { + return _dtl.lotIdToTokenId(_lotId); + } + + // ========== Assertions ========== // + + function _assertPoolState(uint160 sqrtPriceX96_) internal view { + // Get the pool + address pool = _getPool(); + + (uint160 sqrtPriceX96,,,,,,) = IUniswapV3Pool(pool).slot0(); + assertEq(sqrtPriceX96, sqrtPriceX96_, "pool sqrt price"); + } + + function _assertLpTokenBalance() internal view { + uint256 tokenId = _getTokenId(); + + assertEq(_positionManager.ownerOf(tokenId), _dtlCreateParams.recipient, "LP token owner"); + } + + function _assertQuoteTokenBalance() internal view { + assertEq(_quoteToken.balanceOf(_dtlAddress), 0, "DTL: quote token balance"); + } + + function _assertBaseTokenBalance() internal view { + assertEq(_baseToken.balanceOf(_dtlAddress), 0, "DTL: base token balance"); + } + + function _assertApprovals() internal view { + // Router + assertEq( + _quoteToken.allowance(address(_dtl), address(_positionManager)), + 0, + "allowance: quote token: position manager" + ); + assertEq( + _baseToken.allowance(address(_dtl), address(_positionManager)), + 0, + "allowance: base token: position manager" + ); + } + + // ========== Modifiers ========== // + + function _performCallback(uint96 lotId_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onSettle( + lotId_, + _proceeds, + _refund, + abi.encode("") + ); + } + + function _performCallback() internal { + _performCallback(_lotId); + } + + function _createPool() internal returns (address) { + (address token0, address token1) = address(_baseToken) < address(_quoteToken) + ? (address(_baseToken), address(_quoteToken)) + : (address(_quoteToken), address(_baseToken)); + + return _factory.createPool(token0, token1, _ramsesCreateParams.poolFee); + } + + function _initializePool(address pool_, uint160 sqrtPriceX96_) internal { + IUniswapV3Pool(pool_).initialize(sqrtPriceX96_); + } + + modifier givenPoolIsCreated() { + _createPool(); + _; + } + + modifier givenPoolIsCreatedAndInitialized(uint160 sqrtPriceX96_) { + address pool = _createPool(); + _initializePool(pool, sqrtPriceX96_); + _; + } + + function _calculateSqrtPriceX96( + uint256 quoteTokenAmount_, + uint256 baseTokenAmount_ + ) internal view returns (uint160) { + return SqrtPriceMath.getSqrtPriceX96( + address(_quoteToken), address(_baseToken), quoteTokenAmount_, baseTokenAmount_ + ); + } + + modifier setCallbackParameters(uint96 proceeds_, uint96 refund_) { + _proceeds = proceeds_; + _refund = refund_; + + // Calculate the capacity utilised + // Any unspent curator payout is included in the refund + // However, curator payouts are linear to the capacity utilised + // Calculate the percent utilisation + uint96 capacityUtilisationPercent = 100e2 + - uint96(FixedPointMathLib.mulDivDown(_refund, 100e2, _LOT_CAPACITY + _curatorPayout)); + _capacityUtilised = _LOT_CAPACITY * capacityUtilisationPercent / 100e2; + + // The proceeds utilisation percent scales the quote tokens and base tokens linearly + _quoteTokensToDeposit = _proceeds * _dtlCreateParams.proceedsUtilisationPercent / 100e2; + _baseTokensToDeposit = + _capacityUtilised * _dtlCreateParams.proceedsUtilisationPercent / 100e2; + + _sqrtPriceX96 = _calculateSqrtPriceX96(_quoteTokensToDeposit, _baseTokensToDeposit); + _; + } + + modifier givenUnboundedProceedsUtilisationPercent(uint24 percent_) { + // Bound the percent + uint24 percent = uint24(bound(percent_, 1, 100e2)); + + // Set the value on the DTL + _dtlCreateParams.proceedsUtilisationPercent = percent; + _; + } + + modifier givenUnboundedOnCurate(uint96 curationPayout_) { + // Bound the value + _curatorPayout = uint96(bound(curationPayout_, 1e17, _LOT_CAPACITY)); + + // Call the onCurate callback + _performOnCurate(_curatorPayout); + _; + } + + modifier whenRefundIsBounded(uint96 refund_) { + // Bound the refund + _refund = uint96(bound(refund_, 1e17, 5e18)); + _; + } + + modifier givenPoolHasDepositLowerPrice() { + _sqrtPriceX96 = _calculateSqrtPriceX96(_PROCEEDS / 2, _LOT_CAPACITY); + _; + } + + modifier givenPoolHasDepositHigherPrice() { + _sqrtPriceX96 = _calculateSqrtPriceX96(_PROCEEDS * 2, _LOT_CAPACITY); + _; + } + + function _getPool() internal view returns (address) { + (address token0, address token1) = address(_baseToken) < address(_quoteToken) + ? (address(_baseToken), address(_quoteToken)) + : (address(_quoteToken), address(_baseToken)); + return _factory.getPool(token0, token1, _ramsesCreateParams.poolFee); + } + + // ========== Tests ========== // + + // [X] given the onSettle callback has already been called + // [X] when onSettle is called + // [ ] it reverts + // [X] when onCancel is called + // [ ] it reverts + // [X] when onCreate is called + // [ ] it reverts + // [X] when onCurate is called + // [ ] it reverts + // [X] given the pool is created + // [X] it initializes the pool + // [X] given the pool is created and initialized + // [X] it succeeds + // [X] given the proceeds utilisation percent is set + // [X] it calculates the deposit amount correctly + // [X] given curation is enabled + // [X] the utilisation percent considers this + // [X] when the refund amount changes + // [X] the utilisation percent considers this + // [X] given minting pool tokens utilises less than the available amount of base tokens + // [X] the excess base tokens are returned + // [X] given minting pool tokens utilises less than the available amount of quote tokens + // [X] the excess quote tokens are returned + // [X] given the send base tokens flag is false + // [X] it transfers the base tokens from the seller + // [X] given the recipient is not the seller + // [X] it mints the LP token to the recipient + // [X] when multiple lots are created + // [X] it performs actions on the correct pool + // [X] it creates and initializes the pool, mints the position, transfers the LP token to the seller and transfers any excess back to the seller + + function test_givenPoolIsCreated() + public + givenCallbackIsCreated + givenOnCreate + givenPoolIsCreated + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performCallback(); + + _assertPoolState(_sqrtPriceX96); + _assertLpTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_givenPoolIsCreatedAndInitialized_givenMaxSlippage() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenPoolIsCreatedAndInitialized(_SQRT_PRICE_X96_OVERRIDE) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + givenMaxSlippage(8100) // 81% + { + _performCallback(); + + _assertPoolState(_SQRT_PRICE_X96_OVERRIDE); + _assertLpTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_givenPoolIsCreatedAndInitialized_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenPoolIsCreatedAndInitialized(_SQRT_PRICE_X96_OVERRIDE) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + // Expect revert + vm.expectRevert("Price slippage check"); + + _performCallback(); + } + + function test_givenProceedsUtilisationPercent_fuzz(uint24 percent_) + public + givenCallbackIsCreated + givenUnboundedProceedsUtilisationPercent(percent_) + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performCallback(); + + _assertPoolState(_sqrtPriceX96); + _assertLpTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_givenCurationPayout_fuzz(uint96 curationPayout_) + public + givenCallbackIsCreated + givenOnCreate + givenUnboundedOnCurate(curationPayout_) + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performCallback(); + + _assertPoolState(_sqrtPriceX96); + _assertLpTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_givenProceedsUtilisationPercent_givenCurationPayout_fuzz( + uint24 percent_, + uint96 curationPayout_ + ) + public + givenCallbackIsCreated + givenUnboundedProceedsUtilisationPercent(percent_) + givenOnCreate + givenUnboundedOnCurate(curationPayout_) + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) + { + _performCallback(); + + _assertPoolState(_sqrtPriceX96); + _assertLpTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_whenRefund_fuzz(uint96 refund_) + public + givenCallbackIsCreated + givenOnCreate + whenRefundIsBounded(refund_) + setCallbackParameters(_PROCEEDS, _refund) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) + { + _performCallback(); + + _assertPoolState(_sqrtPriceX96); + _assertLpTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_givenPoolHasDepositWithLowerPrice() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenPoolHasDepositLowerPrice + givenPoolIsCreatedAndInitialized(_sqrtPriceX96) + givenMaxSlippage(5100) // 51% + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) + { + _performCallback(); + + _assertPoolState(_sqrtPriceX96); + _assertLpTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_givenPoolHasDepositWithHigherPrice() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenPoolHasDepositHigherPrice + givenPoolIsCreatedAndInitialized(_sqrtPriceX96) + givenMaxSlippage(5100) // 51% + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) + { + _performCallback(); + + _assertPoolState(_sqrtPriceX96); + _assertLpTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_lessThanMaxSlippage() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenMaxSlippage(1) // 0.01% + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) + { + _performCallback(); + + _assertPoolState(_sqrtPriceX96); + _assertLpTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_greaterThanMaxSlippage_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenMaxSlippage(0) // 0% + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) + { + // Expect revert + vm.expectRevert("Price slippage check"); + + _performCallback(); + } + + function test_givenInsufficientBaseTokenBalance_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised - 1) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + // Expect revert + bytes memory err = abi.encodeWithSelector( + BaseDirectToLiquidity.Callback_InsufficientBalance.selector, + address(_baseToken), + _SELLER, + _baseTokensToDeposit, + _baseTokensToDeposit - 1 + ); + vm.expectRevert(err); + + _performCallback(); + } + + function test_givenInsufficientBaseTokenAllowance_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised - 1) + { + // Expect revert + vm.expectRevert("TRANSFER_FROM_FAILED"); + + _performCallback(); + } + + function test_success() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performCallback(); + + _assertPoolState(_sqrtPriceX96); + _assertLpTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_success_multiple() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_NOT_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_NOT_SELLER, _dtlAddress, _capacityUtilised) + { + // Create second lot + uint96 lotIdTwo = _createLot(_NOT_SELLER); + + _performCallback(lotIdTwo); + + _assertLpTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_whenRecipientIsNotSeller() + public + givenCallbackIsCreated + whenRecipientIsNotSeller + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performCallback(); + + _assertPoolState(_sqrtPriceX96); + _assertLpTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } +} From 470d9822f657538485175b646cd7dffb58755f2f Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 26 Jul 2024 14:43:47 +0400 Subject: [PATCH 31/47] Fix failing tests --- .../liquidity/RamsesV2/RamsesV2DTLTest.sol | 8 ++- .../liquidity/RamsesV2/onCreate.t.sol | 19 +------ .../liquidity/RamsesV2/onSettle.t.sol | 52 +++++++++---------- 3 files changed, 29 insertions(+), 50 deletions(-) diff --git a/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol b/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol index bd64f8d0..48551dd0 100644 --- a/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol +++ b/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol @@ -6,7 +6,6 @@ import {Test} from "@forge-std-1.9.1/Test.sol"; import {Callbacks} from "@axis-core-1.0.0/lib/Callbacks.sol"; import {Permit2User} from "@axis-core-1.0.0-test/lib/permit2/Permit2User.sol"; import {WithSalts} from "../../../lib/WithSalts.sol"; -import {console2} from "@forge-std-1.9.1/console2.sol"; import {TestConstants} from "../../../Constants.sol"; // Mocks @@ -20,7 +19,6 @@ import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL // Ramses import {RamsesV2DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ramses/RamsesV2DTL.sol"; import {IRamsesV2Factory} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV2Factory.sol"; -import {IRamsesV2Pool} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV2Pool.sol"; import {IRamsesV2PositionManager} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV2PositionManager.sol"; @@ -56,10 +54,10 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, MockERC20 internal _baseToken; // Inputs - RamsesV2DirectToLiquidity.RamsesV2OnCreateParams internal _ramsesCreateParams = RamsesV2DirectToLiquidity - .RamsesV2OnCreateParams({ + RamsesV2DirectToLiquidity.RamsesV2OnCreateParams internal _ramsesCreateParams = + RamsesV2DirectToLiquidity.RamsesV2OnCreateParams({ poolFee: 500, - maxSlippage: 0, + maxSlippage: 1, // 0.01%, to handle rounding errors veRamTokenId: 0 }); BaseDirectToLiquidity.OnCreateParams internal _dtlCreateParams = BaseDirectToLiquidity diff --git a/test/callbacks/liquidity/RamsesV2/onCreate.t.sol b/test/callbacks/liquidity/RamsesV2/onCreate.t.sol index 44ed8c39..0ecdaad9 100644 --- a/test/callbacks/liquidity/RamsesV2/onCreate.t.sol +++ b/test/callbacks/liquidity/RamsesV2/onCreate.t.sol @@ -163,22 +163,6 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { _performCallback(); } - function test_givenRamsesV2PoolAlreadyExists_reverts() - public - givenCallbackIsCreated - givenPoolFee(500) - { - // Create the pool - _factory.createPool(address(_baseToken), address(_quoteToken), 500); - - // Expect revert - bytes memory err = - abi.encodeWithSelector(BaseDirectToLiquidity.Callback_Params_PoolExists.selector); - vm.expectRevert(err); - - _performCallback(); - } - function test_whenRecipientIsZeroAddress_reverts() public givenCallbackIsCreated { _dtlCreateParams.recipient = address(0); @@ -225,8 +209,7 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); RamsesV2DirectToLiquidity.RamsesV2OnCreateParams memory _ramsesCreateParams = abi.decode( - _dtlCreateParams.implParams, - (RamsesV2DirectToLiquidity.RamsesV2OnCreateParams) + _dtlCreateParams.implParams, (RamsesV2DirectToLiquidity.RamsesV2OnCreateParams) ); assertEq(_ramsesCreateParams.poolFee, _ramsesCreateParams.poolFee, "poolFee"); assertEq(_ramsesCreateParams.maxSlippage, _ramsesCreateParams.maxSlippage, "maxSlippage"); diff --git a/test/callbacks/liquidity/RamsesV2/onSettle.t.sol b/test/callbacks/liquidity/RamsesV2/onSettle.t.sol index 30db7f52..1229ded3 100644 --- a/test/callbacks/liquidity/RamsesV2/onSettle.t.sol +++ b/test/callbacks/liquidity/RamsesV2/onSettle.t.sol @@ -5,21 +5,15 @@ import {RamsesV2DirectToLiquidityTest} from "./RamsesV2DTLTest.sol"; // Libraries import {FixedPointMathLib} from "@solmate-6.7.0/utils/FixedPointMathLib.sol"; -import {ERC20} from "@solmate-6.7.0/tokens/ERC20.sol"; -// Uniswap -import {IUniswapV3Pool} from - "@uniswap-v3-core-1.0.1-solc-0.8-simulate/interfaces/IUniswapV3Pool.sol"; +// Ramses +import {IRamsesV2Pool} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV2Pool.sol"; import {SqrtPriceMath} from "../../../../src/lib/uniswap-v3/SqrtPriceMath.sol"; -// G-UNI -import {GUniPool} from "@g-uni-v1-core-0.9.9/GUniPool.sol"; - // AuctionHouse import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; -import {RamsesV2DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ramses/RamsesV2DTL.sol"; -contract RamsesV2OnSettleForkTest is RamsesV2DirectToLiquidityTest { +contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { uint96 internal constant _PROCEEDS = 20e18; uint96 internal constant _REFUND = 0; @@ -38,8 +32,12 @@ contract RamsesV2OnSettleForkTest is RamsesV2DirectToLiquidityTest { // ========== Internal functions ========== // + function _getTokenId(uint96 lotId_) internal view returns (uint256) { + return _dtl.lotIdToTokenId(lotId_); + } + function _getTokenId() internal view returns (uint256) { - return _dtl.lotIdToTokenId(_lotId); + return _getTokenId(_lotId); } // ========== Assertions ========== // @@ -48,14 +46,18 @@ contract RamsesV2OnSettleForkTest is RamsesV2DirectToLiquidityTest { // Get the pool address pool = _getPool(); - (uint160 sqrtPriceX96,,,,,,) = IUniswapV3Pool(pool).slot0(); + (uint160 sqrtPriceX96,,,,,,) = IRamsesV2Pool(pool).slot0(); assertEq(sqrtPriceX96, sqrtPriceX96_, "pool sqrt price"); } - function _assertLpTokenBalance() internal view { - uint256 tokenId = _getTokenId(); + function _assertLpTokenBalance(uint96 lotId_, address recipient_) internal view { + uint256 tokenId = _getTokenId(lotId_); + + assertEq(_positionManager.ownerOf(tokenId), recipient_, "LP token owner"); + } - assertEq(_positionManager.ownerOf(tokenId), _dtlCreateParams.recipient, "LP token owner"); + function _assertLpTokenBalance() internal view { + _assertLpTokenBalance(_lotId, _dtlCreateParams.recipient); } function _assertQuoteTokenBalance() internal view { @@ -84,12 +86,7 @@ contract RamsesV2OnSettleForkTest is RamsesV2DirectToLiquidityTest { function _performCallback(uint96 lotId_) internal { vm.prank(address(_auctionHouse)); - _dtl.onSettle( - lotId_, - _proceeds, - _refund, - abi.encode("") - ); + _dtl.onSettle(lotId_, _proceeds, _refund, abi.encode("")); } function _performCallback() internal { @@ -105,7 +102,7 @@ contract RamsesV2OnSettleForkTest is RamsesV2DirectToLiquidityTest { } function _initializePool(address pool_, uint160 sqrtPriceX96_) internal { - IUniswapV3Pool(pool_).initialize(sqrtPriceX96_); + IRamsesV2Pool(pool_).initialize(sqrtPriceX96_); } modifier givenPoolIsCreated() { @@ -245,13 +242,13 @@ contract RamsesV2OnSettleForkTest is RamsesV2DirectToLiquidityTest { function test_givenPoolIsCreatedAndInitialized_givenMaxSlippage() public givenCallbackIsCreated + givenMaxSlippage(8100) // 81% givenOnCreate setCallbackParameters(_PROCEEDS, _REFUND) givenPoolIsCreatedAndInitialized(_SQRT_PRICE_X96_OVERRIDE) givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) - givenMaxSlippage(8100) // 81% { _performCallback(); @@ -361,11 +358,11 @@ contract RamsesV2OnSettleForkTest is RamsesV2DirectToLiquidityTest { function test_givenPoolHasDepositWithLowerPrice() public givenCallbackIsCreated + givenMaxSlippage(5100) // 51% givenOnCreate setCallbackParameters(_PROCEEDS, _REFUND) givenPoolHasDepositLowerPrice givenPoolIsCreatedAndInitialized(_sqrtPriceX96) - givenMaxSlippage(5100) // 51% givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) @@ -382,11 +379,11 @@ contract RamsesV2OnSettleForkTest is RamsesV2DirectToLiquidityTest { function test_givenPoolHasDepositWithHigherPrice() public givenCallbackIsCreated + givenMaxSlippage(5100) // 51% givenOnCreate setCallbackParameters(_PROCEEDS, _REFUND) givenPoolHasDepositHigherPrice givenPoolIsCreatedAndInitialized(_sqrtPriceX96) - givenMaxSlippage(5100) // 51% givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) @@ -403,9 +400,9 @@ contract RamsesV2OnSettleForkTest is RamsesV2DirectToLiquidityTest { function test_lessThanMaxSlippage() public givenCallbackIsCreated + givenMaxSlippage(1) // 0.01% givenOnCreate setCallbackParameters(_PROCEEDS, _REFUND) - givenMaxSlippage(1) // 0.01% givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) @@ -422,9 +419,9 @@ contract RamsesV2OnSettleForkTest is RamsesV2DirectToLiquidityTest { function test_greaterThanMaxSlippage_reverts() public givenCallbackIsCreated + givenMaxSlippage(0) // 0% givenOnCreate setCallbackParameters(_PROCEEDS, _REFUND) - givenMaxSlippage(0) // 0% givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) @@ -498,13 +495,14 @@ contract RamsesV2OnSettleForkTest is RamsesV2DirectToLiquidityTest { givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) givenAddressHasBaseTokenBalance(_NOT_SELLER, _capacityUtilised) givenAddressHasBaseTokenAllowance(_NOT_SELLER, _dtlAddress, _capacityUtilised) + whenRecipientIsNotSeller // Affects the second lot { // Create second lot uint96 lotIdTwo = _createLot(_NOT_SELLER); _performCallback(lotIdTwo); - _assertLpTokenBalance(); + _assertLpTokenBalance(lotIdTwo, _NOT_SELLER); _assertQuoteTokenBalance(); _assertBaseTokenBalance(); _assertApprovals(); From bfb113bb84c50873051e9f6ab46f027136207f3a Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 26 Jul 2024 15:55:14 +0400 Subject: [PATCH 32/47] Complete remaining tests --- script/salts/salts.json | 2 +- src/callbacks/liquidity/BaseDTL.sol | 14 ++ .../liquidity/Ramses/RamsesV2DTL.sol | 12 +- src/callbacks/liquidity/Ramses/lib/IRAM.sol | 22 ++ .../liquidity/RamsesV2/RamsesV2DTLTest.sol | 100 ++++++++- .../liquidity/RamsesV2/onCancel.t.sol | 70 ++++++- .../liquidity/RamsesV2/onCreate.t.sol | 191 ++++++++++++++---- .../liquidity/RamsesV2/onSettle.t.sol | 172 ++++++++++++---- 8 files changed, 481 insertions(+), 102 deletions(-) create mode 100644 src/callbacks/liquidity/Ramses/lib/IRAM.sol diff --git a/script/salts/salts.json b/script/salts/salts.json index e5ca2e4a..0a7f2bd8 100644 --- a/script/salts/salts.json +++ b/script/salts/salts.json @@ -101,7 +101,7 @@ "0x53bbd9c8a867624e56d19bf63d184f39112c1ad047c11555758b6e6d5e153499": "0x03349021186521ece9eaa54f521a1cb4c378198c47cbca9020936993c02a23c0" }, "Test_RamsesV2DirectToLiquidity": { - "0x0b17a4c7f488dbaa202bc1e5b4375746b93e5627e3a55fedd3359e3c8c343f6f": "0x3208e391ac10f52ded48bb10d3d1de808c85a00e1d99dbb64f83cb7f9beb8d98" + "0x1ec2032d7fbf6eebdd3b481e4890aa60ead6d427e88553e0d767282becd9015e": "0x325be95c1fec7ab9ab52d01d65beae2f07ad2fe20a67314e0ed1e3e3903b54b0" }, "Test_TokenAllowlist": { "0x3cf6404d9502da98e1ed6560fce585a0a87f56f598397915ef5a6207eb9b439f": "0xd8172a08ee7909a096d99b3ef34fe0d4212c85e77206518e9b5e1640926dde85", diff --git a/src/callbacks/liquidity/BaseDTL.sol b/src/callbacks/liquidity/BaseDTL.sol index 08a6ea36..f015b2b7 100644 --- a/src/callbacks/liquidity/BaseDTL.sol +++ b/src/callbacks/liquidity/BaseDTL.sol @@ -45,6 +45,9 @@ abstract contract BaseDirectToLiquidity is BaseCallback { /// @notice The auction lot has already been completed error Callback_AlreadyComplete(); + /// @notice Indicates that the feature is not supported by the contract + error Callback_NotSupported(); + // ========== STRUCTS ========== // /// @notice Configuration for the DTL callback @@ -160,6 +163,11 @@ abstract contract BaseDirectToLiquidity is BaseCallback { // If vesting is enabled if (params.vestingStart != 0 || params.vestingExpiry != 0) { + // Check if linear vesting is supported + if (!_isLinearVestingSupported()) { + revert Callback_NotSupported(); + } + // Get the linear vesting module (or revert) linearVestingModule = LinearVesting(_getLatestLinearVestingModule()); @@ -513,4 +521,10 @@ abstract contract BaseDirectToLiquidity is BaseCallback { // The LinearVesting module will use all of `poolTokenQuantity`, so there is no need to clean up dangling approvals } + + /// @notice Indicates if linear vesting is supported by the callback + /// @dev Implementing contracts can opt to override this in order to disable linear vesting + function _isLinearVestingSupported() internal pure virtual returns (bool) { + return true; + } } diff --git a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol index f1cb8129..f7e5f5d9 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol +++ b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol @@ -32,8 +32,12 @@ import {SqrtPriceMath} from "../../../lib/uniswap-v3/SqrtPriceMath.sol"; contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { // ========== ERRORS ========== // + /// @notice The specified pool fee is not enabled in the Ramses V2 Factory error Callback_Params_PoolFeeNotEnabled(); + /// @notice The specified veRAM token ID is not approved for use by the callback + error Callback_Params_VeRamTokenIdNotApproved(); + // ========== STRUCTS ========== // /// @notice Parameters for the onCreate callback @@ -119,7 +123,7 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { address(this), params.veRamTokenId ) ) { - revert Callback_InvalidParams(); + revert Callback_Params_VeRamTokenIdNotApproved(); } } @@ -187,6 +191,12 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { // Nothing to do } + /// @inheritdoc BaseDirectToLiquidity + /// @dev This implementation disables linear vesting + function _isLinearVestingSupported() internal pure virtual override returns (bool) { + return false; + } + // ========== INTERNAL FUNCTIONS ========== // /// @dev Copied from UniswapV3's PoolInitializer (which is GPL >= 2) diff --git a/src/callbacks/liquidity/Ramses/lib/IRAM.sol b/src/callbacks/liquidity/Ramses/lib/IRAM.sol new file mode 100644 index 00000000..3804b1db --- /dev/null +++ b/src/callbacks/liquidity/Ramses/lib/IRAM.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.4; + +interface IRAM { + event Approval(address indexed owner, address indexed spender, uint256 value); + event Initialized(uint8 version); + event Transfer(address indexed from, address indexed to, uint256 value); + + function allowance(address, address) external view returns (uint256); + function approve(address _spender, uint256 _value) external returns (bool); + function balanceOf(address) external view returns (uint256); + function decimals() external view returns (uint8); + function initialize(address _minter) external; + function mint(address account, uint256 amount) external returns (bool); + function minter() external view returns (address); + function name() external view returns (string memory); + function setMinter(address _minter) external; + function symbol() external view returns (string memory); + function totalSupply() external view returns (uint256); + function transfer(address _to, uint256 _value) external returns (bool); + function transferFrom(address _from, address _to, uint256 _value) external returns (bool); +} diff --git a/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol b/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol index 48551dd0..5faea69c 100644 --- a/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol +++ b/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol @@ -21,6 +21,8 @@ import {RamsesV2DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ram import {IRamsesV2Factory} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV2Factory.sol"; import {IRamsesV2PositionManager} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV2PositionManager.sol"; +import {IVotingEscrow} from "../../../../src/callbacks/liquidity/Ramses/lib/IVotingEscrow.sol"; +import {IRAM} from "../../../../src/callbacks/liquidity/Ramses/lib/IRAM.sol"; // Axis core import {keycodeFromVeecode, toKeycode} from "@axis-core-1.0.0/modules/Keycode.sol"; @@ -48,11 +50,15 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, address internal _dtlAddress; IRamsesV2Factory internal _factory; IRamsesV2PositionManager internal _positionManager; + IRAM internal _ram; MockBatchAuctionModule internal _batchAuctionModule; MockERC20 internal _quoteToken; MockERC20 internal _baseToken; + uint96 internal _proceeds; + uint96 internal _refund; + // Inputs RamsesV2DirectToLiquidity.RamsesV2OnCreateParams internal _ramsesCreateParams = RamsesV2DirectToLiquidity.RamsesV2OnCreateParams({ @@ -87,6 +93,7 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, _factory = IRamsesV2Factory(_RAMSES_V2_FACTORY); _positionManager = IRamsesV2PositionManager(payable(_RAMSES_V2_POSITION_MANAGER)); + _ram = IRAM(IVotingEscrow(_positionManager.veRam()).token()); _batchAuctionModule = new MockBatchAuctionModule(address(_auctionHouse)); @@ -180,6 +187,23 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, _; } + function _performOnCreate(address seller_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onCreate( + _lotId, + seller_, + address(_baseToken), + address(_quoteToken), + _LOT_CAPACITY, + false, + abi.encode(_dtlCreateParams) + ); + } + + function _performOnCreate() internal { + _performOnCreate(_SELLER); + } + function _performOnCurate(uint96 curatorPayout_) internal { vm.prank(address(_auctionHouse)); _dtl.onCurate(_lotId, curatorPayout_, false, abi.encode("")); @@ -190,6 +214,24 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, _; } + function _performOnCancel(uint96 lotId_, uint256 refundAmount_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onCancel(lotId_, refundAmount_, false, abi.encode("")); + } + + function _performOnCancel() internal { + _performOnCancel(_lotId, 0); + } + + function _performOnSettle(uint96 lotId_) internal { + vm.prank(address(_auctionHouse)); + _dtl.onSettle(lotId_, _proceeds, _refund, abi.encode("")); + } + + function _performOnSettle() internal { + _performOnSettle(_lotId); + } + modifier givenProceedsUtilisationPercent(uint24 percent_) { _dtlCreateParams.proceedsUtilisationPercent = percent_; _; @@ -213,13 +255,13 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, _; } - function _setVmRamTokenId(uint24 vmRamTokenId_) internal { - _ramsesCreateParams.veRamTokenId = vmRamTokenId_; + function _setVeRamTokenId(uint256 veRamTokenId_) internal { + _ramsesCreateParams.veRamTokenId = veRamTokenId_; _dtlCreateParams.implParams = abi.encode(_ramsesCreateParams); } - modifier givenVmRamTokenId(uint24 vmRamTokenId_) { - _setVmRamTokenId(vmRamTokenId_); + modifier givenVeRamTokenId() { + _createVeRamDeposit(); _; } @@ -238,6 +280,56 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, _; } + function _createPool() internal returns (address) { + (address token0, address token1) = address(_baseToken) < address(_quoteToken) + ? (address(_baseToken), address(_quoteToken)) + : (address(_quoteToken), address(_baseToken)); + + return _factory.createPool(token0, token1, _ramsesCreateParams.poolFee); + } + + modifier givenPoolIsCreated() { + _createPool(); + _; + } + + function _createVeRamDeposit() internal { + // Mint RAM + vm.prank(address(_ram.minter())); + _ram.mint(_SELLER, 1e18); + + // Approve spending + address veRam = address(_positionManager.veRam()); + vm.prank(_SELLER); + _ram.approve(veRam, 1e18); + + // Deposit into the voting escrow + vm.startPrank(_SELLER); + uint256 tokenId = IVotingEscrow(veRam).create_lock_for( + 1e18, 24 hours * 365, _SELLER + ); + vm.stopPrank(); + + // Update the callback + _setVeRamTokenId(tokenId); + } + + function _mockVeRamTokenIdApproved(uint256 veRamTokenId_, bool approved_) internal { + // TODO this could be shifted to an actual approval, but I couldn't figure out how + vm.mockCall( + address(_positionManager.veRam()), + abi.encodeWithSelector( + IVotingEscrow.isApprovedOrOwner.selector, address(_dtl), veRamTokenId_ + ), + abi.encode(approved_) + ); + } + + modifier givenVeRamTokenIdApproval(bool approved_) { + _mockVeRamTokenIdApproved(_ramsesCreateParams.veRamTokenId, approved_); + _; + } + // ========== FUNCTIONS ========== // function _getDTLConfiguration(uint96 lotId_) diff --git a/test/callbacks/liquidity/RamsesV2/onCancel.t.sol b/test/callbacks/liquidity/RamsesV2/onCancel.t.sol index eaf0bd0e..facdc2e8 100644 --- a/test/callbacks/liquidity/RamsesV2/onCancel.t.sol +++ b/test/callbacks/liquidity/RamsesV2/onCancel.t.sol @@ -11,22 +11,21 @@ contract RamsesV2DTLOnCancelForkTest is RamsesV2DirectToLiquidityTest { // ============ Modifiers ============ // - function _performCallback(uint96 lotId_) internal { - vm.prank(address(_auctionHouse)); - _dtl.onCancel(lotId_, _REFUND_AMOUNT, false, abi.encode("")); + function _performOnCancel(uint96 lotId_) internal { + _performOnCancel(lotId_, _REFUND_AMOUNT); } // ============ Tests ============ // // [X] given the onCancel callback has already been called // [X] when onSettle is called - // [ ] it reverts + // [X] it reverts // [X] when onCancel is called - // [ ] it reverts + // [X] it reverts // [X] when onCurate is called - // [ ] it reverts + // [X] it reverts // [X] when onCreate is called - // [ ] it reverts + // [X] it reverts // [X] when the lot has not been registered // [X] it reverts // [X] when multiple lots are created @@ -39,12 +38,12 @@ contract RamsesV2DTLOnCancelForkTest is RamsesV2DirectToLiquidityTest { vm.expectRevert(err); // Call the function - _performCallback(_lotId); + _performOnCancel(_lotId); } function test_success() public givenCallbackIsCreated givenOnCreate { // Call the function - _performCallback(_lotId); + _performOnCancel(_lotId); // Check the values BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); @@ -61,7 +60,7 @@ contract RamsesV2DTLOnCancelForkTest is RamsesV2DirectToLiquidityTest { // Create a second lot and cancel it uint96 lotIdTwo = _createLot(_NOT_SELLER); - _performCallback(lotIdTwo); + _performOnCancel(lotIdTwo); // Check the values BaseDirectToLiquidity.DTLConfiguration memory configurationOne = @@ -77,4 +76,55 @@ contract RamsesV2DTLOnCancelForkTest is RamsesV2DirectToLiquidityTest { assertEq(_baseToken.balanceOf(_SELLER), 0, "seller base token balance"); assertEq(_baseToken.balanceOf(_NOT_SELLER), 0, "not seller base token balance"); } + + function test_auctionCancelled_onCreate_reverts() public givenCallbackIsCreated givenOnCreate { + // Call the function + _performOnCancel(_lotId); + + // Expect revert + // BaseCallback determines if the lot has already been registered + bytes memory err = abi.encodeWithSelector(BaseCallback.Callback_InvalidParams.selector); + vm.expectRevert(err); + + _performOnCreate(); + } + + function test_auctionCancelled_onCurate_reverts() public givenCallbackIsCreated givenOnCreate { + // Call the function + _performOnCancel(_lotId); + + // Expect revert + // BaseDirectToLiquidity determines if the lot has already been completed + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_AlreadyComplete.selector); + vm.expectRevert(err); + + _performOnCurate(0); + } + + function test_auctionCancelled_onCancel_reverts() public givenCallbackIsCreated givenOnCreate { + // Call the function + _performOnCancel(_lotId); + + // Expect revert + // BaseDirectToLiquidity determines if the lot has already been completed + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_AlreadyComplete.selector); + vm.expectRevert(err); + + _performOnCancel(); + } + + function test_auctionCancelled_onSettle_reverts() public givenCallbackIsCreated givenOnCreate { + // Call the function + _performOnCancel(_lotId); + + // Expect revert + // BaseDirectToLiquidity determines if the lot has already been completed + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_AlreadyComplete.selector); + vm.expectRevert(err); + + _performOnSettle(); + } } diff --git a/test/callbacks/liquidity/RamsesV2/onCreate.t.sol b/test/callbacks/liquidity/RamsesV2/onCreate.t.sol index 0ecdaad9..55e381b0 100644 --- a/test/callbacks/liquidity/RamsesV2/onCreate.t.sol +++ b/test/callbacks/liquidity/RamsesV2/onCreate.t.sol @@ -10,23 +10,6 @@ import {RamsesV2DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ram contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { // ============ Modifiers ============ // - function _performCallback(address seller_) internal { - vm.prank(address(_auctionHouse)); - _dtl.onCreate( - _lotId, - seller_, - address(_baseToken), - address(_quoteToken), - _LOT_CAPACITY, - false, - abi.encode(_dtlCreateParams) - ); - } - - function _performCallback() internal { - _performCallback(_SELLER); - } - // ============ Assertions ============ // function _expectTransferFrom() internal { @@ -61,24 +44,28 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { // [X] it reverts // [X] when the proceeds utilisation is greater than 100% // [X] it reverts - // [ ] when the implParams is not the correct length - // [ ] it reverts - // [ ] when the max slippage is between 0 and 100% - // [ ] it succeeds - // [ ] when the max slippage is greater than 100% - // [ ] it reverts + // [X] when the implParams is not the correct length + // [X] it reverts + // [X] when the max slippage is between 0 and 100% + // [X] it succeeds + // [X] when the max slippage is greater than 100% + // [X] it reverts // [X] given the pool fee is not enabled // [X] it reverts - // [ ] given ramses v2 pool already exists - // [ ] it succeeds - // [ ] when the vesting start timestamp is set - // [ ] it reverts - // [ ] when the vesting expiry timestamp is set - // [ ] it reverts + // [X] given ramses v2 pool already exists + // [X] it succeeds + // [X] when the vesting start timestamp is set + // [X] it reverts + // [X] when the vesting expiry timestamp is set + // [X] it reverts // [X] when the recipient is the zero address // [X] it reverts // [X] when the recipient is not the seller // [X] it records the recipient + // [X] when the veRamTokenId is set + // [X] given the DTL contract is approved to use the veRamTokenId + // [X] it succeeds + // [X] it reverts // [X] when multiple lots are created // [X] it registers each lot // [X] it registers the lot @@ -114,11 +101,11 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { } function test_whenLotHasAlreadyBeenRegistered_reverts() public givenCallbackIsCreated { - _performCallback(); + _performOnCreate(); _expectInvalidParams(); - _performCallback(); + _performOnCreate(); } function test_whenProceedsUtilisationIs0_reverts() @@ -132,7 +119,7 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { ); vm.expectRevert(err); - _performCallback(); + _performOnCreate(); } function test_whenProceedsUtilisationIsGreaterThan100Percent_reverts() @@ -146,7 +133,82 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { ); vm.expectRevert(err); - _performCallback(); + _performOnCreate(); + } + + function test_paramsIncorrectLength_reverts() public givenCallbackIsCreated { + // Set the implParams to an incorrect length + _dtlCreateParams.implParams = abi.encode(uint256(10)); + + // Expect revert + bytes memory err = abi.encodeWithSelector(BaseCallback.Callback_InvalidParams.selector); + vm.expectRevert(err); + + _performOnCreate(); + } + + function test_maxSlippageGreaterThan100Percent_reverts(uint24 maxSlippage_) + public + givenCallbackIsCreated + { + uint24 maxSlippage = uint24(bound(maxSlippage_, 100e2 + 1, type(uint24).max)); + _setMaxSlippage(maxSlippage); + + // Expect revert + bytes memory err = abi.encodeWithSelector( + BaseDirectToLiquidity.Callback_Params_PercentOutOfBounds.selector, maxSlippage, 0, 100e2 + ); + vm.expectRevert(err); + + _performOnCreate(); + } + + function test_givenPoolExists() public givenCallbackIsCreated givenPoolIsCreated { + _performOnCreate(); + + // Assert values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.active, true, "active"); + } + + function test_givenVestingStartParameterIsSet() + public + givenCallbackIsCreated + givenVestingStart(_initialTimestamp + 1) + { + // Expect revert + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_NotSupported.selector); + vm.expectRevert(err); + + _performOnCreate(); + } + + function test_givenVestingExpiryParameterIsSet() + public + givenCallbackIsCreated + givenVestingExpiry(_initialTimestamp + 1) + { + // Expect revert + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_NotSupported.selector); + vm.expectRevert(err); + + _performOnCreate(); + } + + function test_givenVestingStartAndExpiryParameterIsSet() + public + givenCallbackIsCreated + givenVestingStart(_initialTimestamp + 1) + givenVestingExpiry(_initialTimestamp + 2) + { + // Expect revert + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_NotSupported.selector); + vm.expectRevert(err); + + _performOnCreate(); } function test_givenPoolFeeIsNotEnabled_reverts() @@ -160,7 +222,7 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { ); vm.expectRevert(err); - _performCallback(); + _performOnCreate(); } function test_whenRecipientIsZeroAddress_reverts() public givenCallbackIsCreated { @@ -171,7 +233,7 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { abi.encodeWithSelector(BaseDirectToLiquidity.Callback_Params_InvalidAddress.selector); vm.expectRevert(err); - _performCallback(); + _performOnCreate(); } function test_whenRecipientIsNotSeller_succeeds() @@ -179,7 +241,7 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { givenCallbackIsCreated whenRecipientIsNotSeller { - _performCallback(); + _performOnCreate(); // Assert values BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); @@ -190,7 +252,7 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { } function test_succeeds() public givenCallbackIsCreated { - _performCallback(); + _performOnCreate(); // Assert values BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); @@ -208,9 +270,8 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { assertEq(configuration.active, true, "active"); assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); - RamsesV2DirectToLiquidity.RamsesV2OnCreateParams memory _ramsesCreateParams = abi.decode( - _dtlCreateParams.implParams, (RamsesV2DirectToLiquidity.RamsesV2OnCreateParams) - ); + RamsesV2DirectToLiquidity.RamsesV2OnCreateParams memory _ramsesCreateParams = + abi.decode(configuration.implParams, (RamsesV2DirectToLiquidity.RamsesV2OnCreateParams)); assertEq(_ramsesCreateParams.poolFee, _ramsesCreateParams.poolFee, "poolFee"); assertEq(_ramsesCreateParams.maxSlippage, _ramsesCreateParams.maxSlippage, "maxSlippage"); assertEq(_ramsesCreateParams.veRamTokenId, _ramsesCreateParams.veRamTokenId, "veRamTokenId"); @@ -221,12 +282,12 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { function test_succeeds_multiple() public givenCallbackIsCreated { // Lot one - _performCallback(); + _performOnCreate(); // Lot two _dtlCreateParams.recipient = _NOT_SELLER; _lotId = 2; - _performCallback(_NOT_SELLER); + _performOnCreate(_NOT_SELLER); // Assert values BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); @@ -247,4 +308,50 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { // Assert balances _assertBaseTokenBalances(); } + + function test_maxSlippage_fuzz(uint24 maxSlippage_) public givenCallbackIsCreated { + uint24 maxSlippage = uint24(bound(maxSlippage_, 0, 100e2)); + _setMaxSlippage(maxSlippage); + + _performOnCreate(); + + // Assert values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); + + RamsesV2DirectToLiquidity.RamsesV2OnCreateParams memory ramsesCreateParams = + abi.decode(configuration.implParams, (RamsesV2DirectToLiquidity.RamsesV2OnCreateParams)); + assertEq(ramsesCreateParams.maxSlippage, maxSlippage, "maxSlippage"); + } + + function test_veRamTokenId() + public + givenCallbackIsCreated + givenVeRamTokenId + givenVeRamTokenIdApproval(true) + { + _performOnCreate(); + + // Assert values + BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + + RamsesV2DirectToLiquidity.RamsesV2OnCreateParams memory ramsesCreateParams = + abi.decode(configuration.implParams, (RamsesV2DirectToLiquidity.RamsesV2OnCreateParams)); + assertEq(ramsesCreateParams.veRamTokenId, 1000, "veRamTokenId"); + } + + function test_veRamTokenId_notApproved_reverts() + public + givenCallbackIsCreated + givenVeRamTokenId + givenVeRamTokenIdApproval(false) + { + // Expect revert + bytes memory err = abi.encodeWithSelector( + RamsesV2DirectToLiquidity.Callback_Params_VeRamTokenIdNotApproved.selector + ); + vm.expectRevert(err); + + _performOnCreate(); + } } diff --git a/test/callbacks/liquidity/RamsesV2/onSettle.t.sol b/test/callbacks/liquidity/RamsesV2/onSettle.t.sol index 1229ded3..e274d208 100644 --- a/test/callbacks/liquidity/RamsesV2/onSettle.t.sol +++ b/test/callbacks/liquidity/RamsesV2/onSettle.t.sol @@ -11,14 +11,13 @@ import {IRamsesV2Pool} from "../../../../src/callbacks/liquidity/Ramses/lib/IRam import {SqrtPriceMath} from "../../../../src/lib/uniswap-v3/SqrtPriceMath.sol"; // AuctionHouse +import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { uint96 internal constant _PROCEEDS = 20e18; uint96 internal constant _REFUND = 0; - uint96 internal _proceeds; - uint96 internal _refund; uint96 internal _capacityUtilised; uint96 internal _quoteTokensToDeposit; uint96 internal _baseTokensToDeposit; @@ -84,32 +83,10 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { // ========== Modifiers ========== // - function _performCallback(uint96 lotId_) internal { - vm.prank(address(_auctionHouse)); - _dtl.onSettle(lotId_, _proceeds, _refund, abi.encode("")); - } - - function _performCallback() internal { - _performCallback(_lotId); - } - - function _createPool() internal returns (address) { - (address token0, address token1) = address(_baseToken) < address(_quoteToken) - ? (address(_baseToken), address(_quoteToken)) - : (address(_quoteToken), address(_baseToken)); - - return _factory.createPool(token0, token1, _ramsesCreateParams.poolFee); - } - function _initializePool(address pool_, uint160 sqrtPriceX96_) internal { IRamsesV2Pool(pool_).initialize(sqrtPriceX96_); } - modifier givenPoolIsCreated() { - _createPool(); - _; - } - modifier givenPoolIsCreatedAndInitialized(uint160 sqrtPriceX96_) { address pool = _createPool(); _initializePool(pool, sqrtPriceX96_); @@ -191,13 +168,13 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { // [X] given the onSettle callback has already been called // [X] when onSettle is called - // [ ] it reverts + // [X] it reverts // [X] when onCancel is called - // [ ] it reverts + // [X] it reverts // [X] when onCreate is called - // [ ] it reverts + // [X] it reverts // [X] when onCurate is called - // [ ] it reverts + // [X] it reverts // [X] given the pool is created // [X] it initializes the pool // [X] given the pool is created and initialized @@ -218,6 +195,8 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { // [X] it mints the LP token to the recipient // [X] when multiple lots are created // [X] it performs actions on the correct pool + // [X] given the veRamTokenId is set + // [X] it succeeds // [X] it creates and initializes the pool, mints the position, transfers the LP token to the seller and transfers any excess back to the seller function test_givenPoolIsCreated() @@ -230,7 +209,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) { - _performCallback(); + _performOnSettle(); _assertPoolState(_sqrtPriceX96); _assertLpTokenBalance(); @@ -250,7 +229,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) { - _performCallback(); + _performOnSettle(); _assertPoolState(_SQRT_PRICE_X96_OVERRIDE); _assertLpTokenBalance(); @@ -272,7 +251,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { // Expect revert vm.expectRevert("Price slippage check"); - _performCallback(); + _performOnSettle(); } function test_givenProceedsUtilisationPercent_fuzz(uint24 percent_) @@ -285,7 +264,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) { - _performCallback(); + _performOnSettle(); _assertPoolState(_sqrtPriceX96); _assertLpTokenBalance(); @@ -304,7 +283,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) { - _performCallback(); + _performOnSettle(); _assertPoolState(_sqrtPriceX96); _assertLpTokenBalance(); @@ -327,7 +306,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { - _performCallback(); + _performOnSettle(); _assertPoolState(_sqrtPriceX96); _assertLpTokenBalance(); @@ -346,7 +325,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { - _performCallback(); + _performOnSettle(); _assertPoolState(_sqrtPriceX96); _assertLpTokenBalance(); @@ -367,7 +346,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { - _performCallback(); + _performOnSettle(); _assertPoolState(_sqrtPriceX96); _assertLpTokenBalance(); @@ -388,7 +367,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { - _performCallback(); + _performOnSettle(); _assertPoolState(_sqrtPriceX96); _assertLpTokenBalance(); @@ -407,7 +386,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { - _performCallback(); + _performOnSettle(); _assertPoolState(_sqrtPriceX96); _assertLpTokenBalance(); @@ -422,6 +401,8 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { givenMaxSlippage(0) // 0% givenOnCreate setCallbackParameters(_PROCEEDS, _REFUND) + givenPoolHasDepositHigherPrice + givenPoolIsCreatedAndInitialized(_sqrtPriceX96) givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) givenAddressHasBaseTokenBalance(_SELLER, _baseTokensToDeposit) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) @@ -429,7 +410,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { // Expect revert vm.expectRevert("Price slippage check"); - _performCallback(); + _performOnSettle(); } function test_givenInsufficientBaseTokenBalance_reverts() @@ -451,7 +432,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { ); vm.expectRevert(err); - _performCallback(); + _performOnSettle(); } function test_givenInsufficientBaseTokenAllowance_reverts() @@ -466,7 +447,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { // Expect revert vm.expectRevert("TRANSFER_FROM_FAILED"); - _performCallback(); + _performOnSettle(); } function test_success() @@ -478,7 +459,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) { - _performCallback(); + _performOnSettle(); _assertPoolState(_sqrtPriceX96); _assertLpTokenBalance(); @@ -500,7 +481,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { // Create second lot uint96 lotIdTwo = _createLot(_NOT_SELLER); - _performCallback(lotIdTwo); + _performOnSettle(lotIdTwo); _assertLpTokenBalance(lotIdTwo, _NOT_SELLER); _assertQuoteTokenBalance(); @@ -518,7 +499,110 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) { - _performCallback(); + _performOnSettle(); + + _assertPoolState(_sqrtPriceX96); + _assertLpTokenBalance(); + _assertQuoteTokenBalance(); + _assertBaseTokenBalance(); + _assertApprovals(); + } + + function test_auctionCompleted_onCreate_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performOnSettle(); + + // Expect revert + // BaseCallback determines if the lot has already been registered + bytes memory err = abi.encodeWithSelector(BaseCallback.Callback_InvalidParams.selector); + vm.expectRevert(err); + + // Try to call onCreate again + _performOnCreate(); + } + + function test_auctionCompleted_onCurate_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performOnSettle(); + + // Expect revert + // BaseDirectToLiquidity determines if the lot has already been completed + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_AlreadyComplete.selector); + vm.expectRevert(err); + + // Try to call onCurate + _performOnCurate(_curatorPayout); + } + + function test_auctionCompleted_onCancel_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performOnSettle(); + + // Expect revert + // BaseDirectToLiquidity determines if the lot has already been completed + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_AlreadyComplete.selector); + vm.expectRevert(err); + + // Try to call onCancel + _performOnCancel(); + } + + function test_auctionCompleted_onSettle_reverts() + public + givenCallbackIsCreated + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performOnSettle(); + + // Expect revert + // BaseDirectToLiquidity determines if the lot has already been completed + bytes memory err = + abi.encodeWithSelector(BaseDirectToLiquidity.Callback_AlreadyComplete.selector); + vm.expectRevert(err); + + // Try to call onSettle + _performOnSettle(); + } + + function test_veRamTokenId() + public + givenCallbackIsCreated + givenVeRamTokenId + givenVeRamTokenIdApproval(true) + givenOnCreate + setCallbackParameters(_PROCEEDS, _REFUND) + givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + { + _performOnSettle(); _assertPoolState(_sqrtPriceX96); _assertLpTokenBalance(); From 15b4f51bb059283b9093cdd5c5595607ca68e378 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 26 Jul 2024 16:04:58 +0400 Subject: [PATCH 33/47] Remove veRamTokenId support from Ramses V2 DTL --- script/salts/salts.json | 2 +- .../liquidity/Ramses/RamsesV2DTL.sol | 23 ++---- .../liquidity/RamsesV2/RamsesV2DTLTest.sol | 7 +- .../liquidity/RamsesV2/onCreate.t.sol | 70 +++++++++---------- .../liquidity/RamsesV2/onSettle.t.sol | 42 +++++------ 5 files changed, 64 insertions(+), 80 deletions(-) diff --git a/script/salts/salts.json b/script/salts/salts.json index 0a7f2bd8..90d1a85b 100644 --- a/script/salts/salts.json +++ b/script/salts/salts.json @@ -101,7 +101,7 @@ "0x53bbd9c8a867624e56d19bf63d184f39112c1ad047c11555758b6e6d5e153499": "0x03349021186521ece9eaa54f521a1cb4c378198c47cbca9020936993c02a23c0" }, "Test_RamsesV2DirectToLiquidity": { - "0x1ec2032d7fbf6eebdd3b481e4890aa60ead6d427e88553e0d767282becd9015e": "0x325be95c1fec7ab9ab52d01d65beae2f07ad2fe20a67314e0ed1e3e3903b54b0" + "0xfa9378779c19bf226919c2116dc8e8c5eda1d74f888d67a122685e7fd9be77de": "0x5b3de30d1297453a8f0901d01dcba86a83418e2ae11d1f9bc638d680fbbc09f4" }, "Test_TokenAllowlist": { "0x3cf6404d9502da98e1ed6560fce585a0a87f56f598397915ef5a6207eb9b439f": "0xd8172a08ee7909a096d99b3ef34fe0d4212c85e77206518e9b5e1640926dde85", diff --git a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol index f7e5f5d9..20e17aa8 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol +++ b/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol @@ -35,20 +35,15 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { /// @notice The specified pool fee is not enabled in the Ramses V2 Factory error Callback_Params_PoolFeeNotEnabled(); - /// @notice The specified veRAM token ID is not approved for use by the callback - error Callback_Params_VeRamTokenIdNotApproved(); - // ========== STRUCTS ========== // /// @notice Parameters for the onCreate callback /// @dev This will be encoded in the `callbackData_` parameter /// /// @param maxSlippage The maximum slippage allowed when adding liquidity (in terms of basis points, where 1% = 1e2) - /// @param veRamTokenId The token ID of the veRAM token to use for the position (optional) struct RamsesV2OnCreateParams { uint24 poolFee; uint24 maxSlippage; - uint256 veRamTokenId; } // ========== STATE VARIABLES ========== // @@ -89,7 +84,6 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { /// This function reverts if: /// - `RamsesV2OnCreateParams.poolFee` is not enabled /// - `RamsesV2OnCreateParams.maxSlippage` is out of bounds - /// - This contract does not have permission to use `RamsesV2OnCreateParams.veRamTokenId` function __onCreate( uint96 lotId_, address, @@ -114,17 +108,6 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { if (params.maxSlippage > ONE_HUNDRED_PERCENT) { revert Callback_Params_PercentOutOfBounds(params.maxSlippage, 0, ONE_HUNDRED_PERCENT); } - - // Check that the callback has been given permission to use the veRamTokenId - // Similar to maxSlippage, this needs to be stored during onCreate - if ( - params.veRamTokenId > 0 - && !IVotingEscrow(ramsesV2PositionManager.veRam()).isApprovedOrOwner( - address(this), params.veRamTokenId - ) - ) { - revert Callback_Params_VeRamTokenIdNotApproved(); - } } /// @inheritdoc BaseDirectToLiquidity @@ -250,10 +233,12 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { amount1Min: _getAmountWithSlippage(amount1, params.maxSlippage), recipient: lotConfiguration[lotId_].recipient, deadline: block.timestamp, - veRamTokenId: params.veRamTokenId + veRamTokenId: 0 // Not supported at the moment }); } + /// @notice Decodes the configuration parameters from the DTLConfiguration + /// @dev The configuration parameters are stored in `DTLConfiguration.implParams` function _decodeParameters(uint96 lotId_) internal view @@ -261,7 +246,7 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { { DTLConfiguration memory lotConfig = lotConfiguration[lotId_]; // Validate that the callback data is of the correct length - if (lotConfig.implParams.length != 96) { + if (lotConfig.implParams.length != 64) { revert Callback_InvalidParams(); } diff --git a/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol b/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol index 5faea69c..a396a497 100644 --- a/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol +++ b/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol @@ -63,8 +63,7 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, RamsesV2DirectToLiquidity.RamsesV2OnCreateParams internal _ramsesCreateParams = RamsesV2DirectToLiquidity.RamsesV2OnCreateParams({ poolFee: 500, - maxSlippage: 1, // 0.01%, to handle rounding errors - veRamTokenId: 0 + maxSlippage: 1 // 0.01%, to handle rounding errors }); BaseDirectToLiquidity.OnCreateParams internal _dtlCreateParams = BaseDirectToLiquidity .OnCreateParams({ @@ -256,7 +255,7 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, } function _setVeRamTokenId(uint256 veRamTokenId_) internal { - _ramsesCreateParams.veRamTokenId = veRamTokenId_; + // _ramsesCreateParams.veRamTokenId = veRamTokenId_; _dtlCreateParams.implParams = abi.encode(_ramsesCreateParams); } @@ -326,7 +325,7 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, } modifier givenVeRamTokenIdApproval(bool approved_) { - _mockVeRamTokenIdApproved(_ramsesCreateParams.veRamTokenId, approved_); + // _mockVeRamTokenIdApproved(_ramsesCreateParams.veRamTokenId, approved_); _; } diff --git a/test/callbacks/liquidity/RamsesV2/onCreate.t.sol b/test/callbacks/liquidity/RamsesV2/onCreate.t.sol index 55e381b0..597ff67e 100644 --- a/test/callbacks/liquidity/RamsesV2/onCreate.t.sol +++ b/test/callbacks/liquidity/RamsesV2/onCreate.t.sol @@ -62,10 +62,10 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { // [X] it reverts // [X] when the recipient is not the seller // [X] it records the recipient - // [X] when the veRamTokenId is set - // [X] given the DTL contract is approved to use the veRamTokenId - // [X] it succeeds - // [X] it reverts + // [ ] when the veRamTokenId is set + // [ ] given the DTL contract is approved to use the veRamTokenId + // [ ] it succeeds + // [ ] it reverts // [X] when multiple lots are created // [X] it registers each lot // [X] it registers the lot @@ -274,7 +274,7 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { abi.decode(configuration.implParams, (RamsesV2DirectToLiquidity.RamsesV2OnCreateParams)); assertEq(_ramsesCreateParams.poolFee, _ramsesCreateParams.poolFee, "poolFee"); assertEq(_ramsesCreateParams.maxSlippage, _ramsesCreateParams.maxSlippage, "maxSlippage"); - assertEq(_ramsesCreateParams.veRamTokenId, _ramsesCreateParams.veRamTokenId, "veRamTokenId"); + // assertEq(_ramsesCreateParams.veRamTokenId, _ramsesCreateParams.veRamTokenId, "veRamTokenId"); // Assert balances _assertBaseTokenBalances(); @@ -324,34 +324,34 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { assertEq(ramsesCreateParams.maxSlippage, maxSlippage, "maxSlippage"); } - function test_veRamTokenId() - public - givenCallbackIsCreated - givenVeRamTokenId - givenVeRamTokenIdApproval(true) - { - _performOnCreate(); - - // Assert values - BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); - - RamsesV2DirectToLiquidity.RamsesV2OnCreateParams memory ramsesCreateParams = - abi.decode(configuration.implParams, (RamsesV2DirectToLiquidity.RamsesV2OnCreateParams)); - assertEq(ramsesCreateParams.veRamTokenId, 1000, "veRamTokenId"); - } - - function test_veRamTokenId_notApproved_reverts() - public - givenCallbackIsCreated - givenVeRamTokenId - givenVeRamTokenIdApproval(false) - { - // Expect revert - bytes memory err = abi.encodeWithSelector( - RamsesV2DirectToLiquidity.Callback_Params_VeRamTokenIdNotApproved.selector - ); - vm.expectRevert(err); - - _performOnCreate(); - } + // function test_veRamTokenId() + // public + // givenCallbackIsCreated + // givenVeRamTokenId + // givenVeRamTokenIdApproval(true) + // { + // _performOnCreate(); + + // // Assert values + // BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); + + // RamsesV2DirectToLiquidity.RamsesV2OnCreateParams memory ramsesCreateParams = + // abi.decode(configuration.implParams, (RamsesV2DirectToLiquidity.RamsesV2OnCreateParams)); + // assertEq(ramsesCreateParams.veRamTokenId, 1000, "veRamTokenId"); + // } + + // function test_veRamTokenId_notApproved_reverts() + // public + // givenCallbackIsCreated + // givenVeRamTokenId + // givenVeRamTokenIdApproval(false) + // { + // // Expect revert + // bytes memory err = abi.encodeWithSelector( + // RamsesV2DirectToLiquidity.Callback_Params_VeRamTokenIdNotApproved.selector + // ); + // vm.expectRevert(err); + + // _performOnCreate(); + // } } diff --git a/test/callbacks/liquidity/RamsesV2/onSettle.t.sol b/test/callbacks/liquidity/RamsesV2/onSettle.t.sol index e274d208..38d4d0d3 100644 --- a/test/callbacks/liquidity/RamsesV2/onSettle.t.sol +++ b/test/callbacks/liquidity/RamsesV2/onSettle.t.sol @@ -195,8 +195,8 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { // [X] it mints the LP token to the recipient // [X] when multiple lots are created // [X] it performs actions on the correct pool - // [X] given the veRamTokenId is set - // [X] it succeeds + // [ ] given the veRamTokenId is set + // [ ] it succeeds // [X] it creates and initializes the pool, mints the position, transfers the LP token to the seller and transfers any excess back to the seller function test_givenPoolIsCreated() @@ -591,23 +591,23 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { _performOnSettle(); } - function test_veRamTokenId() - public - givenCallbackIsCreated - givenVeRamTokenId - givenVeRamTokenIdApproval(true) - givenOnCreate - setCallbackParameters(_PROCEEDS, _REFUND) - givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) - givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) - givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) - { - _performOnSettle(); - - _assertPoolState(_sqrtPriceX96); - _assertLpTokenBalance(); - _assertQuoteTokenBalance(); - _assertBaseTokenBalance(); - _assertApprovals(); - } + // function test_veRamTokenId() + // public + // givenCallbackIsCreated + // givenVeRamTokenId + // givenVeRamTokenIdApproval(true) + // givenOnCreate + // setCallbackParameters(_PROCEEDS, _REFUND) + // givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) + // givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) + // givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) + // { + // _performOnSettle(); + + // _assertPoolState(_sqrtPriceX96); + // _assertLpTokenBalance(); + // _assertQuoteTokenBalance(); + // _assertBaseTokenBalance(); + // _assertApprovals(); + // } } From 482c4df15e8903eac8cab5c21fa881510b41606d Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 26 Jul 2024 16:07:27 +0400 Subject: [PATCH 34/47] Update remaining test salts --- script/salts/salts.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/script/salts/salts.json b/script/salts/salts.json index 90d1a85b..8c68378a 100644 --- a/script/salts/salts.json +++ b/script/salts/salts.json @@ -53,19 +53,19 @@ "0x3e786da8306fcbed7cac84942338a1270f92283628b6c00f68be021274a9398c": "0x0f25886775a191f8793d3da1493fe07c98943f0d34227e3ceebae3dfb555b56a" }, "Test_BaselineAllocatedAllowlist": { - "0x5d746474bd6f58dea871b468d53e3ab576b19043d224904750600a2d128829a1": "0x01e8a1097ae9c6b111d41a7083a3a951186ae7e57740c033f8b1ee1f65d75a69" + "0x5d746474bd6f58dea871b468d53e3ab576b19043d224904750600a2d128829a1": "0x54b0050dbb650e1e968be88b75dc8354fd668ba1c9d404303f33dac5ffa0d9fa" }, "Test_BaselineAllowlist": { - "0x5994dd0cafe772dd48a75ac3eba409d0654bf2cac96c9f15c8096296038e2a00": "0x46385df55b069cfb03a79134ed0238753451d5c759ec65ec1862c46696aa74a0" + "0x5994dd0cafe772dd48a75ac3eba409d0654bf2cac96c9f15c8096296038e2a00": "0x9fefe6be328d53df69523f980edaf65730c4938eac67dd0ab3aa32fde0c6d24f" }, "Test_BaselineAxisLaunch": { - "0x52496df1c08f8ccb2ff3d5890297b3619ae3d670581df798367699ac94530d12": "0x7bb31290b0bb64c5cf5f07d5a6a805e8b90546a54cd74216908eccfef0f2731a" + "0x52496df1c08f8ccb2ff3d5890297b3619ae3d670581df798367699ac94530d12": "0x2041068fbbad4940b256c7e16cf6a283248a550bb0b524e30c58ddf22e1662de" }, "Test_BaselineCappedAllowlist": { - "0x1ed7b5269875d6fd24f19a8b6a1906cca2ca94bba1d23cf1fb3e95877b8b0338": "0xbd4d24db803bc5f1c5dc27826b7e2bd8e4b52dd8854225ac8278bb280ae24b92" + "0x1ed7b5269875d6fd24f19a8b6a1906cca2ca94bba1d23cf1fb3e95877b8b0338": "0xaba1c15b4006c6584a63f6ee625f9486ce3edf34139f6b9b52289837d32d6f95" }, "Test_BaselineTokenAllowlist": { - "0x3f8ca4e10bd4e9daa2aee9c323586dc24d4a3358d7599a3668eed1dd1860a829": "0xd513ec98769f0961837fec2fad1c59af4b94d69e322bc78fc3672ebc6049c2c0" + "0x3f8ca4e10bd4e9daa2aee9c323586dc24d4a3358d7599a3668eed1dd1860a829": "0xe5893ce7eff3f88fda53743fa9b06fda694a726bcdcc76b23ab0974559d70342" }, "Test_CappedMerkleAllowlist": { "0x1dc038ba91f15889eedf059525478d760c3b01dbf5288c0c995ef5c8f1395e8b": "0x6bfac83b21063468377650c02a19f8b39750ffa72f1763596c48d62833e54e12", @@ -98,7 +98,7 @@ "0x73857a6649000d8d1c6d627e9eda2a6079605eda4cd860cffe3bb939dfe3e3ef": "0xe9bd28f6c7d9ac5ecc0f59b51855093220efab06f25f16c4d0e82f04eabaf13c" }, "Test_RamsesV1DirectToLiquidity": { - "0x53bbd9c8a867624e56d19bf63d184f39112c1ad047c11555758b6e6d5e153499": "0x03349021186521ece9eaa54f521a1cb4c378198c47cbca9020936993c02a23c0" + "0xa39d2640eec5bd97dd5073b05dccd616857fdca9f9e1ccf1894340217e109b4f": "0xf0401a9a5af98fccfba400ba36143831ea05ea5b0c734cb1478c227115322dcd" }, "Test_RamsesV2DirectToLiquidity": { "0xfa9378779c19bf226919c2116dc8e8c5eda1d74f888d67a122685e7fd9be77de": "0x5b3de30d1297453a8f0901d01dcba86a83418e2ae11d1f9bc638d680fbbc09f4" @@ -108,13 +108,13 @@ "0xdb5689fd93b97ad35ee469bbcb38d4472c005f1ae951b154f2437bd1207a03f3": "0xf35c7565f04f89db7d1783343067728c4db35f70d7aea4d7dbd732fa462bb956" }, "Test_UniswapV2DirectToLiquidity": { - "0x3b9597ed8d70c8fe0f4beda2a2c5d3f99be1daf59b5d28221d2f1609d53730e6": "0x777f030735cde785ecbdc7a0f3d5984e7e0f5cb385b37c47622960af95347464" + "0xc6457ead866939ba79fbe24976c01de6a4e5350b66873ae2444500fcf3d0a68a": "0x34d7d49c713c6454e5d96bb09e9496895a1faf94d45e3a6c8550b81c9ba15117" }, "Test_UniswapV2Router": { "0x3aeb5c743a058e3c9d871533d2537013b819c4e401acaca3619f046cd9422258": "0x4c096423447dffd4ffce23f8e15e2d1b08619f72abc81536b5492d95b7c8f03f" }, "Test_UniswapV3DirectToLiquidity": { - "0x177a207e41b48a7ff6d561046056b1dacbbe01ef97bf6381a7cda6d781d04e02": "0x2ab7082b39720b3cd945b25a712c093f7ab7924c9db5095a80099d9b3746604c" + "0xfea4b3d341404a1e1c342f18c0eef626604a03f4293ad8eeabe8e66df65f89b5": "0xa4fad84e0fc223785c278b66da03e1ded6ad816e80401a05f24840367aa11846" }, "Test_UniswapV3Factory": { "0x33479a3955b5801da918af0aa61f4501368d483ac02e8ae036d4e6f9c56d2038": "0x715638753ed78c13d0d385c1758204daad3b308083604fb0d555eb285199ed30" From 1dd77d571c9de1e161293a1a0146e5812ff5241d Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 26 Jul 2024 16:07:55 +0400 Subject: [PATCH 35/47] Update test TODOs for Uniswap DTL --- test/callbacks/liquidity/UniswapV2DTL/onCancel.t.sol | 11 +++++++++-- test/callbacks/liquidity/UniswapV2DTL/onSettle.t.sol | 9 ++++++++- test/callbacks/liquidity/UniswapV3DTL/onCancel.t.sol | 11 +++++++++-- test/callbacks/liquidity/UniswapV3DTL/onSettle.t.sol | 9 ++++++++- 4 files changed, 34 insertions(+), 6 deletions(-) diff --git a/test/callbacks/liquidity/UniswapV2DTL/onCancel.t.sol b/test/callbacks/liquidity/UniswapV2DTL/onCancel.t.sol index 36139308..27290b53 100644 --- a/test/callbacks/liquidity/UniswapV2DTL/onCancel.t.sol +++ b/test/callbacks/liquidity/UniswapV2DTL/onCancel.t.sol @@ -18,10 +18,17 @@ contract UniswapV2DirectToLiquidityOnCancelTest is UniswapV2DirectToLiquidityTes // ============ Tests ============ // + // [ ] given the onCancel callback has already been called + // [ ] when onSettle is called + // [ ] it reverts + // [ ] when onCancel is called + // [ ] it reverts + // [ ] when onCurate is called + // [ ] it reverts + // [ ] when onCreate is called + // [ ] it reverts // [X] when the lot has not been registered // [X] it reverts - // [ ] when the auction lot has already been completed - // [ ] it reverts // [X] when multiple lots are created // [X] it marks the correct lot as inactive // [X] it marks the lot as inactive diff --git a/test/callbacks/liquidity/UniswapV2DTL/onSettle.t.sol b/test/callbacks/liquidity/UniswapV2DTL/onSettle.t.sol index 4a401bf0..80e07c25 100644 --- a/test/callbacks/liquidity/UniswapV2DTL/onSettle.t.sol +++ b/test/callbacks/liquidity/UniswapV2DTL/onSettle.t.sol @@ -270,7 +270,14 @@ contract UniswapV2DirectToLiquidityOnSettleTest is UniswapV2DirectToLiquidityTes // ========== Tests ========== // // [ ] given the onSettle callback has already been called - // [ ] it reverts + // [ ] when onSettle is called + // [ ] it reverts + // [ ] when onCancel is called + // [ ] it reverts + // [ ] when onCurate is called + // [ ] it reverts + // [ ] when onCreate is called + // [ ] it reverts // [X] given the pool is created // [X] it initializes the pool // [X] given the pool is created and initialized diff --git a/test/callbacks/liquidity/UniswapV3DTL/onCancel.t.sol b/test/callbacks/liquidity/UniswapV3DTL/onCancel.t.sol index 267f580d..1fec2951 100644 --- a/test/callbacks/liquidity/UniswapV3DTL/onCancel.t.sol +++ b/test/callbacks/liquidity/UniswapV3DTL/onCancel.t.sol @@ -18,8 +18,15 @@ contract UniswapV3DirectToLiquidityOnCancelTest is UniswapV3DirectToLiquidityTes // ============ Tests ============ // - // [ ] when the auction lot has already been completed - // [ ] it reverts + // [ ] given the onCancel callback has already been called + // [ ] when onSettle is called + // [ ] it reverts + // [ ] when onCancel is called + // [ ] it reverts + // [ ] when onCurate is called + // [ ] it reverts + // [ ] when onCreate is called + // [ ] it reverts // [X] when the lot has not been registered // [X] it reverts // [X] when multiple lots are created diff --git a/test/callbacks/liquidity/UniswapV3DTL/onSettle.t.sol b/test/callbacks/liquidity/UniswapV3DTL/onSettle.t.sol index 716004d2..fe1dd4ba 100644 --- a/test/callbacks/liquidity/UniswapV3DTL/onSettle.t.sol +++ b/test/callbacks/liquidity/UniswapV3DTL/onSettle.t.sol @@ -280,7 +280,14 @@ contract UniswapV3DirectToLiquidityOnSettleTest is UniswapV3DirectToLiquidityTes // ========== Tests ========== // // [ ] given the onSettle callback has already been called - // [ ] it reverts + // [ ] when onSettle is called + // [ ] it reverts + // [ ] when onCancel is called + // [ ] it reverts + // [ ] when onCurate is called + // [ ] it reverts + // [ ] when onCreate is called + // [ ] it reverts // [X] given the pool is created // [X] it initializes the pool // [X] given the pool is created and initialized From 8100ee9eab2762cdbcce96aa06c928112b2bcb90 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Fri, 26 Jul 2024 17:56:29 +0400 Subject: [PATCH 36/47] Disable accessing veRam() (not supported on Cleo) --- test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol b/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol index a396a497..0f6be3ca 100644 --- a/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol +++ b/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol @@ -92,7 +92,7 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, _factory = IRamsesV2Factory(_RAMSES_V2_FACTORY); _positionManager = IRamsesV2PositionManager(payable(_RAMSES_V2_POSITION_MANAGER)); - _ram = IRAM(IVotingEscrow(_positionManager.veRam()).token()); + // _ram = IRAM(IVotingEscrow(_positionManager.veRam()).token()); _batchAuctionModule = new MockBatchAuctionModule(address(_auctionHouse)); From 3f6437101637893d4041ca403a5b7a620ef2f548 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 29 Jul 2024 10:10:17 +0400 Subject: [PATCH 37/47] Rename contracts reference Cleopatra instead of Ramses --- .solhintignore | 2 +- script/deploy/Deploy.s.sol | 56 ++++++------- .../CleopatraDTLSalts.s.sol} | 52 ++++++------ .../dtl_salts.sh} | 4 +- script/salts/salts.json | 4 +- script/salts/test/TestSalts.s.sol | 24 +++--- .../CleopatraV1DTL.sol} | 40 ++++----- .../CleopatraV2DTL.sol} | 82 +++++++++---------- .../lib/ICleopatraV1Factory.sol} | 2 +- .../lib/ICleopatraV1Pool.sol} | 2 +- .../lib/ICleopatraV1Router.sol} | 2 +- .../lib/ICleopatraV2Factory.sol} | 2 +- .../lib/ICleopatraV2Pool.sol} | 2 +- .../lib/ICleopatraV2PositionManager.sol} | 4 +- .../{Ramses => Cleopatra}/lib/IRAM.sol | 0 .../lib/IVotingEscrow.sol | 0 test/Constants.sol | 18 ++-- .../CleopatraV1DTLTest.sol} | 40 ++++----- .../{RamsesV1 => CleopatraV1}/onCancel.t.sol | 4 +- .../{RamsesV1 => CleopatraV1}/onCreate.t.sol | 34 ++++---- .../{RamsesV1 => CleopatraV1}/onCurate.t.sol | 4 +- .../{RamsesV1 => CleopatraV1}/onSettle.t.sol | 42 +++++----- .../CleopatraV2DTLTest.sol} | 54 ++++++------ .../{RamsesV2 => CleopatraV2}/onCancel.t.sol | 4 +- .../{RamsesV2 => CleopatraV2}/onCreate.t.sol | 34 ++++---- .../{RamsesV2 => CleopatraV2}/onCurate.t.sol | 4 +- .../{RamsesV2 => CleopatraV2}/onSettle.t.sol | 14 ++-- 27 files changed, 265 insertions(+), 265 deletions(-) rename script/salts/{dtl-ramses/RamsesDTLSalts.s.sol => dtl-cleopatra/CleopatraDTLSalts.s.sol} (63%) rename script/salts/{dtl-ramses/ramses_dtl_salts.sh => dtl-cleopatra/dtl_salts.sh} (85%) rename src/callbacks/liquidity/{Ramses/RamsesV1DTL.sol => Cleopatra/CleopatraV1DTL.sol} (85%) rename src/callbacks/liquidity/{Ramses/RamsesV2DTL.sol => Cleopatra/CleopatraV2DTL.sol} (74%) rename src/callbacks/liquidity/{Ramses/lib/IRamsesV1Factory.sol => Cleopatra/lib/ICleopatraV1Factory.sol} (98%) rename src/callbacks/liquidity/{Ramses/lib/IRamsesV1Pool.sol => Cleopatra/lib/ICleopatraV1Pool.sol} (99%) rename src/callbacks/liquidity/{Ramses/lib/IRamsesV1Router.sol => Cleopatra/lib/ICleopatraV1Router.sol} (99%) rename src/callbacks/liquidity/{Ramses/lib/IRamsesV2Factory.sol => Cleopatra/lib/ICleopatraV2Factory.sol} (98%) rename src/callbacks/liquidity/{Ramses/lib/IRamsesV2Pool.sol => Cleopatra/lib/ICleopatraV2Pool.sol} (99%) rename src/callbacks/liquidity/{Ramses/lib/IRamsesV2PositionManager.sol => Cleopatra/lib/ICleopatraV2PositionManager.sol} (98%) rename src/callbacks/liquidity/{Ramses => Cleopatra}/lib/IRAM.sol (100%) rename src/callbacks/liquidity/{Ramses => Cleopatra}/lib/IVotingEscrow.sol (100%) rename test/callbacks/liquidity/{RamsesV1/RamsesV1DTLTest.sol => CleopatraV1/CleopatraV1DTLTest.sol} (87%) rename test/callbacks/liquidity/{RamsesV1 => CleopatraV1}/onCancel.t.sol (96%) rename test/callbacks/liquidity/{RamsesV1 => CleopatraV1}/onCreate.t.sol (89%) rename test/callbacks/liquidity/{RamsesV1 => CleopatraV1}/onCurate.t.sol (95%) rename test/callbacks/liquidity/{RamsesV1 => CleopatraV1}/onSettle.t.sol (95%) rename test/callbacks/liquidity/{RamsesV2/RamsesV2DTLTest.sol => CleopatraV2/CleopatraV2DTLTest.sol} (85%) rename test/callbacks/liquidity/{RamsesV2 => CleopatraV2}/onCancel.t.sol (96%) rename test/callbacks/liquidity/{RamsesV2 => CleopatraV2}/onCreate.t.sol (87%) rename test/callbacks/liquidity/{RamsesV2 => CleopatraV2}/onCurate.t.sol (95%) rename test/callbacks/liquidity/{RamsesV2 => CleopatraV2}/onSettle.t.sol (97%) diff --git a/.solhintignore b/.solhintignore index 17c2fd75..6a51dd00 100644 --- a/.solhintignore +++ b/.solhintignore @@ -5,4 +5,4 @@ test/lib/uniswap-v2/** test/lib/uniswap-v3/** src/callbacks/liquidity/BaselineV2/lib/** -src/callbacks/liquidity/Ramses/lib/** +src/callbacks/liquidity/Cleopatra/lib/** diff --git a/script/deploy/Deploy.s.sol b/script/deploy/Deploy.s.sol index 82f604f7..548da046 100644 --- a/script/deploy/Deploy.s.sol +++ b/script/deploy/Deploy.s.sol @@ -33,13 +33,13 @@ import {BALwithCappedAllowlist} from "../../src/callbacks/liquidity/BaselineV2/BALwithCappedAllowlist.sol"; import {BALwithTokenAllowlist} from "../../src/callbacks/liquidity/BaselineV2/BALwithTokenAllowlist.sol"; -import {RamsesV1DirectToLiquidity} from "../../src/callbacks/liquidity/Ramses/RamsesV1DTL.sol"; -import {RamsesV2DirectToLiquidity} from "../../src/callbacks/liquidity/Ramses/RamsesV2DTL.sol"; +import {CleopatraV1DirectToLiquidity} from "../../src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol"; +import {CleopatraV2DirectToLiquidity} from "../../src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol"; -// Ramses -import {IRamsesV1Router} from "../../src/callbacks/liquidity/Ramses/lib/IRamsesV1Router.sol"; -import {IRamsesV2PositionManager} from - "../../src/callbacks/liquidity/Ramses/lib/IRamsesV2PositionManager.sol"; +// Cleopatra +import {ICleopatraV1Router} from "../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Router.sol"; +import {ICleopatraV2PositionManager} from + "../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2PositionManager.sol"; // Baseline import { @@ -979,32 +979,32 @@ contract Deploy is Script, WithDeploySequence, WithSalts { return (address(batchAllowlist), _PREFIX_CALLBACKS, deploymentKey); } - function deployBatchRamsesV1DirectToLiquidity(string memory sequenceName_) + function deployBatchCleopatraV1DirectToLiquidity(string memory sequenceName_) public returns (address, string memory, string memory) { // Get configuration variables address batchAuctionHouse = _getAddressNotZero("deployments.BatchAuctionHouse"); - address ramsesV1PairFactory = _getEnvAddressOrOverride( - "constants.ramsesV1.pairFactory", sequenceName_, "args.pairFactory" + address cleopatraV1PairFactory = _getEnvAddressOrOverride( + "constants.cleopatraV1.pairFactory", sequenceName_, "args.pairFactory" ); - address payable ramsesV1Router = payable( - _getEnvAddressOrOverride("constants.ramsesV1.router", sequenceName_, "args.router") + address payable cleopatraV1Router = payable( + _getEnvAddressOrOverride("constants.cleopatraV1.router", sequenceName_, "args.router") ); string memory deploymentKey = _getDeploymentKey(sequenceName_); console2.log(" deploymentKey:", deploymentKey); // Check that the router and factory match require( - IRamsesV1Router(ramsesV1Router).factory() == ramsesV1PairFactory, - "IRamsesV1Router.factory() does not match given Ramses V1 pair factory address" + ICleopatraV1Router(cleopatraV1Router).factory() == cleopatraV1PairFactory, + "ICleopatraV1Router.factory() does not match given Cleopatra V1 pair factory address" ); // Get the salt bytes32 salt_ = _getSalt( deploymentKey, - type(RamsesV1DirectToLiquidity).creationCode, - abi.encode(batchAuctionHouse, ramsesV1PairFactory, ramsesV1Router) + type(CleopatraV1DirectToLiquidity).creationCode, + abi.encode(batchAuctionHouse, cleopatraV1PairFactory, cleopatraV1Router) ); // Revert if the salt is not set @@ -1014,8 +1014,8 @@ contract Deploy is Script, WithDeploySequence, WithSalts { console2.log(" salt:", vm.toString(salt_)); vm.broadcast(); - RamsesV1DirectToLiquidity dtl = new RamsesV1DirectToLiquidity{salt: salt_}( - batchAuctionHouse, ramsesV1PairFactory, ramsesV1Router + CleopatraV1DirectToLiquidity dtl = new CleopatraV1DirectToLiquidity{salt: salt_}( + batchAuctionHouse, cleopatraV1PairFactory, cleopatraV1Router ); console2.log(""); console2.log(" deployed at:", address(dtl)); @@ -1023,17 +1023,17 @@ contract Deploy is Script, WithDeploySequence, WithSalts { return (address(dtl), _PREFIX_CALLBACKS, deploymentKey); } - function deployBatchRamsesV2DirectToLiquidity(string memory sequenceName_) + function deployBatchCleopatraV2DirectToLiquidity(string memory sequenceName_) public returns (address, string memory, string memory) { // Get configuration variables address batchAuctionHouse = _getAddressNotZero("deployments.BatchAuctionHouse"); - address ramsesV2Factory = - _getEnvAddressOrOverride("constants.ramsesV2.factory", sequenceName_, "args.factory"); - address payable ramsesV2PositionManager = payable( + address cleopatraV2Factory = + _getEnvAddressOrOverride("constants.cleopatraV2.factory", sequenceName_, "args.factory"); + address payable cleopatraV2PositionManager = payable( _getEnvAddressOrOverride( - "constants.ramsesV2.positionManager", sequenceName_, "args.positionManager" + "constants.cleopatraV2.positionManager", sequenceName_, "args.positionManager" ) ); string memory deploymentKey = _getDeploymentKey(sequenceName_); @@ -1041,15 +1041,15 @@ contract Deploy is Script, WithDeploySequence, WithSalts { // Check that the router and factory match require( - IRamsesV2PositionManager(ramsesV2PositionManager).factory() == ramsesV2Factory, - "IRamsesV2PositionManager.factory() does not match given Ramses V2 factory address" + ICleopatraV2PositionManager(cleopatraV2PositionManager).factory() == cleopatraV2Factory, + "ICleopatraV2PositionManager.factory() does not match given Cleopatra V2 factory address" ); // Get the salt bytes32 salt_ = _getSalt( deploymentKey, - type(RamsesV1DirectToLiquidity).creationCode, - abi.encode(batchAuctionHouse, ramsesV2Factory, ramsesV2PositionManager) + type(CleopatraV1DirectToLiquidity).creationCode, + abi.encode(batchAuctionHouse, cleopatraV2Factory, cleopatraV2PositionManager) ); // Revert if the salt is not set @@ -1059,8 +1059,8 @@ contract Deploy is Script, WithDeploySequence, WithSalts { console2.log(" salt:", vm.toString(salt_)); vm.broadcast(); - RamsesV2DirectToLiquidity dtl = new RamsesV2DirectToLiquidity{salt: salt_}( - batchAuctionHouse, ramsesV2Factory, ramsesV2PositionManager + CleopatraV2DirectToLiquidity dtl = new CleopatraV2DirectToLiquidity{salt: salt_}( + batchAuctionHouse, cleopatraV2Factory, cleopatraV2PositionManager ); console2.log(""); console2.log(" deployed at:", address(dtl)); diff --git a/script/salts/dtl-ramses/RamsesDTLSalts.s.sol b/script/salts/dtl-cleopatra/CleopatraDTLSalts.s.sol similarity index 63% rename from script/salts/dtl-ramses/RamsesDTLSalts.s.sol rename to script/salts/dtl-cleopatra/CleopatraDTLSalts.s.sol index 18ee4d93..1a684378 100644 --- a/script/salts/dtl-ramses/RamsesDTLSalts.s.sol +++ b/script/salts/dtl-cleopatra/CleopatraDTLSalts.s.sol @@ -6,11 +6,11 @@ import {Script, console2} from "@forge-std-1.9.1/Script.sol"; import {WithSalts} from "../WithSalts.s.sol"; import {WithDeploySequence} from "../../deploy/WithDeploySequence.s.sol"; -// Ramses -import {RamsesV1DirectToLiquidity} from "../../../src/callbacks/liquidity/Ramses/RamsesV1DTL.sol"; -import {RamsesV2DirectToLiquidity} from "../../../src/callbacks/liquidity/Ramses/RamsesV2DTL.sol"; +// Cleopatra +import {CleopatraV1DirectToLiquidity} from "../../../src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol"; +import {CleopatraV2DirectToLiquidity} from "../../../src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol"; -contract RamsesDTLSalts is Script, WithDeploySequence, WithSalts { +contract CleopatraDTLSalts is Script, WithDeploySequence, WithSalts { string internal constant _ADDRESS_PREFIX = "E6"; function _setUp(string calldata chain_, string calldata sequenceFilePath_) internal { @@ -31,37 +31,37 @@ contract RamsesDTLSalts is Script, WithDeploySequence, WithSalts { string memory deploymentKey = _getDeploymentKey(sequenceName); console2.log(" deploymentKey: %s", deploymentKey); - // Atomic Ramses V1 + // Atomic Cleopatra V1 if ( keccak256(abi.encodePacked(sequenceName)) - == keccak256(abi.encodePacked("AtomicRamsesV1DirectToLiquidity")) + == keccak256(abi.encodePacked("AtomicCleopatraV1DirectToLiquidity")) ) { address auctionHouse = _envAddressNotZero("deployments.AtomicAuctionHouse"); _generateV1(sequenceName, auctionHouse, deploymentKey); } - // Batch Ramses V1 + // Batch Cleopatra V1 else if ( keccak256(abi.encodePacked(sequenceName)) - == keccak256(abi.encodePacked("BatchRamsesV1DirectToLiquidity")) + == keccak256(abi.encodePacked("BatchCleopatraV1DirectToLiquidity")) ) { address auctionHouse = _envAddressNotZero("deployments.BatchAuctionHouse"); _generateV1(sequenceName, auctionHouse, deploymentKey); } - // Atomic Ramses V2 + // Atomic Cleopatra V2 else if ( keccak256(abi.encodePacked(sequenceName)) - == keccak256(abi.encodePacked("AtomicRamsesV2DirectToLiquidity")) + == keccak256(abi.encodePacked("AtomicCleopatraV2DirectToLiquidity")) ) { address auctionHouse = _envAddressNotZero("deployments.AtomicAuctionHouse"); _generateV2(sequenceName, auctionHouse, deploymentKey); } - // Batch Ramses V2 + // Batch Cleopatra V2 else if ( keccak256(abi.encodePacked(sequenceName)) - == keccak256(abi.encodePacked("BatchRamsesV2DirectToLiquidity")) + == keccak256(abi.encodePacked("BatchCleopatraV2DirectToLiquidity")) ) { address auctionHouse = _envAddressNotZero("deployments.BatchAuctionHouse"); @@ -80,18 +80,18 @@ contract RamsesDTLSalts is Script, WithDeploySequence, WithSalts { string memory deploymentKey_ ) internal { // Get input variables or overrides - address envRamsesV1PairFactory = _getEnvAddressOrOverride( - "constants.ramsesV1.pairFactory", sequenceName_, "args.pairFactory" + address envCleopatraV1PairFactory = _getEnvAddressOrOverride( + "constants.cleopatraV1.pairFactory", sequenceName_, "args.pairFactory" ); - address envRamsesV1Router = - _getEnvAddressOrOverride("constants.ramsesV1.router", sequenceName_, "args.router"); + address envCleopatraV1Router = + _getEnvAddressOrOverride("constants.cleopatraV1.router", sequenceName_, "args.router"); - // Calculate salt for the RamsesV1DirectToLiquidity - bytes memory contractCode = type(RamsesV1DirectToLiquidity).creationCode; + // Calculate salt for the CleopatraV1DirectToLiquidity + bytes memory contractCode = type(CleopatraV1DirectToLiquidity).creationCode; (string memory bytecodePath, bytes32 bytecodeHash) = _writeBytecode( deploymentKey_, contractCode, - abi.encode(auctionHouse_, envRamsesV1PairFactory, envRamsesV1Router) + abi.encode(auctionHouse_, envCleopatraV1PairFactory, envCleopatraV1Router) ); _setSalt(bytecodePath, _ADDRESS_PREFIX, deploymentKey_, bytecodeHash); } @@ -102,18 +102,18 @@ contract RamsesDTLSalts is Script, WithDeploySequence, WithSalts { string memory deploymentKey_ ) internal { // Get input variables or overrides - address envRamsesV2Factory = - _getEnvAddressOrOverride("constants.ramsesV2.factory", sequenceName_, "args.factory"); - address envRamsesV2PositionManager = _getEnvAddressOrOverride( - "constants.ramsesV2.positionManager", sequenceName_, "args.positionManager" + address envCleopatraV2Factory = + _getEnvAddressOrOverride("constants.cleopatraV2.factory", sequenceName_, "args.factory"); + address envCleopatraV2PositionManager = _getEnvAddressOrOverride( + "constants.cleopatraV2.positionManager", sequenceName_, "args.positionManager" ); - // Calculate salt for the RamsesV2DirectToLiquidity - bytes memory contractCode = type(RamsesV2DirectToLiquidity).creationCode; + // Calculate salt for the CleopatraV2DirectToLiquidity + bytes memory contractCode = type(CleopatraV2DirectToLiquidity).creationCode; (string memory bytecodePath, bytes32 bytecodeHash) = _writeBytecode( deploymentKey_, contractCode, - abi.encode(auctionHouse_, envRamsesV2Factory, envRamsesV2PositionManager) + abi.encode(auctionHouse_, envCleopatraV2Factory, envCleopatraV2PositionManager) ); _setSalt(bytecodePath, _ADDRESS_PREFIX, deploymentKey_, bytecodeHash); } diff --git a/script/salts/dtl-ramses/ramses_dtl_salts.sh b/script/salts/dtl-cleopatra/dtl_salts.sh similarity index 85% rename from script/salts/dtl-ramses/ramses_dtl_salts.sh rename to script/salts/dtl-cleopatra/dtl_salts.sh index b768db03..40cc5567 100755 --- a/script/salts/dtl-ramses/ramses_dtl_salts.sh +++ b/script/salts/dtl-cleopatra/dtl_salts.sh @@ -1,7 +1,7 @@ #!/bin/bash # Usage: -# ./ramses_dtl_salts.sh --deployFile --envFile <.env> +# ./dtl_salts.sh --deployFile --envFile <.env> # # Expects the following environment variables: # CHAIN: The chain to deploy to, based on values from the ./script/env.json file. @@ -46,4 +46,4 @@ echo "Using chain: $CHAIN" echo "Using RPC at URL: $RPC_URL" echo "Using deploy file: $DEPLOY_FILE" -forge script ./script/salts/dtl-ramses/RamsesDTLSalts.s.sol:RamsesDTLSalts --sig "generate(string,string)()" $CHAIN $DEPLOY_FILE +forge script ./script/salts/dtl-cleopatra/CleopatraDTLSalts.s.sol:CleopatraDTLSalts --sig "generate(string,string)()" $CHAIN $DEPLOY_FILE diff --git a/script/salts/salts.json b/script/salts/salts.json index 8c68378a..6ae490ed 100644 --- a/script/salts/salts.json +++ b/script/salts/salts.json @@ -97,10 +97,10 @@ "Test_QuoteToken": { "0x73857a6649000d8d1c6d627e9eda2a6079605eda4cd860cffe3bb939dfe3e3ef": "0xe9bd28f6c7d9ac5ecc0f59b51855093220efab06f25f16c4d0e82f04eabaf13c" }, - "Test_RamsesV1DirectToLiquidity": { + "Test_CleopatraV1DirectToLiquidity": { "0xa39d2640eec5bd97dd5073b05dccd616857fdca9f9e1ccf1894340217e109b4f": "0xf0401a9a5af98fccfba400ba36143831ea05ea5b0c734cb1478c227115322dcd" }, - "Test_RamsesV2DirectToLiquidity": { + "Test_CleopatraV2DirectToLiquidity": { "0xfa9378779c19bf226919c2116dc8e8c5eda1d74f888d67a122685e7fd9be77de": "0x5b3de30d1297453a8f0901d01dcba86a83418e2ae11d1f9bc638d680fbbc09f4" }, "Test_TokenAllowlist": { diff --git a/script/salts/test/TestSalts.s.sol b/script/salts/test/TestSalts.s.sol index 13a8d9da..dbb4e894 100644 --- a/script/salts/test/TestSalts.s.sol +++ b/script/salts/test/TestSalts.s.sol @@ -34,8 +34,8 @@ import {BALwithCappedAllowlist} from "../../../src/callbacks/liquidity/BaselineV2/BALwithCappedAllowlist.sol"; import {BALwithTokenAllowlist} from "../../../src/callbacks/liquidity/BaselineV2/BALwithTokenAllowlist.sol"; -import {RamsesV1DirectToLiquidity} from "../../../src/callbacks/liquidity/Ramses/RamsesV1DTL.sol"; -import {RamsesV2DirectToLiquidity} from "../../../src/callbacks/liquidity/Ramses/RamsesV2DTL.sol"; +import {CleopatraV1DirectToLiquidity} from "../../../src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol"; +import {CleopatraV2DirectToLiquidity} from "../../../src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol"; contract TestSalts is Script, WithEnvironment, Permit2User, WithSalts, TestConstants { string internal constant _CAPPED_MERKLE_ALLOWLIST = "CappedMerkleAllowlist"; @@ -315,20 +315,20 @@ contract TestSalts is Script, WithEnvironment, Permit2User, WithSalts, TestConst _setTestSalt(callbackBytecodePath, "EF", "BaselineTokenAllowlist", callbackBytecodeHash); } - function generateRamsesV1DirectToLiquidity() public { - bytes memory args = abi.encode(_AUCTION_HOUSE, _RAMSES_V1_FACTORY, _RAMSES_V1_ROUTER); - bytes memory contractCode = type(RamsesV1DirectToLiquidity).creationCode; + function generateCleopatraV1DirectToLiquidity() public { + bytes memory args = abi.encode(_AUCTION_HOUSE, _CLEOPATRA_V1_FACTORY, _CLEOPATRA_V1_ROUTER); + bytes memory contractCode = type(CleopatraV1DirectToLiquidity).creationCode; (string memory bytecodePath, bytes32 bytecodeHash) = - _writeBytecode("RamsesV1DirectToLiquidity", contractCode, args); - _setTestSalt(bytecodePath, "E6", "RamsesV1DirectToLiquidity", bytecodeHash); + _writeBytecode("CleopatraV1DirectToLiquidity", contractCode, args); + _setTestSalt(bytecodePath, "E6", "CleopatraV1DirectToLiquidity", bytecodeHash); } - function generateRamsesV2DirectToLiquidity() public { + function generateCleopatraV2DirectToLiquidity() public { bytes memory args = - abi.encode(_AUCTION_HOUSE, _RAMSES_V2_FACTORY, _RAMSES_V2_POSITION_MANAGER); - bytes memory contractCode = type(RamsesV2DirectToLiquidity).creationCode; + abi.encode(_AUCTION_HOUSE, _CLEOPATRA_V2_FACTORY, _CLEOPATRA_V2_POSITION_MANAGER); + bytes memory contractCode = type(CleopatraV2DirectToLiquidity).creationCode; (string memory bytecodePath, bytes32 bytecodeHash) = - _writeBytecode("RamsesV2DirectToLiquidity", contractCode, args); - _setTestSalt(bytecodePath, "E6", "RamsesV2DirectToLiquidity", bytecodeHash); + _writeBytecode("CleopatraV2DirectToLiquidity", contractCode, args); + _setTestSalt(bytecodePath, "E6", "CleopatraV2DirectToLiquidity", bytecodeHash); } } diff --git a/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol b/src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol similarity index 85% rename from src/callbacks/liquidity/Ramses/RamsesV1DTL.sol rename to src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol index d6097f2c..4fe20a78 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV1DTL.sol +++ b/src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol @@ -8,12 +8,12 @@ import {SafeTransferLib} from "@solmate-6.7.0/utils/SafeTransferLib.sol"; // Callbacks import {BaseDirectToLiquidity} from "../BaseDTL.sol"; -// Ramses -import {IRamsesV1Factory} from "./lib/IRamsesV1Factory.sol"; -import {IRamsesV1Router} from "./lib/IRamsesV1Router.sol"; +// Cleopatra +import {ICleopatraV1Factory} from "./lib/ICleopatraV1Factory.sol"; +import {ICleopatraV1Router} from "./lib/ICleopatraV1Router.sol"; -/// @title RamsesV1DirectToLiquidity -/// @notice This Callback contract deposits the proceeds from a batch auction into a Ramses V1 pool +/// @title CleopatraV1DirectToLiquidity +/// @notice This Callback contract deposits the proceeds from a batch auction into a Cleopatra V1 pool /// in order to create liquidity immediately. /// /// The LP tokens are transferred to `DTLConfiguration.recipient`, or can optionally vest to the auction seller. @@ -24,7 +24,7 @@ import {IRamsesV1Router} from "./lib/IRamsesV1Router.sol"; /// /// @dev As a general rule, this callback contract does not retain balances of tokens between calls. /// Transfers are performed within the same function that requires the balance. -contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { +contract CleopatraV1DirectToLiquidity is BaseDirectToLiquidity { using SafeTransferLib for ERC20; // ========== STRUCTS ========== // @@ -34,20 +34,20 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { /// /// @param stable Whether the pool will be stable or volatile /// @param maxSlippage The maximum slippage allowed when adding liquidity (in terms of basis points, where 1% = 1e2) - struct RamsesV1OnCreateParams { + struct CleopatraV1OnCreateParams { bool stable; uint24 maxSlippage; } // ========== STATE VARIABLES ========== // - /// @notice The Ramses PairFactory contract - /// @dev This contract is used to create Ramses pairs - IRamsesV1Factory public pairFactory; + /// @notice The Cleopatra PairFactory contract + /// @dev This contract is used to create Cleopatra pairs + ICleopatraV1Factory public pairFactory; - /// @notice The Ramses Router contract - /// @dev This contract is used to add liquidity to Ramses pairs - IRamsesV1Router public router; + /// @notice The Cleopatra Router contract + /// @dev This contract is used to add liquidity to Cleopatra pairs + ICleopatraV1Router public router; /// @notice Mapping of lot ID to pool token mapping(uint96 => address) public lotIdToPoolToken; @@ -65,8 +65,8 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { if (router_ == address(0)) { revert Callback_Params_InvalidAddress(); } - pairFactory = IRamsesV1Factory(pairFactory_); - router = IRamsesV1Router(router_); + pairFactory = ICleopatraV1Factory(pairFactory_); + router = ICleopatraV1Router(router_); } // ========== CALLBACK FUNCTIONS ========== // @@ -77,7 +77,7 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { /// /// This function reverts if: /// - The callback data is of the incorrect length - /// - `RamsesV1OnCreateParams.maxSlippage` is out of bounds + /// - `CleopatraV1OnCreateParams.maxSlippage` is out of bounds function __onCreate( uint96 lotId_, address, @@ -87,7 +87,7 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { bool, bytes calldata ) internal virtual override { - RamsesV1OnCreateParams memory params = _decodeParameters(lotId_); + CleopatraV1OnCreateParams memory params = _decodeParameters(lotId_); // Check that the slippage amount is within bounds // The maxSlippage is stored during onCreate, as the callback data is passed in by the auction seller. @@ -110,7 +110,7 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { uint256 baseTokenAmount_, bytes memory ) internal virtual override { - RamsesV1OnCreateParams memory params = _decodeParameters(lotId_); + CleopatraV1OnCreateParams memory params = _decodeParameters(lotId_); // Create and initialize the pool if necessary // Token orientation is irrelevant @@ -186,7 +186,7 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { function _decodeParameters(uint96 lotId_) internal view - returns (RamsesV1OnCreateParams memory) + returns (CleopatraV1OnCreateParams memory) { DTLConfiguration memory lotConfig = lotConfiguration[lotId_]; // Validate that the callback data is of the correct length @@ -194,6 +194,6 @@ contract RamsesV1DirectToLiquidity is BaseDirectToLiquidity { revert Callback_InvalidParams(); } - return abi.decode(lotConfig.implParams, (RamsesV1OnCreateParams)); + return abi.decode(lotConfig.implParams, (CleopatraV1OnCreateParams)); } } diff --git a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol b/src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol similarity index 74% rename from src/callbacks/liquidity/Ramses/RamsesV2DTL.sol rename to src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol index 20e17aa8..4b6dbdd2 100644 --- a/src/callbacks/liquidity/Ramses/RamsesV2DTL.sol +++ b/src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol @@ -7,18 +7,18 @@ import {ERC20} from "@solmate-6.7.0/tokens/ERC20.sol"; // Callbacks import {BaseDirectToLiquidity} from "../BaseDTL.sol"; -// Ramses -import {IRamsesV2Pool} from "./lib/IRamsesV2Pool.sol"; -import {IRamsesV2Factory} from "./lib/IRamsesV2Factory.sol"; -import {IRamsesV2PositionManager} from "./lib/IRamsesV2PositionManager.sol"; +// Cleopatra +import {ICleopatraV2Pool} from "./lib/ICleopatraV2Pool.sol"; +import {ICleopatraV2Factory} from "./lib/ICleopatraV2Factory.sol"; +import {ICleopatraV2PositionManager} from "./lib/ICleopatraV2PositionManager.sol"; import {IVotingEscrow} from "./lib/IVotingEscrow.sol"; // Uniswap import {TickMath} from "@uniswap-v3-core-1.0.1-solc-0.8-simulate/libraries/TickMath.sol"; import {SqrtPriceMath} from "../../../lib/uniswap-v3/SqrtPriceMath.sol"; -/// @title RamsesV2DirectToLiquidity -/// @notice This Callback contract deposits the proceeds from a batch auction into a Ramses V2 pool +/// @title CleopatraV2DirectToLiquidity +/// @notice This Callback contract deposits the proceeds from a batch auction into a Cleopatra V2 pool /// in order to create full-range liquidity immediately. /// /// The LP tokens are transferred to `DTLConfiguration.recipient`, which must be an EOA or a contract that can receive ERC721 tokens. @@ -29,10 +29,10 @@ import {SqrtPriceMath} from "../../../lib/uniswap-v3/SqrtPriceMath.sol"; /// /// @dev As a general rule, this callback contract does not retain balances of tokens between calls. /// Transfers are performed within the same function that requires the balance. -contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { +contract CleopatraV2DirectToLiquidity is BaseDirectToLiquidity { // ========== ERRORS ========== // - /// @notice The specified pool fee is not enabled in the Ramses V2 Factory + /// @notice The specified pool fee is not enabled in the Cleopatra V2 Factory error Callback_Params_PoolFeeNotEnabled(); // ========== STRUCTS ========== // @@ -41,38 +41,38 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { /// @dev This will be encoded in the `callbackData_` parameter /// /// @param maxSlippage The maximum slippage allowed when adding liquidity (in terms of basis points, where 1% = 1e2) - struct RamsesV2OnCreateParams { + struct CleopatraV2OnCreateParams { uint24 poolFee; uint24 maxSlippage; } // ========== STATE VARIABLES ========== // - /// @notice The Ramses V2 factory - IRamsesV2Factory public ramsesV2Factory; + /// @notice The Cleopatra V2 factory + ICleopatraV2Factory public cleopatraV2Factory; - /// @notice The Ramses V2 position manager - IRamsesV2PositionManager public ramsesV2PositionManager; + /// @notice The Cleopatra V2 position manager + ICleopatraV2PositionManager public cleopatraV2PositionManager; - /// @notice Mapping of lot ID to Ramses V2 token ID + /// @notice Mapping of lot ID to Cleopatra V2 token ID mapping(uint96 => uint256) public lotIdToTokenId; // ========== CONSTRUCTOR ========== // constructor( address auctionHouse_, - address ramsesV2Factory_, - address payable ramsesV2PositionManager_ + address cleopatraV2Factory_, + address payable cleopatraV2PositionManager_ ) BaseDirectToLiquidity(auctionHouse_) { - if (ramsesV2Factory_ == address(0)) { + if (cleopatraV2Factory_ == address(0)) { revert Callback_Params_InvalidAddress(); } - ramsesV2Factory = IRamsesV2Factory(ramsesV2Factory_); + cleopatraV2Factory = ICleopatraV2Factory(cleopatraV2Factory_); - if (ramsesV2PositionManager_ == address(0)) { + if (cleopatraV2PositionManager_ == address(0)) { revert Callback_Params_InvalidAddress(); } - ramsesV2PositionManager = IRamsesV2PositionManager(ramsesV2PositionManager_); + cleopatraV2PositionManager = ICleopatraV2PositionManager(cleopatraV2PositionManager_); } // ========== CALLBACK FUNCTIONS ========== // @@ -82,8 +82,8 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { /// - Validates the input data /// /// This function reverts if: - /// - `RamsesV2OnCreateParams.poolFee` is not enabled - /// - `RamsesV2OnCreateParams.maxSlippage` is out of bounds + /// - `CleopatraV2OnCreateParams.poolFee` is not enabled + /// - `CleopatraV2OnCreateParams.maxSlippage` is out of bounds function __onCreate( uint96 lotId_, address, @@ -93,12 +93,12 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { bool, bytes calldata ) internal virtual override { - RamsesV2OnCreateParams memory params = _decodeParameters(lotId_); + CleopatraV2OnCreateParams memory params = _decodeParameters(lotId_); // Validate the parameters // Pool fee // Fee not enabled - if (ramsesV2Factory.feeAmountTickSpacing(params.poolFee) == 0) { + if (cleopatraV2Factory.feeAmountTickSpacing(params.poolFee) == 0) { revert Callback_Params_PoolFeeNotEnabled(); } @@ -149,21 +149,21 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { // Mint the position and add liquidity { - IRamsesV2PositionManager.MintParams memory mintParams = + ICleopatraV2PositionManager.MintParams memory mintParams = _getMintParams(lotId_, quoteToken_, quoteTokenAmount_, baseToken_, baseTokenAmount_); // Approve spending - ERC20(quoteToken_).approve(address(ramsesV2PositionManager), quoteTokenAmount_); - ERC20(baseToken_).approve(address(ramsesV2PositionManager), baseTokenAmount_); + ERC20(quoteToken_).approve(address(cleopatraV2PositionManager), quoteTokenAmount_); + ERC20(baseToken_).approve(address(cleopatraV2PositionManager), baseTokenAmount_); // Mint the position - (uint256 tokenId,,,) = ramsesV2PositionManager.mint(mintParams); + (uint256 tokenId,,,) = cleopatraV2PositionManager.mint(mintParams); lotIdToTokenId[lotId_] = tokenId; // Reset dangling approvals // The position manager may not spend all tokens - ERC20(quoteToken_).approve(address(ramsesV2PositionManager), 0); - ERC20(baseToken_).approve(address(ramsesV2PositionManager), 0); + ERC20(quoteToken_).approve(address(cleopatraV2PositionManager), 0); + ERC20(baseToken_).approve(address(cleopatraV2PositionManager), 0); } } @@ -190,15 +190,15 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { uint160 sqrtPriceX96 ) internal returns (address pool) { require(token0 < token1); - pool = ramsesV2Factory.getPool(token0, token1, fee); + pool = cleopatraV2Factory.getPool(token0, token1, fee); if (pool == address(0)) { - pool = ramsesV2Factory.createPool(token0, token1, fee); - IRamsesV2Pool(pool).initialize(sqrtPriceX96); + pool = cleopatraV2Factory.createPool(token0, token1, fee); + ICleopatraV2Pool(pool).initialize(sqrtPriceX96); } else { - (uint160 sqrtPriceX96Existing,,,,,,) = IRamsesV2Pool(pool).slot0(); + (uint160 sqrtPriceX96Existing,,,,,,) = ICleopatraV2Pool(pool).slot0(); if (sqrtPriceX96Existing == 0) { - IRamsesV2Pool(pool).initialize(sqrtPriceX96); + ICleopatraV2Pool(pool).initialize(sqrtPriceX96); } } } @@ -209,10 +209,10 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { uint256 quoteTokenAmount_, address baseToken_, uint256 baseTokenAmount_ - ) internal view returns (IRamsesV2PositionManager.MintParams memory) { - RamsesV2OnCreateParams memory params = _decodeParameters(lotId_); + ) internal view returns (ICleopatraV2PositionManager.MintParams memory) { + CleopatraV2OnCreateParams memory params = _decodeParameters(lotId_); - int24 tickSpacing = ramsesV2Factory.feeAmountTickSpacing(params.poolFee); + int24 tickSpacing = cleopatraV2Factory.feeAmountTickSpacing(params.poolFee); // Determine the ordering of tokens bool quoteTokenIsToken0 = quoteToken_ < baseToken_; @@ -221,7 +221,7 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { uint256 amount0 = quoteTokenIsToken0 ? quoteTokenAmount_ : baseTokenAmount_; uint256 amount1 = quoteTokenIsToken0 ? baseTokenAmount_ : quoteTokenAmount_; - return IRamsesV2PositionManager.MintParams({ + return ICleopatraV2PositionManager.MintParams({ token0: quoteTokenIsToken0 ? quoteToken_ : baseToken_, token1: quoteTokenIsToken0 ? baseToken_ : quoteToken_, fee: params.poolFee, @@ -242,7 +242,7 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { function _decodeParameters(uint96 lotId_) internal view - returns (RamsesV2OnCreateParams memory) + returns (CleopatraV2OnCreateParams memory) { DTLConfiguration memory lotConfig = lotConfiguration[lotId_]; // Validate that the callback data is of the correct length @@ -250,6 +250,6 @@ contract RamsesV2DirectToLiquidity is BaseDirectToLiquidity { revert Callback_InvalidParams(); } - return abi.decode(lotConfig.implParams, (RamsesV2OnCreateParams)); + return abi.decode(lotConfig.implParams, (CleopatraV2OnCreateParams)); } } diff --git a/src/callbacks/liquidity/Ramses/lib/IRamsesV1Factory.sol b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Factory.sol similarity index 98% rename from src/callbacks/liquidity/Ramses/lib/IRamsesV1Factory.sol rename to src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Factory.sol index 2bf46f44..7d7d8c25 100644 --- a/src/callbacks/liquidity/Ramses/lib/IRamsesV1Factory.sol +++ b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Factory.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.4; /// @dev Generated from https://arbiscan.io/address/0xaa9b8a7430474119a442ef0c2bf88f7c3c776f2f -interface IRamsesV1Factory { +interface ICleopatraV1Factory { event Initialized(uint8 version); event PairCreated( address indexed token0, address indexed token1, bool stable, address pair, uint256 diff --git a/src/callbacks/liquidity/Ramses/lib/IRamsesV1Pool.sol b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Pool.sol similarity index 99% rename from src/callbacks/liquidity/Ramses/lib/IRamsesV1Pool.sol rename to src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Pool.sol index 2956050c..bcc3df99 100644 --- a/src/callbacks/liquidity/Ramses/lib/IRamsesV1Pool.sol +++ b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Pool.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.4; -interface IRamsesV1Pool { +interface ICleopatraV1Pool { struct Observation { uint256 timestamp; uint256 reserve0Cumulative; diff --git a/src/callbacks/liquidity/Ramses/lib/IRamsesV1Router.sol b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Router.sol similarity index 99% rename from src/callbacks/liquidity/Ramses/lib/IRamsesV1Router.sol rename to src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Router.sol index 7daceedd..9be088e2 100644 --- a/src/callbacks/liquidity/Ramses/lib/IRamsesV1Router.sol +++ b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Router.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.4; /// @dev Generated from https://arbiscan.io/address/0x0e216dd4f1b5ea81006d41b79f9a1a69a38f3e37 -interface IRamsesV1Router { +interface ICleopatraV1Router { struct route { address from; address to; diff --git a/src/callbacks/liquidity/Ramses/lib/IRamsesV2Factory.sol b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Factory.sol similarity index 98% rename from src/callbacks/liquidity/Ramses/lib/IRamsesV2Factory.sol rename to src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Factory.sol index 84034926..b0cf5340 100644 --- a/src/callbacks/liquidity/Ramses/lib/IRamsesV2Factory.sol +++ b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Factory.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.4; /// @dev Generated from https://arbiscan.io/address/0xf896d16fa56a625802b6013f9f9202790ec69908 -interface IRamsesV2Factory { +interface ICleopatraV2Factory { event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing); event FeeCollectorChanged(address indexed oldFeeCollector, address indexed newFeeCollector); event FeeSetterChanged(address indexed oldSetter, address indexed newSetter); diff --git a/src/callbacks/liquidity/Ramses/lib/IRamsesV2Pool.sol b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Pool.sol similarity index 99% rename from src/callbacks/liquidity/Ramses/lib/IRamsesV2Pool.sol rename to src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Pool.sol index 0c2c862e..c7bffdae 100644 --- a/src/callbacks/liquidity/Ramses/lib/IRamsesV2Pool.sol +++ b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Pool.sol @@ -1,7 +1,7 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.4; -interface IRamsesV2Pool { +interface ICleopatraV2Pool { event Burn( address indexed owner, int24 indexed tickLower, diff --git a/src/callbacks/liquidity/Ramses/lib/IRamsesV2PositionManager.sol b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2PositionManager.sol similarity index 98% rename from src/callbacks/liquidity/Ramses/lib/IRamsesV2PositionManager.sol rename to src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2PositionManager.sol index bae0eb39..318b5fe1 100644 --- a/src/callbacks/liquidity/Ramses/lib/IRamsesV2PositionManager.sol +++ b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2PositionManager.sol @@ -2,7 +2,7 @@ pragma solidity ^0.8.4; /// @dev Generated from https://arbiscan.io/address/0xac9d1dfa5483ebb06e623df31547b2a4dc8bf7ca -interface IRamsesV2PositionManager { +interface ICleopatraV2PositionManager { struct CollectParams { uint256 tokenId; address recipient; @@ -121,7 +121,7 @@ interface IRamsesV2PositionManager { uint128 tokensOwed0, uint128 tokensOwed1 ); - function ramsesV2MintCallback( + function cleopatraV2MintCallback( uint256 amount0Owed, uint256 amount1Owed, bytes memory data diff --git a/src/callbacks/liquidity/Ramses/lib/IRAM.sol b/src/callbacks/liquidity/Cleopatra/lib/IRAM.sol similarity index 100% rename from src/callbacks/liquidity/Ramses/lib/IRAM.sol rename to src/callbacks/liquidity/Cleopatra/lib/IRAM.sol diff --git a/src/callbacks/liquidity/Ramses/lib/IVotingEscrow.sol b/src/callbacks/liquidity/Cleopatra/lib/IVotingEscrow.sol similarity index 100% rename from src/callbacks/liquidity/Ramses/lib/IVotingEscrow.sol rename to src/callbacks/liquidity/Cleopatra/lib/IVotingEscrow.sol diff --git a/test/Constants.sol b/test/Constants.sol index 79f9d715..45983e84 100644 --- a/test/Constants.sol +++ b/test/Constants.sol @@ -17,13 +17,13 @@ abstract contract TestConstants is TestConstantsCore { address internal constant _CREATE2_DEPLOYER = address(0x4e59b44847b379578588920cA78FbF26c0B4956C); - // Ramses addresses on Arbitrum - address internal constant _RAMSES_V1_FACTORY = - address(0xAAA20D08e59F6561f242b08513D36266C5A29415); - address internal constant _RAMSES_V1_ROUTER = - address(0xAAA87963EFeB6f7E0a2711F397663105Acb1805e); - address internal constant _RAMSES_V2_FACTORY = - address(0xAA2cd7477c451E703f3B9Ba5663334914763edF8); - address internal constant _RAMSES_V2_POSITION_MANAGER = - address(0xAA277CB7914b7e5514946Da92cb9De332Ce610EF); + // Cleopatra addresses on Mantle + address internal constant _CLEOPATRA_V1_FACTORY = + address(0xAAA16c016BF556fcD620328f0759252E29b1AB57); + address internal constant _CLEOPATRA_V1_ROUTER = + address(0xAAA45c8F5ef92a000a121d102F4e89278a711Faa); + address internal constant _CLEOPATRA_V2_FACTORY = + address(0xAAA32926fcE6bE95ea2c51cB4Fcb60836D320C42); + address internal constant _CLEOPATRA_V2_POSITION_MANAGER = + address(0xAAA78E8C4241990B4ce159E105dA08129345946A); } diff --git a/test/callbacks/liquidity/RamsesV1/RamsesV1DTLTest.sol b/test/callbacks/liquidity/CleopatraV1/CleopatraV1DTLTest.sol similarity index 87% rename from test/callbacks/liquidity/RamsesV1/RamsesV1DTLTest.sol rename to test/callbacks/liquidity/CleopatraV1/CleopatraV1DTLTest.sol index 019c7185..6188b119 100644 --- a/test/callbacks/liquidity/RamsesV1/RamsesV1DTLTest.sol +++ b/test/callbacks/liquidity/CleopatraV1/CleopatraV1DTLTest.sol @@ -16,10 +16,10 @@ import {MockBatchAuctionModule} from // Callbacks import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; -// Ramses -import {RamsesV1DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ramses/RamsesV1DTL.sol"; -import {IRamsesV1Factory} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV1Factory.sol"; -import {IRamsesV1Router} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV1Router.sol"; +// Cleopatra +import {CleopatraV1DirectToLiquidity} from "../../../../src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol"; +import {ICleopatraV1Factory} from "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Factory.sol"; +import {ICleopatraV1Router} from "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Router.sol"; // Axis core import {keycodeFromVeecode, toKeycode} from "@axis-core-1.0.0/modules/Keycode.sol"; @@ -28,8 +28,8 @@ import {IAuctionHouse} from "@axis-core-1.0.0/interfaces/IAuctionHouse.sol"; import {BatchAuctionHouse} from "@axis-core-1.0.0/BatchAuctionHouse.sol"; import {LinearVesting} from "@axis-core-1.0.0/modules/derivatives/LinearVesting.sol"; -abstract contract RamsesV1DirectToLiquidityTest is Test, Permit2User, WithSalts, TestConstants { - using Callbacks for RamsesV1DirectToLiquidity; +abstract contract CleopatraV1DirectToLiquidityTest is Test, Permit2User, WithSalts, TestConstants { + using Callbacks for CleopatraV1DirectToLiquidity; address internal constant _SELLER = address(0x2); address internal constant _PROTOCOL = address(0x3); @@ -43,11 +43,11 @@ abstract contract RamsesV1DirectToLiquidityTest is Test, Permit2User, WithSalts, uint96 internal _lotId = 1; BatchAuctionHouse internal _auctionHouse; - RamsesV1DirectToLiquidity internal _dtl; + CleopatraV1DirectToLiquidity internal _dtl; address internal _dtlAddress; - IRamsesV1Factory internal _factory; - IRamsesV1Router internal _router; + ICleopatraV1Factory internal _factory; + ICleopatraV1Router internal _router; LinearVesting internal _linearVesting; MockBatchAuctionModule internal _batchAuctionModule; @@ -58,15 +58,15 @@ abstract contract RamsesV1DirectToLiquidityTest is Test, Permit2User, WithSalts, uint96 internal _refund; // Inputs - RamsesV1DirectToLiquidity.RamsesV1OnCreateParams internal _ramsesCreateParams = - RamsesV1DirectToLiquidity.RamsesV1OnCreateParams({stable: false, maxSlippage: uint24(0)}); + CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams internal _cleopatraCreateParams = + CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams({stable: false, maxSlippage: uint24(0)}); BaseDirectToLiquidity.OnCreateParams internal _dtlCreateParams = BaseDirectToLiquidity .OnCreateParams({ proceedsUtilisationPercent: 100e2, vestingStart: 0, vestingExpiry: 0, recipient: _SELLER, - implParams: abi.encode(_ramsesCreateParams) + implParams: abi.encode(_cleopatraCreateParams) }); function setUp() public { @@ -85,8 +85,8 @@ abstract contract RamsesV1DirectToLiquidityTest is Test, Permit2User, WithSalts, vm.store(address(_auctionHouse), bytes32(uint256(6)), bytes32(abi.encode(1))); // Reentrancy vm.store(address(_auctionHouse), bytes32(uint256(10)), bytes32(abi.encode(_PROTOCOL))); // Protocol - _factory = IRamsesV1Factory(_RAMSES_V1_FACTORY); - _router = IRamsesV1Router(payable(_RAMSES_V1_ROUTER)); + _factory = ICleopatraV1Factory(_CLEOPATRA_V1_FACTORY); + _router = ICleopatraV1Router(payable(_CLEOPATRA_V1_ROUTER)); _linearVesting = new LinearVesting(address(_auctionHouse)); _batchAuctionModule = new MockBatchAuctionModule(address(_auctionHouse)); @@ -111,13 +111,13 @@ abstract contract RamsesV1DirectToLiquidityTest is Test, Permit2User, WithSalts, // Get the salt bytes memory args = abi.encode(address(_auctionHouse), address(_factory), address(_router)); bytes32 salt = _getTestSalt( - "RamsesV1DirectToLiquidity", type(RamsesV1DirectToLiquidity).creationCode, args + "CleopatraV1DirectToLiquidity", type(CleopatraV1DirectToLiquidity).creationCode, args ); // Required for CREATE2 address to work correctly. doesn't do anything in a test // Source: https://github.com/foundry-rs/foundry/issues/6402 vm.startBroadcast(); - _dtl = new RamsesV1DirectToLiquidity{salt: salt}( + _dtl = new CleopatraV1DirectToLiquidity{salt: salt}( address(_auctionHouse), address(_factory), payable(_router) ); vm.stopBroadcast(); @@ -252,16 +252,16 @@ abstract contract RamsesV1DirectToLiquidityTest is Test, Permit2User, WithSalts, } modifier givenStable(bool stable_) { - _ramsesCreateParams.stable = stable_; + _cleopatraCreateParams.stable = stable_; // Update the callback data - _dtlCreateParams.implParams = abi.encode(_ramsesCreateParams); + _dtlCreateParams.implParams = abi.encode(_cleopatraCreateParams); _; } function _setMaxSlippage(uint24 maxSlippage_) internal { - _ramsesCreateParams.maxSlippage = maxSlippage_; - _dtlCreateParams.implParams = abi.encode(_ramsesCreateParams); + _cleopatraCreateParams.maxSlippage = maxSlippage_; + _dtlCreateParams.implParams = abi.encode(_cleopatraCreateParams); } modifier givenMaxSlippage(uint24 maxSlippage_) { diff --git a/test/callbacks/liquidity/RamsesV1/onCancel.t.sol b/test/callbacks/liquidity/CleopatraV1/onCancel.t.sol similarity index 96% rename from test/callbacks/liquidity/RamsesV1/onCancel.t.sol rename to test/callbacks/liquidity/CleopatraV1/onCancel.t.sol index 61b33335..67d8649d 100644 --- a/test/callbacks/liquidity/RamsesV1/onCancel.t.sol +++ b/test/callbacks/liquidity/CleopatraV1/onCancel.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; -import {RamsesV1DirectToLiquidityTest} from "./RamsesV1DTLTest.sol"; +import {CleopatraV1DirectToLiquidityTest} from "./CleopatraV1DTLTest.sol"; import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; -contract RamsesV1DTLOnCancelForkTest is RamsesV1DirectToLiquidityTest { +contract CleopatraV1DTLOnCancelForkTest is CleopatraV1DirectToLiquidityTest { uint96 internal constant _REFUND_AMOUNT = 2e18; // ============ Modifiers ============ // diff --git a/test/callbacks/liquidity/RamsesV1/onCreate.t.sol b/test/callbacks/liquidity/CleopatraV1/onCreate.t.sol similarity index 89% rename from test/callbacks/liquidity/RamsesV1/onCreate.t.sol rename to test/callbacks/liquidity/CleopatraV1/onCreate.t.sol index fb6ed572..8ff8d9cd 100644 --- a/test/callbacks/liquidity/RamsesV1/onCreate.t.sol +++ b/test/callbacks/liquidity/CleopatraV1/onCreate.t.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; -import {RamsesV1DirectToLiquidityTest} from "./RamsesV1DTLTest.sol"; +import {CleopatraV1DirectToLiquidityTest} from "./CleopatraV1DTLTest.sol"; import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; -import {RamsesV1DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ramses/RamsesV1DTL.sol"; +import {CleopatraV1DirectToLiquidity} from "../../../../src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol"; -contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { +contract CleopatraV1DTLOnCreateForkTest is CleopatraV1DirectToLiquidityTest { // ============ Modifiers ============ // // ============ Assertions ============ // @@ -50,9 +50,9 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { // [X] it succeeds // [X] when the max slippage is greater than 100% // [X] it reverts - // [X] given ramses v1 pool stable already exists + // [X] given cleopatra v1 pool stable already exists // [X] it succeeds - // [X] given ramses v1 pool volatile already exists + // [X] given cleopatra v1 pool volatile already exists // [X] it succeeds // [X] when the start and expiry timestamps are the same // [X] it reverts @@ -323,11 +323,11 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { assertEq(configuration.active, true, "active"); assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); - RamsesV1DirectToLiquidity.RamsesV1OnCreateParams memory ramsesCreateParams = abi.decode( - _dtlCreateParams.implParams, (RamsesV1DirectToLiquidity.RamsesV1OnCreateParams) + CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams memory cleopatraCreateParams = abi.decode( + _dtlCreateParams.implParams, (CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams) ); - assertEq(ramsesCreateParams.stable, _ramsesCreateParams.stable, "stable"); - assertEq(ramsesCreateParams.maxSlippage, _ramsesCreateParams.maxSlippage, "maxSlippage"); + assertEq(cleopatraCreateParams.stable, _cleopatraCreateParams.stable, "stable"); + assertEq(cleopatraCreateParams.maxSlippage, _cleopatraCreateParams.maxSlippage, "maxSlippage"); // Assert balances _assertBaseTokenBalances(); @@ -360,11 +360,11 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { assertEq(configuration.active, true, "active"); assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); - RamsesV1DirectToLiquidity.RamsesV1OnCreateParams memory ramsesCreateParams = abi.decode( - _dtlCreateParams.implParams, (RamsesV1DirectToLiquidity.RamsesV1OnCreateParams) + CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams memory cleopatraCreateParams = abi.decode( + _dtlCreateParams.implParams, (CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams) ); - assertEq(ramsesCreateParams.stable, _ramsesCreateParams.stable, "stable"); - assertEq(ramsesCreateParams.maxSlippage, _ramsesCreateParams.maxSlippage, "maxSlippage"); + assertEq(cleopatraCreateParams.stable, _cleopatraCreateParams.stable, "stable"); + assertEq(cleopatraCreateParams.maxSlippage, _cleopatraCreateParams.maxSlippage, "maxSlippage"); // Assert balances _assertBaseTokenBalances(); @@ -382,11 +382,11 @@ contract RamsesV1DTLOnCreateForkTest is RamsesV1DirectToLiquidityTest { BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); - RamsesV1DirectToLiquidity.RamsesV1OnCreateParams memory ramsesCreateParams = abi.decode( - _dtlCreateParams.implParams, (RamsesV1DirectToLiquidity.RamsesV1OnCreateParams) + CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams memory cleopatraCreateParams = abi.decode( + _dtlCreateParams.implParams, (CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams) ); - assertEq(ramsesCreateParams.stable, _ramsesCreateParams.stable, "stable"); - assertEq(ramsesCreateParams.maxSlippage, maxSlippage, "maxSlippage"); + assertEq(cleopatraCreateParams.stable, _cleopatraCreateParams.stable, "stable"); + assertEq(cleopatraCreateParams.maxSlippage, maxSlippage, "maxSlippage"); } function test_givenStablePoolExists() public givenCallbackIsCreated { diff --git a/test/callbacks/liquidity/RamsesV1/onCurate.t.sol b/test/callbacks/liquidity/CleopatraV1/onCurate.t.sol similarity index 95% rename from test/callbacks/liquidity/RamsesV1/onCurate.t.sol rename to test/callbacks/liquidity/CleopatraV1/onCurate.t.sol index efb8dde9..4e22a009 100644 --- a/test/callbacks/liquidity/RamsesV1/onCurate.t.sol +++ b/test/callbacks/liquidity/CleopatraV1/onCurate.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; -import {RamsesV1DirectToLiquidityTest} from "./RamsesV1DTLTest.sol"; +import {CleopatraV1DirectToLiquidityTest} from "./CleopatraV1DTLTest.sol"; import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; -contract RamsesV1DTLOnCurateForkTest is RamsesV1DirectToLiquidityTest { +contract CleopatraV1DTLOnCurateForkTest is CleopatraV1DirectToLiquidityTest { uint96 internal constant _PAYOUT_AMOUNT = 1e18; // ============ Modifiers ============ // diff --git a/test/callbacks/liquidity/RamsesV1/onSettle.t.sol b/test/callbacks/liquidity/CleopatraV1/onSettle.t.sol similarity index 95% rename from test/callbacks/liquidity/RamsesV1/onSettle.t.sol rename to test/callbacks/liquidity/CleopatraV1/onSettle.t.sol index e7f8aafc..c08bf96f 100644 --- a/test/callbacks/liquidity/RamsesV1/onSettle.t.sol +++ b/test/callbacks/liquidity/CleopatraV1/onSettle.t.sol @@ -1,21 +1,21 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; -import {RamsesV1DirectToLiquidityTest} from "./RamsesV1DTLTest.sol"; +import {CleopatraV1DirectToLiquidityTest} from "./CleopatraV1DTLTest.sol"; // Libraries import {FixedPointMathLib} from "@solmate-6.7.0/utils/FixedPointMathLib.sol"; import {ERC20} from "@solmate-6.7.0/tokens/ERC20.sol"; -// Ramses -import {IRamsesV1Pool} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV1Pool.sol"; +// Cleopatra +import {ICleopatraV1Pool} from "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Pool.sol"; // AuctionHouse import {ILinearVesting} from "@axis-core-1.0.0/interfaces/modules/derivatives/ILinearVesting.sol"; import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; -contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { +contract CleopatraV1OnSettleForkTest is CleopatraV1DirectToLiquidityTest { uint96 internal constant _PROCEEDS = 20e18; uint96 internal constant _REFUND = 0; @@ -31,17 +31,17 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { // ========== Internal functions ========== // - function _getRamsesV1Pool(bool stable_) internal view returns (IRamsesV1Pool) { - return IRamsesV1Pool(_factory.getPair(address(_quoteToken), address(_baseToken), stable_)); + function _getCleopatraV1Pool(bool stable_) internal view returns (ICleopatraV1Pool) { + return ICleopatraV1Pool(_factory.getPair(address(_quoteToken), address(_baseToken), stable_)); } - function _getRamsesV1Pool() internal view returns (IRamsesV1Pool) { - return _getRamsesV1Pool(_ramsesCreateParams.stable); + function _getCleopatraV1Pool() internal view returns (ICleopatraV1Pool) { + return _getCleopatraV1Pool(_cleopatraCreateParams.stable); } function _getVestingTokenId() internal view returns (uint256) { // Get the pools deployed by the DTL callback - address pool = address(_getRamsesV1Pool()); + address pool = address(_getCleopatraV1Pool()); return _linearVesting.computeId( pool, @@ -58,7 +58,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { function _assertLpTokenBalance() internal view { // Get the pools deployed by the DTL callback - IRamsesV1Pool pool = _getRamsesV1Pool(); + ICleopatraV1Pool pool = _getCleopatraV1Pool(); // Exclude the LP token balance on this contract uint256 testBalance = pool.balanceOf(address(this)); @@ -96,7 +96,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { } // Get the pools deployed by the DTL callback - address pool = address(_getRamsesV1Pool()); + address pool = address(_getCleopatraV1Pool()); // Get the wrapped address (, address wrappedVestingTokenAddress) = _linearVesting.deploy( @@ -136,7 +136,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { function _createPool() internal returns (address) { return _factory.createPair( - address(_quoteToken), address(_baseToken), _ramsesCreateParams.stable + address(_quoteToken), address(_baseToken), _cleopatraCreateParams.stable ); } @@ -204,7 +204,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { _router.addLiquidity( address(_quoteToken), address(_baseToken), - _ramsesCreateParams.stable, + _cleopatraCreateParams.stable, quoteTokensToDeposit, baseTokensToDeposit, quoteTokensToDeposit, @@ -231,7 +231,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { _router.addLiquidity( address(_quoteToken), address(_baseToken), - _ramsesCreateParams.stable, + _cleopatraCreateParams.stable, quoteTokensToDeposit, baseTokensToDeposit, quoteTokensToDeposit, @@ -524,7 +524,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { _linearVesting.redeemMax(tokenId); // Assert that the LP token has been transferred to the seller - IRamsesV1Pool pool = _getRamsesV1Pool(); + ICleopatraV1Pool pool = _getCleopatraV1Pool(); assertEq( pool.balanceOf(_SELLER), pool.totalSupply() - _MINIMUM_LIQUIDITY, @@ -545,7 +545,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { _performOnSettle(); // Get the pools deployed by the DTL callback - IRamsesV1Pool pool = _getRamsesV1Pool(); + ICleopatraV1Pool pool = _getCleopatraV1Pool(); // Approve the spending of the LP token uint256 lpTokenAmount = pool.balanceOf(_SELLER); @@ -557,7 +557,7 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { _router.removeLiquidity( address(_quoteToken), address(_baseToken), - _ramsesCreateParams.stable, + _cleopatraCreateParams.stable, lpTokenAmount, _quoteTokensToDeposit * 99 / 100, _baseTokensToDeposit * 99 / 100, @@ -673,8 +673,8 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { { _performOnSettle(); - address stablePool = address(_getRamsesV1Pool(true)); - address volatilePool = address(_getRamsesV1Pool(false)); + address stablePool = address(_getCleopatraV1Pool(true)); + address volatilePool = address(_getCleopatraV1Pool(false)); assertNotEq(stablePool, address(0), "stable pool address"); assertEq(volatilePool, address(0), "volatile pool address"); @@ -692,8 +692,8 @@ contract RamsesV1OnSettleForkTest is RamsesV1DirectToLiquidityTest { { _performOnSettle(); - address stablePool = address(_getRamsesV1Pool(true)); - address volatilePool = address(_getRamsesV1Pool(false)); + address stablePool = address(_getCleopatraV1Pool(true)); + address volatilePool = address(_getCleopatraV1Pool(false)); assertEq(stablePool, address(0), "stable pool address"); assertNotEq(volatilePool, address(0), "volatile pool address"); diff --git a/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol b/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol similarity index 85% rename from test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol rename to test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol index 0f6be3ca..d50756d7 100644 --- a/test/callbacks/liquidity/RamsesV2/RamsesV2DTLTest.sol +++ b/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol @@ -16,13 +16,13 @@ import {MockBatchAuctionModule} from // Callbacks import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; -// Ramses -import {RamsesV2DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ramses/RamsesV2DTL.sol"; -import {IRamsesV2Factory} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV2Factory.sol"; -import {IRamsesV2PositionManager} from - "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV2PositionManager.sol"; -import {IVotingEscrow} from "../../../../src/callbacks/liquidity/Ramses/lib/IVotingEscrow.sol"; -import {IRAM} from "../../../../src/callbacks/liquidity/Ramses/lib/IRAM.sol"; +// Cleopatra +import {CleopatraV2DirectToLiquidity} from "../../../../src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol"; +import {ICleopatraV2Factory} from "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Factory.sol"; +import {ICleopatraV2PositionManager} from + "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2PositionManager.sol"; +import {IVotingEscrow} from "../../../../src/callbacks/liquidity/Cleopatra/lib/IVotingEscrow.sol"; +import {IRAM} from "../../../../src/callbacks/liquidity/Cleopatra/lib/IRAM.sol"; // Axis core import {keycodeFromVeecode, toKeycode} from "@axis-core-1.0.0/modules/Keycode.sol"; @@ -31,8 +31,8 @@ import {IAuctionHouse} from "@axis-core-1.0.0/interfaces/IAuctionHouse.sol"; import {BatchAuctionHouse} from "@axis-core-1.0.0/BatchAuctionHouse.sol"; import {LinearVesting} from "@axis-core-1.0.0/modules/derivatives/LinearVesting.sol"; -abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, TestConstants { - using Callbacks for RamsesV2DirectToLiquidity; +abstract contract CleopatraV2DirectToLiquidityTest is Test, Permit2User, WithSalts, TestConstants { + using Callbacks for CleopatraV2DirectToLiquidity; address internal constant _SELLER = address(0x2); address internal constant _PROTOCOL = address(0x3); @@ -46,10 +46,10 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, uint96 internal _lotId = 1; BatchAuctionHouse internal _auctionHouse; - RamsesV2DirectToLiquidity internal _dtl; + CleopatraV2DirectToLiquidity internal _dtl; address internal _dtlAddress; - IRamsesV2Factory internal _factory; - IRamsesV2PositionManager internal _positionManager; + ICleopatraV2Factory internal _factory; + ICleopatraV2PositionManager internal _positionManager; IRAM internal _ram; MockBatchAuctionModule internal _batchAuctionModule; @@ -60,8 +60,8 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, uint96 internal _refund; // Inputs - RamsesV2DirectToLiquidity.RamsesV2OnCreateParams internal _ramsesCreateParams = - RamsesV2DirectToLiquidity.RamsesV2OnCreateParams({ + CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams internal _cleopatraCreateParams = + CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams({ poolFee: 500, maxSlippage: 1 // 0.01%, to handle rounding errors }); @@ -71,7 +71,7 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, vestingStart: 0, vestingExpiry: 0, recipient: _SELLER, - implParams: abi.encode(_ramsesCreateParams) + implParams: abi.encode(_cleopatraCreateParams) }); function setUp() public { @@ -90,8 +90,8 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, vm.store(address(_auctionHouse), bytes32(uint256(6)), bytes32(abi.encode(1))); // Reentrancy vm.store(address(_auctionHouse), bytes32(uint256(10)), bytes32(abi.encode(_PROTOCOL))); // Protocol - _factory = IRamsesV2Factory(_RAMSES_V2_FACTORY); - _positionManager = IRamsesV2PositionManager(payable(_RAMSES_V2_POSITION_MANAGER)); + _factory = ICleopatraV2Factory(_CLEOPATRA_V2_FACTORY); + _positionManager = ICleopatraV2PositionManager(payable(_CLEOPATRA_V2_POSITION_MANAGER)); // _ram = IRAM(IVotingEscrow(_positionManager.veRam()).token()); _batchAuctionModule = new MockBatchAuctionModule(address(_auctionHouse)); @@ -111,13 +111,13 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, bytes memory args = abi.encode(address(_auctionHouse), address(_factory), address(_positionManager)); bytes32 salt = _getTestSalt( - "RamsesV2DirectToLiquidity", type(RamsesV2DirectToLiquidity).creationCode, args + "CleopatraV2DirectToLiquidity", type(CleopatraV2DirectToLiquidity).creationCode, args ); // Required for CREATE2 address to work correctly. doesn't do anything in a test // Source: https://github.com/foundry-rs/foundry/issues/6402 vm.startBroadcast(); - _dtl = new RamsesV2DirectToLiquidity{salt: salt}( + _dtl = new CleopatraV2DirectToLiquidity{salt: salt}( address(_auctionHouse), address(_factory), payable(_positionManager) ); vm.stopBroadcast(); @@ -237,16 +237,16 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, } modifier givenPoolFee(uint24 fee_) { - _ramsesCreateParams.poolFee = fee_; + _cleopatraCreateParams.poolFee = fee_; // Update the callback data - _dtlCreateParams.implParams = abi.encode(_ramsesCreateParams); + _dtlCreateParams.implParams = abi.encode(_cleopatraCreateParams); _; } function _setMaxSlippage(uint24 maxSlippage_) internal { - _ramsesCreateParams.maxSlippage = maxSlippage_; - _dtlCreateParams.implParams = abi.encode(_ramsesCreateParams); + _cleopatraCreateParams.maxSlippage = maxSlippage_; + _dtlCreateParams.implParams = abi.encode(_cleopatraCreateParams); } modifier givenMaxSlippage(uint24 maxSlippage_) { @@ -255,8 +255,8 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, } function _setVeRamTokenId(uint256 veRamTokenId_) internal { - // _ramsesCreateParams.veRamTokenId = veRamTokenId_; - _dtlCreateParams.implParams = abi.encode(_ramsesCreateParams); + // _cleopatraCreateParams.veRamTokenId = veRamTokenId_; + _dtlCreateParams.implParams = abi.encode(_cleopatraCreateParams); } modifier givenVeRamTokenId() { @@ -284,7 +284,7 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, ? (address(_baseToken), address(_quoteToken)) : (address(_quoteToken), address(_baseToken)); - return _factory.createPool(token0, token1, _ramsesCreateParams.poolFee); + return _factory.createPool(token0, token1, _cleopatraCreateParams.poolFee); } modifier givenPoolIsCreated() { @@ -325,7 +325,7 @@ abstract contract RamsesV2DirectToLiquidityTest is Test, Permit2User, WithSalts, } modifier givenVeRamTokenIdApproval(bool approved_) { - // _mockVeRamTokenIdApproved(_ramsesCreateParams.veRamTokenId, approved_); + // _mockVeRamTokenIdApproved(_cleopatraCreateParams.veRamTokenId, approved_); _; } diff --git a/test/callbacks/liquidity/RamsesV2/onCancel.t.sol b/test/callbacks/liquidity/CleopatraV2/onCancel.t.sol similarity index 96% rename from test/callbacks/liquidity/RamsesV2/onCancel.t.sol rename to test/callbacks/liquidity/CleopatraV2/onCancel.t.sol index facdc2e8..568d2130 100644 --- a/test/callbacks/liquidity/RamsesV2/onCancel.t.sol +++ b/test/callbacks/liquidity/CleopatraV2/onCancel.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; -import {RamsesV2DirectToLiquidityTest} from "./RamsesV2DTLTest.sol"; +import {CleopatraV2DirectToLiquidityTest} from "./CleopatraV2DTLTest.sol"; import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; -contract RamsesV2DTLOnCancelForkTest is RamsesV2DirectToLiquidityTest { +contract CleopatraV2DTLOnCancelForkTest is CleopatraV2DirectToLiquidityTest { uint96 internal constant _REFUND_AMOUNT = 2e18; // ============ Modifiers ============ // diff --git a/test/callbacks/liquidity/RamsesV2/onCreate.t.sol b/test/callbacks/liquidity/CleopatraV2/onCreate.t.sol similarity index 87% rename from test/callbacks/liquidity/RamsesV2/onCreate.t.sol rename to test/callbacks/liquidity/CleopatraV2/onCreate.t.sol index 597ff67e..71b0bf4e 100644 --- a/test/callbacks/liquidity/RamsesV2/onCreate.t.sol +++ b/test/callbacks/liquidity/CleopatraV2/onCreate.t.sol @@ -1,13 +1,13 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; -import {RamsesV2DirectToLiquidityTest} from "./RamsesV2DTLTest.sol"; +import {CleopatraV2DirectToLiquidityTest} from "./CleopatraV2DTLTest.sol"; import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; -import {RamsesV2DirectToLiquidity} from "../../../../src/callbacks/liquidity/Ramses/RamsesV2DTL.sol"; +import {CleopatraV2DirectToLiquidity} from "../../../../src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol"; -contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { +contract CleopatraV2DTLOnCreateForkTest is CleopatraV2DirectToLiquidityTest { // ============ Modifiers ============ // // ============ Assertions ============ // @@ -52,7 +52,7 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { // [X] it reverts // [X] given the pool fee is not enabled // [X] it reverts - // [X] given ramses v2 pool already exists + // [X] given cleopatra v2 pool already exists // [X] it succeeds // [X] when the vesting start timestamp is set // [X] it reverts @@ -218,7 +218,7 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { { // Expect revert bytes memory err = abi.encodeWithSelector( - RamsesV2DirectToLiquidity.Callback_Params_PoolFeeNotEnabled.selector + CleopatraV2DirectToLiquidity.Callback_Params_PoolFeeNotEnabled.selector ); vm.expectRevert(err); @@ -270,11 +270,11 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { assertEq(configuration.active, true, "active"); assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); - RamsesV2DirectToLiquidity.RamsesV2OnCreateParams memory _ramsesCreateParams = - abi.decode(configuration.implParams, (RamsesV2DirectToLiquidity.RamsesV2OnCreateParams)); - assertEq(_ramsesCreateParams.poolFee, _ramsesCreateParams.poolFee, "poolFee"); - assertEq(_ramsesCreateParams.maxSlippage, _ramsesCreateParams.maxSlippage, "maxSlippage"); - // assertEq(_ramsesCreateParams.veRamTokenId, _ramsesCreateParams.veRamTokenId, "veRamTokenId"); + CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams memory _cleopatraCreateParams = + abi.decode(configuration.implParams, (CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams)); + assertEq(_cleopatraCreateParams.poolFee, _cleopatraCreateParams.poolFee, "poolFee"); + assertEq(_cleopatraCreateParams.maxSlippage, _cleopatraCreateParams.maxSlippage, "maxSlippage"); + // assertEq(_cleopatraCreateParams.veRamTokenId, _cleopatraCreateParams.veRamTokenId, "veRamTokenId"); // Assert balances _assertBaseTokenBalances(); @@ -319,9 +319,9 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); - RamsesV2DirectToLiquidity.RamsesV2OnCreateParams memory ramsesCreateParams = - abi.decode(configuration.implParams, (RamsesV2DirectToLiquidity.RamsesV2OnCreateParams)); - assertEq(ramsesCreateParams.maxSlippage, maxSlippage, "maxSlippage"); + CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams memory cleopatraCreateParams = + abi.decode(configuration.implParams, (CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams)); + assertEq(cleopatraCreateParams.maxSlippage, maxSlippage, "maxSlippage"); } // function test_veRamTokenId() @@ -335,9 +335,9 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { // // Assert values // BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); - // RamsesV2DirectToLiquidity.RamsesV2OnCreateParams memory ramsesCreateParams = - // abi.decode(configuration.implParams, (RamsesV2DirectToLiquidity.RamsesV2OnCreateParams)); - // assertEq(ramsesCreateParams.veRamTokenId, 1000, "veRamTokenId"); + // CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams memory cleopatraCreateParams = + // abi.decode(configuration.implParams, (CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams)); + // assertEq(cleopatraCreateParams.veRamTokenId, 1000, "veRamTokenId"); // } // function test_veRamTokenId_notApproved_reverts() @@ -348,7 +348,7 @@ contract RamsesV2DTLOnCreateForkTest is RamsesV2DirectToLiquidityTest { // { // // Expect revert // bytes memory err = abi.encodeWithSelector( - // RamsesV2DirectToLiquidity.Callback_Params_VeRamTokenIdNotApproved.selector + // CleopatraV2DirectToLiquidity.Callback_Params_VeRamTokenIdNotApproved.selector // ); // vm.expectRevert(err); diff --git a/test/callbacks/liquidity/RamsesV2/onCurate.t.sol b/test/callbacks/liquidity/CleopatraV2/onCurate.t.sol similarity index 95% rename from test/callbacks/liquidity/RamsesV2/onCurate.t.sol rename to test/callbacks/liquidity/CleopatraV2/onCurate.t.sol index 8dd1ea9f..1a6d1a6d 100644 --- a/test/callbacks/liquidity/RamsesV2/onCurate.t.sol +++ b/test/callbacks/liquidity/CleopatraV2/onCurate.t.sol @@ -1,12 +1,12 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; -import {RamsesV2DirectToLiquidityTest} from "./RamsesV2DTLTest.sol"; +import {CleopatraV2DirectToLiquidityTest} from "./CleopatraV2DTLTest.sol"; import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; -contract RamsesV2DTLOnCurateForkTest is RamsesV2DirectToLiquidityTest { +contract CleopatraV2DTLOnCurateForkTest is CleopatraV2DirectToLiquidityTest { uint96 internal constant _PAYOUT_AMOUNT = 1e18; // ============ Modifiers ============ // diff --git a/test/callbacks/liquidity/RamsesV2/onSettle.t.sol b/test/callbacks/liquidity/CleopatraV2/onSettle.t.sol similarity index 97% rename from test/callbacks/liquidity/RamsesV2/onSettle.t.sol rename to test/callbacks/liquidity/CleopatraV2/onSettle.t.sol index 38d4d0d3..7a73b1f9 100644 --- a/test/callbacks/liquidity/RamsesV2/onSettle.t.sol +++ b/test/callbacks/liquidity/CleopatraV2/onSettle.t.sol @@ -1,20 +1,20 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity 0.8.19; -import {RamsesV2DirectToLiquidityTest} from "./RamsesV2DTLTest.sol"; +import {CleopatraV2DirectToLiquidityTest} from "./CleopatraV2DTLTest.sol"; // Libraries import {FixedPointMathLib} from "@solmate-6.7.0/utils/FixedPointMathLib.sol"; -// Ramses -import {IRamsesV2Pool} from "../../../../src/callbacks/liquidity/Ramses/lib/IRamsesV2Pool.sol"; +// Cleopatra +import {ICleopatraV2Pool} from "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Pool.sol"; import {SqrtPriceMath} from "../../../../src/lib/uniswap-v3/SqrtPriceMath.sol"; // AuctionHouse import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; -contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { +contract CleopatraV2DTLOnSettleForkTest is CleopatraV2DirectToLiquidityTest { uint96 internal constant _PROCEEDS = 20e18; uint96 internal constant _REFUND = 0; @@ -45,7 +45,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { // Get the pool address pool = _getPool(); - (uint160 sqrtPriceX96,,,,,,) = IRamsesV2Pool(pool).slot0(); + (uint160 sqrtPriceX96,,,,,,) = ICleopatraV2Pool(pool).slot0(); assertEq(sqrtPriceX96, sqrtPriceX96_, "pool sqrt price"); } @@ -84,7 +84,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { // ========== Modifiers ========== // function _initializePool(address pool_, uint160 sqrtPriceX96_) internal { - IRamsesV2Pool(pool_).initialize(sqrtPriceX96_); + ICleopatraV2Pool(pool_).initialize(sqrtPriceX96_); } modifier givenPoolIsCreatedAndInitialized(uint160 sqrtPriceX96_) { @@ -161,7 +161,7 @@ contract RamsesV2DTLOnSettleForkTest is RamsesV2DirectToLiquidityTest { (address token0, address token1) = address(_baseToken) < address(_quoteToken) ? (address(_baseToken), address(_quoteToken)) : (address(_quoteToken), address(_baseToken)); - return _factory.getPool(token0, token1, _ramsesCreateParams.poolFee); + return _factory.getPool(token0, token1, _cleopatraCreateParams.poolFee); } // ========== Tests ========== // From c9aa1ef5e7e9bffcfed2320df3ef8f64078251e8 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 29 Jul 2024 10:16:32 +0400 Subject: [PATCH 38/47] Remove/disable redundant code --- .../liquidity/Cleopatra/lib/IRAM.sol | 22 ------ .../liquidity/Cleopatra/lib/IVotingEscrow.sol | 68 ----------------- .../CleopatraV2/CleopatraV2DTLTest.sol | 76 +++++++++---------- 3 files changed, 38 insertions(+), 128 deletions(-) delete mode 100644 src/callbacks/liquidity/Cleopatra/lib/IRAM.sol delete mode 100644 src/callbacks/liquidity/Cleopatra/lib/IVotingEscrow.sol diff --git a/src/callbacks/liquidity/Cleopatra/lib/IRAM.sol b/src/callbacks/liquidity/Cleopatra/lib/IRAM.sol deleted file mode 100644 index 3804b1db..00000000 --- a/src/callbacks/liquidity/Cleopatra/lib/IRAM.sol +++ /dev/null @@ -1,22 +0,0 @@ -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.4; - -interface IRAM { - event Approval(address indexed owner, address indexed spender, uint256 value); - event Initialized(uint8 version); - event Transfer(address indexed from, address indexed to, uint256 value); - - function allowance(address, address) external view returns (uint256); - function approve(address _spender, uint256 _value) external returns (bool); - function balanceOf(address) external view returns (uint256); - function decimals() external view returns (uint8); - function initialize(address _minter) external; - function mint(address account, uint256 amount) external returns (bool); - function minter() external view returns (address); - function name() external view returns (string memory); - function setMinter(address _minter) external; - function symbol() external view returns (string memory); - function totalSupply() external view returns (uint256); - function transfer(address _to, uint256 _value) external returns (bool); - function transferFrom(address _from, address _to, uint256 _value) external returns (bool); -} diff --git a/src/callbacks/liquidity/Cleopatra/lib/IVotingEscrow.sol b/src/callbacks/liquidity/Cleopatra/lib/IVotingEscrow.sol deleted file mode 100644 index 292169c8..00000000 --- a/src/callbacks/liquidity/Cleopatra/lib/IVotingEscrow.sol +++ /dev/null @@ -1,68 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity =0.7.6 || ^0.8.13; -pragma abicoder v2; - -interface IVotingEscrow { - struct Point { - int128 bias; - int128 slope; // # -dweight / dt - uint256 ts; - uint256 blk; // block - } - - struct LockedBalance { - int128 amount; - uint256 end; - } - - function token() external view returns (address); - - function team() external returns (address); - - function epoch() external view returns (uint256); - - function point_history(uint256 loc) external view returns (Point memory); - - function user_point_history( - uint256 tokenId, - uint256 loc - ) external view returns (Point memory); - - function user_point_epoch(uint256 tokenId) external view returns (uint256); - - function ownerOf(uint256) external view returns (address); - - function isApprovedOrOwner(address, uint256) external view returns (bool); - - function transferFrom(address, address, uint256) external; - - function voting(uint256 tokenId) external; - - function abstain(uint256 tokenId) external; - - function attach(uint256 tokenId) external; - - function detach(uint256 tokenId) external; - - function checkpoint() external; - - function deposit_for(uint256 tokenId, uint256 value) external; - - function create_lock_for(uint256, uint256, address) external returns (uint256); - - function balanceOfNFT(uint256) external view returns (uint256); - - function balanceOfNFTAt(uint256, uint256) external view returns (uint256); - - function totalSupply() external view returns (uint256); - - function locked__end(uint256) external view returns (uint256); - - function balanceOf(address) external view returns (uint256); - - function tokenOfOwnerByIndex(address, uint256) external view returns (uint256); - - function locked(uint256) external view returns (LockedBalance memory); - - function isDelegate(address _operator, uint256 _tokenId) external view returns (bool); -} diff --git a/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol b/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol index d50756d7..09d5a8cf 100644 --- a/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol +++ b/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol @@ -21,8 +21,8 @@ import {CleopatraV2DirectToLiquidity} from "../../../../src/callbacks/liquidity/ import {ICleopatraV2Factory} from "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Factory.sol"; import {ICleopatraV2PositionManager} from "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2PositionManager.sol"; -import {IVotingEscrow} from "../../../../src/callbacks/liquidity/Cleopatra/lib/IVotingEscrow.sol"; -import {IRAM} from "../../../../src/callbacks/liquidity/Cleopatra/lib/IRAM.sol"; +// import {IVotingEscrow} from "../../../../src/callbacks/liquidity/Cleopatra/lib/IVotingEscrow.sol"; +// import {IRAM} from "../../../../src/callbacks/liquidity/Cleopatra/lib/IRAM.sol"; // Axis core import {keycodeFromVeecode, toKeycode} from "@axis-core-1.0.0/modules/Keycode.sol"; @@ -292,42 +292,42 @@ abstract contract CleopatraV2DirectToLiquidityTest is Test, Permit2User, WithSal _; } - function _createVeRamDeposit() internal { - // Mint RAM - vm.prank(address(_ram.minter())); - _ram.mint(_SELLER, 1e18); - - // Approve spending - address veRam = address(_positionManager.veRam()); - vm.prank(_SELLER); - _ram.approve(veRam, 1e18); - - // Deposit into the voting escrow - vm.startPrank(_SELLER); - uint256 tokenId = IVotingEscrow(veRam).create_lock_for( - 1e18, 24 hours * 365, _SELLER - ); - vm.stopPrank(); - - // Update the callback - _setVeRamTokenId(tokenId); - } - - function _mockVeRamTokenIdApproved(uint256 veRamTokenId_, bool approved_) internal { - // TODO this could be shifted to an actual approval, but I couldn't figure out how - vm.mockCall( - address(_positionManager.veRam()), - abi.encodeWithSelector( - IVotingEscrow.isApprovedOrOwner.selector, address(_dtl), veRamTokenId_ - ), - abi.encode(approved_) - ); - } - - modifier givenVeRamTokenIdApproval(bool approved_) { - // _mockVeRamTokenIdApproved(_cleopatraCreateParams.veRamTokenId, approved_); - _; - } + // function _createVeRamDeposit() internal { + // // Mint RAM + // vm.prank(address(_ram.minter())); + // _ram.mint(_SELLER, 1e18); + + // // Approve spending + // address veRam = address(_positionManager.veRam()); + // vm.prank(_SELLER); + // _ram.approve(veRam, 1e18); + + // // Deposit into the voting escrow + // vm.startPrank(_SELLER); + // uint256 tokenId = IVotingEscrow(veRam).create_lock_for( + // 1e18, 24 hours * 365, _SELLER + // ); + // vm.stopPrank(); + + // // Update the callback + // _setVeRamTokenId(tokenId); + // } + + // function _mockVeRamTokenIdApproved(uint256 veRamTokenId_, bool approved_) internal { + // // TODO this could be shifted to an actual approval, but I couldn't figure out how + // vm.mockCall( + // address(_positionManager.veRam()), + // abi.encodeWithSelector( + // IVotingEscrow.isApprovedOrOwner.selector, address(_dtl), veRamTokenId_ + // ), + // abi.encode(approved_) + // ); + // } + + // modifier givenVeRamTokenIdApproval(bool approved_) { + // _mockVeRamTokenIdApproved(_cleopatraCreateParams.veRamTokenId, approved_); + // _; + // } // ========== FUNCTIONS ========== // From 3fa7730e22fc770a5c0ff98db0658413526669f8 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 29 Jul 2024 10:17:13 +0400 Subject: [PATCH 39/47] chore: linting --- script/deploy/Deploy.s.sol | 9 ++++++--- .../dtl-cleopatra/CleopatraDTLSalts.s.sol | 6 ++++-- script/salts/test/TestSalts.s.sol | 6 ++++-- .../CleopatraV1/CleopatraV1DTLTest.sol | 18 ++++++++++++----- .../liquidity/CleopatraV1/onCreate.t.sol | 20 +++++++++++++------ .../liquidity/CleopatraV1/onSettle.t.sol | 6 ++++-- .../CleopatraV2/CleopatraV2DTLTest.sol | 13 +++++++++--- .../liquidity/CleopatraV2/onCreate.t.sol | 15 ++++++++------ .../liquidity/CleopatraV2/onSettle.t.sol | 3 ++- 9 files changed, 66 insertions(+), 30 deletions(-) diff --git a/script/deploy/Deploy.s.sol b/script/deploy/Deploy.s.sol index 548da046..c3b70244 100644 --- a/script/deploy/Deploy.s.sol +++ b/script/deploy/Deploy.s.sol @@ -33,11 +33,14 @@ import {BALwithCappedAllowlist} from "../../src/callbacks/liquidity/BaselineV2/BALwithCappedAllowlist.sol"; import {BALwithTokenAllowlist} from "../../src/callbacks/liquidity/BaselineV2/BALwithTokenAllowlist.sol"; -import {CleopatraV1DirectToLiquidity} from "../../src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol"; -import {CleopatraV2DirectToLiquidity} from "../../src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol"; +import {CleopatraV1DirectToLiquidity} from + "../../src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol"; +import {CleopatraV2DirectToLiquidity} from + "../../src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol"; // Cleopatra -import {ICleopatraV1Router} from "../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Router.sol"; +import {ICleopatraV1Router} from + "../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Router.sol"; import {ICleopatraV2PositionManager} from "../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2PositionManager.sol"; diff --git a/script/salts/dtl-cleopatra/CleopatraDTLSalts.s.sol b/script/salts/dtl-cleopatra/CleopatraDTLSalts.s.sol index 1a684378..64c300ae 100644 --- a/script/salts/dtl-cleopatra/CleopatraDTLSalts.s.sol +++ b/script/salts/dtl-cleopatra/CleopatraDTLSalts.s.sol @@ -7,8 +7,10 @@ import {WithSalts} from "../WithSalts.s.sol"; import {WithDeploySequence} from "../../deploy/WithDeploySequence.s.sol"; // Cleopatra -import {CleopatraV1DirectToLiquidity} from "../../../src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol"; -import {CleopatraV2DirectToLiquidity} from "../../../src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol"; +import {CleopatraV1DirectToLiquidity} from + "../../../src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol"; +import {CleopatraV2DirectToLiquidity} from + "../../../src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol"; contract CleopatraDTLSalts is Script, WithDeploySequence, WithSalts { string internal constant _ADDRESS_PREFIX = "E6"; diff --git a/script/salts/test/TestSalts.s.sol b/script/salts/test/TestSalts.s.sol index dbb4e894..ebe5f7c3 100644 --- a/script/salts/test/TestSalts.s.sol +++ b/script/salts/test/TestSalts.s.sol @@ -34,8 +34,10 @@ import {BALwithCappedAllowlist} from "../../../src/callbacks/liquidity/BaselineV2/BALwithCappedAllowlist.sol"; import {BALwithTokenAllowlist} from "../../../src/callbacks/liquidity/BaselineV2/BALwithTokenAllowlist.sol"; -import {CleopatraV1DirectToLiquidity} from "../../../src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol"; -import {CleopatraV2DirectToLiquidity} from "../../../src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol"; +import {CleopatraV1DirectToLiquidity} from + "../../../src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol"; +import {CleopatraV2DirectToLiquidity} from + "../../../src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol"; contract TestSalts is Script, WithEnvironment, Permit2User, WithSalts, TestConstants { string internal constant _CAPPED_MERKLE_ALLOWLIST = "CappedMerkleAllowlist"; diff --git a/test/callbacks/liquidity/CleopatraV1/CleopatraV1DTLTest.sol b/test/callbacks/liquidity/CleopatraV1/CleopatraV1DTLTest.sol index 6188b119..8629dd15 100644 --- a/test/callbacks/liquidity/CleopatraV1/CleopatraV1DTLTest.sol +++ b/test/callbacks/liquidity/CleopatraV1/CleopatraV1DTLTest.sol @@ -17,9 +17,12 @@ import {MockBatchAuctionModule} from import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; // Cleopatra -import {CleopatraV1DirectToLiquidity} from "../../../../src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol"; -import {ICleopatraV1Factory} from "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Factory.sol"; -import {ICleopatraV1Router} from "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Router.sol"; +import {CleopatraV1DirectToLiquidity} from + "../../../../src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol"; +import {ICleopatraV1Factory} from + "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Factory.sol"; +import {ICleopatraV1Router} from + "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Router.sol"; // Axis core import {keycodeFromVeecode, toKeycode} from "@axis-core-1.0.0/modules/Keycode.sol"; @@ -28,7 +31,12 @@ import {IAuctionHouse} from "@axis-core-1.0.0/interfaces/IAuctionHouse.sol"; import {BatchAuctionHouse} from "@axis-core-1.0.0/BatchAuctionHouse.sol"; import {LinearVesting} from "@axis-core-1.0.0/modules/derivatives/LinearVesting.sol"; -abstract contract CleopatraV1DirectToLiquidityTest is Test, Permit2User, WithSalts, TestConstants { +abstract contract CleopatraV1DirectToLiquidityTest is + Test, + Permit2User, + WithSalts, + TestConstants +{ using Callbacks for CleopatraV1DirectToLiquidity; address internal constant _SELLER = address(0x2); @@ -59,7 +67,7 @@ abstract contract CleopatraV1DirectToLiquidityTest is Test, Permit2User, WithSal // Inputs CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams internal _cleopatraCreateParams = - CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams({stable: false, maxSlippage: uint24(0)}); + CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams({stable: false, maxSlippage: uint24(0)}); BaseDirectToLiquidity.OnCreateParams internal _dtlCreateParams = BaseDirectToLiquidity .OnCreateParams({ proceedsUtilisationPercent: 100e2, diff --git a/test/callbacks/liquidity/CleopatraV1/onCreate.t.sol b/test/callbacks/liquidity/CleopatraV1/onCreate.t.sol index 8ff8d9cd..12743cb2 100644 --- a/test/callbacks/liquidity/CleopatraV1/onCreate.t.sol +++ b/test/callbacks/liquidity/CleopatraV1/onCreate.t.sol @@ -5,7 +5,8 @@ import {CleopatraV1DirectToLiquidityTest} from "./CleopatraV1DTLTest.sol"; import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; -import {CleopatraV1DirectToLiquidity} from "../../../../src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol"; +import {CleopatraV1DirectToLiquidity} from + "../../../../src/callbacks/liquidity/Cleopatra/CleopatraV1DTL.sol"; contract CleopatraV1DTLOnCreateForkTest is CleopatraV1DirectToLiquidityTest { // ============ Modifiers ============ // @@ -323,11 +324,14 @@ contract CleopatraV1DTLOnCreateForkTest is CleopatraV1DirectToLiquidityTest { assertEq(configuration.active, true, "active"); assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); - CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams memory cleopatraCreateParams = abi.decode( + CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams memory cleopatraCreateParams = abi + .decode( _dtlCreateParams.implParams, (CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams) ); assertEq(cleopatraCreateParams.stable, _cleopatraCreateParams.stable, "stable"); - assertEq(cleopatraCreateParams.maxSlippage, _cleopatraCreateParams.maxSlippage, "maxSlippage"); + assertEq( + cleopatraCreateParams.maxSlippage, _cleopatraCreateParams.maxSlippage, "maxSlippage" + ); // Assert balances _assertBaseTokenBalances(); @@ -360,11 +364,14 @@ contract CleopatraV1DTLOnCreateForkTest is CleopatraV1DirectToLiquidityTest { assertEq(configuration.active, true, "active"); assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); - CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams memory cleopatraCreateParams = abi.decode( + CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams memory cleopatraCreateParams = abi + .decode( _dtlCreateParams.implParams, (CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams) ); assertEq(cleopatraCreateParams.stable, _cleopatraCreateParams.stable, "stable"); - assertEq(cleopatraCreateParams.maxSlippage, _cleopatraCreateParams.maxSlippage, "maxSlippage"); + assertEq( + cleopatraCreateParams.maxSlippage, _cleopatraCreateParams.maxSlippage, "maxSlippage" + ); // Assert balances _assertBaseTokenBalances(); @@ -382,7 +389,8 @@ contract CleopatraV1DTLOnCreateForkTest is CleopatraV1DirectToLiquidityTest { BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); - CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams memory cleopatraCreateParams = abi.decode( + CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams memory cleopatraCreateParams = abi + .decode( _dtlCreateParams.implParams, (CleopatraV1DirectToLiquidity.CleopatraV1OnCreateParams) ); assertEq(cleopatraCreateParams.stable, _cleopatraCreateParams.stable, "stable"); diff --git a/test/callbacks/liquidity/CleopatraV1/onSettle.t.sol b/test/callbacks/liquidity/CleopatraV1/onSettle.t.sol index c08bf96f..0b277d8d 100644 --- a/test/callbacks/liquidity/CleopatraV1/onSettle.t.sol +++ b/test/callbacks/liquidity/CleopatraV1/onSettle.t.sol @@ -8,7 +8,8 @@ import {FixedPointMathLib} from "@solmate-6.7.0/utils/FixedPointMathLib.sol"; import {ERC20} from "@solmate-6.7.0/tokens/ERC20.sol"; // Cleopatra -import {ICleopatraV1Pool} from "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Pool.sol"; +import {ICleopatraV1Pool} from + "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Pool.sol"; // AuctionHouse import {ILinearVesting} from "@axis-core-1.0.0/interfaces/modules/derivatives/ILinearVesting.sol"; @@ -32,7 +33,8 @@ contract CleopatraV1OnSettleForkTest is CleopatraV1DirectToLiquidityTest { // ========== Internal functions ========== // function _getCleopatraV1Pool(bool stable_) internal view returns (ICleopatraV1Pool) { - return ICleopatraV1Pool(_factory.getPair(address(_quoteToken), address(_baseToken), stable_)); + return + ICleopatraV1Pool(_factory.getPair(address(_quoteToken), address(_baseToken), stable_)); } function _getCleopatraV1Pool() internal view returns (ICleopatraV1Pool) { diff --git a/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol b/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol index 09d5a8cf..985ac6be 100644 --- a/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol +++ b/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol @@ -17,8 +17,10 @@ import {MockBatchAuctionModule} from import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; // Cleopatra -import {CleopatraV2DirectToLiquidity} from "../../../../src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol"; -import {ICleopatraV2Factory} from "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Factory.sol"; +import {CleopatraV2DirectToLiquidity} from + "../../../../src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol"; +import {ICleopatraV2Factory} from + "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Factory.sol"; import {ICleopatraV2PositionManager} from "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2PositionManager.sol"; // import {IVotingEscrow} from "../../../../src/callbacks/liquidity/Cleopatra/lib/IVotingEscrow.sol"; @@ -31,7 +33,12 @@ import {IAuctionHouse} from "@axis-core-1.0.0/interfaces/IAuctionHouse.sol"; import {BatchAuctionHouse} from "@axis-core-1.0.0/BatchAuctionHouse.sol"; import {LinearVesting} from "@axis-core-1.0.0/modules/derivatives/LinearVesting.sol"; -abstract contract CleopatraV2DirectToLiquidityTest is Test, Permit2User, WithSalts, TestConstants { +abstract contract CleopatraV2DirectToLiquidityTest is + Test, + Permit2User, + WithSalts, + TestConstants +{ using Callbacks for CleopatraV2DirectToLiquidity; address internal constant _SELLER = address(0x2); diff --git a/test/callbacks/liquidity/CleopatraV2/onCreate.t.sol b/test/callbacks/liquidity/CleopatraV2/onCreate.t.sol index 71b0bf4e..fe49c205 100644 --- a/test/callbacks/liquidity/CleopatraV2/onCreate.t.sol +++ b/test/callbacks/liquidity/CleopatraV2/onCreate.t.sol @@ -5,7 +5,8 @@ import {CleopatraV2DirectToLiquidityTest} from "./CleopatraV2DTLTest.sol"; import {BaseCallback} from "@axis-core-1.0.0/bases/BaseCallback.sol"; import {BaseDirectToLiquidity} from "../../../../src/callbacks/liquidity/BaseDTL.sol"; -import {CleopatraV2DirectToLiquidity} from "../../../../src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol"; +import {CleopatraV2DirectToLiquidity} from + "../../../../src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol"; contract CleopatraV2DTLOnCreateForkTest is CleopatraV2DirectToLiquidityTest { // ============ Modifiers ============ // @@ -270,10 +271,12 @@ contract CleopatraV2DTLOnCreateForkTest is CleopatraV2DirectToLiquidityTest { assertEq(configuration.active, true, "active"); assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); - CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams memory _cleopatraCreateParams = - abi.decode(configuration.implParams, (CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams)); + CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams memory _cleopatraCreateParams = abi + .decode(configuration.implParams, (CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams)); assertEq(_cleopatraCreateParams.poolFee, _cleopatraCreateParams.poolFee, "poolFee"); - assertEq(_cleopatraCreateParams.maxSlippage, _cleopatraCreateParams.maxSlippage, "maxSlippage"); + assertEq( + _cleopatraCreateParams.maxSlippage, _cleopatraCreateParams.maxSlippage, "maxSlippage" + ); // assertEq(_cleopatraCreateParams.veRamTokenId, _cleopatraCreateParams.veRamTokenId, "veRamTokenId"); // Assert balances @@ -319,8 +322,8 @@ contract CleopatraV2DTLOnCreateForkTest is CleopatraV2DirectToLiquidityTest { BaseDirectToLiquidity.DTLConfiguration memory configuration = _getDTLConfiguration(_lotId); assertEq(configuration.implParams, _dtlCreateParams.implParams, "implParams"); - CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams memory cleopatraCreateParams = - abi.decode(configuration.implParams, (CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams)); + CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams memory cleopatraCreateParams = abi + .decode(configuration.implParams, (CleopatraV2DirectToLiquidity.CleopatraV2OnCreateParams)); assertEq(cleopatraCreateParams.maxSlippage, maxSlippage, "maxSlippage"); } diff --git a/test/callbacks/liquidity/CleopatraV2/onSettle.t.sol b/test/callbacks/liquidity/CleopatraV2/onSettle.t.sol index 7a73b1f9..2359ecfb 100644 --- a/test/callbacks/liquidity/CleopatraV2/onSettle.t.sol +++ b/test/callbacks/liquidity/CleopatraV2/onSettle.t.sol @@ -7,7 +7,8 @@ import {CleopatraV2DirectToLiquidityTest} from "./CleopatraV2DTLTest.sol"; import {FixedPointMathLib} from "@solmate-6.7.0/utils/FixedPointMathLib.sol"; // Cleopatra -import {ICleopatraV2Pool} from "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Pool.sol"; +import {ICleopatraV2Pool} from + "../../../../src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Pool.sol"; import {SqrtPriceMath} from "../../../../src/lib/uniswap-v3/SqrtPriceMath.sol"; // AuctionHouse From 19e58e5eaba9bab5c83ed52a12ec31650cda0b26 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 29 Jul 2024 10:22:36 +0400 Subject: [PATCH 40/47] Remove/disable redundant code --- src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol b/src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol index 4b6dbdd2..37fc9f51 100644 --- a/src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol +++ b/src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol @@ -11,7 +11,6 @@ import {BaseDirectToLiquidity} from "../BaseDTL.sol"; import {ICleopatraV2Pool} from "./lib/ICleopatraV2Pool.sol"; import {ICleopatraV2Factory} from "./lib/ICleopatraV2Factory.sol"; import {ICleopatraV2PositionManager} from "./lib/ICleopatraV2PositionManager.sol"; -import {IVotingEscrow} from "./lib/IVotingEscrow.sol"; // Uniswap import {TickMath} from "@uniswap-v3-core-1.0.1-solc-0.8-simulate/libraries/TickMath.sol"; From 4a642eb3b5213614a72e011f4fc373eaaee123c2 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 29 Jul 2024 10:23:32 +0400 Subject: [PATCH 41/47] Remove/disable redundant code --- .../CleopatraV2/CleopatraV2DTLTest.sol | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol b/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol index 985ac6be..b76bb228 100644 --- a/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol +++ b/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol @@ -57,7 +57,7 @@ abstract contract CleopatraV2DirectToLiquidityTest is address internal _dtlAddress; ICleopatraV2Factory internal _factory; ICleopatraV2PositionManager internal _positionManager; - IRAM internal _ram; + // IRAM internal _ram; MockBatchAuctionModule internal _batchAuctionModule; MockERC20 internal _quoteToken; @@ -261,15 +261,15 @@ abstract contract CleopatraV2DirectToLiquidityTest is _; } - function _setVeRamTokenId(uint256 veRamTokenId_) internal { - // _cleopatraCreateParams.veRamTokenId = veRamTokenId_; - _dtlCreateParams.implParams = abi.encode(_cleopatraCreateParams); - } + // function _setVeRamTokenId(uint256 veRamTokenId_) internal { + // _cleopatraCreateParams.veRamTokenId = veRamTokenId_; + // _dtlCreateParams.implParams = abi.encode(_cleopatraCreateParams); + // } - modifier givenVeRamTokenId() { - _createVeRamDeposit(); - _; - } + // modifier givenVeRamTokenId() { + // _createVeRamDeposit(); + // _; + // } modifier givenVestingStart(uint48 start_) { _dtlCreateParams.vestingStart = start_; From ec1067c0ab3c0e65baeebf3059e4e1c0cc81c8cf Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 29 Jul 2024 10:29:32 +0400 Subject: [PATCH 42/47] Update Cleopatra interfaces --- .../Cleopatra/lib/ICleopatraV1Factory.sol | 23 +++++++++++-------- .../Cleopatra/lib/ICleopatraV1Router.sol | 4 ++-- .../Cleopatra/lib/ICleopatraV2Factory.sol | 16 +++++++++---- .../lib/ICleopatraV2PositionManager.sol | 18 ++++++--------- 4 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Factory.sol b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Factory.sol index 7d7d8c25..8e5d780a 100644 --- a/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Factory.sol +++ b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Factory.sol @@ -1,20 +1,22 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.4; -/// @dev Generated from https://arbiscan.io/address/0xaa9b8a7430474119a442ef0c2bf88f7c3c776f2f interface ICleopatraV1Factory { + event ImplementationChanged( + address indexed oldImplementation, address indexed newImplementation + ); event Initialized(uint8 version); + event OwnerChanged(address indexed oldOwner, address indexed newOwner); event PairCreated( address indexed token0, address indexed token1, bool stable, address pair, uint256 ); - event SetFee(bool stable, uint256 fee); event SetFeeSplit(uint8 toFeesOld, uint8 toTreasuryOld, uint8 toFeesNew, uint8 toTreasuryNew); - event SetPairFee(address pair, uint256 fee); event SetPoolFeeSplit( address pool, uint8 toFeesOld, uint8 toTreasuryOld, uint8 toFeesNew, uint8 toTreasuryNew ); function MAX_FEE() external view returns (uint256); + function _pairFee(address pair) external view returns (uint256 fee); function acceptFeeManager() external; function acceptPauser() external; function allPairs(uint256) external view returns (address); @@ -28,27 +30,28 @@ interface ICleopatraV1Factory { function feeSplit() external view returns (uint8); function getFee(bool _stable) external view returns (uint256); function getPair(address, address, bool) external view returns (address); + function getPairFee(address _pair, bool _stable) external view returns (uint256); function getPoolFeeSplit(address _pool) external view returns (uint8 _poolFeeSplit); + function implementation() external view returns (address); function initialize( - address _proxyAdmin, - address _pairImplementation, address _voter, - address msig + address msig, + address _owner, + address _implementation ) external; - function initializeTreasury() external; function isPair(address) external view returns (bool); function isPaused() external view returns (bool); - function pairCodeHash() external view returns (bytes32); + function owner() external view returns (address); + function pairCodeHash() external pure returns (bytes32); function pairFee(address _pool) external view returns (uint256 fee); - function pairImplementation() external view returns (address); function pauser() external view returns (address); function pendingFeeManager() external view returns (address); function pendingPauser() external view returns (address); - function proxyAdmin() external view returns (address); function setFee(bool _stable, uint256 _fee) external; function setFeeManager(address _feeManager) external; function setFeeSplit(uint8 _toFees, uint8 _toTreasury) external; function setImplementation(address _implementation) external; + function setOwner(address _owner) external; function setPairFee(address _pair, uint256 _fee) external; function setPause(bool _state) external; function setPauser(address _pauser) external; diff --git a/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Router.sol b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Router.sol index 9be088e2..19521f9c 100644 --- a/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Router.sol +++ b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV1Router.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.4; -/// @dev Generated from https://arbiscan.io/address/0x0e216dd4f1b5ea81006d41b79f9a1a69a38f3e37 interface ICleopatraV1Router { struct route { address from; @@ -54,7 +53,7 @@ interface ICleopatraV1Router { address tokenB, bool stable ) external view returns (uint256 reserveA, uint256 reserveB); - function initialize(address _factory, address _weth) external; + function initialize(address _factory, address _weth, address _timelock) external; function isPair(address pair) external view returns (bool); function pairFor( address tokenA, @@ -196,5 +195,6 @@ interface ICleopatraV1Router { address to, uint256 deadline ) external; + function timelock() external view returns (address); function weth() external view returns (address); } diff --git a/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Factory.sol b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Factory.sol index b0cf5340..47d9559f 100644 --- a/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Factory.sol +++ b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2Factory.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.4; -/// @dev Generated from https://arbiscan.io/address/0xf896d16fa56a625802b6013f9f9202790ec69908 interface ICleopatraV2Factory { event FeeAmountEnabled(uint24 indexed fee, int24 indexed tickSpacing); event FeeCollectorChanged(address indexed oldFeeCollector, address indexed newFeeCollector); @@ -32,7 +31,8 @@ interface ICleopatraV2Factory { function createPool( address tokenA, address tokenB, - uint24 fee + uint24 fee, + uint160 sqrtPriceX96 ) external returns (address pool); function enableFeeAmount(uint24 fee, int24 tickSpacing) external; function feeAmountTickSpacing(uint24) external view returns (int24); @@ -43,10 +43,17 @@ interface ICleopatraV2Factory { function implementation() external view returns (address); function initialize( address _nfpManager, - address _veRam, + address _votingEscrow, address _voter, address _implementation ) external; + function initializeFeeSetter() external; + function initializePool( + address token0, + address token1, + uint24 fee, + uint160 sqrtPriceX96 + ) external returns (address pool); function nfpManager() external view returns (address); function owner() external view returns (address); function poolFeeProtocol(address pool) external view returns (uint8 __poolFeeProtocol); @@ -56,8 +63,7 @@ interface ICleopatraV2Factory { function setFeeSetter(address _newFeeSetter) external; function setImplementation(address _implementation) external; function setOwner(address _owner) external; - function setPoolFeeProtocol(address pool, uint8 _feeProtocol) external; function setPoolFeeProtocol(address pool, uint8 feeProtocol0, uint8 feeProtocol1) external; - function veRam() external view returns (address); function voter() external view returns (address); + function votingEscrow() external view returns (address); } diff --git a/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2PositionManager.sol b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2PositionManager.sol index 318b5fe1..c2ad566c 100644 --- a/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2PositionManager.sol +++ b/src/callbacks/liquidity/Cleopatra/lib/ICleopatraV2PositionManager.sol @@ -1,7 +1,6 @@ // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.4; -/// @dev Generated from https://arbiscan.io/address/0xac9d1dfa5483ebb06e623df31547b2a4dc8bf7ca interface ICleopatraV2PositionManager { struct CollectParams { uint256 tokenId; @@ -39,7 +38,7 @@ interface ICleopatraV2PositionManager { uint256 amount1Min; address recipient; uint256 deadline; - uint256 veRamTokenId; + uint256 veNFTTokenId; } event Approval(address indexed owner, address indexed approved, uint256 indexed tokenId); @@ -51,9 +50,7 @@ interface ICleopatraV2PositionManager { event IncreaseLiquidity( uint256 indexed tokenId, uint128 liquidity, uint256 amount0, uint256 amount1 ); - event SwitchAttachment( - uint256 indexed tokenId, uint256 oldVeRamTokenId, uint256 newVeRamTokenId - ); + event SwitchAttachment(uint256 indexed tokenId, uint256 oldVeTokenId, uint256 newVeTokenId); event Transfer(address indexed from, address indexed to, uint256 indexed tokenId); receive() external payable; @@ -87,8 +84,8 @@ interface ICleopatraV2PositionManager { external payable returns (uint128 liquidity, uint256 amount0, uint256 amount1); - function initialize() external; function isApprovedForAll(address owner, address operator) external view returns (bool); + function isApprovedOrOwner(address spender, uint256 tokenId) external view returns (bool); function mint(MintParams memory params) external payable @@ -121,7 +118,7 @@ interface ICleopatraV2PositionManager { uint128 tokensOwed0, uint128 tokensOwed1 ); - function cleopatraV2MintCallback( + function ramsesV2MintCallback( uint256 amount0Owed, uint256 amount1Owed, bytes memory data @@ -169,15 +166,14 @@ interface ICleopatraV2PositionManager { function setApprovalForAll(address operator, bool approved) external; function supportsInterface(bytes4 interfaceId) external view returns (bool); function sweepToken(address token, uint256 amountMinimum, address recipient) external payable; - function switchAttachment(uint256 tokenId, uint256 veRamTokenId) external; + function switchAttachment(uint256 tokenId, uint256 veNftTokenId) external; function symbol() external view returns (string memory); - function timelock() external pure returns (address _timelock); function tokenByIndex(uint256 index) external view returns (uint256); function tokenOfOwnerByIndex(address owner, uint256 index) external view returns (uint256); function tokenURI(uint256 tokenId) external view returns (string memory); function totalSupply() external view returns (uint256); function transferFrom(address from, address to, uint256 tokenId) external; function unwrapWETH9(uint256 amountMinimum, address recipient) external payable; - function veRam() external view returns (address); - function voter() external pure returns (address _voter); + function voter() external view returns (address); + function votingEscrow() external view returns (address); } From bcf6fb634184b112de0753c1ebbfc4d77f234207 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 29 Jul 2024 10:37:40 +0400 Subject: [PATCH 43/47] Update tests for new interfaces --- src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol | 5 ++--- .../liquidity/CleopatraV2/CleopatraV2DTLTest.sol | 8 ++++---- test/callbacks/liquidity/CleopatraV2/onCreate.t.sol | 6 +++++- test/callbacks/liquidity/CleopatraV2/onSettle.t.sol | 12 +----------- 4 files changed, 12 insertions(+), 19 deletions(-) diff --git a/src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol b/src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol index 37fc9f51..88f6cc7d 100644 --- a/src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol +++ b/src/callbacks/liquidity/Cleopatra/CleopatraV2DTL.sol @@ -192,8 +192,7 @@ contract CleopatraV2DirectToLiquidity is BaseDirectToLiquidity { pool = cleopatraV2Factory.getPool(token0, token1, fee); if (pool == address(0)) { - pool = cleopatraV2Factory.createPool(token0, token1, fee); - ICleopatraV2Pool(pool).initialize(sqrtPriceX96); + pool = cleopatraV2Factory.createPool(token0, token1, fee, sqrtPriceX96); } else { (uint160 sqrtPriceX96Existing,,,,,,) = ICleopatraV2Pool(pool).slot0(); if (sqrtPriceX96Existing == 0) { @@ -232,7 +231,7 @@ contract CleopatraV2DirectToLiquidity is BaseDirectToLiquidity { amount1Min: _getAmountWithSlippage(amount1, params.maxSlippage), recipient: lotConfiguration[lotId_].recipient, deadline: block.timestamp, - veRamTokenId: 0 // Not supported at the moment + veNFTTokenId: 0 // Not supported at the moment }); } diff --git a/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol b/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol index b76bb228..7997e43f 100644 --- a/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol +++ b/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol @@ -286,16 +286,16 @@ abstract contract CleopatraV2DirectToLiquidityTest is _; } - function _createPool() internal returns (address) { + function _createPool(uint160 sqrtPriceX96_) internal returns (address) { (address token0, address token1) = address(_baseToken) < address(_quoteToken) ? (address(_baseToken), address(_quoteToken)) : (address(_quoteToken), address(_baseToken)); - return _factory.createPool(token0, token1, _cleopatraCreateParams.poolFee); + return _factory.createPool(token0, token1, _cleopatraCreateParams.poolFee, sqrtPriceX96_); } - modifier givenPoolIsCreated() { - _createPool(); + modifier givenPoolIsCreatedAndInitialized(uint160 sqrtPriceX96_) { + address pool = _createPool(sqrtPriceX96_); _; } diff --git a/test/callbacks/liquidity/CleopatraV2/onCreate.t.sol b/test/callbacks/liquidity/CleopatraV2/onCreate.t.sol index fe49c205..67b4b514 100644 --- a/test/callbacks/liquidity/CleopatraV2/onCreate.t.sol +++ b/test/callbacks/liquidity/CleopatraV2/onCreate.t.sol @@ -164,7 +164,11 @@ contract CleopatraV2DTLOnCreateForkTest is CleopatraV2DirectToLiquidityTest { _performOnCreate(); } - function test_givenPoolExists() public givenCallbackIsCreated givenPoolIsCreated { + function test_givenPoolExists() + public + givenCallbackIsCreated + givenPoolIsCreatedAndInitialized(0) + { _performOnCreate(); // Assert values diff --git a/test/callbacks/liquidity/CleopatraV2/onSettle.t.sol b/test/callbacks/liquidity/CleopatraV2/onSettle.t.sol index 2359ecfb..eba9bc92 100644 --- a/test/callbacks/liquidity/CleopatraV2/onSettle.t.sol +++ b/test/callbacks/liquidity/CleopatraV2/onSettle.t.sol @@ -84,16 +84,6 @@ contract CleopatraV2DTLOnSettleForkTest is CleopatraV2DirectToLiquidityTest { // ========== Modifiers ========== // - function _initializePool(address pool_, uint160 sqrtPriceX96_) internal { - ICleopatraV2Pool(pool_).initialize(sqrtPriceX96_); - } - - modifier givenPoolIsCreatedAndInitialized(uint160 sqrtPriceX96_) { - address pool = _createPool(); - _initializePool(pool, sqrtPriceX96_); - _; - } - function _calculateSqrtPriceX96( uint256 quoteTokenAmount_, uint256 baseTokenAmount_ @@ -204,8 +194,8 @@ contract CleopatraV2DTLOnSettleForkTest is CleopatraV2DirectToLiquidityTest { public givenCallbackIsCreated givenOnCreate - givenPoolIsCreated setCallbackParameters(_PROCEEDS, _REFUND) + givenPoolIsCreatedAndInitialized(_sqrtPriceX96) givenAddressHasQuoteTokenBalance(_dtlAddress, _proceeds) givenAddressHasBaseTokenBalance(_SELLER, _capacityUtilised) givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _capacityUtilised) From 78eed6a9b00af6677126f60a9932ef7b57b25f48 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 29 Jul 2024 10:37:48 +0400 Subject: [PATCH 44/47] Adjust fork settings --- .../liquidity/CleopatraV1/CleopatraV1DTLTest.sol | 8 ++++---- .../liquidity/CleopatraV2/CleopatraV2DTLTest.sol | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/test/callbacks/liquidity/CleopatraV1/CleopatraV1DTLTest.sol b/test/callbacks/liquidity/CleopatraV1/CleopatraV1DTLTest.sol index 8629dd15..d6d2463b 100644 --- a/test/callbacks/liquidity/CleopatraV1/CleopatraV1DTLTest.sol +++ b/test/callbacks/liquidity/CleopatraV1/CleopatraV1DTLTest.sol @@ -78,10 +78,10 @@ abstract contract CleopatraV1DirectToLiquidityTest is }); function setUp() public { - // Create a fork on Arbitrum - string memory arbitrumRpcUrl = vm.envString("ARBITRUM_RPC_URL"); - vm.createSelectFork(arbitrumRpcUrl); - require(block.chainid == 42_161, "Must be on Arbitrum"); + // Create a fork on Mantle + string memory mantleRpcUrl = vm.envString("MANTLE_RPC_URL"); + vm.createSelectFork(mantleRpcUrl); + require(block.chainid == 5000, "Must be on Mantle"); _initialTimestamp = uint48(block.timestamp); diff --git a/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol b/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol index 7997e43f..811afc33 100644 --- a/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol +++ b/test/callbacks/liquidity/CleopatraV2/CleopatraV2DTLTest.sol @@ -82,10 +82,10 @@ abstract contract CleopatraV2DirectToLiquidityTest is }); function setUp() public { - // Create a fork on Arbitrum - string memory arbitrumRpcUrl = vm.envString("ARBITRUM_RPC_URL"); - vm.createSelectFork(arbitrumRpcUrl); - require(block.chainid == 42_161, "Must be on Arbitrum"); + // Create a fork on Mantle + string memory mantleRpcUrl = vm.envString("MANTLE_RPC_URL"); + vm.createSelectFork(mantleRpcUrl); + require(block.chainid == 5000, "Must be on Mantle"); _initialTimestamp = uint48(block.timestamp); From bda83441ef7a57e077a455e99e5168f049df191c Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 29 Jul 2024 10:38:41 +0400 Subject: [PATCH 45/47] Test salts --- script/salts/salts.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/script/salts/salts.json b/script/salts/salts.json index 6ae490ed..1c3449a1 100644 --- a/script/salts/salts.json +++ b/script/salts/salts.json @@ -71,6 +71,12 @@ "0x1dc038ba91f15889eedf059525478d760c3b01dbf5288c0c995ef5c8f1395e8b": "0x6bfac83b21063468377650c02a19f8b39750ffa72f1763596c48d62833e54e12", "0xb092f03e11d329d47afaec052626436946facd0fa3cb8821145d3dcfc13f6dff": "0x89152c018a4041b7ae10dacb9da894f32cdb91bd07774c0b61ac105db77f12ba" }, + "Test_CleopatraV1DirectToLiquidity": { + "0xc5fdd09bf3deb40dadc05bcec48fce4be48191e7f2413d537c8f52b27549a1d6": "0xc34811ce2ea63c87da00e769e3963413fb9140d8d05c518168e3a20d918b1fd0" + }, + "Test_CleopatraV2DirectToLiquidity": { + "0x26c36d9cca50ed8dd922d52f9237db0e6b79696b1bdd94faaa39ad9f12e3a589": "0x873b1c35209448361c305aa3d658b7b6361e0897668adc2770b286bc3b7b6919" + }, "Test_GUniFactory": { "0x049627578a379e876af59b3ba6d1971a2095f048ea7dafb5449c84380e8bfd15": "0x32842bb4a2d9dcf8ae6970d43167191b5de3e3faca6c89d40cc1376d75ab61f0" }, @@ -97,12 +103,6 @@ "Test_QuoteToken": { "0x73857a6649000d8d1c6d627e9eda2a6079605eda4cd860cffe3bb939dfe3e3ef": "0xe9bd28f6c7d9ac5ecc0f59b51855093220efab06f25f16c4d0e82f04eabaf13c" }, - "Test_CleopatraV1DirectToLiquidity": { - "0xa39d2640eec5bd97dd5073b05dccd616857fdca9f9e1ccf1894340217e109b4f": "0xf0401a9a5af98fccfba400ba36143831ea05ea5b0c734cb1478c227115322dcd" - }, - "Test_CleopatraV2DirectToLiquidity": { - "0xfa9378779c19bf226919c2116dc8e8c5eda1d74f888d67a122685e7fd9be77de": "0x5b3de30d1297453a8f0901d01dcba86a83418e2ae11d1f9bc638d680fbbc09f4" - }, "Test_TokenAllowlist": { "0x3cf6404d9502da98e1ed6560fce585a0a87f56f598397915ef5a6207eb9b439f": "0xd8172a08ee7909a096d99b3ef34fe0d4212c85e77206518e9b5e1640926dde85", "0xdb5689fd93b97ad35ee469bbcb38d4472c005f1ae951b154f2437bd1207a03f3": "0xf35c7565f04f89db7d1783343067728c4db35f70d7aea4d7dbd732fa462bb956" From ea70b2162df03f61b77a80393e9af2db97b1e200 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 29 Jul 2024 10:48:54 +0400 Subject: [PATCH 46/47] Fix test failures --- test/callbacks/liquidity/CleopatraV1/onSettle.t.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/callbacks/liquidity/CleopatraV1/onSettle.t.sol b/test/callbacks/liquidity/CleopatraV1/onSettle.t.sol index 0b277d8d..7ddc2f56 100644 --- a/test/callbacks/liquidity/CleopatraV1/onSettle.t.sol +++ b/test/callbacks/liquidity/CleopatraV1/onSettle.t.sol @@ -396,7 +396,7 @@ contract CleopatraV1OnSettleForkTest is CleopatraV1DirectToLiquidityTest { givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { // Expect revert - vm.expectRevert("Router: INSUFFICIENT_B_AMOUNT"); + vm.expectRevert("INSUFFICIENT B"); _performOnSettle(); } @@ -434,7 +434,7 @@ contract CleopatraV1OnSettleForkTest is CleopatraV1DirectToLiquidityTest { givenAddressHasBaseTokenAllowance(_SELLER, _dtlAddress, _baseTokensToDeposit) { // Expect revert - vm.expectRevert("Router: INSUFFICIENT_A_AMOUNT"); + vm.expectRevert("INSUFFICIENT A"); _performOnSettle(); } From f33c3a2854e75e6d40d6bc70c8b8042278710cc3 Mon Sep 17 00:00:00 2001 From: Jem <0x0xjem@gmail.com> Date: Mon, 29 Jul 2024 12:11:53 +0400 Subject: [PATCH 47/47] Add deployment sequence file. Fix deployment script. Salts. --- script/deploy/Deploy.s.sol | 2 +- script/deploy/sequences/cleopatra-dtl.json | 10 ++++++++++ script/env.json | 14 ++++++-------- script/salts/salts.json | 6 ++++++ 4 files changed, 23 insertions(+), 9 deletions(-) create mode 100644 script/deploy/sequences/cleopatra-dtl.json diff --git a/script/deploy/Deploy.s.sol b/script/deploy/Deploy.s.sol index c3b70244..ff2a6c6e 100644 --- a/script/deploy/Deploy.s.sol +++ b/script/deploy/Deploy.s.sol @@ -1051,7 +1051,7 @@ contract Deploy is Script, WithDeploySequence, WithSalts { // Get the salt bytes32 salt_ = _getSalt( deploymentKey, - type(CleopatraV1DirectToLiquidity).creationCode, + type(CleopatraV2DirectToLiquidity).creationCode, abi.encode(batchAuctionHouse, cleopatraV2Factory, cleopatraV2PositionManager) ); diff --git a/script/deploy/sequences/cleopatra-dtl.json b/script/deploy/sequences/cleopatra-dtl.json new file mode 100644 index 00000000..0c33b672 --- /dev/null +++ b/script/deploy/sequences/cleopatra-dtl.json @@ -0,0 +1,10 @@ +{ + "sequence": [ + { + "name": "BatchCleopatraV1DirectToLiquidity" + }, + { + "name": "BatchCleopatraV2DirectToLiquidity" + } + ] +} diff --git a/script/env.json b/script/env.json index fde6c41a..98407d10 100644 --- a/script/env.json +++ b/script/env.json @@ -160,15 +160,13 @@ }, "mantle": { "constants": { - "gUni": { - "factory": "0x0000000000000000000000000000000000000000" - }, - "uniswapV2": { - "factory": "0x0000000000000000000000000000000000000000", - "router": "0x0000000000000000000000000000000000000000" + "cleopatraV1": { + "pairFactory": "0xAAA16c016BF556fcD620328f0759252E29b1AB57", + "router": "0xAAA45c8F5ef92a000a121d102F4e89278a711Faa" }, - "uniswapV3": { - "factory": "0x0000000000000000000000000000000000000000" + "cleopatraV2": { + "factory": "0xAAA32926fcE6bE95ea2c51cB4Fcb60836D320C42", + "positionManager": "0xAAA78E8C4241990B4ce159E105dA08129345946A" } } }, diff --git a/script/salts/salts.json b/script/salts/salts.json index 1c3449a1..e3c3a88d 100644 --- a/script/salts/salts.json +++ b/script/salts/salts.json @@ -13,6 +13,12 @@ "0xaa5c1bd02f7b04980f0e4ef0778e9165f8404f41c1c2bc372f2902f80dc646b0": "0x3c781e03bd8272c21052708923b6b00b60d976b84bc792a25a8eeb527d166944", "0xc329d36cea27b1f8e044c99a5eda27503fd6087f50b6d27b7bb12ac4493507e1": "0xb724ad108002e85a8144c2b305013b4d302fb1ef4a39d477f6d18e96bc219a3d" }, + "BatchCleopatraV1DirectToLiquidity": { + "0x214daf0edf11bf09aaa737f0eb7d75e2a8d78e8f84cd0fc355c51e0fb3471de1": "0xb5ded029c8eef0136f18c6898b79b9a38972352ba7f2223e4e8d826ac35e5373" + }, + "BatchCleopatraV2DirectToLiquidity": { + "0xde101247ba561eba6901aa19a1c718f71765f182b32e1be09a7f8cb664253de4": "0x59c7481d2d7cd383969401c474b1a88f2a3073387c97877752766392988d1c7c" + }, "CappedMerkleAllowlist": { "0x0249dded8310d17581f166a526ea9ded65b81112ccac0ee8a144e9770d2b8432": "0xf5746fa34aeff9866dc5ec58712f7f47045aea15db39a0aabf4dadc9d35c8854", "0x0ae7777d88dd21a8b9ca3dd9212307295a83aed438fef9dad0b62d57fbaf1025": "0x1de5ae5b126bd2cee8eb4f083080f5c30baa692580cf823fa5f382a7bfc70ac5",