Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Baseline Callback Fixes #24

Merged
merged 14 commits into from
Sep 10, 2024
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## 1.0.1

- Add additional `poolTargetTick` parameter to Baseline callbacks.

## 1.0.0

- Introduces direct-to-liquidity callbacks. Multi-use, permissionless versions are available for UniswapV2 and UniswapV3 pools. Permissioned launches are available for Baseline pools on Blast.
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "axis-periphery",
"version": "0.9.0",
"version": "1.0.1",
"description": "",
"main": "index.js",
"engines": {
Expand Down
10 changes: 5 additions & 5 deletions script/salts/salts.json
Original file line number Diff line number Diff line change
Expand Up @@ -65,19 +65,19 @@
"0xfafe60b7eb7a21b3b9a1b72d01dddba667a59544624fcd58b2b6e02fea347d7f": "0x78e03f1906753cf0f03e02e6af8940b82fe6dd0d45b6b2861c8f9af6bb968696"
},
"Test_BaselineAllocatedAllowlist": {
"0x71af7b8cb507e6f210338d9ecd860830fdae66b8bfcfcd4b09992f478c1db762": "0x75847aa632b809de1dc6e0b65ddcb67cc927630dc73589d0fdc425b8f8fd39a8"
"0xb133668ea5f1cc3afafee11e56078afbbb1ffe4d23ec638987d0477ea495db5a": "0x74781287ef01abc3e621d05060df7f01e92ffdf3b4acb3c79117106ca38581b5"
},
"Test_BaselineAllowlist": {
"0x96af9f52be0a74d16b32511e95a0ab41543135e7365337f5aa5c909f858d1bce": "0x33f9a171ed5fd424f995c2deac8fcff836f2575fb43df0d957456629e54020ab"
"0xe517818434f06d9492471339187e9205747c630ce094ed49ff35e928e33a2283": "0xae95f34753337eabf64d5b98b84d6c5fc1fd29660888c161ad56e62dbfd9dec0"
},
"Test_BaselineAxisLaunch": {
"0x31b0450a728dd3bed00d83e9c5e9b3b4d30a1b83e1527bd587519aab02500667": "0x42c9ff158bfe2f663311f9f75cff7335a86d597946fc892b01b4cd0f20e93dd4"
"0x2f476f9b5bb33e4e5bac03b2629b6e497f3006b1a796f1fca9dcb0cb21f576d2": "0xbc56f8c8efdbf307236c882dc060cc924cfbb12c85019d3d89b8749dd491fb68"
},
"Test_BaselineCappedAllowlist": {
"0x70c1941dfe3f605e8136f574eaa6c2c394fb304755c5d79fc7fd73d6dd5c0361": "0x975b4921658727ab3a8413f512d77e71b735e48f0e3c0c9e56d5a61001c97796"
"0x041e66864210310403c655d560afcb9ba7839b2e1b1dceea80fa9cea470fd076": "0x158fe5ccac21fa721e24a8fcc76f594e049fd715ddca3adb24c41758b60e7085"
},
"Test_BaselineTokenAllowlist": {
"0x21993fcd4b05b8ad948628ddcaf0e8066926a7f2f504009c6b562e5aa9026160": "0xb50386120448533c5d0eaf4d45809bcb3d2631c7109617a60283970a31b09584"
"0x9ae109f43da7475196cc5e20f104a5e8fab1a9d7fabde78a888153e6c7e367c6": "0xf27d72bf7ad7320d3fb0880ec903863125a3de6fa265ded72433b90cf308964d"
},
"Test_CappedMerkleAllowlist": {
"0x29dc229c79db0e97b991dc5816522298310d7167312ac507208acbfdf1dbf5b4": "0xb587e8409114393923bd3bc128bdbcfb7f09ccbd55f1213e94471ae03434c0a6",
Expand Down
56 changes: 43 additions & 13 deletions src/callbacks/liquidity/BaselineV2/BaselineAxisLaunch.sol
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ contract BaselineAxisLaunch is BaseCallback, Policy, Owned {
/// @notice The anchor tick upper is invalid
error Callback_Params_InvalidAnchorTickUpper();

/// @notice The pool target tick is invalid
/// @dev The pool target tick must be >= `anchorRangeLower` and <= `anchorRangeUpper`
///
/// @param anchorRangeLower The lower tick of the anchor range
/// @param anchorRangeUpper The upper tick of the anchor range
error Callback_Params_InvalidPoolTargetTick(int24 anchorRangeLower, int24 anchorRangeUpper);

/// @notice One of the ranges is out of bounds
error Callback_Params_RangeOutOfBounds();

Expand Down Expand Up @@ -125,6 +132,7 @@ contract BaselineAxisLaunch is BaseCallback, Policy, Owned {
/// @param floorRangeGap The gap between the floor and anchor ranges, as a multiple of the pool tick spacing.
/// @param anchorTickU The upper tick of the anchor range. Validated against the calculated upper bound of the anchor range. This is provided off-chain to prevent front-running.
/// @param anchorTickWidth The width of the anchor tick range, as a multiple of the pool tick spacing.
/// @param poolTargetTick The target tick for the pool. This is provided off-chain to prevent front-running.
/// @param allowlistParams Additional parameters for an allowlist, passed to `__onCreate()` for further processing
struct CreateData {
address recipient;
Expand All @@ -133,15 +141,27 @@ contract BaselineAxisLaunch is BaseCallback, Policy, Owned {
int24 floorRangeGap;
int24 anchorTickU;
int24 anchorTickWidth;
int24 poolTargetTick;
bytes allowlistParams;
}

// ========== STATE VARIABLES ========== //
// ========== CONSTANTS ========== //

// TickMath constants
int24 internal constant _MAX_TICK = 887_272;
/// @notice The minimum tick value
int24 internal constant _MIN_TICK = -887_272;

/// @notice The maximum tick value
int24 internal constant _MAX_TICK = 887_272;

/// @notice The value for 100%
// solhint-disable-next-line private-vars-leading-underscore
uint48 internal constant ONE_HUNDRED_PERCENT = 100e2;

/// @notice The tick spacing width of the discovery range
int24 internal constant _DISCOVERY_TICK_SPACING_WIDTH = 350;

// ========== STATE VARIABLES ========== //

// Baseline Modules
// solhint-disable var-name-mixedcase
IBPOOLv1 public BPOOL;
Expand Down Expand Up @@ -175,11 +195,9 @@ contract BaselineAxisLaunch is BaseCallback, Policy, Owned {
/// @dev This value is set in the `onCreate()` callback.
address public recipient;

// solhint-disable-next-line private-vars-leading-underscore
uint48 internal constant ONE_HUNDRED_PERCENT = 100e2;

/// @notice The tick spacing width of the discovery range
int24 internal constant _DISCOVERY_TICK_SPACING_WIDTH = 350;
/// @notice The target tick for the pool
/// @dev This value is set in the `onCreate()` callback.
int24 public poolTargetTick;

// ========== CONSTRUCTOR ========== //

Expand Down Expand Up @@ -312,14 +330,15 @@ contract BaselineAxisLaunch is BaseCallback, Policy, Owned {
/// - `recipient` is the zero address
/// - `lotId` is already set
/// - The pool fee tier is not supported
/// - The auction format is not supported
/// - The auction is not prefunded
/// - `CreateData.floorReservesPercent` is less than 10% or greater than 90%
/// - `CreateData.poolPercent` is less than 10% or greater than 100%
/// - `CreateData.floorRangeGap` is < 0
/// - `CreateData.anchorTickWidth` is < 10 or > 50
/// - The auction format is not supported
/// - The auction is not prefunded
/// - `CreateData.anchorTickU` is not the same as the calculated value
/// - `CreateData.poolTargetTick` is not >= anchorRangeLower and < anchorRangeUpper
/// - Any of the tick ranges would exceed the tick bounds
/// - The provided anchor range upper tick is not the same as the calculated value
/// - The pool tick is less than the auction price (in terms of ticks)
/// - The pool capacity is not sufficient to support the intended supply
function _onCreate(
Expand Down Expand Up @@ -446,6 +465,17 @@ contract BaselineAxisLaunch is BaseCallback, Policy, Owned {
// Anchor range lower is the anchor tick width below the anchor range upper
int24 anchorRangeLower = anchorRangeUpper - cbData.anchorTickWidth * tickSpacing;

// Validate that the pool target tick is within the anchor range
// The `_onSettle()` function will perform swaps (if necessary) to move the pool tick to the target tick in order to mitigate manipulation.
// The value must be in the anchor range and below the anchor upper tick (which is where the discovery range begins).
if (
cbData.poolTargetTick < anchorRangeLower
|| cbData.poolTargetTick >= anchorRangeUpper
) {
revert Callback_Params_InvalidPoolTargetTick(anchorRangeLower, anchorRangeUpper);
}
poolTargetTick = cbData.poolTargetTick;

// Set the anchor range
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

another useful check would be to ensure the pools active tick == poolTargetTick

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but should be fine without it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be DoS'd by moving it prior to auction creation, which is the same reason we pass it in instead of reading from the pool.

BPOOL.setTicks(Range.ANCHOR, anchorRangeLower, anchorRangeUpper);

Expand Down Expand Up @@ -745,8 +775,8 @@ contract BaselineAxisLaunch is BaseCallback, Policy, Owned {
// Current price of the pool
(uint160 currentSqrtPrice,,,,,,) = pool.slot0();

// Get the target sqrt price from the anchor position
uint160 targetSqrtPrice = BPOOL.getPosition(Range.ANCHOR).sqrtPriceU;
// Get the target sqrt price from the target tick
uint160 targetSqrtPrice = TickMath.getSqrtRatioAtTick(poolTargetTick);

// We assume there are no circulating bAssets that could be provided as liquidity yet.
// Therefore, there are three cases:
Expand Down
12 changes: 12 additions & 0 deletions test/callbacks/liquidity/BaselineV2/BaselineAxisLaunchTest.sol
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ abstract contract BaselineAxisLaunchTest is Test, Permit2User, WithSalts, TestCo
uint24 internal constant _POOL_PERCENT = 87e2; // 87%
uint256 internal constant _FIXED_PRICE = 3e18;
int24 internal constant _FIXED_PRICE_TICK_UPPER = 11_000; // 10986 rounded up
int24 internal constant _POOL_TARGET_TICK = 10_986; // 10986 being the default
uint256 internal constant _INITIAL_POOL_PRICE = 3e18; // 3
uint24 internal constant _FEE_TIER = 10_000;
uint256 internal constant _BASE_SCALE = 1e18;
Expand Down Expand Up @@ -119,6 +120,7 @@ abstract contract BaselineAxisLaunchTest is Test, Permit2User, WithSalts, TestCo
floorRangeGap: _floorRangeGap,
anchorTickU: _FIXED_PRICE_TICK_UPPER,
anchorTickWidth: _ANCHOR_TICK_WIDTH,
poolTargetTick: _POOL_TARGET_TICK,
allowlistParams: abi.encode("")
});

Expand Down Expand Up @@ -671,6 +673,16 @@ abstract contract BaselineAxisLaunchTest is Test, Permit2User, WithSalts, TestCo
_;
}

function _setPoolTargetTick(int24 poolTargetTick_) internal {
_createData.poolTargetTick = poolTargetTick_;
console2.log("Pool target tick set to: ", poolTargetTick_);
}

modifier givenPoolTargetTick(int24 poolTargetTick_) {
_setPoolTargetTick(poolTargetTick_);
_;
}

// ========== MOCKS ========== //

function _mockGetAuctionModuleForId() internal {
Expand Down
65 changes: 65 additions & 0 deletions test/callbacks/liquidity/BaselineV2/onCreate.t.sol
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,10 @@ contract BaselineOnCreateTest is BaselineAxisLaunchTest {
// [X] when the referrer fee would result in a solvency check failure
// [X] it reverts
// [X] it correctly performs the solvency check
// [X] when the pool target tick is below the anchor lower tick
// [X] it reverts
// [X] when the pool target tick is above the anchor upper tick
// [X] it reverts
// [X] it transfers the base token to the auction house, updates circulating supply, sets the state variables, initializes the pool and sets the tick ranges

function test_callbackDataIncorrect_reverts()
Expand Down Expand Up @@ -510,6 +514,7 @@ contract BaselineOnCreateTest is BaselineAxisLaunchTest {
public
givenPoolInitialTick(TickMath.MIN_TICK + 200 * _ANCHOR_TICK_WIDTH) // This will result in the floor range to be below the MIN_TICK, which should cause a revert
givenAnchorUpperTick(-885_200)
givenPoolTargetTick(-885_300)
givenFloorRangeGap(1)
givenBPoolIsCreated
givenCallbackIsCreated
Expand Down Expand Up @@ -710,6 +715,54 @@ contract BaselineOnCreateTest is BaselineAxisLaunchTest {
_assertTicks(fixedPriceTick);
}

function test_poolTargetTick_belowAnchorLowerTick_reverts(
int24 poolTargetTick_
) public givenBPoolIsCreated givenCallbackIsCreated givenAuctionIsCreated {
// Bound the pool target tick
int24 poolTargetTick = int24(bound(poolTargetTick_, type(int24).min, 8999)); // Below anchor lower tick of 9000
_setPoolTargetTick(poolTargetTick);

// Expect a revert
bytes memory err = abi.encodeWithSelector(
BaselineAxisLaunch.Callback_Params_InvalidPoolTargetTick.selector, 9000, 11_000
);
vm.expectRevert(err);

// Perform the call
_onCreate();
}

function test_poolTargetTick_aboveAnchorUpperTick_reverts(
int24 poolTargetTick_
) public givenBPoolIsCreated givenCallbackIsCreated givenAuctionIsCreated {
// Bound the pool target tick
int24 poolTargetTick = int24(bound(poolTargetTick_, 11_000, type(int24).max));
_setPoolTargetTick(poolTargetTick);

// Expect a revert
bytes memory err = abi.encodeWithSelector(
BaselineAxisLaunch.Callback_Params_InvalidPoolTargetTick.selector, 9000, 11_000
);
vm.expectRevert(err);

// Perform the call
_onCreate();
}

function test_poolTargetTick_fuzz(
int24 poolTargetTick_
) public givenBPoolIsCreated givenCallbackIsCreated givenAuctionIsCreated {
// Bound the pool target tick
int24 poolTargetTick = int24(bound(poolTargetTick_, 9000, 10_999));
_setPoolTargetTick(poolTargetTick);

// Perform the call
_onCreate();

// Assert pool target tick is set
assertEq(_dtl.poolTargetTick(), poolTargetTick, "pool target tick");
}

function test_success()
public
givenBPoolIsCreated
Expand All @@ -725,6 +778,9 @@ contract BaselineOnCreateTest is BaselineAxisLaunchTest {
// Lot ID is set
assertEq(_dtl.lotId(), _lotId, "lot ID");

// Pool target tick is set
assertEq(_dtl.poolTargetTick(), _POOL_TARGET_TICK, "pool target tick");

// Check circulating supply
assertEq(_baseToken.totalSupply(), _LOT_CAPACITY, "circulating supply");

Expand Down Expand Up @@ -920,6 +976,7 @@ contract BaselineOnCreateTest is BaselineAxisLaunchTest {
public
givenFixedPrice(1e32) // Seems to cause a revert above this when calculating the tick
givenAnchorUpperTick(322_400)
givenPoolTargetTick(322_300)
givenBPoolIsCreated
givenCallbackIsCreated
givenAuctionIsCreated
Expand Down Expand Up @@ -950,6 +1007,7 @@ contract BaselineOnCreateTest is BaselineAxisLaunchTest {
public
givenFixedPrice(1e6)
givenAnchorUpperTick(-276_200)
givenPoolTargetTick(-276_300)
givenBPoolIsCreated
givenCallbackIsCreated
givenAuctionIsCreated
Expand Down Expand Up @@ -1034,6 +1092,7 @@ contract BaselineOnCreateTest is BaselineAxisLaunchTest {
public
givenBaseTokenDecimals(19)
givenAnchorUpperTick(-12_000)
givenPoolTargetTick(-12_100)
givenBPoolIsCreated
givenCallbackIsCreated
givenAuctionIsCreated
Expand Down Expand Up @@ -1062,6 +1121,7 @@ contract BaselineOnCreateTest is BaselineAxisLaunchTest {
public
givenBaseTokenDecimals(17)
givenAnchorUpperTick(34_200)
givenPoolTargetTick(34_100)
givenBPoolIsCreated
givenCallbackIsCreated
givenAuctionIsCreated
Expand Down Expand Up @@ -1091,6 +1151,7 @@ contract BaselineOnCreateTest is BaselineAxisLaunchTest {
givenBPoolFeeTier(10_000)
givenFixedPrice(1e18)
givenAnchorUpperTick(200) // Rounded up
givenPoolTargetTick(199) // Below anchor upper tick
givenBPoolIsCreated
givenCallbackIsCreated
givenAuctionIsCreated
Expand All @@ -1116,6 +1177,7 @@ contract BaselineOnCreateTest is BaselineAxisLaunchTest {
public
givenPoolInitialTick(TickMath.MAX_TICK - 1) // This will result in the upper tick of the anchor range to be above the MAX_TICK, which should cause a revert
givenAnchorUpperTick(887_400)
givenPoolTargetTick(887_300)
givenBPoolIsCreated
givenCallbackIsCreated
givenAuctionIsCreated
Expand All @@ -1134,6 +1196,7 @@ contract BaselineOnCreateTest is BaselineAxisLaunchTest {
public
givenPoolInitialTick(TickMath.MIN_TICK + 1) // This will result in the lower tick of the anchor range to be below the MIN_TICK, which should cause a revert
givenAnchorUpperTick(-887_200)
givenPoolTargetTick(-887_300)
givenBPoolIsCreated
givenCallbackIsCreated
givenAuctionIsCreated
Expand All @@ -1152,6 +1215,7 @@ contract BaselineOnCreateTest is BaselineAxisLaunchTest {
public
givenPoolInitialTick(TickMath.MAX_TICK - _tickSpacing + 1) // This will result in the upper tick of the discovery range to be above the MAX_TICK, which should cause a revert
givenAnchorUpperTick(887_200)
givenPoolTargetTick(887_100)
givenBPoolIsCreated
givenCallbackIsCreated
givenAuctionIsCreated
Expand All @@ -1174,6 +1238,7 @@ contract BaselineOnCreateTest is BaselineAxisLaunchTest {
public
givenPoolInitialTick(TickMath.MIN_TICK) // This will result in the lower tick of the floor range to be below the MIN_TICK, which should cause a revert
givenAnchorUpperTick(-887_200)
givenPoolTargetTick(-887_300)
givenBPoolIsCreated
givenCallbackIsCreated
givenAuctionIsCreated
Expand Down
Loading
Loading