-
Notifications
You must be signed in to change notification settings - Fork 592
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
[feat: CL hooks]: Add CL hooks into core CL logic and test hook-specific behavior #6859
Conversation
@@ -89,15 +92,22 @@ func (k Keeper) CreatePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddr | |||
return CreatePositionData{}, err | |||
} | |||
|
|||
positionId := k.getNextPositionIdAndIncrement(ctx) | |||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note: this was moved below the hasPositions
check to ensure the before hook was triggered after validation logic but before any state changes (getNextPositionIdAndIncrement
mutates state)
} | ||
|
||
// BeforeAddToPosition is a hook that is called before liquidity is added to a position. | ||
func (k Keeper) BeforeAddToPosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, positionId uint64, amount0Added osmomath.Int, amount1Added osmomath.Int, amount0Min osmomath.Int, amount1Min osmomath.Int) error { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
AddToPosition
hooks were removed since the function just calls WithdrawPosition
then CreatePosition
under the hood. This makes it tricky to separate out AddToPosition
hooks from the other two and imo unnecessary complexity that would increase the surface for circumventing hooks.
Opted to remove and keep hooks on the most primitive actions.
@@ -178,9 +178,136 @@ func (s *KeeperTestSuite) TestCallPoolActionListener() { | |||
} | |||
} | |||
|
|||
// Pool hook tests |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see PR description for further justification of this testing approach
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice work. Left some comments, please take a look.
Could you please also add a link to where the test contract code is written and make sure that it is easy to locate in case someone needs to find it in the future?
For example, I can see that there are some contract sources here:
https://github.com/osmosis-labs/osmosis/blob/c387e00c6338fa113ccb3abcb129f633670445f6/x/concentrated-liquidity/testcontracts/contract-sources/
Let's put the hooks source in there as well?
// General testing strategy: | ||
// 1. Build a pre-defined contract that defines the following behavior for all hooks: | ||
// if triggered, transfer 1 token with denom corresponding to the action prefix | ||
// e.g. if action prefix is "beforeSwap", transfer 1 token with denom "beforeSwap" | ||
// 2. Set this contract for all hooks defined by the test case (each case should have a list | ||
// of action prefixes it wants to "activate") | ||
// 3. Run a series of actions that would trigger all the hooks (create, add to, withdraw from, swap against a position), | ||
// and ensure that the correct denoms are in the account balance after each action/at the end. | ||
// | ||
// NOTE: we assume that set contracts have valid implementations for all hooks and that this is validated | ||
// at the contract setting stage at a higher level of abstraction. Thus, this class of errors is not covered | ||
// by these tests. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice!
// Trigger before hook for SwapExactAmountIn prior to mutating state. | ||
// If no contract is set, this will be a no-op. | ||
err = k.BeforeSwapExactAmountIn(ctx, pool.GetId(), sender, tokenIn, tokenOutDenom, tokenOutMinAmount, spreadFactor) | ||
if err != nil { | ||
return osmomath.Int{}, err | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we have a mechanism in our tests for validating that BeforeSwapExactAmountIn
is triggered before and not after the swap.
I think it would be valuable to add such an assertion so that we minimize the chances of a move-by-mistake in the future
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like it will require a meaningful change to the current testing approach so I created a separate issue with what I think would be a viable design in #6862
I agree that it's valuable to add this to ensure future changes/additions to hooks don't break this invariant. Would be great if we can handle this separately as I think it will take careful implementation to keep tests readable and would like to get the remaining core feature work in first
@@ -81,26 +99,6 @@ type AfterCreatePositionMsg struct { | |||
UpperTick int64 `json:"upper_tick"` | |||
} | |||
|
|||
type BeforeAddToPositionMsg struct { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why did we decide to remove these?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please see the note here: https://github.com/osmosis-labs/osmosis/pull/6859/files#r1389677483
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, let's make sure #6862 gets in as high priority! Left some nit comments please take a look!
x/concentrated-liquidity/testcontracts/compiled-wasm/hooks.wasm
Outdated
Show resolved
Hide resolved
"single hook: before create position": { | ||
actionPrefixes: []string{before(types.CreatePositionPrefix)}, | ||
}, | ||
"all before hooks": { | ||
actionPrefixes: allBeforeHooks, | ||
}, | ||
"all after hooks": { | ||
actionPrefixes: allAfterHooks, | ||
}, | ||
"all hooks": { | ||
actionPrefixes: allHooks, | ||
}, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Seems like there will be dupicated test runs for before hook & after hook, was it that we wanted to test compound integration of hooks? 👀
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I follow – the "all hooks" case is intended to run every hook simultaneously whereas the others run different subsets
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, I was just curious of the significance of running hooks simultaneously vs running subset. (But it was a nit question, can be ignored)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah I see – no particular reason other than general coverage (ensuring that various subsets can correctly run without triggering the others)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
Closes: #XXX
What is the purpose of the change
This PR adds the CL hooks that were implemented in previous PRs into core CL logic.
Testing and Verifying
Testing this behavior in a way that does not circumvent important operations is actually quite tricky. While it is technically possible to cover this behavior using mock hooks, this felt much less robust than having a contract that actually implements the hooks to test against. This is what is done in
pool_hooks_test.go
.As explained in the test comments, the tests lean on a CW contracts that implements all hooks with the following logic: when a hook is triggered, send 1 token with a denom corresponding to the hook to the originator of the tx (e.g. if I create a position, I would get 1
beforeCreatePosition
token and 1afterCreatePosition
token). This makes testing behavior relatively straightforward (and hopefully readable).Documentation and Release Note
Unreleased
section ofCHANGELOG.md
?Where is the change documented?
x/{module}/README.md
)