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

Speedup CL pool hooks, by only JSON marshalling if there is ahook #7685

Merged
merged 5 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,8 +58,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
* [#7619](https://github.com/osmosis-labs/osmosis/pull/7619) Slight speedup/gas improvement to CL GetTotalPoolLiquidity queries
* [#7622](https://github.com/osmosis-labs/osmosis/pull/7622) Create/remove tick events.
* [#7623](https://github.com/osmosis-labs/osmosis/pull/7623) Add query for querying all before send hooks
* [#7625](https://github.com/osmosis-labs/osmosis/pull/7625) Remove duplicate CL accumulator update logic.
* [#7622](https://github.com/osmosis-labs/osmosis/pull/7622) Remove duplicate CL accumulator update logic.
* [#7665](https://github.com/osmosis-labs/osmosis/pull/7665) feat(x/protorev): Use Transient store to store swap backruns.
* [#7685](https://github.com/osmosis-labs/osmosis/pull/7685) Speedup CL actions by only marshalling for CL hooks if they will be used.
* [#7503](https://github.com/osmosis-labs/osmosis/pull/7503) Add IBC wasm light clients module

## v23.0.4-iavl-v1 (contains everything in v23.0.4)
Expand Down
4 changes: 2 additions & 2 deletions x/concentrated-liquidity/export_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,8 @@ func (k Keeper) SetPoolHookContract(ctx sdk.Context, poolID uint64, actionPrefix
return k.setPoolHookContract(ctx, poolID, actionPrefix, cosmwasmAddress)
}

func (k Keeper) CallPoolActionListener(ctx sdk.Context, msgBz []byte, poolId uint64, actionPrefix string) (err error) {
return k.callPoolActionListener(ctx, msgBz, poolId, actionPrefix)
func (k Keeper) CallPoolActionListener(ctx sdk.Context, msgBuilderFn func(poolId uint64) ([]byte, error), poolId uint64, actionPrefix string) (err error) {
return k.callPoolActionListener(ctx, msgBuilderFn, poolId, actionPrefix)
}

func (k Keeper) GetPoolHookContract(ctx sdk.Context, poolId uint64, actionPrefix string) string {
Expand Down
89 changes: 40 additions & 49 deletions x/concentrated-liquidity/pool_hooks.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,92 +13,78 @@ import (

// --- Pool Hooks ---

type msgBuilderFn func(poolId uint64) ([]byte, error)

// BeforeCreatePosition is a hook that is called before a position is created.
func (k Keeper) BeforeCreatePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, tokensProvided sdk.Coins, amount0Min osmomath.Int, amount1Min osmomath.Int, lowerTick int64, upperTick int64) error {
// Build and marshal the message to be passed to the contract
msg := types.BeforeCreatePositionMsg{PoolId: poolId, Owner: owner, TokensProvided: osmoutils.CWCoinsFromSDKCoins(tokensProvided), Amount0Min: amount0Min, Amount1Min: amount1Min, LowerTick: lowerTick, UpperTick: upperTick}
msgBz, err := json.Marshal(types.BeforeCreatePositionSudoMsg{BeforeCreatePosition: msg})
if err != nil {
return err
msgBuilderFn := func(poolId uint64) ([]byte, error) {
Copy link
Member

Choose a reason for hiding this comment

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

I'm surprised that this was getting called every time! Good catch :)

msg := types.BeforeCreatePositionMsg{PoolId: poolId, Owner: owner, TokensProvided: osmoutils.CWCoinsFromSDKCoins(tokensProvided), Amount0Min: amount0Min, Amount1Min: amount1Min, LowerTick: lowerTick, UpperTick: upperTick}
return json.Marshal(types.BeforeCreatePositionSudoMsg{BeforeCreatePosition: msg})
}
return k.callPoolActionListener(ctx, msgBz, poolId, types.BeforeActionPrefix(types.CreatePositionPrefix))
return k.callPoolActionListener(ctx, msgBuilderFn, poolId, types.BeforeActionPrefix(types.CreatePositionPrefix))
}

// AfterCreatePosition is a hook that is called after a position is created.
func (k Keeper) AfterCreatePosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, tokensProvided sdk.Coins, amount0Min osmomath.Int, amount1Min osmomath.Int, lowerTick int64, upperTick int64) error {
// Build and marshal the message to be passed to the contract
msg := types.AfterCreatePositionMsg{PoolId: poolId, Owner: owner, TokensProvided: osmoutils.CWCoinsFromSDKCoins(tokensProvided), Amount0Min: amount0Min, Amount1Min: amount1Min, LowerTick: lowerTick, UpperTick: upperTick}
msgBz, err := json.Marshal(types.AfterCreatePositionSudoMsg{AfterCreatePosition: msg})
if err != nil {
return err
msgBuilderFn := func(poolId uint64) ([]byte, error) {
msg := types.AfterCreatePositionMsg{PoolId: poolId, Owner: owner, TokensProvided: osmoutils.CWCoinsFromSDKCoins(tokensProvided), Amount0Min: amount0Min, Amount1Min: amount1Min, LowerTick: lowerTick, UpperTick: upperTick}
return json.Marshal(types.AfterCreatePositionSudoMsg{AfterCreatePosition: msg})
}
return k.callPoolActionListener(ctx, msgBz, poolId, types.AfterActionPrefix(types.CreatePositionPrefix))
return k.callPoolActionListener(ctx, msgBuilderFn, poolId, types.AfterActionPrefix(types.CreatePositionPrefix))
}

// BeforeWithdrawPosition is a hook that is called before liquidity is withdrawn from a position.
func (k Keeper) BeforeWithdrawPosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, positionId uint64, amountToWithdraw osmomath.Dec) error {
// Build and marshal the message to be passed to the contract
msg := types.BeforeWithdrawPositionMsg{PoolId: poolId, Owner: owner, PositionId: positionId, AmountToWithdraw: amountToWithdraw}
msgBz, err := json.Marshal(types.BeforeWithdrawPositionSudoMsg{BeforeWithdrawPosition: msg})
if err != nil {
return err
msgBuilderFn := func(poolId uint64) ([]byte, error) {
msg := types.BeforeWithdrawPositionMsg{PoolId: poolId, Owner: owner, PositionId: positionId, AmountToWithdraw: amountToWithdraw}
return json.Marshal(types.BeforeWithdrawPositionSudoMsg{BeforeWithdrawPosition: msg})
}
return k.callPoolActionListener(ctx, msgBz, poolId, types.BeforeActionPrefix(types.WithdrawPositionPrefix))
return k.callPoolActionListener(ctx, msgBuilderFn, poolId, types.BeforeActionPrefix(types.WithdrawPositionPrefix))
}

// AfterWithdrawPosition is a hook that is called after liquidity is withdrawn from a position.
func (k Keeper) AfterWithdrawPosition(ctx sdk.Context, poolId uint64, owner sdk.AccAddress, positionId uint64, amountToWithdraw osmomath.Dec) error {
// Build and marshal the message to be passed to the contract
msg := types.AfterWithdrawPositionMsg{PoolId: poolId, Owner: owner, PositionId: positionId, AmountToWithdraw: amountToWithdraw}
msgBz, err := json.Marshal(types.AfterWithdrawPositionSudoMsg{AfterWithdrawPosition: msg})
if err != nil {
return err
msgBuilderFn := func(poolId uint64) ([]byte, error) {
msg := types.AfterWithdrawPositionMsg{PoolId: poolId, Owner: owner, PositionId: positionId, AmountToWithdraw: amountToWithdraw}
return json.Marshal(types.AfterWithdrawPositionSudoMsg{AfterWithdrawPosition: msg})
}
return k.callPoolActionListener(ctx, msgBz, poolId, types.AfterActionPrefix(types.WithdrawPositionPrefix))
return k.callPoolActionListener(ctx, msgBuilderFn, poolId, types.AfterActionPrefix(types.WithdrawPositionPrefix))
}

// BeforeSwapExactAmountIn is a hook that is called before a swap is executed (exact amount in).
func (k Keeper) BeforeSwapExactAmountIn(ctx sdk.Context, poolId uint64, sender sdk.AccAddress, tokenIn sdk.Coin, tokenOutDenom string, tokenOutMinAmount osmomath.Int, spreadFactor osmomath.Dec) error {
// Build and marshal the message to be passed to the contract
msg := types.BeforeSwapExactAmountInMsg{PoolId: poolId, Sender: sender, TokenIn: osmoutils.CWCoinFromSDKCoin(tokenIn), TokenOutDenom: tokenOutDenom, TokenOutMinAmount: tokenOutMinAmount, SpreadFactor: spreadFactor}
msgBz, err := json.Marshal(types.BeforeSwapExactAmountInSudoMsg{BeforeSwapExactAmountIn: msg})
if err != nil {
return err
msgBuilderFn := func(poolId uint64) ([]byte, error) {
msg := types.BeforeSwapExactAmountInMsg{PoolId: poolId, Sender: sender, TokenIn: osmoutils.CWCoinFromSDKCoin(tokenIn), TokenOutDenom: tokenOutDenom, TokenOutMinAmount: tokenOutMinAmount, SpreadFactor: spreadFactor}
return json.Marshal(types.BeforeSwapExactAmountInSudoMsg{BeforeSwapExactAmountIn: msg})
}
return k.callPoolActionListener(ctx, msgBz, poolId, types.BeforeActionPrefix(types.SwapExactAmountInPrefix))
return k.callPoolActionListener(ctx, msgBuilderFn, poolId, types.BeforeActionPrefix(types.SwapExactAmountInPrefix))
}

// AfterSwapExactAmountIn is a hook that is called after a swap is executed (exact amount in).
func (k Keeper) AfterSwapExactAmountIn(ctx sdk.Context, poolId uint64, sender sdk.AccAddress, tokenIn sdk.Coin, tokenOutDenom string, tokenOutMinAmount osmomath.Int, spreadFactor osmomath.Dec) error {
// Build and marshal the message to be passed to the contract
msg := types.AfterSwapExactAmountInMsg{PoolId: poolId, Sender: sender, TokenIn: osmoutils.CWCoinFromSDKCoin(tokenIn), TokenOutDenom: tokenOutDenom, TokenOutMinAmount: tokenOutMinAmount, SpreadFactor: spreadFactor}
msgBz, err := json.Marshal(types.AfterSwapExactAmountInSudoMsg{AfterSwapExactAmountIn: msg})
if err != nil {
return err
msgBuilderFn := func(poolId uint64) ([]byte, error) {
msg := types.AfterSwapExactAmountInMsg{PoolId: poolId, Sender: sender, TokenIn: osmoutils.CWCoinFromSDKCoin(tokenIn), TokenOutDenom: tokenOutDenom, TokenOutMinAmount: tokenOutMinAmount, SpreadFactor: spreadFactor}
return json.Marshal(types.AfterSwapExactAmountInSudoMsg{AfterSwapExactAmountIn: msg})
}
return k.callPoolActionListener(ctx, msgBz, poolId, types.AfterActionPrefix(types.SwapExactAmountInPrefix))
return k.callPoolActionListener(ctx, msgBuilderFn, poolId, types.AfterActionPrefix(types.SwapExactAmountInPrefix))
}

// BeforeSwapExactAmountOut is a hook that is called before a swap is executed (exact amount out).
func (k Keeper) BeforeSwapExactAmountOut(ctx sdk.Context, poolId uint64, sender sdk.AccAddress, tokenInDenom string, tokenInMaxAmount osmomath.Int, tokenOut sdk.Coin, spreadFactor osmomath.Dec) error {
// Build and marshal the message to be passed to the contract
msg := types.BeforeSwapExactAmountOutMsg{PoolId: poolId, Sender: sender, TokenInDenom: tokenInDenom, TokenInMaxAmount: tokenInMaxAmount, TokenOut: osmoutils.CWCoinFromSDKCoin(tokenOut), SpreadFactor: spreadFactor}
msgBz, err := json.Marshal(types.BeforeSwapExactAmountOutSudoMsg{BeforeSwapExactAmountOut: msg})
if err != nil {
return err
msgBuilderFn := func(poolId uint64) ([]byte, error) {
msg := types.BeforeSwapExactAmountOutMsg{PoolId: poolId, Sender: sender, TokenInDenom: tokenInDenom, TokenInMaxAmount: tokenInMaxAmount, TokenOut: osmoutils.CWCoinFromSDKCoin(tokenOut), SpreadFactor: spreadFactor}
return json.Marshal(types.BeforeSwapExactAmountOutSudoMsg{BeforeSwapExactAmountOut: msg})
}
return k.callPoolActionListener(ctx, msgBz, poolId, types.BeforeActionPrefix(types.SwapExactAmountOutPrefix))
return k.callPoolActionListener(ctx, msgBuilderFn, poolId, types.BeforeActionPrefix(types.SwapExactAmountOutPrefix))
}

// AfterSwapExactAmountOut is a hook that is called after a swap is executed (exact amount out).
func (k Keeper) AfterSwapExactAmountOut(ctx sdk.Context, poolId uint64, sender sdk.AccAddress, tokenInDenom string, tokenInMaxAmount osmomath.Int, tokenOut sdk.Coin, spreadFactor osmomath.Dec) error {
// Build and marshal the message to be passed to the contract
msg := types.AfterSwapExactAmountOutMsg{PoolId: poolId, Sender: sender, TokenInDenom: tokenInDenom, TokenInMaxAmount: tokenInMaxAmount, TokenOut: osmoutils.CWCoinFromSDKCoin(tokenOut), SpreadFactor: spreadFactor}
msgBz, err := json.Marshal(types.AfterSwapExactAmountOutSudoMsg{AfterSwapExactAmountOut: msg})
if err != nil {
return err
msgBuilderFn := func(poolId uint64) ([]byte, error) {
msg := types.AfterSwapExactAmountOutMsg{PoolId: poolId, Sender: sender, TokenInDenom: tokenInDenom, TokenInMaxAmount: tokenInMaxAmount, TokenOut: osmoutils.CWCoinFromSDKCoin(tokenOut), SpreadFactor: spreadFactor}
return json.Marshal(types.AfterSwapExactAmountOutSudoMsg{AfterSwapExactAmountOut: msg})
}
return k.callPoolActionListener(ctx, msgBz, poolId, types.AfterActionPrefix(types.SwapExactAmountOutPrefix))
return k.callPoolActionListener(ctx, msgBuilderFn, poolId, types.AfterActionPrefix(types.SwapExactAmountOutPrefix))
}

// callPoolActionListener processes and dispatches the passed in message to the contract corresponding to the hook
Expand All @@ -108,7 +94,7 @@ func (k Keeper) AfterSwapExactAmountOut(ctx sdk.Context, poolId uint64, sender s
//
// Since it is possible for this function to be triggered in begin block code, we need to directly meter its execution and set a limit.
// If no contract is linked to the hook, this function is a no-op.
func (k Keeper) callPoolActionListener(ctx sdk.Context, msgBz []byte, poolId uint64, actionPrefix string) (err error) {
func (k Keeper) callPoolActionListener(ctx sdk.Context, msgBuilderFn msgBuilderFn, poolId uint64, actionPrefix string) (err error) {
defer func() {
if r := recover(); r != nil {
err = types.ContractHookOutOfGasError{GasLimit: k.GetParams(ctx).HookGasLimit}
Expand All @@ -120,6 +106,11 @@ func (k Keeper) callPoolActionListener(ctx sdk.Context, msgBz []byte, poolId uin
return nil
}

msgBz, err := msgBuilderFn(poolId)
if err != nil {
return err
}

cwAddr, err := sdk.AccAddressFromBech32(cosmwasmAddress)
if err != nil {
return err
Expand Down
9 changes: 6 additions & 3 deletions x/concentrated-liquidity/pool_hooks_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,15 @@ func (s *KeeperTestSuite) TestCallPoolActionListener() {
s.Require().NoError(err)

// Marshal test case msg to pass into contract
msgBz, err := json.Marshal(tc.msg)
s.Require().NoError(err)
msgBuilderFn := func(uint64) ([]byte, error) {
msgBz, err := json.Marshal(tc.msg)
s.Require().NoError(err)
return msgBz, nil
}

// --- System under test ---

err = s.Clk.CallPoolActionListener(s.Ctx, msgBz, validPoolId, validActionPrefix)
err = s.Clk.CallPoolActionListener(s.Ctx, msgBuilderFn, validPoolId, validActionPrefix)

// --- Assertions ---

Expand Down