From 45bdfc2e7e5a937d45006321873f80837538c740 Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Thu, 18 May 2023 20:01:34 -0500 Subject: [PATCH 1/3] separate account for fees --- .../osmosis/concentrated-liquidity/pool.proto | 23 ++- x/concentrated-liquidity/export_test.go | 8 +- x/concentrated-liquidity/fees.go | 2 +- x/concentrated-liquidity/fees_test.go | 6 +- x/concentrated-liquidity/lp_test.go | 10 +- x/concentrated-liquidity/model/pool.go | 12 +- x/concentrated-liquidity/model/pool.pb.go | 184 +++++++++++------- x/concentrated-liquidity/swaps.go | 102 ++++++---- x/concentrated-liquidity/swaps_test.go | 54 +++-- x/concentrated-liquidity/types/pool.go | 1 + 10 files changed, 258 insertions(+), 144 deletions(-) diff --git a/proto/osmosis/concentrated-liquidity/pool.proto b/proto/osmosis/concentrated-liquidity/pool.proto index 6c0b47ef047..f1eb2a84942 100644 --- a/proto/osmosis/concentrated-liquidity/pool.proto +++ b/proto/osmosis/concentrated-liquidity/pool.proto @@ -23,32 +23,35 @@ message Pool { string incentives_address = 2 [ (gogoproto.moretags) = "yaml:\"incentives_address\"" ]; - uint64 id = 3; + // address holding fees from swaps. + string fees_address = 3 [ (gogoproto.moretags) = "yaml:\"fees_address\"" ]; + + uint64 id = 4; // Amount of total liquidity - string current_tick_liquidity = 4 [ + string current_tick_liquidity = 5 [ (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.moretags) = "yaml:\"current_tick_liquidity\"", (gogoproto.nullable) = false ]; - string token0 = 5; - string token1 = 6; + string token0 = 6; + string token1 = 7; - string current_sqrt_price = 7 [ + string current_sqrt_price = 8 [ (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.moretags) = "yaml:\"spot_price\"", (gogoproto.nullable) = false ]; - int64 current_tick = 8 [ (gogoproto.moretags) = "yaml:\"current_tick\"" ]; + int64 current_tick = 9 [ (gogoproto.moretags) = "yaml:\"current_tick\"" ]; // tick_spacing must be one of the authorized_tick_spacing values set in the // concentrated-liquidity parameters - uint64 tick_spacing = 9 [ (gogoproto.moretags) = "yaml:\"tick_spacing\"" ]; - int64 exponent_at_price_one = 10 + uint64 tick_spacing = 10 [ (gogoproto.moretags) = "yaml:\"tick_spacing\"" ]; + int64 exponent_at_price_one = 11 [ (gogoproto.moretags) = "yaml:\"exponent_at_price_one\"" ]; // swap_fee is the ratio that is charged on the amount of token in. - string swap_fee = 11 [ + string swap_fee = 12 [ (gogoproto.customtype) = "github.com/cosmos/cosmos-sdk/types.Dec", (gogoproto.moretags) = "yaml:\"swap_fee\"", (gogoproto.nullable) = false @@ -56,7 +59,7 @@ message Pool { // last_liquidity_update is the last time either the pool liquidity or the // active tick changed - google.protobuf.Timestamp last_liquidity_update = 12 [ + google.protobuf.Timestamp last_liquidity_update = 13 [ (gogoproto.nullable) = false, (gogoproto.stdtime) = true, (gogoproto.moretags) = "yaml:\"last_liquidity_update\"" diff --git a/x/concentrated-liquidity/export_test.go b/x/concentrated-liquidity/export_test.go index 03a95b0de32..a3e8a9e98d2 100644 --- a/x/concentrated-liquidity/export_test.go +++ b/x/concentrated-liquidity/export_test.go @@ -70,7 +70,7 @@ func (k Keeper) ComputeOutAmtGivenIn( swapFee sdk.Dec, priceLimit sdk.Dec, -) (calcTokenIn, calcTokenOut sdk.Coin, currentTick int64, liquidity, sqrtPrice sdk.Dec, err error) { +) (calcTokenIn, calcTokenOut sdk.Coin, currentTick int64, liquidity, sqrtPrice sdk.Dec, totalFees sdk.Dec, err error) { return k.computeOutAmtGivenIn(ctx, poolId, tokenInMin, tokenOutDenom, swapFee, priceLimit) } @@ -93,7 +93,7 @@ func (k Keeper) ComputeInAmtGivenOut( priceLimit sdk.Dec, poolId uint64, -) (calcTokenIn, calcTokenOut sdk.Coin, currentTick int64, liquidity, sqrtPrice sdk.Dec, err error) { +) (calcTokenIn, calcTokenOut sdk.Coin, currentTick int64, liquidity, sqrtPrice sdk.Dec, totalFees sdk.Dec, err error) { return k.computeInAmtGivenOut(ctx, desiredTokenOut, tokenInDenom, swapFee, priceLimit, poolId) } @@ -300,8 +300,8 @@ func (k Keeper) GetAllPositions(ctx sdk.Context) ([]model.Position, error) { return k.getAllPositions(ctx) } -func (k Keeper) UpdatePoolForSwap(ctx sdk.Context, pool types.ConcentratedPoolExtension, sender sdk.AccAddress, tokenIn sdk.Coin, tokenOut sdk.Coin, newCurrentTick int64, newLiquidity sdk.Dec, newSqrtPrice sdk.Dec) error { - return k.updatePoolForSwap(ctx, pool, sender, tokenIn, tokenOut, newCurrentTick, newLiquidity, newSqrtPrice) +func (k Keeper) UpdatePoolForSwap(ctx sdk.Context, pool types.ConcentratedPoolExtension, sender sdk.AccAddress, tokenIn sdk.Coin, tokenOut sdk.Coin, newCurrentTick int64, newLiquidity sdk.Dec, newSqrtPrice sdk.Dec, totalFees sdk.Dec) error { + return k.updatePoolForSwap(ctx, pool, sender, tokenIn, tokenOut, newCurrentTick, newLiquidity, newSqrtPrice, totalFees) } func (k Keeper) PrepareBalancerPoolAsFullRange(ctx sdk.Context, clPoolId uint64) (uint64, sdk.Dec, error) { diff --git a/x/concentrated-liquidity/fees.go b/x/concentrated-liquidity/fees.go index e6b3d5cc2a2..b88417c7605 100644 --- a/x/concentrated-liquidity/fees.go +++ b/x/concentrated-liquidity/fees.go @@ -205,7 +205,7 @@ func (k Keeper) collectFees(ctx sdk.Context, sender sdk.AccAddress, positionId u if err != nil { return sdk.Coins{}, err } - if err := k.bankKeeper.SendCoins(ctx, pool.GetAddress(), sender, feesClaimed); err != nil { + if err := k.bankKeeper.SendCoins(ctx, pool.GetFeesAddress(), sender, feesClaimed); err != nil { return sdk.Coins{}, err } diff --git a/x/concentrated-liquidity/fees_test.go b/x/concentrated-liquidity/fees_test.go index 972741103df..17f16988cf1 100644 --- a/x/concentrated-liquidity/fees_test.go +++ b/x/concentrated-liquidity/fees_test.go @@ -901,7 +901,7 @@ func (s *KeeperTestSuite) TestQueryAndCollectFees() { validPool := s.PrepareConcentratedPool() validPoolId := validPool.GetId() - s.FundAcc(validPool.GetAddress(), tc.expectedFeesClaimed) + s.FundAcc(validPool.GetFeesAddress(), tc.expectedFeesClaimed) clKeeper := s.App.ConcentratedLiquidityKeeper ctx := s.Ctx @@ -923,7 +923,7 @@ func (s *KeeperTestSuite) TestQueryAndCollectFees() { err = clKeeper.ChargeFee(ctx, validPoolId, tc.globalFeeGrowth[0]) s.Require().NoError(err) - poolBalanceBeforeCollect := s.App.BankKeeper.GetBalance(ctx, validPool.GetAddress(), ETH) + poolBalanceBeforeCollect := s.App.BankKeeper.GetBalance(ctx, validPool.GetFeesAddress(), ETH) ownerBalancerBeforeCollect := s.App.BankKeeper.GetBalance(ctx, tc.owner, ETH) var preQueryPosition accum.Record @@ -949,7 +949,7 @@ func (s *KeeperTestSuite) TestQueryAndCollectFees() { // Assertions. - poolBalanceAfterCollect := s.App.BankKeeper.GetBalance(ctx, validPool.GetAddress(), ETH) + poolBalanceAfterCollect := s.App.BankKeeper.GetBalance(ctx, validPool.GetFeesAddress(), ETH) ownerBalancerAfterCollect := s.App.BankKeeper.GetBalance(ctx, tc.owner, ETH) if tc.expectedError != nil { diff --git a/x/concentrated-liquidity/lp_test.go b/x/concentrated-liquidity/lp_test.go index 101d156412e..69568f7bc46 100644 --- a/x/concentrated-liquidity/lp_test.go +++ b/x/concentrated-liquidity/lp_test.go @@ -567,7 +567,7 @@ func (s *KeeperTestSuite) TestWithdrawPosition() { // Fund the pool account with the expected fees claimed. if expectedRemainingLiquidity.IsZero() { expectedFeesClaimed = expectedFeesClaimed.Add(sdk.NewCoin(ETH, liquidityCreated.TruncateInt())) - s.FundAcc(pool.GetAddress(), expectedFeesClaimed) + s.FundAcc(pool.GetFeesAddress(), expectedFeesClaimed) } communityPoolBalanceBefore := s.App.BankKeeper.GetAllBalances(s.Ctx, s.App.AccountKeeper.GetModuleAddress(distributiontypes.ModuleName)) @@ -581,10 +581,11 @@ func (s *KeeperTestSuite) TestWithdrawPosition() { // Note the pool and owner balances before withdrawal of the position. poolBalanceBeforeWithdraw := s.App.BankKeeper.GetAllBalances(s.Ctx, pool.GetAddress()) + poolFeeBalanceBeforeWithdraw := s.App.BankKeeper.GetAllBalances(s.Ctx, pool.GetFeesAddress()) incentivesBalanceBeforeWithdraw := s.App.BankKeeper.GetAllBalances(s.Ctx, pool.GetIncentivesAddress()) ownerBalancerBeforeWithdraw := s.App.BankKeeper.GetAllBalances(s.Ctx, owner) - expectedPoolBalanceDelta := expectedFeesClaimed.Add(sdk.NewCoin(ETH, config.amount0Expected.Abs())).Add(sdk.NewCoin(USDC, config.amount1Expected.Abs())) + expectedPoolBalanceDelta := sdk.NewCoins(sdk.NewCoin(ETH, config.amount0Expected.Abs()), sdk.NewCoin(USDC, config.amount1Expected.Abs())) var withdrawAccount sdk.AccAddress if tc.withdrawWithNonOwner { @@ -610,12 +611,13 @@ func (s *KeeperTestSuite) TestWithdrawPosition() { // If the remaining liquidity is zero, all fees and incentives should be collected and the position should be deleted. // Check if all fees and incentives were collected. poolBalanceAfterWithdraw := s.App.BankKeeper.GetAllBalances(s.Ctx, pool.GetAddress()) + poolFeeBalanceAfterWithdraw := s.App.BankKeeper.GetAllBalances(s.Ctx, pool.GetFeesAddress()) incentivesBalanceAfterWithdraw := s.App.BankKeeper.GetAllBalances(s.Ctx, pool.GetIncentivesAddress()) ownerBalancerAfterWithdraw := s.App.BankKeeper.GetAllBalances(s.Ctx, owner) communityPoolBalanceAfter := s.App.BankKeeper.GetAllBalances(s.Ctx, s.App.AccountKeeper.GetModuleAddress(distributiontypes.ModuleName)) // owner should only have tokens equivilent to the delta balance of the pool - expectedOwnerBalanceDelta := expectedPoolBalanceDelta.Add(expectedIncentivesClaimed...) + expectedOwnerBalanceDelta := expectedPoolBalanceDelta.Add(expectedIncentivesClaimed...).Add(expectedFeesClaimed...) actualOwnerBalancerDelta := ownerBalancerAfterWithdraw.Sub(ownerBalancerBeforeWithdraw) communityPoolBalanceDelta := communityPoolBalanceAfter.Sub(communityPoolBalanceBefore) @@ -638,6 +640,8 @@ func (s *KeeperTestSuite) TestWithdrawPosition() { s.Require().True(expected.Equal(actual)) } + s.Require().Equal(poolFeeBalanceBeforeWithdraw.Sub(poolFeeBalanceAfterWithdraw).String(), expectedFeesClaimed.String()) + // if the position's expected remaining liquidity is equal to zero, we check if all state // have been correctly deleted. if expectedRemainingLiquidity.IsZero() { diff --git a/x/concentrated-liquidity/model/pool.go b/x/concentrated-liquidity/model/pool.go index 5869ba60f09..927aac9c80b 100644 --- a/x/concentrated-liquidity/model/pool.go +++ b/x/concentrated-liquidity/model/pool.go @@ -15,6 +15,7 @@ import ( const ( incentivesAddressPrefix = "incentives" + feesAddressPrefix = "fees" ) var ( @@ -39,6 +40,7 @@ func NewConcentratedLiquidityPool(poolId uint64, denom0, denom1 string, tickSpac pool := Pool{ Address: poolmanagertypes.NewPoolAddress(poolId).String(), IncentivesAddress: osmoutils.NewModuleAddressWithPrefix(types.ModuleName, incentivesAddressPrefix, sdk.Uint64ToBigEndian(poolId)).String(), + FeesAddress: osmoutils.NewModuleAddressWithPrefix(types.ModuleName, feesAddressPrefix, sdk.Uint64ToBigEndian(poolId)).String(), Id: poolId, CurrentSqrtPrice: sdk.ZeroDec(), CurrentTick: 0, @@ -65,7 +67,15 @@ func (p Pool) GetAddress() sdk.AccAddress { func (p Pool) GetIncentivesAddress() sdk.AccAddress { addr, err := sdk.AccAddressFromBech32(p.IncentivesAddress) if err != nil { - panic(fmt.Sprintf("could not bech32 decode address of pool with id: %d", p.GetId())) + panic(fmt.Sprintf("could not bech32 decode incentive address of pool with id: %d", p.GetId())) + } + return addr +} + +func (p Pool) GetFeesAddress() sdk.AccAddress { + addr, err := sdk.AccAddressFromBech32(p.FeesAddress) + if err != nil { + panic(fmt.Sprintf("could not bech32 decode fee address of pool with id: %d", p.GetId())) } return addr } diff --git a/x/concentrated-liquidity/model/pool.pb.go b/x/concentrated-liquidity/model/pool.pb.go index fcce45b1a57..c0a4dac8093 100644 --- a/x/concentrated-liquidity/model/pool.pb.go +++ b/x/concentrated-liquidity/model/pool.pb.go @@ -39,22 +39,24 @@ type Pool struct { Address string `protobuf:"bytes,1,opt,name=address,proto3" json:"address,omitempty" yaml:"address"` // address holding the incentives liquidity. IncentivesAddress string `protobuf:"bytes,2,opt,name=incentives_address,json=incentivesAddress,proto3" json:"incentives_address,omitempty" yaml:"incentives_address"` - Id uint64 `protobuf:"varint,3,opt,name=id,proto3" json:"id,omitempty"` + // address holding fees from swaps. + FeesAddress string `protobuf:"bytes,3,opt,name=fees_address,json=feesAddress,proto3" json:"fees_address,omitempty" yaml:"fees_address"` + Id uint64 `protobuf:"varint,4,opt,name=id,proto3" json:"id,omitempty"` // Amount of total liquidity - CurrentTickLiquidity github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,4,opt,name=current_tick_liquidity,json=currentTickLiquidity,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"current_tick_liquidity" yaml:"current_tick_liquidity"` - Token0 string `protobuf:"bytes,5,opt,name=token0,proto3" json:"token0,omitempty"` - Token1 string `protobuf:"bytes,6,opt,name=token1,proto3" json:"token1,omitempty"` - CurrentSqrtPrice github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,7,opt,name=current_sqrt_price,json=currentSqrtPrice,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"current_sqrt_price" yaml:"spot_price"` - CurrentTick int64 `protobuf:"varint,8,opt,name=current_tick,json=currentTick,proto3" json:"current_tick,omitempty" yaml:"current_tick"` + CurrentTickLiquidity github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,5,opt,name=current_tick_liquidity,json=currentTickLiquidity,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"current_tick_liquidity" yaml:"current_tick_liquidity"` + Token0 string `protobuf:"bytes,6,opt,name=token0,proto3" json:"token0,omitempty"` + Token1 string `protobuf:"bytes,7,opt,name=token1,proto3" json:"token1,omitempty"` + CurrentSqrtPrice github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,8,opt,name=current_sqrt_price,json=currentSqrtPrice,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"current_sqrt_price" yaml:"spot_price"` + CurrentTick int64 `protobuf:"varint,9,opt,name=current_tick,json=currentTick,proto3" json:"current_tick,omitempty" yaml:"current_tick"` // tick_spacing must be one of the authorized_tick_spacing values set in the // concentrated-liquidity parameters - TickSpacing uint64 `protobuf:"varint,9,opt,name=tick_spacing,json=tickSpacing,proto3" json:"tick_spacing,omitempty" yaml:"tick_spacing"` - ExponentAtPriceOne int64 `protobuf:"varint,10,opt,name=exponent_at_price_one,json=exponentAtPriceOne,proto3" json:"exponent_at_price_one,omitempty" yaml:"exponent_at_price_one"` + TickSpacing uint64 `protobuf:"varint,10,opt,name=tick_spacing,json=tickSpacing,proto3" json:"tick_spacing,omitempty" yaml:"tick_spacing"` + ExponentAtPriceOne int64 `protobuf:"varint,11,opt,name=exponent_at_price_one,json=exponentAtPriceOne,proto3" json:"exponent_at_price_one,omitempty" yaml:"exponent_at_price_one"` // swap_fee is the ratio that is charged on the amount of token in. - SwapFee github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,11,opt,name=swap_fee,json=swapFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"swap_fee" yaml:"swap_fee"` + SwapFee github_com_cosmos_cosmos_sdk_types.Dec `protobuf:"bytes,12,opt,name=swap_fee,json=swapFee,proto3,customtype=github.com/cosmos/cosmos-sdk/types.Dec" json:"swap_fee" yaml:"swap_fee"` // last_liquidity_update is the last time either the pool liquidity or the // active tick changed - LastLiquidityUpdate time.Time `protobuf:"bytes,12,opt,name=last_liquidity_update,json=lastLiquidityUpdate,proto3,stdtime" json:"last_liquidity_update" yaml:"last_liquidity_update"` + LastLiquidityUpdate time.Time `protobuf:"bytes,13,opt,name=last_liquidity_update,json=lastLiquidityUpdate,proto3,stdtime" json:"last_liquidity_update" yaml:"last_liquidity_update"` } func (m *Pool) Reset() { *m = Pool{} } @@ -98,46 +100,47 @@ func init() { } var fileDescriptor_3526ea5373d96c9a = []byte{ - // 617 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xcd, 0x6e, 0xd3, 0x40, - 0x10, 0xb6, 0xfb, 0x93, 0xb6, 0x9b, 0xaa, 0xd0, 0xed, 0x0f, 0x6e, 0x45, 0xed, 0xc8, 0x12, 0x28, - 0x48, 0xc4, 0x26, 0x20, 0x2e, 0xbd, 0x35, 0x20, 0x24, 0xa4, 0x4a, 0xad, 0xdc, 0x72, 0x41, 0x95, - 0x2c, 0xc7, 0xde, 0x9a, 0x55, 0x6c, 0xaf, 0xe3, 0xdd, 0x84, 0xe4, 0x01, 0x90, 0x38, 0xf6, 0xc8, - 0xb1, 0x0f, 0xc1, 0x43, 0x54, 0x9c, 0x7a, 0x44, 0x1c, 0x0c, 0x4a, 0xde, 0x20, 0x12, 0x77, 0xe4, - 0xf5, 0x3a, 0xb1, 0xd4, 0x70, 0xe8, 0x29, 0x9e, 0x6f, 0xbe, 0xf9, 0xf6, 0x9b, 0xd9, 0xcc, 0x82, - 0x67, 0x84, 0x86, 0x84, 0x62, 0x6a, 0xba, 0x24, 0x72, 0x51, 0xc4, 0x12, 0x87, 0x21, 0xaf, 0x11, - 0xe0, 0x6e, 0x0f, 0x7b, 0x98, 0x0d, 0xcd, 0x98, 0x90, 0xc0, 0x88, 0x13, 0xc2, 0x08, 0x7c, 0x22, - 0xa8, 0x46, 0x99, 0x3a, 0x65, 0x1a, 0xfd, 0x66, 0x1b, 0x31, 0xa7, 0xb9, 0xbf, 0xe7, 0x72, 0x9e, - 0xcd, 0x8b, 0xcc, 0x3c, 0xc8, 0x15, 0xf6, 0xb7, 0x7d, 0xe2, 0x93, 0x1c, 0xcf, 0xbe, 0x04, 0xaa, - 0xf9, 0x84, 0xf8, 0x01, 0x32, 0x79, 0xd4, 0xee, 0x5d, 0x9a, 0x0c, 0x87, 0x88, 0x32, 0x27, 0x8c, - 0x73, 0x82, 0xfe, 0xb7, 0x02, 0x96, 0x4e, 0x09, 0x09, 0xe0, 0x73, 0xb0, 0xe2, 0x78, 0x5e, 0x82, - 0x28, 0x55, 0xe4, 0x9a, 0x5c, 0x5f, 0x6b, 0xc1, 0x49, 0xaa, 0x6d, 0x0c, 0x9d, 0x30, 0x38, 0xd4, - 0x45, 0x42, 0xb7, 0x0a, 0x0a, 0x3c, 0x06, 0x10, 0x73, 0xa3, 0xb8, 0x8f, 0xa8, 0x5d, 0x14, 0x2e, - 0xf0, 0xc2, 0x83, 0x49, 0xaa, 0xed, 0xe5, 0x85, 0x77, 0x39, 0xba, 0xb5, 0x39, 0x03, 0x8f, 0x84, - 0xda, 0x06, 0x58, 0xc0, 0x9e, 0xb2, 0x58, 0x93, 0xeb, 0x4b, 0xd6, 0x02, 0xf6, 0xe0, 0x17, 0x19, - 0xec, 0xba, 0xbd, 0x24, 0x41, 0x11, 0xb3, 0x19, 0x76, 0x3b, 0xf6, 0x74, 0x12, 0xca, 0x12, 0x3f, - 0xe2, 0xe4, 0x26, 0xd5, 0xa4, 0x5f, 0xa9, 0xf6, 0xd4, 0xc7, 0xec, 0x53, 0xaf, 0x6d, 0xb8, 0x24, - 0x14, 0xd3, 0x10, 0x3f, 0x0d, 0xea, 0x75, 0x4c, 0x36, 0x8c, 0x11, 0x35, 0xde, 0x22, 0x77, 0x92, - 0x6a, 0x07, 0xb9, 0xa1, 0xf9, 0xaa, 0xba, 0xb5, 0x2d, 0x12, 0xe7, 0xd8, 0xed, 0x1c, 0x17, 0x30, - 0xdc, 0x05, 0x15, 0x46, 0x3a, 0x28, 0x7a, 0xa1, 0x2c, 0x67, 0xc7, 0x5a, 0x22, 0x9a, 0xe2, 0x4d, - 0xa5, 0x52, 0xc2, 0x9b, 0xb0, 0x0b, 0x60, 0x71, 0x00, 0xed, 0x26, 0xcc, 0x8e, 0x13, 0xec, 0x22, - 0x65, 0x85, 0x5b, 0x7e, 0x73, 0x6f, 0xcb, 0x9b, 0xb9, 0x65, 0x1a, 0x13, 0xa1, 0xa4, 0x5b, 0x0f, - 0x85, 0xfc, 0x59, 0x37, 0x61, 0xa7, 0x19, 0x04, 0x0f, 0xc1, 0x7a, 0xb9, 0x27, 0x65, 0xb5, 0x26, - 0xd7, 0x17, 0x5b, 0x8f, 0x26, 0xa9, 0xb6, 0x75, 0xb7, 0x63, 0xdd, 0xaa, 0x96, 0xfa, 0xcc, 0x6a, - 0xf9, 0x1c, 0x68, 0xec, 0xb8, 0x38, 0xf2, 0x95, 0xb5, 0xec, 0x02, 0xca, 0xb5, 0xe5, 0xac, 0x6e, - 0x55, 0xb3, 0xf0, 0x2c, 0x8f, 0xe0, 0x19, 0xd8, 0x41, 0x83, 0x98, 0x44, 0x99, 0xb4, 0x23, 0xfc, - 0xd9, 0x24, 0x42, 0x0a, 0xe0, 0x06, 0x6a, 0x93, 0x54, 0x7b, 0x9c, 0x8b, 0xcc, 0xa5, 0xe9, 0x16, - 0x2c, 0xf0, 0xa3, 0xbc, 0x93, 0x93, 0x08, 0xc1, 0x0b, 0xb0, 0x4a, 0x3f, 0x3b, 0xb1, 0x7d, 0x89, - 0x90, 0x52, 0xe5, 0x53, 0x3b, 0xba, 0xf7, 0xd4, 0x1e, 0x88, 0xa9, 0x09, 0x1d, 0xdd, 0x5a, 0xc9, - 0x3e, 0xdf, 0x21, 0x04, 0x07, 0x60, 0x27, 0x70, 0x28, 0x9b, 0x5d, 0xbb, 0xdd, 0x8b, 0x3d, 0x87, - 0x21, 0x65, 0xbd, 0x26, 0xd7, 0xab, 0x2f, 0xf7, 0x8d, 0x7c, 0x57, 0x8c, 0x62, 0x57, 0x8c, 0xf3, - 0x62, 0x57, 0x5a, 0xf5, 0xcc, 0xc6, 0xac, 0xa5, 0xb9, 0x32, 0xfa, 0xd5, 0x6f, 0x4d, 0xb6, 0xb6, - 0xb2, 0xdc, 0xf4, 0x1f, 0xf4, 0x81, 0x67, 0x0e, 0x37, 0xbf, 0x5e, 0x6b, 0xd2, 0xb7, 0x6b, 0x4d, - 0xfa, 0xf1, 0xbd, 0xb1, 0x9c, 0x6d, 0xdb, 0xfb, 0xd6, 0xc5, 0xcd, 0x48, 0x95, 0x6f, 0x47, 0xaa, - 0xfc, 0x67, 0xa4, 0xca, 0x57, 0x63, 0x55, 0xba, 0x1d, 0xab, 0xd2, 0xcf, 0xb1, 0x2a, 0x7d, 0x6c, - 0x95, 0x5a, 0x15, 0xaf, 0x42, 0x23, 0x70, 0xda, 0xb4, 0x08, 0xcc, 0x7e, 0xf3, 0xb5, 0x39, 0xf8, - 0xdf, 0x9b, 0x12, 0x12, 0x0f, 0x05, 0xed, 0x0a, 0xef, 0xe1, 0xd5, 0xbf, 0x00, 0x00, 0x00, 0xff, - 0xff, 0x17, 0x07, 0xb8, 0x83, 0x82, 0x04, 0x00, 0x00, + // 633 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x54, 0xcb, 0x6e, 0xd3, 0x40, + 0x14, 0x8d, 0xfb, 0x4a, 0x3b, 0x29, 0x85, 0x4e, 0x1f, 0xb8, 0x15, 0x8d, 0x23, 0x4b, 0xa0, 0x20, + 0x11, 0x9b, 0x80, 0xd8, 0x74, 0xd7, 0x80, 0x90, 0x90, 0x2a, 0xb5, 0x9a, 0x96, 0x0d, 0xaa, 0x64, + 0x39, 0xf6, 0x34, 0x8c, 0x62, 0x7b, 0x1c, 0xcf, 0xa4, 0xb4, 0x1f, 0x80, 0xc4, 0xb2, 0x4b, 0x96, + 0xfd, 0x05, 0x24, 0x3e, 0xa2, 0x62, 0xd5, 0x25, 0x62, 0x61, 0x50, 0xfb, 0x07, 0xf9, 0x02, 0x34, + 0x0f, 0xa7, 0x96, 0x08, 0x8b, 0xae, 0xe2, 0x7b, 0xee, 0xb9, 0xc7, 0xe7, 0x1e, 0x67, 0x06, 0x3c, + 0xa5, 0x2c, 0xa6, 0x8c, 0x30, 0x37, 0xa0, 0x49, 0x80, 0x13, 0x9e, 0xf9, 0x1c, 0x87, 0xad, 0x88, + 0x0c, 0x86, 0x24, 0x24, 0xfc, 0xcc, 0x4d, 0x29, 0x8d, 0x9c, 0x34, 0xa3, 0x9c, 0xc2, 0xc7, 0x9a, + 0xea, 0x94, 0xa9, 0x63, 0xa6, 0x73, 0xd2, 0xee, 0x62, 0xee, 0xb7, 0x37, 0x37, 0x02, 0xc9, 0xf3, + 0xe4, 0x90, 0xab, 0x0a, 0xa5, 0xb0, 0xb9, 0xda, 0xa3, 0x3d, 0xaa, 0x70, 0xf1, 0xa4, 0x51, 0xab, + 0x47, 0x69, 0x2f, 0xc2, 0xae, 0xac, 0xba, 0xc3, 0x63, 0x97, 0x93, 0x18, 0x33, 0xee, 0xc7, 0xa9, + 0x22, 0xd8, 0xdf, 0xaa, 0x60, 0x66, 0x9f, 0xd2, 0x08, 0x3e, 0x03, 0x55, 0x3f, 0x0c, 0x33, 0xcc, + 0x98, 0x69, 0x34, 0x8c, 0xe6, 0x42, 0x07, 0x8e, 0x72, 0x6b, 0xe9, 0xcc, 0x8f, 0xa3, 0x6d, 0x5b, + 0x37, 0x6c, 0x54, 0x50, 0xe0, 0x2e, 0x80, 0x44, 0x1a, 0x25, 0x27, 0x98, 0x79, 0xc5, 0xe0, 0x94, + 0x1c, 0xdc, 0x1a, 0xe5, 0xd6, 0x86, 0x1a, 0xfc, 0x97, 0x63, 0xa3, 0xe5, 0x5b, 0x70, 0x47, 0xab, + 0x6d, 0x83, 0xc5, 0x63, 0x5c, 0xd2, 0x99, 0x96, 0x3a, 0x0f, 0x47, 0xb9, 0xb5, 0xa2, 0x74, 0xca, + 0x5d, 0x1b, 0xd5, 0x44, 0x59, 0xcc, 0x2e, 0x81, 0x29, 0x12, 0x9a, 0x33, 0x0d, 0xa3, 0x39, 0x83, + 0xa6, 0x48, 0x08, 0x3f, 0x1b, 0x60, 0x3d, 0x18, 0x66, 0x19, 0x4e, 0xb8, 0xc7, 0x49, 0xd0, 0xf7, + 0xc6, 0x29, 0x9a, 0xb3, 0x52, 0x76, 0xef, 0x32, 0xb7, 0x2a, 0xbf, 0x72, 0xeb, 0x49, 0x8f, 0xf0, + 0x8f, 0xc3, 0xae, 0x13, 0xd0, 0x58, 0x27, 0xa9, 0x7f, 0x5a, 0x2c, 0xec, 0xbb, 0xfc, 0x2c, 0xc5, + 0xcc, 0x79, 0x83, 0x83, 0x51, 0x6e, 0x6d, 0x29, 0x13, 0x93, 0x55, 0x6d, 0xb4, 0xaa, 0x1b, 0x87, + 0x24, 0xe8, 0xef, 0x16, 0x30, 0x5c, 0x07, 0x73, 0x9c, 0xf6, 0x71, 0xf2, 0xdc, 0x9c, 0x13, 0xaf, + 0x45, 0xba, 0x1a, 0xe3, 0x6d, 0xb3, 0x5a, 0xc2, 0xdb, 0x70, 0x00, 0x60, 0xf1, 0x02, 0x36, 0xc8, + 0xb8, 0x97, 0x66, 0x24, 0xc0, 0xe6, 0xbc, 0xb4, 0xfc, 0xfa, 0xce, 0x96, 0x97, 0x95, 0x65, 0x96, + 0x52, 0xad, 0x64, 0xa3, 0x07, 0x5a, 0xfe, 0x60, 0x90, 0xf1, 0x7d, 0x01, 0x89, 0xd8, 0xcb, 0x3b, + 0x99, 0x0b, 0x0d, 0xa3, 0x39, 0x5d, 0x8e, 0xbd, 0xdc, 0xb5, 0x51, 0xad, 0xb4, 0xa7, 0x98, 0x95, + 0x39, 0xb0, 0xd4, 0x0f, 0x48, 0xd2, 0x33, 0x81, 0xf8, 0x00, 0xe5, 0xd9, 0x72, 0xd7, 0x46, 0x35, + 0x51, 0x1e, 0xa8, 0x0a, 0x1e, 0x80, 0x35, 0x7c, 0x9a, 0xd2, 0x44, 0x48, 0xfb, 0xda, 0x9f, 0x47, + 0x13, 0x6c, 0xd6, 0xa4, 0x81, 0xc6, 0x28, 0xb7, 0x1e, 0x29, 0x91, 0x89, 0x34, 0x1b, 0xc1, 0x02, + 0xdf, 0x51, 0x9b, 0xec, 0x25, 0x18, 0x1e, 0x81, 0x79, 0xf6, 0xc9, 0x4f, 0xbd, 0x63, 0x8c, 0xcd, + 0x45, 0x99, 0xda, 0xce, 0x9d, 0x53, 0xbb, 0xaf, 0x53, 0xd3, 0x3a, 0x36, 0xaa, 0x8a, 0xc7, 0xb7, + 0x18, 0xc3, 0x53, 0xb0, 0x16, 0xf9, 0x8c, 0xdf, 0x7e, 0x76, 0x6f, 0x98, 0x86, 0x3e, 0xc7, 0xe6, + 0xbd, 0x86, 0xd1, 0xac, 0xbd, 0xd8, 0x74, 0xd4, 0x39, 0x73, 0x8a, 0x73, 0xe6, 0x1c, 0x16, 0xe7, + 0xac, 0xd3, 0x14, 0x36, 0x6e, 0x57, 0x9a, 0x28, 0x63, 0x9f, 0xff, 0xb6, 0x0c, 0xb4, 0x22, 0x7a, + 0xe3, 0x7f, 0xd0, 0x7b, 0xd9, 0xd9, 0x5e, 0xfe, 0x72, 0x61, 0x55, 0xbe, 0x5e, 0x58, 0x95, 0x1f, + 0xdf, 0x5b, 0xb3, 0xe2, 0xa4, 0xbe, 0xeb, 0x1c, 0x5d, 0x5e, 0xd7, 0x8d, 0xab, 0xeb, 0xba, 0xf1, + 0xe7, 0xba, 0x6e, 0x9c, 0xdf, 0xd4, 0x2b, 0x57, 0x37, 0xf5, 0xca, 0xcf, 0x9b, 0x7a, 0xe5, 0x43, + 0xa7, 0xb4, 0xaa, 0xbe, 0x51, 0x5a, 0x91, 0xdf, 0x65, 0x45, 0xe1, 0x9e, 0xb4, 0x5f, 0xb9, 0xa7, + 0xff, 0xbb, 0x8f, 0x62, 0x1a, 0xe2, 0xa8, 0x3b, 0x27, 0x77, 0x78, 0xf9, 0x37, 0x00, 0x00, 0xff, + 0xff, 0xb9, 0x3a, 0x2a, 0xb2, 0xbe, 0x04, 0x00, 0x00, } func (m *Pool) Marshal() (dAtA []byte, err error) { @@ -167,7 +170,7 @@ func (m *Pool) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= n1 i = encodeVarintPool(dAtA, i, uint64(n1)) i-- - dAtA[i] = 0x62 + dAtA[i] = 0x6a { size := m.SwapFee.Size() i -= size @@ -177,21 +180,21 @@ func (m *Pool) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintPool(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x5a + dAtA[i] = 0x62 if m.ExponentAtPriceOne != 0 { i = encodeVarintPool(dAtA, i, uint64(m.ExponentAtPriceOne)) i-- - dAtA[i] = 0x50 + dAtA[i] = 0x58 } if m.TickSpacing != 0 { i = encodeVarintPool(dAtA, i, uint64(m.TickSpacing)) i-- - dAtA[i] = 0x48 + dAtA[i] = 0x50 } if m.CurrentTick != 0 { i = encodeVarintPool(dAtA, i, uint64(m.CurrentTick)) i-- - dAtA[i] = 0x40 + dAtA[i] = 0x48 } { size := m.CurrentSqrtPrice.Size() @@ -202,20 +205,20 @@ func (m *Pool) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintPool(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x3a + dAtA[i] = 0x42 if len(m.Token1) > 0 { i -= len(m.Token1) copy(dAtA[i:], m.Token1) i = encodeVarintPool(dAtA, i, uint64(len(m.Token1))) i-- - dAtA[i] = 0x32 + dAtA[i] = 0x3a } if len(m.Token0) > 0 { i -= len(m.Token0) copy(dAtA[i:], m.Token0) i = encodeVarintPool(dAtA, i, uint64(len(m.Token0))) i-- - dAtA[i] = 0x2a + dAtA[i] = 0x32 } { size := m.CurrentTickLiquidity.Size() @@ -226,11 +229,18 @@ func (m *Pool) MarshalToSizedBuffer(dAtA []byte) (int, error) { i = encodeVarintPool(dAtA, i, uint64(size)) } i-- - dAtA[i] = 0x22 + dAtA[i] = 0x2a if m.Id != 0 { i = encodeVarintPool(dAtA, i, uint64(m.Id)) i-- - dAtA[i] = 0x18 + dAtA[i] = 0x20 + } + if len(m.FeesAddress) > 0 { + i -= len(m.FeesAddress) + copy(dAtA[i:], m.FeesAddress) + i = encodeVarintPool(dAtA, i, uint64(len(m.FeesAddress))) + i-- + dAtA[i] = 0x1a } if len(m.IncentivesAddress) > 0 { i -= len(m.IncentivesAddress) @@ -274,6 +284,10 @@ func (m *Pool) Size() (n int) { if l > 0 { n += 1 + l + sovPool(uint64(l)) } + l = len(m.FeesAddress) + if l > 0 { + n += 1 + l + sovPool(uint64(l)) + } if m.Id != 0 { n += 1 + sovPool(uint64(m.Id)) } @@ -405,6 +419,38 @@ func (m *Pool) Unmarshal(dAtA []byte) error { m.IncentivesAddress = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field FeesAddress", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowPool + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthPool + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthPool + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.FeesAddress = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 4: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) } @@ -423,7 +469,7 @@ func (m *Pool) Unmarshal(dAtA []byte) error { break } } - case 4: + case 5: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CurrentTickLiquidity", wireType) } @@ -457,7 +503,7 @@ func (m *Pool) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 5: + case 6: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Token0", wireType) } @@ -489,7 +535,7 @@ func (m *Pool) Unmarshal(dAtA []byte) error { } m.Token0 = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 6: + case 7: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field Token1", wireType) } @@ -521,7 +567,7 @@ func (m *Pool) Unmarshal(dAtA []byte) error { } m.Token1 = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex - case 7: + case 8: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field CurrentSqrtPrice", wireType) } @@ -555,7 +601,7 @@ func (m *Pool) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 8: + case 9: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field CurrentTick", wireType) } @@ -574,7 +620,7 @@ func (m *Pool) Unmarshal(dAtA []byte) error { break } } - case 9: + case 10: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field TickSpacing", wireType) } @@ -593,7 +639,7 @@ func (m *Pool) Unmarshal(dAtA []byte) error { break } } - case 10: + case 11: if wireType != 0 { return fmt.Errorf("proto: wrong wireType = %d for field ExponentAtPriceOne", wireType) } @@ -612,7 +658,7 @@ func (m *Pool) Unmarshal(dAtA []byte) error { break } } - case 11: + case 12: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field SwapFee", wireType) } @@ -646,7 +692,7 @@ func (m *Pool) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex - case 12: + case 13: if wireType != 2 { return fmt.Errorf("proto: wrong wireType = %d for field LastLiquidityUpdate", wireType) } diff --git a/x/concentrated-liquidity/swaps.go b/x/concentrated-liquidity/swaps.go index 5483ee4d807..6ff78b014f2 100644 --- a/x/concentrated-liquidity/swaps.go +++ b/x/concentrated-liquidity/swaps.go @@ -160,7 +160,7 @@ func (k Keeper) swapOutAmtGivenIn( swapFee sdk.Dec, priceLimit sdk.Dec, ) (calcTokenIn, calcTokenOut sdk.Coin, currentTick int64, liquidity, sqrtPrice sdk.Dec, err error) { - tokenIn, tokenOut, newCurrentTick, newLiquidity, newSqrtPrice, err := k.computeOutAmtGivenIn(ctx, pool.GetId(), tokenIn, tokenOutDenom, swapFee, priceLimit) + tokenIn, tokenOut, newCurrentTick, newLiquidity, newSqrtPrice, totalFees, err := k.computeOutAmtGivenIn(ctx, pool.GetId(), tokenIn, tokenOutDenom, swapFee, priceLimit) if err != nil { return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err } @@ -171,7 +171,7 @@ func (k Keeper) swapOutAmtGivenIn( // Settles balances between the tx sender and the pool to match the swap that was executed earlier. // Also emits swap event and updates related liquidity metrics - if err := k.updatePoolForSwap(ctx, pool, sender, tokenIn, tokenOut, newCurrentTick, newLiquidity, newSqrtPrice); err != nil { + if err := k.updatePoolForSwap(ctx, pool, sender, tokenIn, tokenOut, newCurrentTick, newLiquidity, newSqrtPrice, totalFees); err != nil { return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err } @@ -187,7 +187,7 @@ func (k *Keeper) swapInAmtGivenOut( swapFee sdk.Dec, priceLimit sdk.Dec, ) (calcTokenIn, calcTokenOut sdk.Coin, currentTick int64, liquidity, sqrtPrice sdk.Dec, err error) { - tokenIn, tokenOut, newCurrentTick, newLiquidity, newSqrtPrice, err := k.computeInAmtGivenOut(ctx, desiredTokenOut, tokenInDenom, swapFee, priceLimit, pool.GetId()) + tokenIn, tokenOut, newCurrentTick, newLiquidity, newSqrtPrice, totalFees, err := k.computeInAmtGivenOut(ctx, desiredTokenOut, tokenInDenom, swapFee, priceLimit, pool.GetId()) if err != nil { return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err } @@ -199,7 +199,7 @@ func (k *Keeper) swapInAmtGivenOut( // Settles balances between the tx sender and the pool to match the swap that was executed earlier. // Also emits swap event and updates related liquidity metrics - if err := k.updatePoolForSwap(ctx, pool, sender, tokenIn, tokenOut, newCurrentTick, newLiquidity, newSqrtPrice); err != nil { + if err := k.updatePoolForSwap(ctx, pool, sender, tokenIn, tokenOut, newCurrentTick, newLiquidity, newSqrtPrice, totalFees); err != nil { return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err } @@ -214,7 +214,7 @@ func (k Keeper) CalcOutAmtGivenIn( swapFee sdk.Dec, ) (tokenOut sdk.Coin, err error) { cacheCtx, _ := ctx.CacheContext() - _, tokenOut, _, _, _, err = k.computeOutAmtGivenIn(cacheCtx, poolI.GetId(), tokenIn, tokenOutDenom, swapFee, sdk.ZeroDec()) + _, tokenOut, _, _, _, _, err = k.computeOutAmtGivenIn(cacheCtx, poolI.GetId(), tokenIn, tokenOutDenom, swapFee, sdk.ZeroDec()) if err != nil { return sdk.Coin{}, err } @@ -229,7 +229,7 @@ func (k Keeper) CalcInAmtGivenOut( swapFee sdk.Dec, ) (tokenIn sdk.Coin, err error) { cacheCtx, _ := ctx.CacheContext() - tokenIn, _, _, _, _, err = k.computeInAmtGivenOut(cacheCtx, tokenOut, tokenInDenom, swapFee, sdk.ZeroDec(), poolI.GetId()) + tokenIn, _, _, _, _, _, err = k.computeInAmtGivenOut(cacheCtx, tokenOut, tokenInDenom, swapFee, sdk.ZeroDec(), poolI.GetId()) if err != nil { return sdk.Coin{}, err } @@ -248,19 +248,19 @@ func (k Keeper) computeOutAmtGivenIn( tokenOutDenom string, swapFee sdk.Dec, priceLimit sdk.Dec, -) (tokenIn, tokenOut sdk.Coin, updatedTick int64, updatedLiquidity, updatedSqrtPrice sdk.Dec, err error) { +) (tokenIn, tokenOut sdk.Coin, updatedTick int64, updatedLiquidity, updatedSqrtPrice sdk.Dec, totalFees sdk.Dec, err error) { // Get pool and asset info p, err := k.getPoolById(ctx, poolId) if err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, err } hasPositionInPool, err := k.HasAnyPositionForPool(ctx, poolId) if err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, err } if !hasPositionInPool { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, types.NoSpotPriceWhenNoLiquidityError{PoolId: poolId} + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, types.NoSpotPriceWhenNoLiquidityError{PoolId: poolId} } asset0 := p.GetToken0() @@ -280,7 +280,7 @@ func (k Keeper) computeOutAmtGivenIn( // Take provided price limit and turn this into a sqrt price limit since formulas use sqrtPrice sqrtPriceLimit, err := priceLimit.ApproxSqrt() if err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, fmt.Errorf("issue calculating square root of price limit") + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, fmt.Errorf("issue calculating square root of price limit") } // Set the swap strategy @@ -290,20 +290,20 @@ func (k Keeper) computeOutAmtGivenIn( // on the correct side of the price limit given swap direction. curSqrtPrice := p.GetCurrentSqrtPrice() if err := swapStrategy.ValidateSqrtPrice(sqrtPriceLimit, curSqrtPrice); err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, err } // Check that the specified tokenIn matches one of the assets in the specified pool if tokenInMin.Denom != asset0 && tokenInMin.Denom != asset1 { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, types.TokenInDenomNotInPoolError{TokenInDenom: tokenInMin.Denom} + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, types.TokenInDenomNotInPoolError{TokenInDenom: tokenInMin.Denom} } // Check that the specified tokenOut matches one of the assets in the specified pool if tokenOutDenom != asset0 && tokenOutDenom != asset1 { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, types.TokenOutDenomNotInPoolError{TokenOutDenom: tokenOutDenom} + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, types.TokenOutDenomNotInPoolError{TokenOutDenom: tokenOutDenom} } // Check that token in and token out are different denominations if tokenInMin.Denom == tokenOutDenom { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, types.DenomDuplicatedError{TokenInDenom: tokenInMin.Denom, TokenOutDenom: tokenOutDenom} + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, types.DenomDuplicatedError{TokenInDenom: tokenInMin.Denom, TokenOutDenom: tokenOutDenom} } // initialize swap state with the following parameters: @@ -321,9 +321,10 @@ func (k Keeper) computeOutAmtGivenIn( nextTickIter := swapStrategy.InitializeNextTickIterator(ctx, poolId, swapState.tick) defer nextTickIter.Close() if !nextTickIter.Valid() { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, types.RanOutOfTicksForPoolError{PoolId: poolId} + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, types.RanOutOfTicksForPoolError{PoolId: poolId} } + totalFees = sdk.ZeroDec() // Iterate and update swapState until we swap all tokenIn or we reach the specific sqrtPriceLimit // TODO: for now, we check if amountSpecifiedRemaining is GT 0.0000001. This is because there are times when the remaining // amount may be extremely small, and that small amount cannot generate and amountIn/amountOut and we are therefore left @@ -334,7 +335,7 @@ func (k Keeper) computeOutAmtGivenIn( // Iterator must be valid to be able to retrieve the next tick from it below. if !nextTickIter.Valid() { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, types.RanOutOfTicksForPoolError{PoolId: poolId} + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, types.RanOutOfTicksForPoolError{PoolId: poolId} } // We first check to see what the position of the nearest initialized tick is @@ -343,13 +344,13 @@ func (k Keeper) computeOutAmtGivenIn( // if no ticks are initialized (no users have created liquidity positions) then we return an error nextTick, err := types.TickIndexFromBytes(nextTickIter.Key()) if err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, err } // Utilizing the next initialized tick, we find the corresponding nextPrice (the target price). _, nextTickSqrtPrice, err := math.TickToSqrtPrice(nextTick) if err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, fmt.Errorf("could not convert next tick (%v) to nextSqrtPrice", nextTick) + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, fmt.Errorf("could not convert next tick (%v) to nextSqrtPrice", nextTick) } // If nextSqrtPrice exceeds the price limit, we set the nextSqrtPrice to the price limit. @@ -381,6 +382,7 @@ func (k Keeper) computeOutAmtGivenIn( swapState.amountSpecifiedRemaining = swapState.amountSpecifiedRemaining.Sub(amountIn.Add(feeCharge)) // We add the amount of tokens we received (amountOut) from the computeSwapStep above to the amountCalculated accumulator swapState.amountCalculated = swapState.amountCalculated.Add(amountOut) + totalFees = totalFees.Add(feeCharge) // If the computeSwapStep calculated a sqrtPrice that is equal to the nextSqrtPrice, this means all liquidity in the current // tick has been consumed and we must move on to the next tick to complete the swap @@ -388,12 +390,12 @@ func (k Keeper) computeOutAmtGivenIn( // Retrieve the liquidity held in the next closest initialized tick liquidityNet, err := k.crossTick(ctx, p.GetId(), nextTick, sdk.NewDecCoinFromDec(tokenInMin.Denom, swapState.feeGrowthGlobal)) if err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, err } // Move next tick iterator to the next tick as the tick is crossed. if !nextTickIter.Valid() { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, types.RanOutOfTicksForPoolError{PoolId: poolId} + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, types.RanOutOfTicksForPoolError{PoolId: poolId} } nextTickIter.Next() @@ -410,13 +412,13 @@ func (k Keeper) computeOutAmtGivenIn( price := sqrtPrice.Mul(sqrtPrice) swapState.tick, err = math.PriceToTickRoundDown(price, p.GetTickSpacing()) if err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, err } } } if err := k.chargeFee(ctx, poolId, sdk.NewDecCoinFromDec(tokenInMin.Denom, swapState.feeGrowthGlobal)); err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, err } // Coin amounts require int values @@ -431,7 +433,7 @@ func (k Keeper) computeOutAmtGivenIn( tokenIn = sdk.NewCoin(tokenInMin.Denom, amt0) tokenOut = sdk.NewCoin(tokenOutDenom, amt1) - return tokenIn, tokenOut, swapState.tick, swapState.liquidity, swapState.sqrtPrice, nil + return tokenIn, tokenOut, swapState.tick, swapState.liquidity, swapState.sqrtPrice, totalFees, nil } // computeInAmtGivenOut calculates tokens to be swapped in given the desired token out and fee deducted. It also returns @@ -445,18 +447,18 @@ func (k Keeper) computeInAmtGivenOut( swapFee sdk.Dec, priceLimit sdk.Dec, poolId uint64, -) (tokenIn, tokenOut sdk.Coin, updatedTick int64, updatedLiquidity, updatedSqrtPrice sdk.Dec, err error) { +) (tokenIn, tokenOut sdk.Coin, updatedTick int64, updatedLiquidity, updatedSqrtPrice sdk.Dec, totalFees sdk.Dec, err error) { p, err := k.getPoolById(ctx, poolId) if err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, err } hasPositionInPool, err := k.HasAnyPositionForPool(ctx, poolId) if err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, err } if !hasPositionInPool { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, types.NoSpotPriceWhenNoLiquidityError{PoolId: poolId} + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, types.NoSpotPriceWhenNoLiquidityError{PoolId: poolId} } asset0 := p.GetToken0() @@ -475,7 +477,7 @@ func (k Keeper) computeInAmtGivenOut( // take provided price limit and turn this into a sqrt price limit since formulas use sqrtPrice sqrtPriceLimit, err := priceLimit.ApproxSqrt() if err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, fmt.Errorf("issue calculating square root of price limit") + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, fmt.Errorf("issue calculating square root of price limit") } // set the swap strategy @@ -485,20 +487,20 @@ func (k Keeper) computeInAmtGivenOut( curSqrtPrice := p.GetCurrentSqrtPrice() if err := swapStrategy.ValidateSqrtPrice(sqrtPriceLimit, curSqrtPrice); err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, err } // check that the specified tokenOut matches one of the assets in the specified pool if desiredTokenOut.Denom != asset0 && desiredTokenOut.Denom != asset1 { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, types.TokenOutDenomNotInPoolError{TokenOutDenom: desiredTokenOut.Denom} + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, types.TokenOutDenomNotInPoolError{TokenOutDenom: desiredTokenOut.Denom} } // check that the specified tokenIn matches one of the assets in the specified pool if tokenInDenom != asset0 && tokenInDenom != asset1 { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, types.TokenInDenomNotInPoolError{TokenInDenom: tokenInDenom} + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, types.TokenInDenomNotInPoolError{TokenInDenom: tokenInDenom} } // check that token in and token out are different denominations if desiredTokenOut.Denom == tokenInDenom { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, types.DenomDuplicatedError{TokenInDenom: tokenInDenom, TokenOutDenom: desiredTokenOut.Denom} + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, types.DenomDuplicatedError{TokenInDenom: tokenInDenom, TokenOutDenom: desiredTokenOut.Denom} } // initialize swap state with the following parameters: @@ -512,6 +514,8 @@ func (k Keeper) computeInAmtGivenOut( feeGrowthGlobal: sdk.ZeroDec(), } + totalFees = sdk.ZeroDec() + // TODO: This should be GT 0 but some instances have very small remainder // need to look into fixing this for swapState.amountSpecifiedRemaining.GT(sdk.SmallestDec()) && !swapState.sqrtPrice.Equal(sqrtPriceLimit) { @@ -524,13 +528,13 @@ func (k Keeper) computeInAmtGivenOut( // if no ticks are initialized (no users have created liquidity positions) then we return an error nextTick, ok := swapStrategy.NextInitializedTick(ctx, poolId, swapState.tick) if !ok { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, fmt.Errorf("there are no more ticks initialized to fill the swap") + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, fmt.Errorf("there are no more ticks initialized to fill the swap") } // utilizing the next initialized tick, we find the corresponding nextPrice (the target price) _, sqrtPriceNextTick, err := math.TickToSqrtPrice(nextTick) if err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, fmt.Errorf("could not convert next tick (%v) to nextSqrtPrice", nextTick) + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, fmt.Errorf("could not convert next tick (%v) to nextSqrtPrice", nextTick) } sqrtPriceTarget := swapStrategy.GetSqrtTargetPrice(sqrtPriceNextTick) @@ -558,6 +562,7 @@ func (k Keeper) computeInAmtGivenOut( swapState.sqrtPrice = sqrtPrice swapState.amountSpecifiedRemaining = swapState.amountSpecifiedRemaining.Sub(amountOut) swapState.amountCalculated = swapState.amountCalculated.Add(amountIn.Add(feeChargeTotal)) + totalFees = totalFees.Add(feeChargeTotal) // if the computeSwapStep calculated a sqrtPrice that is equal to the nextSqrtPrice, this means all liquidity in the current // tick has been consumed and we must move on to the next tick to complete the swap @@ -565,7 +570,7 @@ func (k Keeper) computeInAmtGivenOut( // retrieve the liquidity held in the next closest initialized tick liquidityNet, err := k.crossTick(ctx, p.GetId(), nextTick, sdk.NewDecCoinFromDec(desiredTokenOut.Denom, swapState.feeGrowthGlobal)) if err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, err } liquidityNet = swapStrategy.SetLiquidityDeltaSign(liquidityNet) // update the swapState's liquidity with the new tick's liquidity @@ -580,13 +585,13 @@ func (k Keeper) computeInAmtGivenOut( price := sqrtPrice.Mul(sqrtPrice) swapState.tick, err = math.PriceToTickRoundDown(price, p.GetTickSpacing()) if err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, err } } } if err := k.chargeFee(ctx, poolId, sdk.NewDecCoinFromDec(tokenInDenom, swapState.feeGrowthGlobal)); err != nil { - return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, err + return sdk.Coin{}, sdk.Coin{}, 0, sdk.Dec{}, sdk.Dec{}, sdk.Dec{}, err } // coin amounts require int values @@ -601,7 +606,7 @@ func (k Keeper) computeInAmtGivenOut( tokenIn = sdk.NewCoin(tokenInDenom, amt0) tokenOut = sdk.NewCoin(desiredTokenOut.Denom, amt1) - return tokenIn, tokenOut, swapState.tick, swapState.liquidity, swapState.sqrtPrice, nil + return tokenIn, tokenOut, swapState.tick, swapState.liquidity, swapState.sqrtPrice, totalFees, nil } // updatePoolForSwap updates the given pool object with the results of a swap operation. @@ -624,6 +629,7 @@ func (k Keeper) updatePoolForSwap( newCurrentTick int64, newLiquidity sdk.Dec, newSqrtPrice sdk.Dec, + totalFees sdk.Dec, ) error { // Fixed gas consumption per swap to prevent spam poolId := pool.GetId() @@ -633,6 +639,13 @@ func (k Keeper) updatePoolForSwap( return err } + // Fees should already be rounded up to a whole number dec, but we do this as a precaution + feesRoundedUp := sdk.NewCoin(tokenIn.Denom, totalFees.Ceil().TruncateInt()) + + // Remove the fees from the input token + tokenIn.Amount = tokenIn.Amount.Sub(feesRoundedUp.Amount) + + // Send the input token from the user to the pool's primary address err = k.bankKeeper.SendCoins(ctx, sender, pool.GetAddress(), sdk.Coins{ tokenIn, }) @@ -640,6 +653,17 @@ func (k Keeper) updatePoolForSwap( return types.InsufficientUserBalanceError{Err: err} } + // Send the fees taken from the input token from the user to the pool's fee account + if !feesRoundedUp.IsZero() { + err = k.bankKeeper.SendCoins(ctx, sender, pool.GetFeesAddress(), sdk.Coins{ + feesRoundedUp, + }) + if err != nil { + return types.InsufficientUserBalanceError{Err: err} + } + } + + // Send the output token to the sender from the pool err = k.bankKeeper.SendCoins(ctx, pool.GetAddress(), sender, sdk.Coins{ tokenOut, }) diff --git a/x/concentrated-liquidity/swaps_test.go b/x/concentrated-liquidity/swaps_test.go index ec60790e681..9f45a2acbc2 100644 --- a/x/concentrated-liquidity/swaps_test.go +++ b/x/concentrated-liquidity/swaps_test.go @@ -1465,7 +1465,10 @@ func (s *KeeperTestSuite) TestComputeAndSwapOutAmtGivenIn() { s.FundAcc(s.TestAccs[1], sdk.NewCoins(sdk.NewCoin("eth", sdk.NewInt(10000000000000)), sdk.NewCoin("usdc", sdk.NewInt(1000000000000)))) // Create default CL pool - pool := s.PrepareConcentratedPool() + clParams := s.App.ConcentratedLiquidityKeeper.GetParams(s.Ctx) + clParams.AuthorizedSwapFees = append(clParams.AuthorizedSwapFees, test.swapFee) + s.App.ConcentratedLiquidityKeeper.SetParams(s.Ctx, clParams) + pool := s.PrepareCustomConcentratedPool(s.TestAccs[0], "eth", "usdc", DefaultTickSpacing, test.swapFee) // add default position s.SetupDefaultPosition(pool.GetId()) @@ -1490,7 +1493,7 @@ func (s *KeeperTestSuite) TestComputeAndSwapOutAmtGivenIn() { // perform compute cacheCtx, _ := s.Ctx.CacheContext() - tokenIn, tokenOut, updatedTick, updatedLiquidity, sqrtPrice, err := s.App.ConcentratedLiquidityKeeper.ComputeOutAmtGivenIn( + tokenIn, tokenOut, updatedTick, updatedLiquidity, sqrtPrice, totalFees, err := s.App.ConcentratedLiquidityKeeper.ComputeOutAmtGivenIn( cacheCtx, pool.GetId(), test.tokenIn, test.tokenOutDenom, @@ -1507,6 +1510,9 @@ func (s *KeeperTestSuite) TestComputeAndSwapOutAmtGivenIn() { s.Require().Equal(test.expectedTokenOut.String(), tokenOut.String()) s.Require().Equal(test.expectedSqrtPrice, sqrtPrice) + expectedFees := tokenIn.Amount.ToDec().Mul(pool.GetSwapFee(s.Ctx)).TruncateInt() + s.Require().Equal(expectedFees.String(), totalFees.TruncateInt().String()) + if test.newLowerPrice.IsNil() && test.newUpperPrice.IsNil() { test.newLowerPrice = DefaultLowerPrice test.newUpperPrice = DefaultUpperPrice @@ -1735,7 +1741,10 @@ func (s *KeeperTestSuite) TestComputeAndSwapInAmtGivenOut() { s.FundAcc(s.TestAccs[1], sdk.NewCoins(sdk.NewCoin("eth", sdk.NewInt(10000000000000)), sdk.NewCoin("usdc", sdk.NewInt(1000000000000)))) // Create default CL pool - pool := s.PrepareConcentratedPool() + clParams := s.App.ConcentratedLiquidityKeeper.GetParams(s.Ctx) + clParams.AuthorizedSwapFees = append(clParams.AuthorizedSwapFees, test.swapFee) + s.App.ConcentratedLiquidityKeeper.SetParams(s.Ctx, clParams) + pool := s.PrepareCustomConcentratedPool(s.TestAccs[0], "eth", "usdc", DefaultTickSpacing, test.swapFee) // add default position s.SetupDefaultPosition(pool.GetId()) @@ -1756,7 +1765,7 @@ func (s *KeeperTestSuite) TestComputeAndSwapInAmtGivenOut() { // perform compute cacheCtx, _ := s.Ctx.CacheContext() - tokenIn, tokenOut, updatedTick, updatedLiquidity, sqrtPrice, err := s.App.ConcentratedLiquidityKeeper.ComputeInAmtGivenOut( + tokenIn, tokenOut, updatedTick, updatedLiquidity, sqrtPrice, totalFees, err := s.App.ConcentratedLiquidityKeeper.ComputeInAmtGivenOut( cacheCtx, test.tokenOut, test.tokenInDenom, test.swapFee, test.priceLimit, pool.GetId()) @@ -1771,6 +1780,9 @@ func (s *KeeperTestSuite) TestComputeAndSwapInAmtGivenOut() { s.Require().Equal(test.expectedTokenIn.String(), tokenIn.String()) s.Require().Equal(test.expectedTick, updatedTick) + expectedFees := tokenIn.Amount.ToDec().Mul(pool.GetSwapFee(s.Ctx)).TruncateInt() + s.Require().Equal(expectedFees.String(), totalFees.TruncateInt().String()) + if test.newLowerPrice.IsNil() && test.newUpperPrice.IsNil() { test.newLowerPrice = DefaultLowerPrice test.newUpperPrice = DefaultUpperPrice @@ -2355,7 +2367,7 @@ func (s *KeeperTestSuite) TestComputeOutAmtGivenIn() { s.Require().NoError(err) // perform calc - _, _, _, _, _, err = s.App.ConcentratedLiquidityKeeper.ComputeOutAmtGivenIn( + _, _, _, _, _, _, err = s.App.ConcentratedLiquidityKeeper.ComputeOutAmtGivenIn( s.Ctx, pool.GetId(), test.tokenIn, test.tokenOutDenom, @@ -2559,7 +2571,7 @@ func (s *KeeperTestSuite) TestComputeInAmtGivenOut() { s.Require().NoError(err) // perform calc - _, _, _, _, _, err = s.App.ConcentratedLiquidityKeeper.ComputeInAmtGivenOut( + _, _, _, _, _, _, err = s.App.ConcentratedLiquidityKeeper.ComputeInAmtGivenOut( s.Ctx, test.tokenOut, test.tokenInDenom, test.swapFee, test.priceLimit, pool.GetId()) @@ -2752,6 +2764,7 @@ func (s *KeeperTestSuite) TestUpdatePoolForSwap() { poolInitialBalance sdk.Coins tokenIn sdk.Coin tokenOut sdk.Coin + swapFee sdk.Dec newCurrentTick int64 newLiquidity sdk.Dec newSqrtPrice sdk.Dec @@ -2762,6 +2775,7 @@ func (s *KeeperTestSuite) TestUpdatePoolForSwap() { poolInitialBalance: defaultInitialBalance, tokenIn: oneHundredETH, tokenOut: oneHundredUSDC, + swapFee: sdk.MustNewDecFromStr("0.003"), // 0.3% newCurrentTick: 2, newLiquidity: sdk.NewDec(2), newSqrtPrice: sdk.NewDec(2), @@ -2771,6 +2785,7 @@ func (s *KeeperTestSuite) TestUpdatePoolForSwap() { poolInitialBalance: defaultInitialBalance, tokenIn: oneHundredETH.Add(oneHundredETH), tokenOut: oneHundredUSDC, + swapFee: sdk.MustNewDecFromStr("0.002"), // 0.2% newCurrentTick: 8, newLiquidity: sdk.NewDec(37), newSqrtPrice: sdk.NewDec(91), @@ -2780,6 +2795,7 @@ func (s *KeeperTestSuite) TestUpdatePoolForSwap() { poolInitialBalance: defaultInitialBalance, tokenIn: oneHundredETH.Add(oneHundredETH), tokenOut: oneHundredUSDC, + swapFee: sdk.MustNewDecFromStr("0.003"), newCurrentTick: 2, newLiquidity: sdk.NewDec(2), newSqrtPrice: sdk.NewDec(2), @@ -2790,6 +2806,7 @@ func (s *KeeperTestSuite) TestUpdatePoolForSwap() { poolInitialBalance: defaultInitialBalance, tokenIn: oneHundredETH, tokenOut: oneHundredUSDC.Add(oneHundredUSDC), + swapFee: sdk.MustNewDecFromStr("0.003"), newCurrentTick: 2, newLiquidity: sdk.NewDec(2), newSqrtPrice: sdk.NewDec(2), @@ -2804,7 +2821,11 @@ func (s *KeeperTestSuite) TestUpdatePoolForSwap() { concentratedLiquidityKeeper := s.App.ConcentratedLiquidityKeeper // Create pool with initial balance - pool := s.PrepareConcentratedPool() + clParams := s.App.ConcentratedLiquidityKeeper.GetParams(s.Ctx) + clParams.AuthorizedSwapFees = append(clParams.AuthorizedSwapFees, tc.swapFee) + s.App.ConcentratedLiquidityKeeper.SetParams(s.Ctx, clParams) + pool := s.PrepareCustomConcentratedPool(s.TestAccs[0], "eth", "usdc", DefaultTickSpacing, tc.swapFee) + s.FundAcc(pool.GetAddress(), tc.poolInitialBalance) // Create account with empty balance and fund with initial balance sender := apptesting.CreateRandomAccounts(1)[0] @@ -2821,7 +2842,9 @@ func (s *KeeperTestSuite) TestUpdatePoolForSwap() { // Set mock listener to make sure that is is called when desired. s.setListenerMockOnConcentratedLiquidityKeeper() - err = concentratedLiquidityKeeper.UpdatePoolForSwap(s.Ctx, pool, sender, tc.tokenIn, tc.tokenOut, tc.newCurrentTick, tc.newLiquidity, tc.newSqrtPrice) + expectedFees := tc.tokenIn.Amount.ToDec().Mul(pool.GetSwapFee(s.Ctx)).Ceil() + expectedFeesCoins := sdk.NewCoins(sdk.NewCoin(tc.tokenIn.Denom, expectedFees.TruncateInt())) + err = concentratedLiquidityKeeper.UpdatePoolForSwap(s.Ctx, pool, sender, tc.tokenIn, tc.tokenOut, tc.newCurrentTick, tc.newLiquidity, tc.newSqrtPrice, expectedFees) // Test that pool is updated poolAfterUpdate, err2 := concentratedLiquidityKeeper.GetPoolById(s.Ctx, pool.GetId()) @@ -2843,7 +2866,7 @@ func (s *KeeperTestSuite) TestUpdatePoolForSwap() { // Estimate expected final balances from inputs. expectedSenderFinalBalance := tc.senderInitialBalance.Sub(sdk.NewCoins(tc.tokenIn)).Add(tc.tokenOut) - expectedPoolFinalBalance := tc.poolInitialBalance.Add(tc.tokenIn).Sub(sdk.NewCoins(tc.tokenOut)) + expectedPoolFinalBalance := tc.poolInitialBalance.Add(tc.tokenIn).Sub(sdk.NewCoins(tc.tokenOut)).Sub(expectedFeesCoins) // Test that token out is sent from pool to sender. senderBalanceAfterSwap := s.App.BankKeeper.GetAllBalances(s.Ctx, sender) @@ -2853,6 +2876,9 @@ func (s *KeeperTestSuite) TestUpdatePoolForSwap() { poolBalanceAfterSwap := s.App.BankKeeper.GetAllBalances(s.Ctx, pool.GetAddress()) s.Require().Equal(expectedPoolFinalBalance.String(), poolBalanceAfterSwap.String()) + feeBalanceAfterSwap := s.App.BankKeeper.GetAllBalances(s.Ctx, pool.GetFeesAddress()) + s.Require().Equal(expectedFeesCoins.String(), feeBalanceAfterSwap.String()) + // Validate that listeners were called the desired number of times s.validateListenerCallCount(0, 0, 0, 1) }) @@ -2949,7 +2975,7 @@ func (s *KeeperTestSuite) TestFunctionalSwaps() { s.TestAccs = apptesting.CreateRandomAccounts(positions.numAccounts) // Create a default CL pool, but with a 0.3 percent swap fee. - clPool := s.PrepareCustomConcentratedPool(s.TestAccs[0], ETH, USDC, DefaultTickSpacing, sdk.MustNewDecFromStr("0.002")) + clPool := s.PrepareCustomConcentratedPool(s.TestAccs[0], ETH, USDC, DefaultTickSpacing, sdk.MustNewDecFromStr("0.003")) positionIds := make([][]uint64, 4) // Setup full range position across all four accounts @@ -3017,7 +3043,7 @@ func (s *KeeperTestSuite) TestFunctionalSwaps() { expectedSqrtPrice := osmomath.MustNewDecFromStr("71.74138432587113364823838192") actualSqrtPrice := osmomath.BigDecFromSDKDec(clPool.GetCurrentSqrtPrice()) expectedTokenIn := swapCoin1.Amount.Mul(sdk.NewInt(int64(positions.numSwaps))) - expectedTokenOut := sdk.NewInt(983645) + expectedTokenOut := sdk.NewInt(982676) // Compare the expected and actual values with a multiplicative tolerance of 0.0001% s.Require().Equal(0, multiplicativeTolerance.CompareBigDec(expectedSqrtPrice, actualSqrtPrice), "expected sqrt price: %s, actual sqrt price: %s", expectedSqrtPrice, actualSqrtPrice) @@ -3077,7 +3103,7 @@ func (s *KeeperTestSuite) TestFunctionalSwaps() { expectedSqrtPrice = osmomath.MustNewDecFromStr("70.64112736841825140176332377") actualSqrtPrice = osmomath.BigDecFromSDKDec(clPool.GetCurrentSqrtPrice()) expectedTokenIn = swapCoin0.Amount.Mul(sdk.NewInt(int64(positions.numSwaps))) - expectedTokenOut = sdk.NewInt(5057205729) + expectedTokenOut = sdk.NewInt(5052068983) // Compare the expected and actual values with a multiplicative tolerance of 0.0001% s.Require().Equal(0, multiplicativeTolerance.CompareBigDec(expectedSqrtPrice, actualSqrtPrice)) @@ -3137,7 +3163,7 @@ func (s *KeeperTestSuite) TestFunctionalSwaps() { expectedSqrtPrice = osmomath.MustNewDecFromStr("76.22545423006231767390422658") actualSqrtPrice = osmomath.BigDecFromSDKDec(clPool.GetCurrentSqrtPrice()) expectedTokenIn = swapCoin1.Amount.Mul(sdk.NewInt(int64(positions.numSwaps))) - expectedTokenOut = sdk.NewInt(883663) + expectedTokenOut = sdk.NewInt(882804) // Compare the expected and actual values with a multiplicative tolerance of 0.0001% s.Require().Equal(0, multiplicativeTolerance.CompareBigDec(expectedSqrtPrice, actualSqrtPrice)) @@ -3183,7 +3209,7 @@ func (s *KeeperTestSuite) TestFunctionalSwaps() { expectedSqrtPrice = osmomath.MustNewDecFromStr("63.97671895942244949922335999") actualSqrtPrice = osmomath.BigDecFromSDKDec(clPool.GetCurrentSqrtPrice()) expectedTokenIn = swapCoin0.Amount.Mul(sdk.NewInt(int64(positions.numSwaps))) - expectedTokenOut = sdk.NewInt(4513904710) + expectedTokenOut = sdk.NewInt(4509814620) // Compare the expected and actual values with a multiplicative tolerance of 0.0001% s.Require().Equal(0, multiplicativeTolerance.CompareBigDec(expectedSqrtPrice, actualSqrtPrice)) diff --git a/x/concentrated-liquidity/types/pool.go b/x/concentrated-liquidity/types/pool.go index f4496c6e5ac..615cab434d2 100644 --- a/x/concentrated-liquidity/types/pool.go +++ b/x/concentrated-liquidity/types/pool.go @@ -12,6 +12,7 @@ type ConcentratedPoolExtension interface { poolmanagertypes.PoolI GetIncentivesAddress() sdk.AccAddress + GetFeesAddress() sdk.AccAddress GetToken0() string GetToken1() string GetCurrentSqrtPrice() sdk.Dec From dce0aeb9d068a72b3bf6b8bc063857729366d8c6 Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Fri, 19 May 2023 10:12:37 -0500 Subject: [PATCH 2/3] update test var name --- x/concentrated-liquidity/fees_test.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/x/concentrated-liquidity/fees_test.go b/x/concentrated-liquidity/fees_test.go index 17f16988cf1..6d10ee94ddb 100644 --- a/x/concentrated-liquidity/fees_test.go +++ b/x/concentrated-liquidity/fees_test.go @@ -923,7 +923,7 @@ func (s *KeeperTestSuite) TestQueryAndCollectFees() { err = clKeeper.ChargeFee(ctx, validPoolId, tc.globalFeeGrowth[0]) s.Require().NoError(err) - poolBalanceBeforeCollect := s.App.BankKeeper.GetBalance(ctx, validPool.GetFeesAddress(), ETH) + poolFeeBalanceBeforeCollect := s.App.BankKeeper.GetBalance(ctx, validPool.GetFeesAddress(), ETH) ownerBalancerBeforeCollect := s.App.BankKeeper.GetBalance(ctx, tc.owner, ETH) var preQueryPosition accum.Record @@ -949,7 +949,7 @@ func (s *KeeperTestSuite) TestQueryAndCollectFees() { // Assertions. - poolBalanceAfterCollect := s.App.BankKeeper.GetBalance(ctx, validPool.GetFeesAddress(), ETH) + poolFeeBalanceAfterCollect := s.App.BankKeeper.GetBalance(ctx, validPool.GetFeesAddress(), ETH) ownerBalancerAfterCollect := s.App.BankKeeper.GetBalance(ctx, tc.owner, ETH) if tc.expectedError != nil { @@ -958,7 +958,7 @@ func (s *KeeperTestSuite) TestQueryAndCollectFees() { s.Require().Equal(sdk.Coins{}, actualFeesClaimed) // balances are unchanged - s.Require().Equal(poolBalanceBeforeCollect, poolBalanceAfterCollect) + s.Require().Equal(poolFeeBalanceAfterCollect, poolFeeBalanceBeforeCollect) s.Require().Equal(ownerBalancerAfterCollect, ownerBalancerBeforeCollect) return } @@ -969,7 +969,7 @@ func (s *KeeperTestSuite) TestQueryAndCollectFees() { s.Require().Equal(feeQueryAmount.String(), actualFeesClaimed.String()) expectedETHAmount := tc.expectedFeesClaimed.AmountOf(ETH) - s.Require().Equal(expectedETHAmount.String(), poolBalanceBeforeCollect.Sub(poolBalanceAfterCollect).Amount.String()) + s.Require().Equal(expectedETHAmount.String(), poolFeeBalanceBeforeCollect.Sub(poolFeeBalanceAfterCollect).Amount.String()) s.Require().Equal(expectedETHAmount.String(), ownerBalancerAfterCollect.Sub(ownerBalancerBeforeCollect).Amount.String()) }) } From 731a9305959cbf42c5dfbaa883d9645cea2b1b9c Mon Sep 17 00:00:00 2001 From: Adam Tucker Date: Fri, 19 May 2023 10:14:56 -0500 Subject: [PATCH 3/3] Update x/concentrated-liquidity/swaps.go Co-authored-by: Matt, Park <45252226+mattverse@users.noreply.github.com> --- x/concentrated-liquidity/swaps.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/x/concentrated-liquidity/swaps.go b/x/concentrated-liquidity/swaps.go index 0ba3e3edc3f..1d702403ac8 100644 --- a/x/concentrated-liquidity/swaps.go +++ b/x/concentrated-liquidity/swaps.go @@ -391,7 +391,7 @@ func (k Keeper) computeOutAmtGivenIn( // We deduct the amount of tokens we input in the computeSwapStep above from the user's defined tokenIn amount swapState.amountSpecifiedRemaining.SubMut(amountIn.Add(feeCharge)) // We add the amount of tokens we received (amountOut) from the computeSwapStep above to the amountCalculated accumulator - swapState.amountCalculated = swapState.amountCalculated.AddMut(amountOut) + swapState.amountCalculated.AddMut(amountOut) totalFees = totalFees.Add(feeCharge) // If the computeSwapStep calculated a sqrtPrice that is equal to the nextSqrtPrice, this means all liquidity in the current