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

[PART-1] enable hooks to return deltas to influence pool behavior #44

Merged
merged 4 commits into from
May 27, 2024

Conversation

chefburger
Copy link
Collaborator

@chefburger chefburger commented May 15, 2024

This PR allows hooks contract to return extra parameter which can have impact on pool's behavior. More specifically:

  1. beforeSwap: a extra value is returned which will be added onto amtSpecified/amtIn which can be used to update current swap amount
  2. afterSwap: a new value is returned which will be combined with the value returned by beforeSwap. Those two together work as a update to the balanceDelta i.e. through it payment can be transferred between user and hook contract.
  3. afterAddLiquidity/afterMint/afterRemoveLiquidity/afterBurn: a balanceDelta is returned which will be added onto user's balanceDelta i.e. through it payment can be transferred between user and hook contract.

NO_OP feature has been removed because its functionality has overlapped with this feature:

  1. for swap, consider value returned = -amtSpecified/amtIn
  2. for modifyLiquidity: consider doing operation in opposite direction in afterXXX as user

However, there are also some reasons that NO_OP might be the way to go:

  1. NO_OP can naturally consume less gas
  2. When mint position in binPool and compositionFee is generated, in this case operation in opposite direction won't help
  3. donate

@@ -135,27 +136,19 @@ contract BinPoolManager is IBinPoolManager, ProtocolFees, Extsload {
whenNotPaused
returns (BalanceDelta delta)
{
if (amountIn == 0) revert InsufficientAmountIn();
Copy link
Collaborator Author

@chefburger chefburger May 15, 2024

Choose a reason for hiding this comment

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

This check has been moved from BinPool.sol to here otherwise the call will revert if hook contract updates the actual amountIn to 0 through beforeSwap

bytes calldata hookData
) internal returns (BalanceDelta callerDelta, BalanceDelta hookDelta) {
IBinHooks hooks = IBinHooks(address(key.hooks));
callerDelta = delta;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

if skip i.e. caller is already hook, then callerDelta is unchanged and hookDelta is 0


function beforeSwap(PoolKey memory key, bool swapForY, uint128 amountIn, bytes calldata hookData)
internal
returns (uint128 amountToSwap, int128 hookDeltaSpecified)
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This is uint128 because unlike cl-pool, in bin-pool users are always specifying exactlyIn amount

}
}

if (hookDeltaUnspecified != 0 || hookDeltaSpecified != 0) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

This can still be true even "afterSwapReturnDelta" is disabled

Copy link
Collaborator

Choose a reason for hiding this comment

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

is it better to rename hookDeltaSpecified as beforeSwapHookDeltaSpecified?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

already updated in part3

revert Hooks.InvalidHookResponse();
}

