From 9f0520fd30f6391b128e821f2cf1065b9226ee1f Mon Sep 17 00:00:00 2001 From: Deividas Petraitis Date: Fri, 17 Jan 2025 16:43:53 +0200 Subject: [PATCH 01/12] BE-675 | InGivenOut APIs for balancer, stableswap, transmuter pools Implements InGivenOut APIs for balancer, stableswap, transmuter pools --- domain/mocks/pool_mock.go | 31 +++++++++++ domain/routable_pool.go | 2 + .../usecase/pools/routable_balancer_pool.go | 25 +++++++-- .../pools/routable_concentrated_pool.go | 22 ++++++-- .../routable_cw_alloy_transmuter_pool.go | 52 +++++++++++++++--- .../pools/routable_cw_orderbook_pool.go | 26 ++++++--- router/usecase/pools/routable_cw_pool.go | 28 +++++++--- .../pools/routable_cw_transmuter_pool.go | 23 ++++++-- router/usecase/pools/routable_pool_test.go | 55 ++++++++++++++++++- router/usecase/pools/routable_result_pool.go | 27 ++++++--- .../usecase/pools/routable_stableswap_pool.go | 35 +++++++++--- 11 files changed, 271 insertions(+), 55 deletions(-) diff --git a/domain/mocks/pool_mock.go b/domain/mocks/pool_mock.go index a393680a..d52308bd 100644 --- a/domain/mocks/pool_mock.go +++ b/domain/mocks/pool_mock.go @@ -19,6 +19,7 @@ import ( type MockRoutablePool struct { CalculateTokenOutByTokenInFunc func(ctx context.Context, tokenIn sdk.Coin) (sdk.Coin, error) + CalculateTokenInByTokenOutFunc func(ctx context.Context, tokenOut sdk.Coin) (sdk.Coin, error) ChainPoolModel poolmanagertypes.PoolI TickModel *ingesttypes.TickModel @@ -33,6 +34,7 @@ type MockRoutablePool struct { TakerFee osmomath.Dec SpreadFactor osmomath.Dec mockedTokenOut sdk.Coin + mockedTokenIn sdk.Coin IncentiveType api.IncentiveType APRData sqspassthroughdomain.PoolAPRDataStatusWrap @@ -132,6 +134,30 @@ func (mp *MockRoutablePool) CalculateTokenOutByTokenIn(_ctx context.Context, tok return balancerPool.CalcOutAmtGivenIn(sdk.Context{}, sdk.NewCoins(tokenIn), mp.TokenOutDenom, mp.SpreadFactor) } +// CalculateTokenInByTokenOut implements routerusecase.RoutablePool. +func (mp *MockRoutablePool) CalculateTokenInByTokenOut(ctx context.Context, tokenOut sdk.Coin) (sdk.Coin, error) { + if mp.CalculateTokenInByTokenOutFunc != nil { + return mp.CalculateTokenInByTokenOutFunc(ctx, tokenOut) + } + + // We allow the ability to mock out the token out amount. + if !mp.mockedTokenIn.IsNil() { + return mp.mockedTokenIn, nil + } + + if mp.PoolType == poolmanagertypes.CosmWasm { + return sdk.NewCoin(mp.TokenInDenom, tokenOut.Amount), nil + } + + // Cast to balancer + balancerPool, ok := mp.ChainPoolModel.(*balancer.Pool) + if !ok { + panic("not a balancer pool") + } + + return balancerPool.CalcOutAmtGivenIn(sdk.Context{}, sdk.NewCoins(tokenOut), mp.TokenInDenom, mp.SpreadFactor) +} + // String implements domain.RoutablePool. func (*MockRoutablePool) String() string { panic("unimplemented") @@ -174,6 +200,11 @@ func (mp *MockRoutablePool) ChargeTakerFeeExactIn(tokenIn sdk.Coin) (tokenInAfte return tokenIn.Sub(sdk.NewCoin(tokenIn.Denom, mp.TakerFee.Mul(tokenIn.Amount.ToLegacyDec()).TruncateInt())) } +// ChargeTakerFeeExactOut implements domain.RoutablePool. +func (mp *MockRoutablePool) ChargeTakerFeeExactOut(tokenOut sdk.Coin) (tokenInAfterFee sdk.Coin) { + return tokenOut.Sub(sdk.NewCoin(tokenOut.Denom, mp.TakerFee.Mul(tokenOut.Amount.ToLegacyDec()).TruncateInt())) +} + // GetTakerFee implements ingesttypes.PoolI. func (mp *MockRoutablePool) GetTakerFee() math.LegacyDec { return mp.TakerFee diff --git a/domain/routable_pool.go b/domain/routable_pool.go index 06bdb2a9..5041fa35 100644 --- a/domain/routable_pool.go +++ b/domain/routable_pool.go @@ -59,8 +59,10 @@ type RoutablePool interface { CalcSpotPrice(ctx context.Context, baseDenom string, quoteDenom string) (osmomath.BigDec, error) CalculateTokenOutByTokenIn(ctx context.Context, tokenIn sdk.Coin) (sdk.Coin, error) + CalculateTokenInByTokenOut(ctx context.Context, tokenOut sdk.Coin) (sdk.Coin, error) ChargeTakerFeeExactIn(tokenIn sdk.Coin) (tokenInAfterFee sdk.Coin) + ChargeTakerFeeExactOut(tokenOut sdk.Coin) (tokenOutAfterFee sdk.Coin) GetTakerFee() osmomath.Dec diff --git a/router/usecase/pools/routable_balancer_pool.go b/router/usecase/pools/routable_balancer_pool.go index 8c6ee14f..95dc7953 100644 --- a/router/usecase/pools/routable_balancer_pool.go +++ b/router/usecase/pools/routable_balancer_pool.go @@ -19,10 +19,10 @@ import ( var _ domain.RoutablePool = &routableBalancerPoolImpl{} type routableBalancerPoolImpl struct { - ChainPool *balancer.Pool "json:\"pool\"" - TokenInDenom string "json:\"token_in_denom,omitempty\"" - TokenOutDenom string "json:\"token_out_denom,omitempty\"" - TakerFee osmomath.Dec "json:\"taker_fee\"" + ChainPool *balancer.Pool `json:"pool"` + TokenInDenom string `json:"token_in_denom,omitempty"` + TokenOutDenom string `json:"token_out_denom,omitempty"` + TakerFee osmomath.Dec `json:"taker_fee"` } // CalculateTokenOutByTokenIn implements RoutablePool. @@ -35,6 +35,16 @@ func (r *routableBalancerPoolImpl) CalculateTokenOutByTokenIn(ctx context.Contex return tokenOut, nil } +// CalculateTokenInByTokenOut implements RoutablePool. +func (r *routableBalancerPoolImpl) CalculateTokenInByTokenOut(ctx context.Context, tokenOut sdk.Coin) (sdk.Coin, error) { + tokenIn, err := r.ChainPool.CalcInAmtGivenOut(sdk.Context{}, sdk.Coins{tokenOut}, r.TokenInDenom, r.GetSpreadFactor()) + if err != nil { + return sdk.Coin{}, err + } + + return tokenIn, nil +} + // GetTokenOutDenom implements RoutablePool. func (r *routableBalancerPoolImpl) GetTokenOutDenom() string { return r.TokenOutDenom @@ -57,6 +67,13 @@ func (r *routableBalancerPoolImpl) ChargeTakerFeeExactIn(tokenIn sdk.Coin) (toke return tokenInAfterTakerFee } +// ChargeTakerFee implements domain.RoutablePool. +// Charges the taker fee for the given token in and returns the token in after the fee has been charged. +func (r *routableBalancerPoolImpl) ChargeTakerFeeExactOut(tokenOut sdk.Coin) (tokenOutAfterFee sdk.Coin) { + tokenOutAfterTakerFee, _ := poolmanager.CalcTakerFeeExactOut(tokenOut, r.TakerFee) + return tokenOutAfterTakerFee +} + // GetTakerFee implements domain.RoutablePool. func (r *routableBalancerPoolImpl) GetTakerFee() math.LegacyDec { return r.TakerFee diff --git a/router/usecase/pools/routable_concentrated_pool.go b/router/usecase/pools/routable_concentrated_pool.go index a2ec451d..3148c05a 100644 --- a/router/usecase/pools/routable_concentrated_pool.go +++ b/router/usecase/pools/routable_concentrated_pool.go @@ -2,6 +2,7 @@ package pools import ( "context" + "errors" "fmt" "cosmossdk.io/math" @@ -24,11 +25,11 @@ var _ domain.RoutablePool = &routableConcentratedPoolImpl{} var smallestDec = osmomath.BigDecFromDec(osmomath.SmallestDec()) type routableConcentratedPoolImpl struct { - ChainPool *concentratedmodel.Pool "json:\"cl_pool\"" - TickModel *ingesttypes.TickModel "json:\"tick_model\"" - TokenInDenom string "json:\"token_in_denom,omitempty\"" - TokenOutDenom string "json:\"token_out_denom,omitempty\"" - TakerFee osmomath.Dec "json:\"taker_fee\"" + ChainPool *concentratedmodel.Pool `json:"cl_pool"` + TickModel *ingesttypes.TickModel `json:"tick_model"` + TokenInDenom string `json:"token_in_denom,omitempty"` + TokenOutDenom string `json:"token_out_denom,omitempty"` + TakerFee osmomath.Dec `json:"taker_fee"` } // Size is roughly `keys * (2.5 * Key_size + 2*value_size)`. (Plus whatever excess overhead hashmaps internally have) @@ -193,6 +194,11 @@ func (r *routableConcentratedPoolImpl) CalculateTokenOutByTokenIn(ctx context.Co return sdk.Coin{Denom: tokenOutDenom, Amount: amountOutTotal.TruncateInt()}, nil } +// CalculateTokenInByTokenOut implements domain.RoutablePool. +func (r *routableConcentratedPoolImpl) CalculateTokenInByTokenOut(ctx context.Context, tokenOut sdk.Coin) (sdk.Coin, error) { + return sdk.Coin{}, errors.New("not implemented") +} + // GetTokenOutDenom implements RoutablePool. func (r *routableConcentratedPoolImpl) GetTokenOutDenom() string { return r.TokenOutDenom @@ -216,6 +222,12 @@ func (r *routableConcentratedPoolImpl) ChargeTakerFeeExactIn(tokenIn sdk.Coin) ( return tokenInAfterTakerFee } +// ChargeTakerFee implements domain.RoutablePool. +// Charges the taker fee for the given token out and returns the token out after the fee has been charged. +func (r *routableConcentratedPoolImpl) ChargeTakerFeeExactOut(tokenOut sdk.Coin) (tokenOutAfterFee sdk.Coin) { + return sdk.Coin{} +} + // SetTokenInDenom implements domain.RoutablePool. func (r *routableConcentratedPoolImpl) SetTokenInDenom(tokenInDenom string) { r.TokenInDenom = tokenInDenom diff --git a/router/usecase/pools/routable_cw_alloy_transmuter_pool.go b/router/usecase/pools/routable_cw_alloy_transmuter_pool.go index c778a161..5c539d50 100644 --- a/router/usecase/pools/routable_cw_alloy_transmuter_pool.go +++ b/router/usecase/pools/routable_cw_alloy_transmuter_pool.go @@ -2,6 +2,7 @@ package pools import ( "context" + "errors" "fmt" "cosmossdk.io/math" @@ -19,13 +20,13 @@ import ( var _ domain.RoutablePool = &routableAlloyTransmuterPoolImpl{} type routableAlloyTransmuterPoolImpl struct { - ChainPool *cwpoolmodel.CosmWasmPool "json:\"pool\"" - AlloyTransmuterData *cosmwasmpool.AlloyTransmuterData "json:\"alloy_transmuter_data\"" - Balances sdk.Coins "json:\"balances\"" - TokenInDenom string "json:\"token_in_denom,omitempty\"" - TokenOutDenom string "json:\"token_out_denom,omitempty\"" - TakerFee osmomath.Dec "json:\"taker_fee\"" - SpreadFactor osmomath.Dec "json:\"spread_factor\"" + ChainPool *cwpoolmodel.CosmWasmPool `json:"pool"` + AlloyTransmuterData *cosmwasmpool.AlloyTransmuterData `json:"alloy_transmuter_data"` + Balances sdk.Coins `json:"balances"` + TokenInDenom string `json:"token_in_denom,omitempty"` + TokenOutDenom string `json:"token_out_denom,omitempty"` + TakerFee osmomath.Dec `json:"taker_fee"` + SpreadFactor osmomath.Dec `json:"spread_factor"` } // GetId implements domain.RoutablePool. @@ -79,6 +80,11 @@ func (r *routableAlloyTransmuterPoolImpl) CalculateTokenOutByTokenIn(ctx context return sdk.Coin{Denom: r.TokenOutDenom, Amount: tokenOutAmtInt}, nil } +// CalculateTokenInByTokenOut implements domain.RoutablePool. +func (r *routableAlloyTransmuterPoolImpl) CalculateTokenInByTokenOut(ctx context.Context, tokenOut sdk.Coin) (sdk.Coin, error) { + return sdk.Coin{}, errors.New("not implemented") +} + // GetTokenOutDenom implements RoutablePool. func (r *routableAlloyTransmuterPoolImpl) GetTokenOutDenom() string { return r.TokenOutDenom @@ -101,6 +107,12 @@ func (r *routableAlloyTransmuterPoolImpl) ChargeTakerFeeExactIn(tokenIn sdk.Coin return tokenInAfterTakerFee } +// ChargeTakerFeeExactOut implements domain.RoutablePool. +// Returns tokenOutAmount and does not charge any fee for transmuter pools. +func (r *routableAlloyTransmuterPoolImpl) ChargeTakerFeeExactOut(tokenOut sdk.Coin) (outAmountAfterFee sdk.Coin) { + return sdk.Coin{} +} + // GetTakerFee implements domain.RoutablePool. func (r *routableAlloyTransmuterPoolImpl) GetTakerFee() math.LegacyDec { return r.TakerFee @@ -196,6 +208,32 @@ func (r *routableAlloyTransmuterPoolImpl) CalcTokenOutAmt(tokenIn sdk.Coin, toke return tokenOutAmount, nil } +// Calculate the token out amount based on the normalization factors: +// +// token_in_amt / token_in_norm_factor = token_out_amt / token_out_norm_factor +// token_in_amt = token_out_amt * token_in_norm_factor / token_out_norm_factor +func (r *routableAlloyTransmuterPoolImpl) CalcTokenInAmt(tokenInDenom string, tokenOut sdk.Coin) (osmomath.BigDec, error) { + tokenOutNormFactor, tokenInNormFactor, err := r.FindNormalizationFactors(tokenOut.Denom, tokenInDenom) + if err != nil { + return osmomath.BigDec{}, err + } + + if tokenInNormFactor.IsZero() { + return osmomath.BigDec{}, domain.ZeroNormalizationFactorError{Denom: tokenOut.Denom, PoolId: r.GetId()} + } + + if tokenOutNormFactor.IsZero() { + return osmomath.BigDec{}, domain.ZeroNormalizationFactorError{Denom: tokenInDenom, PoolId: r.GetId()} + } + + tokenOutAmount := osmomath.NewBigDec(tokenOut.Amount.Int64()) + + tokenOutNormFactorBig := osmomath.NewBigIntFromBigInt(tokenOutNormFactor.BigInt()) + tokenInNormFactorBig := osmomath.NewBigIntFromBigInt(tokenInNormFactor.BigInt()) + + return tokenOutAmount.MulInt(tokenInNormFactorBig).QuoInt(tokenOutNormFactorBig), nil +} + // checkStaticRateLimiter checks the static rate limiter. // If token in denom is not alloyed, we only need to validate the token in balance. // Since the token in balance is the only one that is increased by the current quote. diff --git a/router/usecase/pools/routable_cw_orderbook_pool.go b/router/usecase/pools/routable_cw_orderbook_pool.go index ce5742f4..b3f3bc2d 100644 --- a/router/usecase/pools/routable_cw_orderbook_pool.go +++ b/router/usecase/pools/routable_cw_orderbook_pool.go @@ -2,6 +2,7 @@ package pools import ( "context" + "errors" "fmt" "cosmossdk.io/math" @@ -22,13 +23,13 @@ var oneBigDec = osmomath.OneBigDec() var _ domain.RoutablePool = &routableOrderbookPoolImpl{} type routableOrderbookPoolImpl struct { - ChainPool *cwpoolmodel.CosmWasmPool "json:\"pool\"" - Balances sdk.Coins "json:\"balances\"" - TokenInDenom string "json:\"token_in_denom,omitempty\"" - TokenOutDenom string "json:\"token_out_denom,omitempty\"" - TakerFee osmomath.Dec "json:\"taker_fee\"" - SpreadFactor osmomath.Dec "json:\"spread_factor\"" - OrderbookData *cosmwasmpool.OrderbookData "json:\"orderbook_data\"" + ChainPool *cwpoolmodel.CosmWasmPool `json:"pool"` + Balances sdk.Coins `json:"balances"` + TokenInDenom string `json:"token_in_denom,omitempty"` + TokenOutDenom string `json:"token_out_denom,omitempty"` + TakerFee osmomath.Dec `json:"taker_fee"` + SpreadFactor osmomath.Dec `json:"spread_factor"` + OrderbookData *cosmwasmpool.OrderbookData `json:"orderbook_data"` } // GetId implements domain.RoutablePool. @@ -153,6 +154,11 @@ func (r *routableOrderbookPoolImpl) CalculateTokenOutByTokenIn(ctx context.Conte return sdk.Coin{Denom: r.TokenOutDenom, Amount: amountOutTotal.Dec().TruncateInt()}, nil } +// CalculateTokenInByTokenOut implements domain.RoutablePool. +func (r *routableOrderbookPoolImpl) CalculateTokenInByTokenOut(ctx context.Context, tokenOut sdk.Coin) (sdk.Coin, error) { + return sdk.Coin{}, errors.New("not implemented") +} + // GetTokenOutDenom implements RoutablePool. func (r *routableOrderbookPoolImpl) GetTokenOutDenom() string { return r.TokenOutDenom @@ -175,6 +181,12 @@ func (r *routableOrderbookPoolImpl) ChargeTakerFeeExactIn(tokenIn sdk.Coin) (tok return tokenInAfterTakerFee } +// ChargeTakerFee implements sqsdomain.RoutablePool. +// Charges the taker fee for the given token out and returns the token out after the fee has been charged. +func (r *routableOrderbookPoolImpl) ChargeTakerFeeExactOut(tokenOut sdk.Coin) (tokenOutAfterFee sdk.Coin) { + return sdk.Coin{} +} + // GetTakerFee implements domain.RoutablePool. func (r *routableOrderbookPoolImpl) GetTakerFee() math.LegacyDec { return r.TakerFee diff --git a/router/usecase/pools/routable_cw_pool.go b/router/usecase/pools/routable_cw_pool.go index b9253a3b..19a0eac3 100644 --- a/router/usecase/pools/routable_cw_pool.go +++ b/router/usecase/pools/routable_cw_pool.go @@ -2,6 +2,7 @@ package pools import ( "context" + "errors" "fmt" "cosmossdk.io/math" @@ -29,14 +30,14 @@ var _ domain.RoutablePool = &routableCosmWasmPoolImpl{} // routableCosmWasmPool is an implemenation of the cosm wasm pool // that interacts with the chain for quotes and spot price. type routableCosmWasmPoolImpl struct { - ChainPool *cwpoolmodel.CosmWasmPool "json:\"pool\"" - Balances sdk.Coins "json:\"balances\"" - TokenOutDenom string "json:\"token_out_denom,omitempty\"" - TokenInDenom string "json:\"token_in_denom,omitempty\"" - TakerFee osmomath.Dec "json:\"taker_fee\"" - SpreadFactor osmomath.Dec "json:\"spread_factor\"" - wasmClient wasmtypes.QueryClient "json:\"-\"" - spotPriceQuoteCalculator domain.SpotPriceQuoteCalculator "json:\"-\"" + ChainPool *cwpoolmodel.CosmWasmPool `json:"pool"` + Balances sdk.Coins `json:"balances"` + TokenOutDenom string `json:"token_out_denom,omitempty"` + TokenInDenom string `json:"token_in_denom,omitempty"` + TakerFee osmomath.Dec `json:"taker_fee"` + SpreadFactor osmomath.Dec `json:"spread_factor"` + wasmClient wasmtypes.QueryClient `json:"-"` + spotPriceQuoteCalculator domain.SpotPriceQuoteCalculator `json:"-"` } // NewRoutableCosmWasmPool returns a new routable cosmwasm pool with the given parameters. @@ -84,6 +85,11 @@ func (r *routableCosmWasmPoolImpl) GetSpreadFactor() math.LegacyDec { return r.SpreadFactor } +// CalculateTokenInByTokenOut implements domain.RoutablePool. +func (r *routableCosmWasmPoolImpl) CalculateTokenInByTokenOut(ctx context.Context, tokenOut sdk.Coin) (sdk.Coin, error) { + return sdk.Coin{}, errors.New("not implemented") +} + // CalculateTokenOutByTokenIn implements domain.RoutablePool. // It calculates the amount of token out given the amount of token in for a transmuter pool. // Transmuter pool allows no slippage swaps. It just returns the same amount of token out as token in @@ -148,6 +154,12 @@ func (r *routableCosmWasmPoolImpl) ChargeTakerFeeExactIn(tokenIn sdk.Coin) (inAm return tokenInAfterTakerFee } +// ChargeTakerFeeExactOut implements domain.RoutablePool. +// Returns tokenOutAmount and does not charge any fee for transmuter pools. +func (r *routableCosmWasmPoolImpl) ChargeTakerFeeExactOut(tokenOut sdk.Coin) (outAmountAfterFee sdk.Coin) { + return sdk.Coin{} +} + // GetTakerFee implements domain.RoutablePool. func (r *routableCosmWasmPoolImpl) GetTakerFee() math.LegacyDec { return r.TakerFee diff --git a/router/usecase/pools/routable_cw_transmuter_pool.go b/router/usecase/pools/routable_cw_transmuter_pool.go index eefb365f..231b6bac 100644 --- a/router/usecase/pools/routable_cw_transmuter_pool.go +++ b/router/usecase/pools/routable_cw_transmuter_pool.go @@ -2,6 +2,7 @@ package pools import ( "context" + "errors" "fmt" "cosmossdk.io/math" @@ -18,12 +19,12 @@ import ( var _ domain.RoutablePool = &routableTransmuterPoolImpl{} type routableTransmuterPoolImpl struct { - ChainPool *cwpoolmodel.CosmWasmPool "json:\"pool\"" - Balances sdk.Coins "json:\"balances\"" - TokenInDenom string "json:\"token_in_denom,omitempty\"" - TokenOutDenom string "json:\"token_out_denom,omitempty\"" - TakerFee osmomath.Dec "json:\"taker_fee\"" - SpreadFactor osmomath.Dec "json:\"spread_factor\"" + ChainPool *cwpoolmodel.CosmWasmPool `json:"pool"` + Balances sdk.Coins `json:"balances"` + TokenInDenom string `json:"token_in_denom,omitempty"` + TokenOutDenom string `json:"token_out_denom,omitempty"` + TakerFee osmomath.Dec `json:"taker_fee"` + SpreadFactor osmomath.Dec `json:"spread_factor"` } // GetId implements domain.RoutablePool. @@ -73,6 +74,11 @@ func (r *routableTransmuterPoolImpl) CalculateTokenOutByTokenIn(ctx context.Cont return sdk.Coin{Denom: r.TokenOutDenom, Amount: tokenIn.Amount}, nil } +// CalculateTokenInByTokenOut implements domain.RoutablePool. +func (r *routableTransmuterPoolImpl) CalculateTokenInByTokenOut(ctx context.Context, tokenOut sdk.Coin) (sdk.Coin, error) { + return sdk.Coin{}, errors.New("not implemented") +} + // GetTokenOutDenom implements RoutablePool. func (r *routableTransmuterPoolImpl) GetTokenOutDenom() string { return r.TokenOutDenom @@ -94,6 +100,11 @@ func (r *routableTransmuterPoolImpl) ChargeTakerFeeExactIn(tokenIn sdk.Coin) (in return tokenInAfterTakerFee } +// ChargeTakerFeeExactOut implements domain.RoutablePool. +func (r *routableTransmuterPoolImpl) ChargeTakerFeeExactOut(tokenOut sdk.Coin) (outAmountAfterFee sdk.Coin) { + return sdk.Coin{} +} + // validateTransmuterBalance validates that the balance of the denom to validate is greater than the token in amount. // Returns nil on success, error otherwise. func validateTransmuterBalance(tokenInAmount osmomath.Int, balances sdk.Coins, denomToValidate string) error { diff --git a/router/usecase/pools/routable_pool_test.go b/router/usecase/pools/routable_pool_test.go index 92473282..f0232d09 100644 --- a/router/usecase/pools/routable_pool_test.go +++ b/router/usecase/pools/routable_pool_test.go @@ -65,7 +65,7 @@ func (s *RoutablePoolTestSuite) PrepareCustomTransmuterPool(owner sdk.AccAddress } // Test quote logic over a specific pool that is of CFMM type. -// CFMM pools are balancert and stableswap. +// CFMM pools are balancer and stableswap. func (s *RoutablePoolTestSuite) TestCalculateTokenOutByTokenIn_CFMM() { tests := map[string]struct { tokenIn sdk.Coin @@ -116,3 +116,56 @@ func (s *RoutablePoolTestSuite) TestCalculateTokenOutByTokenIn_CFMM() { }) } } + +// Test quote logic over a specific pool that is of CFMM type. +// CFMM pools are balancer and stableswap. +func (s *RoutablePoolTestSuite) TestCalculateTokenInByTokenOut_CFMM() { + tests := map[string]struct { + tokenOut sdk.Coin + tokenInDenom string + poolType poolmanagertypes.PoolType + expectedTokenOut sdk.Coin + expectError error + }{ + "balancer pool - valid calculation": { + tokenOut: sdk.NewCoin("foo", osmomath.NewInt(100)), + tokenInDenom: "bar", + poolType: poolmanagertypes.Balancer, + }, + "stableswap pool - valid calculation": { + tokenOut: sdk.NewCoin("foo", osmomath.NewInt(100)), + tokenInDenom: "bar", + poolType: poolmanagertypes.Stableswap, + }, + } + + for name, tc := range tests { + s.Run(name, func() { + s.Setup() + + poolID := s.CreatePoolFromType(tc.poolType) + pool, err := s.App.PoolManagerKeeper.GetPool(s.Ctx, poolID) + s.Require().NoError(err) + + mock := &mocks.MockRoutablePool{ChainPoolModel: pool, PoolType: tc.poolType} + cosmWasmPoolsParams := cosmwasmdomain.CosmWasmPoolsParams{ + ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, + } + routablePool, err := pools.NewRoutablePool(mock, tc.tokenInDenom, noTakerFee, cosmWasmPoolsParams) + s.Require().NoError(err) + + tokenOut, err := routablePool.CalculateTokenOutByTokenIn(context.TODO(), tc.tokenOut) + + if tc.expectError != nil { + s.Require().Error(err) + return + } + s.Require().NoError(err) + + // We don't check the exact amount because the correctness of calculations is tested + // at the pool model layer of abstraction. Here, the goal is to make sure that we get + // a positive amount when the pool is valid. + s.Require().True(tokenOut.IsPositive()) + }) + } +} diff --git a/router/usecase/pools/routable_result_pool.go b/router/usecase/pools/routable_result_pool.go index 9e83e0f2..8b50841d 100644 --- a/router/usecase/pools/routable_result_pool.go +++ b/router/usecase/pools/routable_result_pool.go @@ -25,14 +25,14 @@ var ( // routableResultPoolImpl is a generalized implementation that is returned to the client // side in quotes. It contains all the relevant pool data needed for Osmosis frontend type routableResultPoolImpl struct { - ID uint64 "json:\"id\"" - Type poolmanagertypes.PoolType "json:\"type\"" - Balances sdk.Coins "json:\"balances\"" - SpreadFactor osmomath.Dec "json:\"spread_factor\"" - TokenOutDenom string "json:\"token_out_denom,omitempty\"" - TokenInDenom string "json:\"token_in_denom,omitempty\"" - TakerFee osmomath.Dec "json:\"taker_fee\"" - CodeID uint64 "json:\"code_id,omitempty\"" + ID uint64 `json:"id"` + Type poolmanagertypes.PoolType `json:"type"` + Balances sdk.Coins `json:"balances"` + SpreadFactor osmomath.Dec `json:"spread_factor"` + TokenOutDenom string `json:"token_out_denom,omitempty"` + TokenInDenom string `json:"token_in_denom,omitempty"` + TakerFee osmomath.Dec `json:"taker_fee"` + CodeID uint64 `json:"code_id,omitempty"` } // GetCodeID implements domain.RoutablePool. @@ -128,6 +128,11 @@ func (r *routableResultPoolImpl) CalculateTokenOutByTokenIn(ctx context.Context, return sdk.Coin{}, errors.New("not implemented") } +// CalculateTokenInByTokenOut implements RoutablePool. +func (r *routableResultPoolImpl) CalculateTokenInByTokenOut(ctx context.Context, tokenIn sdk.Coin) (sdk.Coin, error) { + return sdk.Coin{}, errors.New("not implemented") +} + // GetTokenOutDenom implements RoutablePool. func (r *routableResultPoolImpl) GetTokenOutDenom() string { return r.TokenOutDenom @@ -150,6 +155,12 @@ func (r *routableResultPoolImpl) ChargeTakerFeeExactIn(tokenIn sdk.Coin) (tokenI return tokenInAfterTakerFee } +// ChargeTakerFee implements domain.RoutablePool. +// Charges the taker fee for the given token out and returns the token out after the fee has been charged. +func (r *routableResultPoolImpl) ChargeTakerFeeExactOut(tokenOut sdk.Coin) (tokenOutAfterFee sdk.Coin) { + return sdk.Coin{} +} + // GetTakerFee implements domain.RoutablePool. func (r *routableResultPoolImpl) GetTakerFee() math.LegacyDec { return r.TakerFee diff --git a/router/usecase/pools/routable_stableswap_pool.go b/router/usecase/pools/routable_stableswap_pool.go index f3f94ee7..44585c14 100644 --- a/router/usecase/pools/routable_stableswap_pool.go +++ b/router/usecase/pools/routable_stableswap_pool.go @@ -19,10 +19,10 @@ import ( var _ domain.RoutablePool = &routableStableswapPoolImpl{} type routableStableswapPoolImpl struct { - ChainPool *stableswap.Pool "json:\"pool\"" - TokenInDenom string "json:\"token_in_denom,omitempty\"" - TokenOutDenom string "json:\"token_out_denom,omitempty\"" - TakerFee osmomath.Dec "json:\"taker_fee\"" + ChainPool *stableswap.Pool `json:"pool"` + TokenInDenom string `json:"token_in_denom,omitempty"` + TokenOutDenom string `json:"token_out_denom,omitempty"` + TakerFee osmomath.Dec `json:"taker_fee"` } // CalculateTokenOutByTokenIn implements RoutablePool. @@ -35,16 +35,38 @@ func (r *routableStableswapPoolImpl) CalculateTokenOutByTokenIn(ctx context.Cont return tokenOut, nil } +// CalculateTokenInByTokenOut implements RoutablePool. +func (r *routableStableswapPoolImpl) CalculateTokenInByTokenOut(ctx context.Context, tokenOut sdk.Coin) (sdk.Coin, error) { + tokenIn, err := r.ChainPool.CalcInAmtGivenOut(sdk.Context{}, sdk.Coins{tokenOut}, r.TokenInDenom, r.GetSpreadFactor()) + if err != nil { + return sdk.Coin{}, err + } + + return tokenIn, nil +} + // GetTokenOutDenom implements RoutablePool. func (r *routableStableswapPoolImpl) GetTokenOutDenom() string { return r.TokenOutDenom } +// GetTokenInDenom implements RoutablePool. +func (r *routableStableswapPoolImpl) GetTokenInDenom() string { + return r.TokenInDenom +} + // String implements domain.RoutablePool. func (r *routableStableswapPoolImpl) String() string { return fmt.Sprintf("pool (%d), pool type (%d), pool denoms (%v), token out (%s)", r.ChainPool.Id, poolmanagertypes.Balancer, r.ChainPool.GetPoolDenoms(sdk.Context{}), r.TokenOutDenom) } +// ChargeTakerFee implements domain.RoutablePool. +// Charges the taker fee for the given token out and returns the token out after the fee has been charged. +func (r *routableStableswapPoolImpl) ChargeTakerFeeExactOut(tokenOut sdk.Coin) (tokenOutAfterFee sdk.Coin) { + tokenOutAfterTakerFee, _ := poolmanager.CalcTakerFeeExactOut(tokenOut, r.TakerFee) + return tokenOutAfterTakerFee +} + // ChargeTakerFee implements domain.RoutablePool. // Charges the taker fee for the given token in and returns the token in after the fee has been charged. func (r *routableStableswapPoolImpl) ChargeTakerFeeExactIn(tokenIn sdk.Coin) (tokenInAfterFee sdk.Coin) { @@ -57,11 +79,6 @@ func (r *routableStableswapPoolImpl) GetTakerFee() math.LegacyDec { return r.TakerFee } -// GetTokenInDenom implements RoutablePool. -func (r *routableStableswapPoolImpl) GetTokenInDenom() string { - return r.TokenInDenom -} - // SetTokenInDenom implements domain.RoutablePool. func (r *routableStableswapPoolImpl) SetTokenInDenom(tokenInDenom string) { r.TokenInDenom = tokenInDenom From 07fbc14e8d4a244d82926c2cd05e073267aa2181 Mon Sep 17 00:00:00 2001 From: Deividas Petraitis Date: Fri, 17 Jan 2025 18:31:16 +0200 Subject: [PATCH 02/12] BE-675 | Taker fee calculation test for CCFM --- .../usecase/pools/routable_balancer_pool.go | 2 +- router/usecase/pools/routable_pool_test.go | 136 ++++++++++++++++++ 2 files changed, 137 insertions(+), 1 deletion(-) diff --git a/router/usecase/pools/routable_balancer_pool.go b/router/usecase/pools/routable_balancer_pool.go index 95dc7953..0c3664cc 100644 --- a/router/usecase/pools/routable_balancer_pool.go +++ b/router/usecase/pools/routable_balancer_pool.go @@ -68,7 +68,7 @@ func (r *routableBalancerPoolImpl) ChargeTakerFeeExactIn(tokenIn sdk.Coin) (toke } // ChargeTakerFee implements domain.RoutablePool. -// Charges the taker fee for the given token in and returns the token in after the fee has been charged. +// Charges the taker fee for the given token out and returns the token out after the fee has been charged. func (r *routableBalancerPoolImpl) ChargeTakerFeeExactOut(tokenOut sdk.Coin) (tokenOutAfterFee sdk.Coin) { tokenOutAfterTakerFee, _ := poolmanager.CalcTakerFeeExactOut(tokenOut, r.TakerFee) return tokenOutAfterTakerFee diff --git a/router/usecase/pools/routable_pool_test.go b/router/usecase/pools/routable_pool_test.go index f0232d09..9fcb22ca 100644 --- a/router/usecase/pools/routable_pool_test.go +++ b/router/usecase/pools/routable_pool_test.go @@ -169,3 +169,139 @@ func (s *RoutablePoolTestSuite) TestCalculateTokenInByTokenOut_CFMM() { }) } } + +func (s *RoutablePoolTestSuite) TestChargeTakerFeeExactIn_CCFM() { + tests := map[string]struct { + poolType poolmanagertypes.PoolType + tokenIn sdk.Coin + takerFee osmomath.Dec + expectedToken sdk.Coin + }{ + "balancer pool - no taker fee": { + poolType: poolmanagertypes.Balancer, + tokenIn: sdk.NewCoin(USDC, osmomath.NewInt(100)), + takerFee: osmomath.NewDec(0), + expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(100)), + }, + "stableswap pool - no taker fee": { + poolType: poolmanagertypes.Stableswap, + tokenIn: sdk.NewCoin(USDC, osmomath.NewInt(100)), + takerFee: osmomath.NewDec(0), + expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(100)), + }, + "balancer pool - small taker fee": { + poolType: poolmanagertypes.Balancer, + tokenIn: sdk.NewCoin(USDT, osmomath.NewInt(100)), + takerFee: osmomath.NewDecWithPrec(1, 2), // 1% + expectedToken: sdk.NewCoin(USDT, osmomath.NewInt(99)), // 100 - 1 = 99 + }, + "stableswap pool - small taker fee": { + poolType: poolmanagertypes.Stableswap, + tokenIn: sdk.NewCoin(USDT, osmomath.NewInt(100)), + takerFee: osmomath.NewDecWithPrec(1, 2), // 1% + expectedToken: sdk.NewCoin(USDT, osmomath.NewInt(99)), // 100 - 1 = 99 + }, + "balancer pool - large taker fee": { + poolType: poolmanagertypes.Balancer, + tokenIn: sdk.NewCoin(USDC, osmomath.NewInt(100)), + takerFee: osmomath.NewDecWithPrec(5, 1), // 50% + expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(50)), // 100 - 50 = 50 + }, + "stableswap pool - large taker fee": { + poolType: poolmanagertypes.Stableswap, + tokenIn: sdk.NewCoin(USDC, osmomath.NewInt(100)), + takerFee: osmomath.NewDecWithPrec(5, 1), // 50% + expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(50)), // 100 - 50 = 50 + }, + } + + for name, tc := range tests { + s.Run(name, func() { + s.Setup() + + poolID := s.CreatePoolFromType(tc.poolType) + pool, err := s.App.PoolManagerKeeper.GetPool(s.Ctx, poolID) + s.Require().NoError(err) + + mock := &mocks.MockRoutablePool{ChainPoolModel: pool, PoolType: tc.poolType} + cosmWasmPoolsParams := cosmwasmdomain.CosmWasmPoolsParams{ + ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, + } + + routablePool, err := pools.NewRoutablePool(mock, tc.tokenIn.Denom, tc.takerFee, cosmWasmPoolsParams) + s.Require().NoError(err) + + tokenAfterFee := routablePool.ChargeTakerFeeExactIn(tc.tokenIn) + + s.Require().Equal(tc.expectedToken, tokenAfterFee) + }) + } +} + +func (s *RoutablePoolTestSuite) TestChargeTakerFeeExactOut_CCFM() { + tests := map[string]struct { + poolType poolmanagertypes.PoolType + tokenOut sdk.Coin + takerFee osmomath.Dec + expectedToken sdk.Coin + }{ + "balancer pool - no taker fee": { + poolType: poolmanagertypes.Balancer, + tokenOut: sdk.NewCoin(USDC, osmomath.NewInt(100)), + takerFee: osmomath.NewDec(0), + expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(100)), + }, + "stableswap pool - no taker fee": { + poolType: poolmanagertypes.Stableswap, + tokenOut: sdk.NewCoin(USDC, osmomath.NewInt(100)), + takerFee: osmomath.NewDec(0), + expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(100)), + }, + "balancer pool - small taker fee": { + poolType: poolmanagertypes.Balancer, + tokenOut: sdk.NewCoin(USDT, osmomath.NewInt(100)), + takerFee: osmomath.NewDecWithPrec(1, 2), // 1% + expectedToken: sdk.NewCoin(USDT, osmomath.NewInt(102)), // 100 + 1 = 101.01 = 102 (round up) + }, + "stableswap pool - small taker fee": { + poolType: poolmanagertypes.Stableswap, + tokenOut: sdk.NewCoin(USDT, osmomath.NewInt(100)), + takerFee: osmomath.NewDecWithPrec(1, 2), // 1% + expectedToken: sdk.NewCoin(USDT, osmomath.NewInt(102)), // 100 + 1 = 101.01 = 102 (round up) + }, + "balancer pool - large taker fee": { + poolType: poolmanagertypes.Balancer, + tokenOut: sdk.NewCoin(USDC, osmomath.NewInt(100)), + takerFee: osmomath.NewDecWithPrec(5, 1), // 50% + expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(200)), // 100 + 100 = 200 + }, + "stableswap pool - large taker fee": { + poolType: poolmanagertypes.Stableswap, + tokenOut: sdk.NewCoin(USDC, osmomath.NewInt(100)), + takerFee: osmomath.NewDecWithPrec(5, 1), // 50% + expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(200)), // 100 + 100 = 200 + }, + } + + for name, tc := range tests { + s.Run(name, func() { + s.Setup() + + poolID := s.CreatePoolFromType(tc.poolType) + pool, err := s.App.PoolManagerKeeper.GetPool(s.Ctx, poolID) + s.Require().NoError(err) + + mock := &mocks.MockRoutablePool{ChainPoolModel: pool, PoolType: tc.poolType} + cosmWasmPoolsParams := cosmwasmdomain.CosmWasmPoolsParams{ + ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, + } + + routablePool, err := pools.NewRoutablePool(mock, tc.tokenOut.Denom, tc.takerFee, cosmWasmPoolsParams) + s.Require().NoError(err) + + tokenAfterFee := routablePool.ChargeTakerFeeExactOut(tc.tokenOut) + + s.Require().Equal(tc.expectedToken, tokenAfterFee) + }) + } +} From 197479d89ae9c327f5037bfdb9a0a867a47e277b Mon Sep 17 00:00:00 2001 From: Deividas Petraitis Date: Fri, 17 Jan 2025 19:41:46 +0200 Subject: [PATCH 03/12] BE-675 | Update token input name passed to ChargeTakerFeeExactOut for CCFM --- router/usecase/pools/routable_pool_test.go | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/router/usecase/pools/routable_pool_test.go b/router/usecase/pools/routable_pool_test.go index 9fcb22ca..ff8c3f8d 100644 --- a/router/usecase/pools/routable_pool_test.go +++ b/router/usecase/pools/routable_pool_test.go @@ -241,43 +241,43 @@ func (s *RoutablePoolTestSuite) TestChargeTakerFeeExactIn_CCFM() { func (s *RoutablePoolTestSuite) TestChargeTakerFeeExactOut_CCFM() { tests := map[string]struct { poolType poolmanagertypes.PoolType - tokenOut sdk.Coin + tokenIn sdk.Coin takerFee osmomath.Dec expectedToken sdk.Coin }{ "balancer pool - no taker fee": { poolType: poolmanagertypes.Balancer, - tokenOut: sdk.NewCoin(USDC, osmomath.NewInt(100)), + tokenIn: sdk.NewCoin(USDC, osmomath.NewInt(100)), takerFee: osmomath.NewDec(0), expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(100)), }, "stableswap pool - no taker fee": { poolType: poolmanagertypes.Stableswap, - tokenOut: sdk.NewCoin(USDC, osmomath.NewInt(100)), + tokenIn: sdk.NewCoin(USDC, osmomath.NewInt(100)), takerFee: osmomath.NewDec(0), expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(100)), }, "balancer pool - small taker fee": { poolType: poolmanagertypes.Balancer, - tokenOut: sdk.NewCoin(USDT, osmomath.NewInt(100)), + tokenIn: sdk.NewCoin(USDT, osmomath.NewInt(100)), takerFee: osmomath.NewDecWithPrec(1, 2), // 1% expectedToken: sdk.NewCoin(USDT, osmomath.NewInt(102)), // 100 + 1 = 101.01 = 102 (round up) }, "stableswap pool - small taker fee": { poolType: poolmanagertypes.Stableswap, - tokenOut: sdk.NewCoin(USDT, osmomath.NewInt(100)), + tokenIn: sdk.NewCoin(USDT, osmomath.NewInt(100)), takerFee: osmomath.NewDecWithPrec(1, 2), // 1% expectedToken: sdk.NewCoin(USDT, osmomath.NewInt(102)), // 100 + 1 = 101.01 = 102 (round up) }, "balancer pool - large taker fee": { poolType: poolmanagertypes.Balancer, - tokenOut: sdk.NewCoin(USDC, osmomath.NewInt(100)), + tokenIn: sdk.NewCoin(USDC, osmomath.NewInt(100)), takerFee: osmomath.NewDecWithPrec(5, 1), // 50% expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(200)), // 100 + 100 = 200 }, "stableswap pool - large taker fee": { poolType: poolmanagertypes.Stableswap, - tokenOut: sdk.NewCoin(USDC, osmomath.NewInt(100)), + tokenIn: sdk.NewCoin(USDC, osmomath.NewInt(100)), takerFee: osmomath.NewDecWithPrec(5, 1), // 50% expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(200)), // 100 + 100 = 200 }, @@ -296,10 +296,10 @@ func (s *RoutablePoolTestSuite) TestChargeTakerFeeExactOut_CCFM() { ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, } - routablePool, err := pools.NewRoutablePool(mock, tc.tokenOut.Denom, tc.takerFee, cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(mock, tc.tokenIn.Denom, tc.takerFee, cosmWasmPoolsParams) s.Require().NoError(err) - tokenAfterFee := routablePool.ChargeTakerFeeExactOut(tc.tokenOut) + tokenAfterFee := routablePool.ChargeTakerFeeExactOut(tc.tokenIn) s.Require().Equal(tc.expectedToken, tokenAfterFee) }) From 3f589cc8e8c0002872e3921ee59df6ec0dd96cef Mon Sep 17 00:00:00 2001 From: Deividas Petraitis Date: Fri, 17 Jan 2025 20:52:45 +0200 Subject: [PATCH 04/12] BE-675 | Update token parameter name for TakerFee calculation method --- .../usecase/pools/routable_balancer_pool.go | 6 +- .../pools/routable_cw_transmuter_pool.go | 24 +++- .../pools/routable_cw_transmuter_pool_test.go | 136 ++++++++++++++++++ .../usecase/pools/routable_stableswap_pool.go | 6 +- 4 files changed, 162 insertions(+), 10 deletions(-) diff --git a/router/usecase/pools/routable_balancer_pool.go b/router/usecase/pools/routable_balancer_pool.go index 0c3664cc..a5a537c9 100644 --- a/router/usecase/pools/routable_balancer_pool.go +++ b/router/usecase/pools/routable_balancer_pool.go @@ -69,9 +69,9 @@ func (r *routableBalancerPoolImpl) ChargeTakerFeeExactIn(tokenIn sdk.Coin) (toke // ChargeTakerFee implements domain.RoutablePool. // Charges the taker fee for the given token out and returns the token out after the fee has been charged. -func (r *routableBalancerPoolImpl) ChargeTakerFeeExactOut(tokenOut sdk.Coin) (tokenOutAfterFee sdk.Coin) { - tokenOutAfterTakerFee, _ := poolmanager.CalcTakerFeeExactOut(tokenOut, r.TakerFee) - return tokenOutAfterTakerFee +func (r *routableBalancerPoolImpl) ChargeTakerFeeExactOut(tokenIn sdk.Coin) (tokenInAfterFee sdk.Coin) { + tokenInAfterTakerFee, _ := poolmanager.CalcTakerFeeExactOut(tokenIn, r.TakerFee) + return tokenInAfterTakerFee } // GetTakerFee implements domain.RoutablePool. diff --git a/router/usecase/pools/routable_cw_transmuter_pool.go b/router/usecase/pools/routable_cw_transmuter_pool.go index 231b6bac..e04456aa 100644 --- a/router/usecase/pools/routable_cw_transmuter_pool.go +++ b/router/usecase/pools/routable_cw_transmuter_pool.go @@ -2,7 +2,6 @@ package pools import ( "context" - "errors" "fmt" "cosmossdk.io/math" @@ -76,7 +75,23 @@ func (r *routableTransmuterPoolImpl) CalculateTokenOutByTokenIn(ctx context.Cont // CalculateTokenInByTokenOut implements domain.RoutablePool. func (r *routableTransmuterPoolImpl) CalculateTokenInByTokenOut(ctx context.Context, tokenOut sdk.Coin) (sdk.Coin, error) { - return sdk.Coin{}, errors.New("not implemented") + poolType := r.GetType() + + // Esnure that the pool is concentrated + if poolType != poolmanagertypes.CosmWasm { + return sdk.Coin{}, domain.InvalidPoolTypeError{PoolType: int32(poolType)} + } + + balances := r.Balances + + // Validate token out balance + if err := validateTransmuterBalance(tokenOut.Amount, balances, r.TokenInDenom); err != nil { + return sdk.Coin{}, err + } + + // No slippage swaps - just return the same amount of token in as token out + // as long as there is enough liquidity in the pool. + return sdk.Coin{Denom: r.TokenInDenom, Amount: tokenOut.Amount}, nil } // GetTokenOutDenom implements RoutablePool. @@ -101,8 +116,9 @@ func (r *routableTransmuterPoolImpl) ChargeTakerFeeExactIn(tokenIn sdk.Coin) (in } // ChargeTakerFeeExactOut implements domain.RoutablePool. -func (r *routableTransmuterPoolImpl) ChargeTakerFeeExactOut(tokenOut sdk.Coin) (outAmountAfterFee sdk.Coin) { - return sdk.Coin{} +func (r *routableTransmuterPoolImpl) ChargeTakerFeeExactOut(tokenIn sdk.Coin) (inAmountAfterFee sdk.Coin) { + tokenInAfterTakerFee, _ := poolmanager.CalcTakerFeeExactOut(tokenIn, r.GetTakerFee()) + return tokenInAfterTakerFee } // validateTransmuterBalance validates that the balance of the denom to validate is greater than the token in amount. diff --git a/router/usecase/pools/routable_cw_transmuter_pool_test.go b/router/usecase/pools/routable_cw_transmuter_pool_test.go index 7781ba93..b1222f74 100644 --- a/router/usecase/pools/routable_cw_transmuter_pool_test.go +++ b/router/usecase/pools/routable_cw_transmuter_pool_test.go @@ -92,3 +92,139 @@ func (s *RoutablePoolTestSuite) TestCalculateTokenOutByTokenIn_Transmuter() { }) } } + +func (s *RoutablePoolTestSuite) TestCalculateTokenInByTokenOut_Transmuter() { + defaultAmount := DefaultAmt0 + defaultBalances := sdk.NewCoins(sdk.NewCoin(USDC, defaultAmount), sdk.NewCoin(ETH, defaultAmount)) + + tests := map[string]struct { + tokenOut sdk.Coin + tokenInDenom string + balances sdk.Coins + isInvalidPoolType bool + expectError error + }{ + "valid transmuter quote": { + tokenOut: sdk.NewCoin(ETH, defaultAmount), + tokenInDenom: USDC, + balances: defaultBalances, + }, + "no error: token in is larger than balance of token in": { + tokenOut: sdk.NewCoin(ETH, defaultAmount), + tokenInDenom: USDC, + // Make token in amount 1 smaller than the default amount + balances: sdk.NewCoins(sdk.NewCoin(USDC, defaultAmount.Sub(osmomath.OneInt())), sdk.NewCoin(ETH, defaultAmount)), + }, + "error: token in is larger than balance of token out": { + tokenOut: sdk.NewCoin(ETH, defaultAmount), + tokenInDenom: USDC, + + // Make token out amount 1 smaller than the default amount + balances: sdk.NewCoins(sdk.NewCoin(ETH, defaultAmount.Sub(osmomath.OneInt())), sdk.NewCoin(USDC, defaultAmount)), + + expectError: domain.TransmuterInsufficientBalanceError{ + Denom: ETH, + BalanceAmount: defaultAmount.Sub(osmomath.OneInt()).String(), + Amount: defaultAmount.String(), + }, + }, + } + + for name, tc := range tests { + s.Run(name, func() { + s.Setup() + + cosmwasmPool := s.PrepareCustomTransmuterPool(s.TestAccs[0], []string{tc.tokenInDenom, tc.tokenOut.Denom}) + + poolType := cosmwasmPool.GetType() + + mock := &mocks.MockRoutablePool{ChainPoolModel: cosmwasmPool.AsSerializablePool(), Balances: tc.balances, PoolType: poolType} + + cosmWasmPoolsParams := cosmwasmdomain.CosmWasmPoolsParams{ + Config: domain.CosmWasmPoolRouterConfig{ + TransmuterCodeIDs: map[uint64]struct{}{ + cosmwasmPool.GetCodeId(): {}, + }, + }, + ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, + } + routablePool, err := pools.NewRoutablePool(mock, tc.tokenInDenom, noTakerFee, cosmWasmPoolsParams) + s.Require().NoError(err) + + // Overwrite pool type for edge case testing + if tc.isInvalidPoolType { + mock.PoolType = poolmanagertypes.Concentrated + } + + tokenIn, err := routablePool.CalculateTokenInByTokenOut(context.TODO(), tc.tokenOut) + + if tc.expectError != nil { + s.Require().Error(err) + s.Require().ErrorIs(err, tc.expectError) + return + } + s.Require().NoError(err) + + // No slippage swaps on success + s.Require().Equal(tc.tokenOut.Amount, tokenIn.Amount) + }) + } +} + +func (s *RoutablePoolTestSuite) TestChargeTakerFeeExactOut_Transmuter() { + defaultAmount := DefaultAmt0 + defaultBalances := sdk.NewCoins(sdk.NewCoin(USDC, defaultAmount), sdk.NewCoin(ETH, defaultAmount)) + + tests := map[string]struct { + poolType poolmanagertypes.PoolType + tokenIn sdk.Coin + takerFee osmomath.Dec + balances sdk.Coins + expectedToken sdk.Coin + }{ + "no taker fee": { + tokenIn: sdk.NewCoin(USDC, osmomath.NewInt(100)), + balances: defaultBalances, + takerFee: osmomath.NewDec(0), + expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(100)), + }, + "small taker fee": { + tokenIn: sdk.NewCoin(USDT, osmomath.NewInt(100)), + takerFee: osmomath.NewDecWithPrec(1, 2), // 1% + expectedToken: sdk.NewCoin(USDT, osmomath.NewInt(102)), // 100 + 1 = 101.01 = 102 (round up) + }, + "large taker fee": { + tokenIn: sdk.NewCoin(USDC, osmomath.NewInt(100)), + takerFee: osmomath.NewDecWithPrec(5, 1), // 50% + expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(200)), // 100 + 100 = 200 + }, + } + + for name, tc := range tests { + s.Run(name, func() { + s.Setup() + + cosmwasmPool := s.PrepareCustomTransmuterPool(s.TestAccs[0], []string{USDC, ETH}) + + poolType := cosmwasmPool.GetType() + + mock := &mocks.MockRoutablePool{ChainPoolModel: cosmwasmPool.AsSerializablePool(), Balances: tc.balances, PoolType: poolType} + + cosmWasmPoolsParams := cosmwasmdomain.CosmWasmPoolsParams{ + Config: domain.CosmWasmPoolRouterConfig{ + TransmuterCodeIDs: map[uint64]struct{}{ + cosmwasmPool.GetCodeId(): {}, + }, + }, + ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, + } + routablePool, err := pools.NewRoutablePool(mock, tc.tokenIn.Denom, tc.takerFee, cosmWasmPoolsParams) + s.Require().NoError(err) + s.Require().NoError(err) + + tokenAfterFee := routablePool.ChargeTakerFeeExactOut(tc.tokenIn) + + s.Require().Equal(tc.expectedToken, tokenAfterFee) + }) + } +} diff --git a/router/usecase/pools/routable_stableswap_pool.go b/router/usecase/pools/routable_stableswap_pool.go index 44585c14..d5a8e80e 100644 --- a/router/usecase/pools/routable_stableswap_pool.go +++ b/router/usecase/pools/routable_stableswap_pool.go @@ -62,9 +62,9 @@ func (r *routableStableswapPoolImpl) String() string { // ChargeTakerFee implements domain.RoutablePool. // Charges the taker fee for the given token out and returns the token out after the fee has been charged. -func (r *routableStableswapPoolImpl) ChargeTakerFeeExactOut(tokenOut sdk.Coin) (tokenOutAfterFee sdk.Coin) { - tokenOutAfterTakerFee, _ := poolmanager.CalcTakerFeeExactOut(tokenOut, r.TakerFee) - return tokenOutAfterTakerFee +func (r *routableStableswapPoolImpl) ChargeTakerFeeExactOut(tokenIn sdk.Coin) (tokenInAfterFee sdk.Coin) { + tokenInAfterTakerFee, _ := poolmanager.CalcTakerFeeExactOut(tokenIn, r.TakerFee) + return tokenInAfterTakerFee } // ChargeTakerFee implements domain.RoutablePool. From 04accfc827a52bf2afd79d083da7e5e2ea91da9a Mon Sep 17 00:00:00 2001 From: Deividas Petraitis Date: Mon, 20 Jan 2025 13:57:36 +0200 Subject: [PATCH 05/12] BE-675 | Implement relevant APIs --- domain/mocks/pools_usecase_mock.go | 2 +- ingest/types/candidate_route.go | 1 + pools/usecase/pools_usecase.go | 6 +++--- pools/usecase/pools_usecase_test.go | 12 +++++------ router/usecase/pools/pool_factory.go | 10 +++++++--- .../pools/routable_concentrated_pool_test.go | 2 +- .../routable_cw_alloy_transmuter_pool_test.go | 2 +- .../pools/routable_cw_orderbook_pool_test.go | 2 +- .../pools/routable_cw_transmuter_pool.go | 2 +- .../pools/routable_cw_transmuter_pool_test.go | 20 +++++++++---------- router/usecase/pools/routable_pool_test.go | 10 +++++----- router/usecase/quote_test.go | 4 ++-- router/usecase/routertesting/quote.go | 10 ++++++++-- 13 files changed, 47 insertions(+), 36 deletions(-) diff --git a/domain/mocks/pools_usecase_mock.go b/domain/mocks/pools_usecase_mock.go index 40565f44..be1ca7bb 100644 --- a/domain/mocks/pools_usecase_mock.go +++ b/domain/mocks/pools_usecase_mock.go @@ -111,7 +111,7 @@ func (pm *PoolsUsecaseMock) GetRoutesFromCandidates(candidateRoutes ingesttypes. } // TODO: note that taker fee is force set to zero - routablePool, err := pools.NewRoutablePool(foundPool, candidatePool.TokenOutDenom, osmomath.ZeroDec(), cosmwasmdomain.CosmWasmPoolsParams{ + routablePool, err := pools.NewRoutablePool(foundPool, candidatePool.TokenInDenom, candidatePool.TokenOutDenom, osmomath.ZeroDec(), cosmwasmdomain.CosmWasmPoolsParams{ ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, }) if err != nil { diff --git a/ingest/types/candidate_route.go b/ingest/types/candidate_route.go index a41a90dc..e71b43a7 100644 --- a/ingest/types/candidate_route.go +++ b/ingest/types/candidate_route.go @@ -4,6 +4,7 @@ package types // candidate pool to be used for routing. type CandidatePool struct { ID uint64 + TokenInDenom string TokenOutDenom string } diff --git a/pools/usecase/pools_usecase.go b/pools/usecase/pools_usecase.go index 8d104234..42ee5104 100644 --- a/pools/usecase/pools_usecase.go +++ b/pools/usecase/pools_usecase.go @@ -180,7 +180,7 @@ func (p *poolsUseCase) GetRoutesFromCandidates(candidateRoutes ingesttypes.Candi takerFee = ingesttypes.DefaultTakerFee } - routablePool, err := pools.NewRoutablePool(pool, candidatePool.TokenOutDenom, takerFee, p.cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(pool, candidatePool.TokenInDenom, candidatePool.TokenOutDenom, takerFee, p.cosmWasmPoolsParams) if err != nil { skipErrorRoute = true break @@ -263,9 +263,9 @@ func (p *poolsUseCase) GetPoolSpotPrice(ctx context.Context, poolID uint64, take return osmomath.BigDec{}, err } - // N.B.: Empty string for token out denom because it is irrelevant for calculating spot price. + // N.B.: Empty string for token in/out denom because it is irrelevant for calculating spot price. // It is only relevant in the context of routing - routablePool, err := pools.NewRoutablePool(pool, "", takerFee, p.cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(pool, "", "", takerFee, p.cosmWasmPoolsParams) if err != nil { return osmomath.BigDec{}, err } diff --git a/pools/usecase/pools_usecase_test.go b/pools/usecase/pools_usecase_test.go index d42a2eaf..0c75d563 100644 --- a/pools/usecase/pools_usecase_test.go +++ b/pools/usecase/pools_usecase_test.go @@ -107,7 +107,7 @@ func (s *PoolsUsecaseTestSuite) TestGetRoutesFromCandidates() { cosmWasmPoolsParams := cosmwasmdomain.CosmWasmPoolsParams{ ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, } - _, err = pools.NewRoutablePool(&brokenChainPool, denomTwo, defaultTakerFee, cosmWasmPoolsParams) + _, err = pools.NewRoutablePool(&brokenChainPool, denomOne, denomTwo, defaultTakerFee, cosmWasmPoolsParams) // Validate that it is indeed broken. s.Require().Error(err) @@ -157,7 +157,7 @@ func (s *PoolsUsecaseTestSuite) TestGetRoutesFromCandidates() { expectedRoutes: []route.RouteImpl{ { Pools: []domain.RoutablePool{ - s.newRoutablePool(defaultPool, denomTwo, defaultTakerFee), + s.newRoutablePool(defaultPool, denomOne, denomTwo, defaultTakerFee), }, }, }, @@ -177,7 +177,7 @@ func (s *PoolsUsecaseTestSuite) TestGetRoutesFromCandidates() { expectedRoutes: []route.RouteImpl{ { Pools: []domain.RoutablePool{ - s.newRoutablePool(defaultPool, denomTwo, ingesttypes.DefaultTakerFee), + s.newRoutablePool(defaultPool, denomOne, denomTwo, ingesttypes.DefaultTakerFee), }, }, }, @@ -211,7 +211,7 @@ func (s *PoolsUsecaseTestSuite) TestGetRoutesFromCandidates() { expectedRoutes: []route.RouteImpl{ { Pools: []domain.RoutablePool{ - s.newRoutablePool(defaultPool, denomTwo, defaultTakerFee), + s.newRoutablePool(defaultPool, denomOne, denomTwo, defaultTakerFee), }, }, }, @@ -1015,11 +1015,11 @@ func (s *PoolsUsecaseTestSuite) TestSetPoolAPRAndFeeDataIfConfigured() { } } -func (s *PoolsUsecaseTestSuite) newRoutablePool(pool ingesttypes.PoolI, tokenOutDenom string, takerFee osmomath.Dec) domain.RoutablePool { +func (s *PoolsUsecaseTestSuite) newRoutablePool(pool ingesttypes.PoolI, tokenInDenom string, tokenOutDenom string, takerFee osmomath.Dec) domain.RoutablePool { cosmWasmPoolsParams := cosmwasmdomain.CosmWasmPoolsParams{ ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, } - routablePool, err := pools.NewRoutablePool(pool, tokenOutDenom, takerFee, cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(pool, tokenInDenom, tokenOutDenom, takerFee, cosmWasmPoolsParams) s.Require().NoError(err) return routablePool } diff --git a/router/usecase/pools/pool_factory.go b/router/usecase/pools/pool_factory.go index 19342646..d31f313d 100644 --- a/router/usecase/pools/pool_factory.go +++ b/router/usecase/pools/pool_factory.go @@ -16,7 +16,7 @@ import ( // NewRoutablePool creates a new RoutablePool. // Panics if pool is of invalid type or if does not contain tick data when a concentrated pool. -func NewRoutablePool(pool ingesttypes.PoolI, tokenOutDenom string, takerFee osmomath.Dec, cosmWasmPoolsParams cosmwasmdomain.CosmWasmPoolsParams) (domain.RoutablePool, error) { +func NewRoutablePool(pool ingesttypes.PoolI, tokenInDenom string, tokenOutDenom string, takerFee osmomath.Dec, cosmWasmPoolsParams cosmwasmdomain.CosmWasmPoolsParams) (domain.RoutablePool, error) { poolType := pool.GetType() chainPool := pool.GetUnderlyingPool() if poolType == poolmanagertypes.Concentrated { @@ -37,6 +37,7 @@ func NewRoutablePool(pool ingesttypes.PoolI, tokenOutDenom string, takerFee osmo return &routableConcentratedPoolImpl{ ChainPool: concentratedPool, TickModel: tickModel, + TokenInDenom: tokenInDenom, TokenOutDenom: tokenOutDenom, TakerFee: takerFee, }, nil @@ -56,6 +57,7 @@ func NewRoutablePool(pool ingesttypes.PoolI, tokenOutDenom string, takerFee osmo return &routableBalancerPoolImpl{ ChainPool: balancerPool, + TokenInDenom: tokenInDenom, TokenOutDenom: tokenOutDenom, TakerFee: takerFee, }, nil @@ -80,17 +82,18 @@ func NewRoutablePool(pool ingesttypes.PoolI, tokenOutDenom string, takerFee osmo return &routableStableswapPoolImpl{ ChainPool: stableswapPool, + TokenInDenom: tokenInDenom, TokenOutDenom: tokenOutDenom, TakerFee: takerFee, }, nil } - return newRoutableCosmWasmPool(pool, tokenOutDenom, takerFee, cosmWasmPoolsParams) + return newRoutableCosmWasmPool(pool, tokenInDenom, tokenOutDenom, takerFee, cosmWasmPoolsParams) } // newRoutableCosmWasmPool creates a new RoutablePool for CosmWasm pools. // Panics if the given pool is not a cosmwasm pool or if the -func newRoutableCosmWasmPool(pool ingesttypes.PoolI, tokenOutDenom string, takerFee osmomath.Dec, cosmWasmPoolsParams cosmwasmdomain.CosmWasmPoolsParams) (domain.RoutablePool, error) { +func newRoutableCosmWasmPool(pool ingesttypes.PoolI, tokenInDenom string, tokenOutDenom string, takerFee osmomath.Dec, cosmWasmPoolsParams cosmwasmdomain.CosmWasmPoolsParams) (domain.RoutablePool, error) { chainPool := pool.GetUnderlyingPool() poolType := pool.GetType() @@ -113,6 +116,7 @@ func newRoutableCosmWasmPool(pool ingesttypes.PoolI, tokenOutDenom string, taker return &routableTransmuterPoolImpl{ ChainPool: cosmwasmPool, Balances: balances, + TokenInDenom: tokenInDenom, TokenOutDenom: tokenOutDenom, TakerFee: takerFee, SpreadFactor: spreadFactor, diff --git a/router/usecase/pools/routable_concentrated_pool_test.go b/router/usecase/pools/routable_concentrated_pool_test.go index 083ef43e..20fc5ff0 100644 --- a/router/usecase/pools/routable_concentrated_pool_test.go +++ b/router/usecase/pools/routable_concentrated_pool_test.go @@ -92,7 +92,7 @@ func (s *RoutablePoolTestSuite) TestCalculateTokenOutByTokenIn_Concentrated_Succ cosmWasmPoolsParams := cosmwasmdomain.CosmWasmPoolsParams{ ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, } - routablePool, err := pools.NewRoutablePool(poolWrapper, tc.TokenOutDenom, noTakerFee, cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(poolWrapper, tc.TokenInDenom, tc.TokenOutDenom, noTakerFee, cosmWasmPoolsParams) s.Require().NoError(err) tokenOut, err := routablePool.CalculateTokenOutByTokenIn(context.TODO(), tc.TokenIn) diff --git a/router/usecase/pools/routable_cw_alloy_transmuter_pool_test.go b/router/usecase/pools/routable_cw_alloy_transmuter_pool_test.go index a2cdd161..044c8895 100644 --- a/router/usecase/pools/routable_cw_alloy_transmuter_pool_test.go +++ b/router/usecase/pools/routable_cw_alloy_transmuter_pool_test.go @@ -66,7 +66,7 @@ func (s *RoutablePoolTestSuite) SetupRoutableAlloyTransmuterPoolCustom(tokenInDe }, ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, } - routablePool, err := pools.NewRoutablePool(mock, tokenOutDenom, takerFee, cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(mock, tokenInDenom, tokenOutDenom, takerFee, cosmWasmPoolsParams) s.Require().NoError(err) return routablePool diff --git a/router/usecase/pools/routable_cw_orderbook_pool_test.go b/router/usecase/pools/routable_cw_orderbook_pool_test.go index 9308e41a..2a393d09 100644 --- a/router/usecase/pools/routable_cw_orderbook_pool_test.go +++ b/router/usecase/pools/routable_cw_orderbook_pool_test.go @@ -71,7 +71,7 @@ func (s *RoutablePoolTestSuite) SetupRoutableOrderbookPool( }, ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, } - routablePool, err := pools.NewRoutablePool(mock, tokenOutDenom, takerFee, cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(mock, tokenInDenom, tokenOutDenom, takerFee, cosmWasmPoolsParams) s.Require().NoError(err) return routablePool diff --git a/router/usecase/pools/routable_cw_transmuter_pool.go b/router/usecase/pools/routable_cw_transmuter_pool.go index e04456aa..601067c9 100644 --- a/router/usecase/pools/routable_cw_transmuter_pool.go +++ b/router/usecase/pools/routable_cw_transmuter_pool.go @@ -84,7 +84,7 @@ func (r *routableTransmuterPoolImpl) CalculateTokenInByTokenOut(ctx context.Cont balances := r.Balances - // Validate token out balance + // Validate token in balance if err := validateTransmuterBalance(tokenOut.Amount, balances, r.TokenInDenom); err != nil { return sdk.Coin{}, err } diff --git a/router/usecase/pools/routable_cw_transmuter_pool_test.go b/router/usecase/pools/routable_cw_transmuter_pool_test.go index b1222f74..9d7d40c9 100644 --- a/router/usecase/pools/routable_cw_transmuter_pool_test.go +++ b/router/usecase/pools/routable_cw_transmuter_pool_test.go @@ -70,7 +70,7 @@ func (s *RoutablePoolTestSuite) TestCalculateTokenOutByTokenIn_Transmuter() { }, ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, } - routablePool, err := pools.NewRoutablePool(mock, tc.tokenOutDenom, noTakerFee, cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(mock, tc.tokenIn.Denom, tc.tokenOutDenom, noTakerFee, cosmWasmPoolsParams) s.Require().NoError(err) // Overwrite pool type for edge case testing @@ -109,21 +109,21 @@ func (s *RoutablePoolTestSuite) TestCalculateTokenInByTokenOut_Transmuter() { tokenInDenom: USDC, balances: defaultBalances, }, - "no error: token in is larger than balance of token in": { + "no error: token out is larger than balance of token out": { tokenOut: sdk.NewCoin(ETH, defaultAmount), tokenInDenom: USDC, - // Make token in amount 1 smaller than the default amount - balances: sdk.NewCoins(sdk.NewCoin(USDC, defaultAmount.Sub(osmomath.OneInt())), sdk.NewCoin(ETH, defaultAmount)), + // Make token out amount 1 smaller than the default amount + balances: sdk.NewCoins(sdk.NewCoin(USDC, defaultAmount), sdk.NewCoin(ETH, defaultAmount.Sub(osmomath.OneInt()))), }, - "error: token in is larger than balance of token out": { + "error: token out is larger than balance of token in": { tokenOut: sdk.NewCoin(ETH, defaultAmount), tokenInDenom: USDC, - // Make token out amount 1 smaller than the default amount - balances: sdk.NewCoins(sdk.NewCoin(ETH, defaultAmount.Sub(osmomath.OneInt())), sdk.NewCoin(USDC, defaultAmount)), + // Make token in amount 1 smaller than the default amount + balances: sdk.NewCoins(sdk.NewCoin(ETH, defaultAmount), sdk.NewCoin(USDC, defaultAmount.Sub(osmomath.OneInt()))), expectError: domain.TransmuterInsufficientBalanceError{ - Denom: ETH, + Denom: USDC, BalanceAmount: defaultAmount.Sub(osmomath.OneInt()).String(), Amount: defaultAmount.String(), }, @@ -148,7 +148,7 @@ func (s *RoutablePoolTestSuite) TestCalculateTokenInByTokenOut_Transmuter() { }, ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, } - routablePool, err := pools.NewRoutablePool(mock, tc.tokenInDenom, noTakerFee, cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(mock, tc.tokenInDenom, tc.tokenOut.Denom, noTakerFee, cosmWasmPoolsParams) s.Require().NoError(err) // Overwrite pool type for edge case testing @@ -218,7 +218,7 @@ func (s *RoutablePoolTestSuite) TestChargeTakerFeeExactOut_Transmuter() { }, ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, } - routablePool, err := pools.NewRoutablePool(mock, tc.tokenIn.Denom, tc.takerFee, cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(mock, tc.tokenIn.Denom, "", tc.takerFee, cosmWasmPoolsParams) s.Require().NoError(err) s.Require().NoError(err) diff --git a/router/usecase/pools/routable_pool_test.go b/router/usecase/pools/routable_pool_test.go index ff8c3f8d..4ff54871 100644 --- a/router/usecase/pools/routable_pool_test.go +++ b/router/usecase/pools/routable_pool_test.go @@ -98,7 +98,7 @@ func (s *RoutablePoolTestSuite) TestCalculateTokenOutByTokenIn_CFMM() { cosmWasmPoolsParams := cosmwasmdomain.CosmWasmPoolsParams{ ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, } - routablePool, err := pools.NewRoutablePool(mock, tc.tokenOutDenom, noTakerFee, cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(mock, tc.tokenIn.Denom, tc.tokenOutDenom, noTakerFee, cosmWasmPoolsParams) s.Require().NoError(err) tokenOut, err := routablePool.CalculateTokenOutByTokenIn(context.TODO(), tc.tokenIn) @@ -151,10 +151,10 @@ func (s *RoutablePoolTestSuite) TestCalculateTokenInByTokenOut_CFMM() { cosmWasmPoolsParams := cosmwasmdomain.CosmWasmPoolsParams{ ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, } - routablePool, err := pools.NewRoutablePool(mock, tc.tokenInDenom, noTakerFee, cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(mock, tc.tokenInDenom, tc.tokenOut.Denom, noTakerFee, cosmWasmPoolsParams) s.Require().NoError(err) - tokenOut, err := routablePool.CalculateTokenOutByTokenIn(context.TODO(), tc.tokenOut) + tokenOut, err := routablePool.CalculateTokenInByTokenOut(context.TODO(), tc.tokenOut) if tc.expectError != nil { s.Require().Error(err) @@ -228,7 +228,7 @@ func (s *RoutablePoolTestSuite) TestChargeTakerFeeExactIn_CCFM() { ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, } - routablePool, err := pools.NewRoutablePool(mock, tc.tokenIn.Denom, tc.takerFee, cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(mock, tc.tokenIn.Denom, "", tc.takerFee, cosmWasmPoolsParams) s.Require().NoError(err) tokenAfterFee := routablePool.ChargeTakerFeeExactIn(tc.tokenIn) @@ -296,7 +296,7 @@ func (s *RoutablePoolTestSuite) TestChargeTakerFeeExactOut_CCFM() { ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, } - routablePool, err := pools.NewRoutablePool(mock, tc.tokenIn.Denom, tc.takerFee, cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(mock, tc.tokenIn.Denom, "", tc.takerFee, cosmWasmPoolsParams) s.Require().NoError(err) tokenAfterFee := routablePool.ChargeTakerFeeExactOut(tc.tokenIn) diff --git a/router/usecase/quote_test.go b/router/usecase/quote_test.go index 6886bb66..bef5e5a6 100644 --- a/router/usecase/quote_test.go +++ b/router/usecase/quote_test.go @@ -292,12 +292,12 @@ func (s *RouterTestSuite) validateRoutes(expectedRoutes []domain.SplitRoute, act } } -func (s *RouterTestSuite) newRoutablePool(pool ingesttypes.PoolI, tokenOutDenom string, takerFee osmomath.Dec, cosmWasmConfig domain.CosmWasmPoolRouterConfig) domain.RoutablePool { +func (s *RouterTestSuite) newRoutablePool(pool ingesttypes.PoolI, tokenInDenom string, tokenOutDenom string, takerFee osmomath.Dec, cosmWasmConfig domain.CosmWasmPoolRouterConfig) domain.RoutablePool { cosmWasmPoolsParams := cosmwasmdomain.CosmWasmPoolsParams{ Config: cosmWasmConfig, ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, } - routablePool, err := pools.NewRoutablePool(pool, tokenOutDenom, takerFee, cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(pool, tokenInDenom, tokenOutDenom, takerFee, cosmWasmPoolsParams) s.Require().NoError(err) return routablePool } diff --git a/router/usecase/routertesting/quote.go b/router/usecase/routertesting/quote.go index 18c9a46b..c74a8558 100644 --- a/router/usecase/routertesting/quote.go +++ b/router/usecase/routertesting/quote.go @@ -41,11 +41,11 @@ var ( ) ) -func (s *RouterTestHelper) newRoutablePool(pool ingesttypes.PoolI, tokenOutDenom string, takerFee osmomath.Dec) domain.RoutablePool { +func (s *RouterTestHelper) newRoutablePool(pool ingesttypes.PoolI, tokenInDenom string, tokenOutDenom string, takerFee osmomath.Dec) domain.RoutablePool { cosmWasmPoolsParams := cosmwasmdomain.CosmWasmPoolsParams{ ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, } - routablePool, err := pools.NewRoutablePool(pool, tokenOutDenom, takerFee, cosmWasmPoolsParams) + routablePool, err := pools.NewRoutablePool(pool, tokenInDenom, tokenOutDenom, takerFee, cosmWasmPoolsParams) s.Require().NoError(err) @@ -66,11 +66,13 @@ func (s *RouterTestHelper) NewExactAmountInQuote(p1, p2, p3 poolmanagertypes.Poo Pools: []domain.RoutablePool{ s.newRoutablePool( ingesttypes.NewPool(p1, p1.GetSpreadFactor(sdk.Context{}), poolOneBalances), + "", USDT, takerFeeOne, ), s.newRoutablePool( ingesttypes.NewPool(p2, p2.GetSpreadFactor(sdk.Context{}), poolTwoBalances), + "", USDC, takerFeeTwo, ), @@ -87,6 +89,7 @@ func (s *RouterTestHelper) NewExactAmountInQuote(p1, p2, p3 poolmanagertypes.Poo Pools: []domain.RoutablePool{ s.newRoutablePool( ingesttypes.NewPool(p3, p3.GetSpreadFactor(sdk.Context{}), poolThreeBalances), + "", USDC, takerFeeThree, ), @@ -115,11 +118,13 @@ func (s *RouterTestHelper) NewExactAmountOutQuote(p1, p2, p3 poolmanagertypes.Po Pools: []domain.RoutablePool{ s.newRoutablePool( ingesttypes.NewPool(p1, p1.GetSpreadFactor(sdk.Context{}), poolOneBalances), + "", USDT, takerFeeOne, ), s.newRoutablePool( ingesttypes.NewPool(p2, p2.GetSpreadFactor(sdk.Context{}), poolTwoBalances), + "", USDC, takerFeeTwo, ), @@ -134,6 +139,7 @@ func (s *RouterTestHelper) NewExactAmountOutQuote(p1, p2, p3 poolmanagertypes.Po Pools: []domain.RoutablePool{ s.newRoutablePool( ingesttypes.NewPool(p3, p3.GetSpreadFactor(sdk.Context{}), poolThreeBalances), + "", USDC, takerFeeThree, ), From 0bcf7faf4f9da6ae7d4564d0172987516beaa2b5 Mon Sep 17 00:00:00 2001 From: Deividas Petraitis Date: Mon, 20 Jan 2025 14:12:38 +0200 Subject: [PATCH 06/12] BE-675 | Pass tokenIn to newRoutablePool in tests --- router/usecase/routertesting/quote.go | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/router/usecase/routertesting/quote.go b/router/usecase/routertesting/quote.go index c74a8558..b020fc4b 100644 --- a/router/usecase/routertesting/quote.go +++ b/router/usecase/routertesting/quote.go @@ -66,13 +66,13 @@ func (s *RouterTestHelper) NewExactAmountInQuote(p1, p2, p3 poolmanagertypes.Poo Pools: []domain.RoutablePool{ s.newRoutablePool( ingesttypes.NewPool(p1, p1.GetSpreadFactor(sdk.Context{}), poolOneBalances), - "", + ETH, USDT, takerFeeOne, ), s.newRoutablePool( ingesttypes.NewPool(p2, p2.GetSpreadFactor(sdk.Context{}), poolTwoBalances), - "", + USDT, USDC, takerFeeTwo, ), @@ -89,7 +89,7 @@ func (s *RouterTestHelper) NewExactAmountInQuote(p1, p2, p3 poolmanagertypes.Poo Pools: []domain.RoutablePool{ s.newRoutablePool( ingesttypes.NewPool(p3, p3.GetSpreadFactor(sdk.Context{}), poolThreeBalances), - "", + ETH, USDC, takerFeeThree, ), @@ -118,13 +118,13 @@ func (s *RouterTestHelper) NewExactAmountOutQuote(p1, p2, p3 poolmanagertypes.Po Pools: []domain.RoutablePool{ s.newRoutablePool( ingesttypes.NewPool(p1, p1.GetSpreadFactor(sdk.Context{}), poolOneBalances), - "", + ETH, USDT, takerFeeOne, ), s.newRoutablePool( ingesttypes.NewPool(p2, p2.GetSpreadFactor(sdk.Context{}), poolTwoBalances), - "", + USDT, USDC, takerFeeTwo, ), @@ -139,7 +139,7 @@ func (s *RouterTestHelper) NewExactAmountOutQuote(p1, p2, p3 poolmanagertypes.Po Pools: []domain.RoutablePool{ s.newRoutablePool( ingesttypes.NewPool(p3, p3.GetSpreadFactor(sdk.Context{}), poolThreeBalances), - "", + ETH, USDC, takerFeeThree, ), From 79118d11db6f6124ad071c67e1bc5267230e64c7 Mon Sep 17 00:00:00 2001 From: Deividas Petraitis Date: Mon, 20 Jan 2025 14:24:15 +0200 Subject: [PATCH 07/12] BE-675 | Add TakerFee test case for Transmuter ExactIn --- .../pools/routable_cw_transmuter_pool_test.go | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/router/usecase/pools/routable_cw_transmuter_pool_test.go b/router/usecase/pools/routable_cw_transmuter_pool_test.go index 9d7d40c9..3729c602 100644 --- a/router/usecase/pools/routable_cw_transmuter_pool_test.go +++ b/router/usecase/pools/routable_cw_transmuter_pool_test.go @@ -93,6 +93,64 @@ func (s *RoutablePoolTestSuite) TestCalculateTokenOutByTokenIn_Transmuter() { } } +func (s *RoutablePoolTestSuite) TestChargeTakerFeeExactIn_Transmuter() { + defaultAmount := DefaultAmt0 + defaultBalances := sdk.NewCoins(sdk.NewCoin(USDC, defaultAmount), sdk.NewCoin(ETH, defaultAmount)) + + tests := map[string]struct { + poolType poolmanagertypes.PoolType + tokenIn sdk.Coin + takerFee osmomath.Dec + balances sdk.Coins + expectedToken sdk.Coin + }{ + "no taker fee": { + tokenIn: sdk.NewCoin(USDC, osmomath.NewInt(100)), + balances: defaultBalances, + takerFee: osmomath.NewDec(0), + expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(100)), + }, + "small taker fee": { + tokenIn: sdk.NewCoin(USDT, osmomath.NewInt(100)), + takerFee: osmomath.NewDecWithPrec(1, 2), // 1% + expectedToken: sdk.NewCoin(USDT, osmomath.NewInt(99)), // 100 - 1 = 99 + }, + "large taker fee": { + tokenIn: sdk.NewCoin(USDC, osmomath.NewInt(100)), + takerFee: osmomath.NewDecWithPrec(5, 1), // 50% + expectedToken: sdk.NewCoin(USDC, osmomath.NewInt(50)), // 100 - 50 = 50 + }, + } + + for name, tc := range tests { + s.Run(name, func() { + s.Setup() + + cosmwasmPool := s.PrepareCustomTransmuterPool(s.TestAccs[0], []string{USDC, ETH}) + + poolType := cosmwasmPool.GetType() + + mock := &mocks.MockRoutablePool{ChainPoolModel: cosmwasmPool.AsSerializablePool(), Balances: tc.balances, PoolType: poolType} + + cosmWasmPoolsParams := cosmwasmdomain.CosmWasmPoolsParams{ + Config: domain.CosmWasmPoolRouterConfig{ + TransmuterCodeIDs: map[uint64]struct{}{ + cosmwasmPool.GetCodeId(): {}, + }, + }, + ScalingFactorGetterCb: domain.UnsetScalingFactorGetterCb, + } + routablePool, err := pools.NewRoutablePool(mock, tc.tokenIn.Denom, "", tc.takerFee, cosmWasmPoolsParams) + s.Require().NoError(err) + s.Require().NoError(err) + + tokenAfterFee := routablePool.ChargeTakerFeeExactIn(tc.tokenIn) + + s.Require().Equal(tc.expectedToken, tokenAfterFee) + }) + } +} + func (s *RoutablePoolTestSuite) TestCalculateTokenInByTokenOut_Transmuter() { defaultAmount := DefaultAmt0 defaultBalances := sdk.NewCoins(sdk.NewCoin(USDC, defaultAmount), sdk.NewCoin(ETH, defaultAmount)) From ceee930c761b6888e589fd0ba4ce9347d55014b2 Mon Sep 17 00:00:00 2001 From: Deividas Petraitis Date: Mon, 20 Jan 2025 14:43:44 +0200 Subject: [PATCH 08/12] BE-675 | Cleanup --- .../routable_cw_alloy_transmuter_pool.go | 26 ------------------- 1 file changed, 26 deletions(-) diff --git a/router/usecase/pools/routable_cw_alloy_transmuter_pool.go b/router/usecase/pools/routable_cw_alloy_transmuter_pool.go index 5c539d50..65fa89fd 100644 --- a/router/usecase/pools/routable_cw_alloy_transmuter_pool.go +++ b/router/usecase/pools/routable_cw_alloy_transmuter_pool.go @@ -208,32 +208,6 @@ func (r *routableAlloyTransmuterPoolImpl) CalcTokenOutAmt(tokenIn sdk.Coin, toke return tokenOutAmount, nil } -// Calculate the token out amount based on the normalization factors: -// -// token_in_amt / token_in_norm_factor = token_out_amt / token_out_norm_factor -// token_in_amt = token_out_amt * token_in_norm_factor / token_out_norm_factor -func (r *routableAlloyTransmuterPoolImpl) CalcTokenInAmt(tokenInDenom string, tokenOut sdk.Coin) (osmomath.BigDec, error) { - tokenOutNormFactor, tokenInNormFactor, err := r.FindNormalizationFactors(tokenOut.Denom, tokenInDenom) - if err != nil { - return osmomath.BigDec{}, err - } - - if tokenInNormFactor.IsZero() { - return osmomath.BigDec{}, domain.ZeroNormalizationFactorError{Denom: tokenOut.Denom, PoolId: r.GetId()} - } - - if tokenOutNormFactor.IsZero() { - return osmomath.BigDec{}, domain.ZeroNormalizationFactorError{Denom: tokenInDenom, PoolId: r.GetId()} - } - - tokenOutAmount := osmomath.NewBigDec(tokenOut.Amount.Int64()) - - tokenOutNormFactorBig := osmomath.NewBigIntFromBigInt(tokenOutNormFactor.BigInt()) - tokenInNormFactorBig := osmomath.NewBigIntFromBigInt(tokenInNormFactor.BigInt()) - - return tokenOutAmount.MulInt(tokenInNormFactorBig).QuoInt(tokenOutNormFactorBig), nil -} - // checkStaticRateLimiter checks the static rate limiter. // If token in denom is not alloyed, we only need to validate the token in balance. // Since the token in balance is the only one that is increased by the current quote. From a915a289989c703c19403ba9d3464c4a17db838d Mon Sep 17 00:00:00 2001 From: Deividas Petraitis Date: Mon, 20 Jan 2025 17:02:33 +0200 Subject: [PATCH 09/12] BE-675 | coderabbitai comments --- domain/mocks/pool_mock.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/domain/mocks/pool_mock.go b/domain/mocks/pool_mock.go index d52308bd..f8478b73 100644 --- a/domain/mocks/pool_mock.go +++ b/domain/mocks/pool_mock.go @@ -155,7 +155,7 @@ func (mp *MockRoutablePool) CalculateTokenInByTokenOut(ctx context.Context, toke panic("not a balancer pool") } - return balancerPool.CalcOutAmtGivenIn(sdk.Context{}, sdk.NewCoins(tokenOut), mp.TokenInDenom, mp.SpreadFactor) + return balancerPool.CalcInAmtGivenOut(sdk.Context{}, sdk.NewCoins(tokenOut), mp.TokenInDenom, mp.SpreadFactor) } // String implements domain.RoutablePool. @@ -202,7 +202,7 @@ func (mp *MockRoutablePool) ChargeTakerFeeExactIn(tokenIn sdk.Coin) (tokenInAfte // ChargeTakerFeeExactOut implements domain.RoutablePool. func (mp *MockRoutablePool) ChargeTakerFeeExactOut(tokenOut sdk.Coin) (tokenInAfterFee sdk.Coin) { - return tokenOut.Sub(sdk.NewCoin(tokenOut.Denom, mp.TakerFee.Mul(tokenOut.Amount.ToLegacyDec()).TruncateInt())) + return tokenOut.Add(sdk.NewCoin(tokenOut.Denom, mp.TakerFee.Mul(tokenOut.Amount.ToLegacyDec()).TruncateInt())) } // GetTakerFee implements ingesttypes.PoolI. From abbaf369d21c30168d1aa9ee603b5e78b3efdb79 Mon Sep 17 00:00:00 2001 From: Deividas Petraitis Date: Tue, 21 Jan 2025 10:30:47 +0200 Subject: [PATCH 10/12] BE-675 | Propagate context --- domain/mocks/pool_mock.go | 10 +++++----- router/usecase/pools/routable_balancer_pool.go | 6 +++--- router/usecase/pools/routable_concentrated_pool.go | 2 +- router/usecase/pools/routable_stableswap_pool.go | 6 +++--- router/usecase/quote_test.go | 2 +- router/usecase/route/route_test.go | 4 ++-- 6 files changed, 15 insertions(+), 15 deletions(-) diff --git a/domain/mocks/pool_mock.go b/domain/mocks/pool_mock.go index f8478b73..f824b3c5 100644 --- a/domain/mocks/pool_mock.go +++ b/domain/mocks/pool_mock.go @@ -70,7 +70,7 @@ func (mp *MockRoutablePool) CalcSpotPrice(ctx context.Context, baseDenom string, return osmomath.OneBigDec(), nil } - spotPrice, err := mp.ChainPoolModel.SpotPrice(sdk.Context{}, quoteDenom, baseDenom) + spotPrice, err := mp.ChainPoolModel.SpotPrice(sdk.Context{}.WithContext(ctx), quoteDenom, baseDenom) if err != nil { return osmomath.BigDec{}, err } @@ -111,9 +111,9 @@ func (mp *MockRoutablePool) GetSQSPoolModel() ingesttypes.SQSPool { } // CalculateTokenOutByTokenIn implements routerusecase.RoutablePool. -func (mp *MockRoutablePool) CalculateTokenOutByTokenIn(_ctx context.Context, tokenIn sdk.Coin) (sdk.Coin, error) { +func (mp *MockRoutablePool) CalculateTokenOutByTokenIn(ctx context.Context, tokenIn sdk.Coin) (sdk.Coin, error) { if mp.CalculateTokenOutByTokenInFunc != nil { - return mp.CalculateTokenOutByTokenInFunc(_ctx, tokenIn) + return mp.CalculateTokenOutByTokenInFunc(ctx, tokenIn) } // We allow the ability to mock out the token out amount. @@ -131,7 +131,7 @@ func (mp *MockRoutablePool) CalculateTokenOutByTokenIn(_ctx context.Context, tok panic("not a balancer pool") } - return balancerPool.CalcOutAmtGivenIn(sdk.Context{}, sdk.NewCoins(tokenIn), mp.TokenOutDenom, mp.SpreadFactor) + return balancerPool.CalcOutAmtGivenIn(sdk.Context{}.WithContext(ctx), sdk.NewCoins(tokenIn), mp.TokenOutDenom, mp.SpreadFactor) } // CalculateTokenInByTokenOut implements routerusecase.RoutablePool. @@ -155,7 +155,7 @@ func (mp *MockRoutablePool) CalculateTokenInByTokenOut(ctx context.Context, toke panic("not a balancer pool") } - return balancerPool.CalcInAmtGivenOut(sdk.Context{}, sdk.NewCoins(tokenOut), mp.TokenInDenom, mp.SpreadFactor) + return balancerPool.CalcInAmtGivenOut(sdk.Context{}.WithContext(ctx), sdk.NewCoins(tokenOut), mp.TokenInDenom, mp.SpreadFactor) } // String implements domain.RoutablePool. diff --git a/router/usecase/pools/routable_balancer_pool.go b/router/usecase/pools/routable_balancer_pool.go index a5a537c9..9da659a3 100644 --- a/router/usecase/pools/routable_balancer_pool.go +++ b/router/usecase/pools/routable_balancer_pool.go @@ -27,7 +27,7 @@ type routableBalancerPoolImpl struct { // CalculateTokenOutByTokenIn implements RoutablePool. func (r *routableBalancerPoolImpl) CalculateTokenOutByTokenIn(ctx context.Context, tokenIn sdk.Coin) (sdk.Coin, error) { - tokenOut, err := r.ChainPool.CalcOutAmtGivenIn(sdk.Context{}, sdk.Coins{tokenIn}, r.TokenOutDenom, r.GetSpreadFactor()) + tokenOut, err := r.ChainPool.CalcOutAmtGivenIn(sdk.Context{}.WithContext(ctx), sdk.Coins{tokenIn}, r.TokenOutDenom, r.GetSpreadFactor()) if err != nil { return sdk.Coin{}, err } @@ -37,7 +37,7 @@ func (r *routableBalancerPoolImpl) CalculateTokenOutByTokenIn(ctx context.Contex // CalculateTokenInByTokenOut implements RoutablePool. func (r *routableBalancerPoolImpl) CalculateTokenInByTokenOut(ctx context.Context, tokenOut sdk.Coin) (sdk.Coin, error) { - tokenIn, err := r.ChainPool.CalcInAmtGivenOut(sdk.Context{}, sdk.Coins{tokenOut}, r.TokenInDenom, r.GetSpreadFactor()) + tokenIn, err := r.ChainPool.CalcInAmtGivenOut(sdk.Context{}.WithContext(ctx), sdk.Coins{tokenOut}, r.TokenInDenom, r.GetSpreadFactor()) if err != nil { return sdk.Coin{}, err } @@ -111,7 +111,7 @@ func (*routableBalancerPoolImpl) GetType() poolmanagertypes.PoolType { // CalcSpotPrice implements domain.RoutablePool. func (r *routableBalancerPoolImpl) CalcSpotPrice(ctx context.Context, baseDenom string, quoteDenom string) (osmomath.BigDec, error) { - spotPrice, err := r.ChainPool.SpotPrice(sdk.Context{}, quoteDenom, baseDenom) + spotPrice, err := r.ChainPool.SpotPrice(sdk.Context{}.WithContext(ctx), quoteDenom, baseDenom) if err != nil { return osmomath.BigDec{}, err } diff --git a/router/usecase/pools/routable_concentrated_pool.go b/router/usecase/pools/routable_concentrated_pool.go index 3148c05a..5775d31d 100644 --- a/router/usecase/pools/routable_concentrated_pool.go +++ b/router/usecase/pools/routable_concentrated_pool.go @@ -240,7 +240,7 @@ func (r *routableConcentratedPoolImpl) SetTokenOutDenom(tokenOutDenom string) { // CalcSpotPrice implements domain.RoutablePool. func (r *routableConcentratedPoolImpl) CalcSpotPrice(ctx context.Context, baseDenom string, quoteDenom string) (osmomath.BigDec, error) { - spotPrice, err := r.ChainPool.SpotPrice(sdk.Context{}, quoteDenom, baseDenom) + spotPrice, err := r.ChainPool.SpotPrice(sdk.Context{}.WithContext(ctx), quoteDenom, baseDenom) if err != nil { return osmomath.BigDec{}, err } diff --git a/router/usecase/pools/routable_stableswap_pool.go b/router/usecase/pools/routable_stableswap_pool.go index d5a8e80e..3ccab007 100644 --- a/router/usecase/pools/routable_stableswap_pool.go +++ b/router/usecase/pools/routable_stableswap_pool.go @@ -27,7 +27,7 @@ type routableStableswapPoolImpl struct { // CalculateTokenOutByTokenIn implements RoutablePool. func (r *routableStableswapPoolImpl) CalculateTokenOutByTokenIn(ctx context.Context, tokenIn sdk.Coin) (sdk.Coin, error) { - tokenOut, err := r.ChainPool.CalcOutAmtGivenIn(sdk.Context{}, sdk.Coins{tokenIn}, r.TokenOutDenom, r.GetSpreadFactor()) + tokenOut, err := r.ChainPool.CalcOutAmtGivenIn(sdk.Context{}.WithContext(ctx), sdk.Coins{tokenIn}, r.TokenOutDenom, r.GetSpreadFactor()) if err != nil { return sdk.Coin{}, err } @@ -37,7 +37,7 @@ func (r *routableStableswapPoolImpl) CalculateTokenOutByTokenIn(ctx context.Cont // CalculateTokenInByTokenOut implements RoutablePool. func (r *routableStableswapPoolImpl) CalculateTokenInByTokenOut(ctx context.Context, tokenOut sdk.Coin) (sdk.Coin, error) { - tokenIn, err := r.ChainPool.CalcInAmtGivenOut(sdk.Context{}, sdk.Coins{tokenOut}, r.TokenInDenom, r.GetSpreadFactor()) + tokenIn, err := r.ChainPool.CalcInAmtGivenOut(sdk.Context{}.WithContext(ctx), sdk.Coins{tokenOut}, r.TokenInDenom, r.GetSpreadFactor()) if err != nil { return sdk.Coin{}, err } @@ -111,7 +111,7 @@ func (r *routableStableswapPoolImpl) GetType() poolmanagertypes.PoolType { // CalcSpotPrice implements domain.RoutablePool. func (r *routableStableswapPoolImpl) CalcSpotPrice(ctx context.Context, baseDenom string, quoteDenom string) (osmomath.BigDec, error) { - spotPrice, err := r.ChainPool.SpotPrice(sdk.Context{}, quoteDenom, baseDenom) + spotPrice, err := r.ChainPool.SpotPrice(sdk.Context{}.WithContext(ctx), quoteDenom, baseDenom) if err != nil { return osmomath.BigDec{}, err } diff --git a/router/usecase/quote_test.go b/router/usecase/quote_test.go index bef5e5a6..6815a466 100644 --- a/router/usecase/quote_test.go +++ b/router/usecase/quote_test.go @@ -225,7 +225,7 @@ func (s *RouterTestSuite) TestPrepareResult_PriceImpact() { s.Require().NoError(err) // Compute spot price before swap - spotPriceInBaseOutQuote, err := poolOne.SpotPrice(sdk.Context{}, USDC, ETH) + spotPriceInBaseOutQuote, err := poolOne.SpotPrice(s.Ctx, USDC, ETH) s.Require().NoError(err) coinIn := sdk.NewCoin(ETH, totalInAmount) diff --git a/router/usecase/route/route_test.go b/router/usecase/route/route_test.go index 11c69e40..3e10b7dd 100644 --- a/router/usecase/route/route_test.go +++ b/router/usecase/route/route_test.go @@ -108,11 +108,11 @@ func (s *RouterTestSuite) TestPrepareResultPools() { defaultTokenIn := sdk.NewCoin(DenomTwo, DefaultAmt0) // Estimate balancer pool spot price - balancerPoolSpotPriceInOverOut, err := balancerPool.SpotPrice(sdk.Context{}, DenomOne, DenomTwo) + balancerPoolSpotPriceInOverOut, err := balancerPool.SpotPrice(s.Ctx, DenomOne, DenomTwo) s.Require().NoError(err) // Estimate balancer pool effective spot price - expectedAmountOutBalancer, err := balancerPool.CalcOutAmtGivenIn(sdk.Context{}, sdk.NewCoins(defaultTokenIn), DenomOne, DefaultSpreadFactor) + expectedAmountOutBalancer, err := balancerPool.CalcOutAmtGivenIn(s.Ctx, sdk.NewCoins(defaultTokenIn), DenomOne, DefaultSpreadFactor) s.Require().NoError(err) expectedEffectivePriceBalancerInOverOut := expectedAmountOutBalancer.Amount.ToLegacyDec().Quo(defaultTokenIn.Amount.ToLegacyDec()) From bd4a767580b12084479f46b202c338724563d0d1 Mon Sep 17 00:00:00 2001 From: Deividas Petraitis Date: Tue, 21 Jan 2025 10:34:43 +0200 Subject: [PATCH 11/12] BE-675 | Fix typos --- router/usecase/pools/routable_cw_transmuter_pool.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/router/usecase/pools/routable_cw_transmuter_pool.go b/router/usecase/pools/routable_cw_transmuter_pool.go index 601067c9..7ba6f362 100644 --- a/router/usecase/pools/routable_cw_transmuter_pool.go +++ b/router/usecase/pools/routable_cw_transmuter_pool.go @@ -56,7 +56,7 @@ func (r *routableTransmuterPoolImpl) GetSpreadFactor() math.LegacyDec { func (r *routableTransmuterPoolImpl) CalculateTokenOutByTokenIn(ctx context.Context, tokenIn sdk.Coin) (sdk.Coin, error) { poolType := r.GetType() - // Esnure that the pool is concentrated + // Ensure that the pool is concentrated if poolType != poolmanagertypes.CosmWasm { return sdk.Coin{}, domain.InvalidPoolTypeError{PoolType: int32(poolType)} } @@ -77,7 +77,7 @@ func (r *routableTransmuterPoolImpl) CalculateTokenOutByTokenIn(ctx context.Cont func (r *routableTransmuterPoolImpl) CalculateTokenInByTokenOut(ctx context.Context, tokenOut sdk.Coin) (sdk.Coin, error) { poolType := r.GetType() - // Esnure that the pool is concentrated + // Ensure that the pool is concentrated if poolType != poolmanagertypes.CosmWasm { return sdk.Coin{}, domain.InvalidPoolTypeError{PoolType: int32(poolType)} } From 6ae40cbf368bd18989db492b2ca449bf91bacc0e Mon Sep 17 00:00:00 2001 From: Deividas Petraitis Date: Tue, 21 Jan 2025 10:37:39 +0200 Subject: [PATCH 12/12] BE-675 | Update validateTransmuterBalance parameter name --- router/usecase/pools/routable_cw_transmuter_pool.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/router/usecase/pools/routable_cw_transmuter_pool.go b/router/usecase/pools/routable_cw_transmuter_pool.go index 7ba6f362..e90a4df2 100644 --- a/router/usecase/pools/routable_cw_transmuter_pool.go +++ b/router/usecase/pools/routable_cw_transmuter_pool.go @@ -121,15 +121,15 @@ func (r *routableTransmuterPoolImpl) ChargeTakerFeeExactOut(tokenIn sdk.Coin) (i return tokenInAfterTakerFee } -// validateTransmuterBalance validates that the balance of the denom to validate is greater than the token in amount. +// validateTransmuterBalance validates that the balance of the denom to validate is greater than the token amount. // Returns nil on success, error otherwise. -func validateTransmuterBalance(tokenInAmount osmomath.Int, balances sdk.Coins, denomToValidate string) error { +func validateTransmuterBalance(tokenAmount osmomath.Int, balances sdk.Coins, denomToValidate string) error { balanceToValidate := balances.AmountOf(denomToValidate) - if tokenInAmount.GT(balanceToValidate) { + if tokenAmount.GT(balanceToValidate) { return domain.TransmuterInsufficientBalanceError{ Denom: denomToValidate, BalanceAmount: balanceToValidate.String(), - Amount: tokenInAmount.String(), + Amount: tokenAmount.String(), } }