if (!key.parameters.hasOffsetEnabled(HOOKS_AFTER_SWAP_RETURNS_DELTA_OFFSET)) {
Copy link
Collaborator Author

@chefburger chefburger May 15, 2024

Choose a reason for hiding this comment

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

Potentially optimization: skip decoding the second return value when afterSwapReturnDelta not set

Copy link
Collaborator

Choose a reason for hiding this comment

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

add a //todo comment in the code if possible for these cases

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

added in part3

@chefburger chefburger marked this pull request as draft May 15, 2024 09:14
@chefburger chefburger force-pushed the feat/hook-return-deltas branch from ea853f2 to 395eb86 Compare May 15, 2024 09:37
@chefburger chefburger marked this pull request as ready for review May 18, 2024 06:24
@@ -145,7 +142,7 @@ library CLPool {
}

// Fees earned from LPing are removed from the pool balance.
feeDelta = toBalanceDelta(feesOwed0.toInt128(), feesOwed1.toInt128());
feeDelta = toBalanceDelta(-feesOwed0.toInt128(), -feesOwed1.toInt128());
Copy link
Collaborator Author

@chefburger chefburger May 18, 2024

Choose a reason for hiding this comment

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

feeDelta must be negative i.e. both feeDelta.amount0 <= 0 and feeDelta.amount1 <= 0 because that's the fee owed to the user by the pool. Our rule for BalanceDelta is:

  1. Positive number means user owes the pool
  2. Negative number means pool owes user

TO CONFIRM: If we want to flip to sign for all the delta across the repo

@ChefMist ChefMist self-requested a review May 20, 2024 03:09
@ChefMist
Copy link
Collaborator

NO_OP feature has been removed because its functionality has overlapped with this feature:

1. for swap, consider value returned = -amtSpecified/amtIn
2. for modifyLiquidity: consider doing operation in opposite direction in afterXXX as user

Do you mean that if we want to replicate no-op for swap/modifyLiquidity, the hook should ensure that balanceDelta accounted for both hook/caller is 0?

@chefburger chefburger changed the title feat: enable hooks to return deltas to influence pool behavior [PART-1] enable hooks to return deltas to influence pool behavior May 21, 2024
@chefburger
Copy link
Collaborator Author

NO_OP feature has been removed because its functionality has overlapped with this feature:

1. for swap, consider value returned = -amtSpecified/amtIn
2. for modifyLiquidity: consider doing operation in opposite direction in afterXXX as user

Do you mean that if we want to replicate no-op for swap/modifyLiquidity, the hook should ensure that balanceDelta accounted for both hook/caller is 0?

yes, i guess so, see:

// CLPoolManager.sol
function swap() {'

  // ...
  if (hookDelta != BalanceDeltaLibrary.ZERO_DELTA) {
      vault.accountPoolBalanceDelta(key, hookDelta, address(key.hooks));
  }

  /// @dev delta already includes protocol fee
  /// all tokens go into the vault
  vault.accountPoolBalanceDelta(key, delta, msg.sender);
}

Another way could be using settleFor if caller/router would like to pay for the hook

// Sentinel return value used to signify that a NoOp occurred.
return (BalanceDeltaLibrary.MAXIMUM_DELTA, binId);
} else if (selector != IBinHooks.beforeDonate.selector) {
if (hooks.beforeDonate(msg.sender, key, amount0, amount1, hookData) != IBinHooks.beforeDonate.selector) {
Copy link
Collaborator

@ChefMist ChefMist May 24, 2024

Choose a reason for hiding this comment

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

im thinking if we can create an issue to refactor (not in this PR but after all parts are merged) -- eg. move all hook call to CLHooks/BinHooks to make it easier to read for the devs. what do you think?

// some are without CLHooks
ICLHooks hooks = ICLHooks(address(key.hooks));
        if (key.parameters.shouldCall(HOOKS_BEFORE_DONATE_OFFSET, hooks)) {
            if (hooks.beforeDonate(msg.sender, key, amount0, amount1, hookData) != ICLHooks.beforeDonate.selector) {
                revert Hooks.InvalidHookResponse();
            }
        }

// some are using CLHooks
(delta, hookDelta) = CLHooks.afterModifyLiquidity(key, params, delta + feeDelta, hookData);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Agree with that, it brings better readability

ChefMist
ChefMist previously approved these changes May 24, 2024
}

if (hookDeltaUnspecified != 0 || hookDeltaSpecified != 0) {
hookDelta = swapForY
Copy link
Collaborator

@ChefMist ChefMist May 24, 2024

Choose a reason for hiding this comment

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

curious why here is only checking for swapForY while CLPool is checking amountSpecified as well (params.amountSpecified > 0 == params.zeroForOne)

ah got it. params.amountSpecified > 0 would tell us if its exactInput or exactOutput but for bin case, its always exactIn

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

exactly 😄

* feat: add salt to distinguish positions for same owner in cl-pool

* feat: add salt to distinguish positions for same owner in bin-pool

* optimiazaiton: small tweaks per comments

* test: added salt != 0 cases

* [PART-3] allow `beforeSwap` to return temp lpFee & delta for both sides  (#47)

* feat: finish updating for cl-pool

* feat: finish updating for bin-pool

* chore: correct comments removed unnecessary isValid function
Copy link
Collaborator

@ChefMist ChefMist left a comment

Choose a reason for hiding this comment

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

re-approved since we merged part 3 and part 2 in this PR

@chefburger chefburger merged commit 47315dc into main May 27, 2024
2 checks passed
@chefburger chefburger deleted the feat/hook-return-deltas branch May 27, 2024 06:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants