Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

BE-675 | InGivenOut APIs for balancer, stableswap, transmuter pools #603

Merged
merged 12 commits into from
Jan 21, 2025
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions domain/mocks/pool_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)
}
deividaspetraitis marked this conversation as resolved.
Show resolved Hide resolved

// String implements domain.RoutablePool.
func (*MockRoutablePool) String() string {
panic("unimplemented")
Expand Down Expand Up @@ -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()))
}
deividaspetraitis marked this conversation as resolved.
Show resolved Hide resolved

// GetTakerFee implements ingesttypes.PoolI.
func (mp *MockRoutablePool) GetTakerFee() math.LegacyDec {
return mp.TakerFee
Expand Down
2 changes: 1 addition & 1 deletion domain/mocks/pools_usecase_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
2 changes: 2 additions & 0 deletions domain/routable_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions ingest/types/candidate_route.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package types
// candidate pool to be used for routing.
type CandidatePool struct {
ID uint64
TokenInDenom string
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Note: Adding new TokenInDenom field at this point led to more elegant and simpler to understand implementation than alternative appoach by simply renaming existing field to be used for either ExactIn or ExactOut.

Copy link
Member

Choose a reason for hiding this comment

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

Please add a note that only one must be set at a time

TokenOutDenom string
}

Expand Down
6 changes: 3 additions & 3 deletions pools/usecase/pools_usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}
Expand Down
12 changes: 6 additions & 6 deletions pools/usecase/pools_usecase_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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),
},
},
},
Expand All @@ -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),
},
},
},
Expand Down Expand Up @@ -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),
},
},
},
Expand Down Expand Up @@ -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
}
Expand Down
10 changes: 7 additions & 3 deletions router/usecase/pools/pool_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand All @@ -37,6 +37,7 @@ func NewRoutablePool(pool ingesttypes.PoolI, tokenOutDenom string, takerFee osmo
return &routableConcentratedPoolImpl{
ChainPool: concentratedPool,
TickModel: tickModel,
TokenInDenom: tokenInDenom,
TokenOutDenom: tokenOutDenom,
Comment on lines +40 to 41
Copy link
Member

Choose a reason for hiding this comment

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

This TokenInDenom and TokenOutDenom makes sense in the context of this PR but I am worried that if we were to look at it from a higher level, it would be confusing and unclear that only one of can be set at a time. Is there a way we could enhance the abstraction to reflect the restriction?

Copy link
Member

Choose a reason for hiding this comment

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

(Since I don't have a specific recommendation and, if you've already spent meaningful time on thinking through this, please feel free not to block on this comment)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Actually both will be set since when we are requesting a quote we specify denoms for both tokenIn and tokenOut, however depending on swap method we specify amount only for one of those.

TakerFee: takerFee,
}, nil
Expand All @@ -56,6 +57,7 @@ func NewRoutablePool(pool ingesttypes.PoolI, tokenOutDenom string, takerFee osmo

return &routableBalancerPoolImpl{
ChainPool: balancerPool,
TokenInDenom: tokenInDenom,
TokenOutDenom: tokenOutDenom,
TakerFee: takerFee,
}, nil
Expand All @@ -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()

Expand All @@ -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,
Expand Down
25 changes: 21 additions & 4 deletions router/usecase/pools/routable_balancer_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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())
deividaspetraitis marked this conversation as resolved.
Show resolved Hide resolved
if err != nil {
return sdk.Coin{}, err
}

return tokenIn, nil
}

// GetTokenOutDenom implements RoutablePool.
func (r *routableBalancerPoolImpl) GetTokenOutDenom() string {
return r.TokenOutDenom
Expand All @@ -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 out and returns the token out after the fee has been charged.
func (r *routableBalancerPoolImpl) ChargeTakerFeeExactOut(tokenIn sdk.Coin) (tokenInAfterFee sdk.Coin) {
tokenInAfterTakerFee, _ := poolmanager.CalcTakerFeeExactOut(tokenIn, r.TakerFee)
return tokenInAfterTakerFee
}
deividaspetraitis marked this conversation as resolved.
Show resolved Hide resolved

// GetTakerFee implements domain.RoutablePool.
func (r *routableBalancerPoolImpl) GetTakerFee() math.LegacyDec {
return r.TakerFee
Expand Down
22 changes: 17 additions & 5 deletions router/usecase/pools/routable_concentrated_pool.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package pools

import (
"context"
"errors"
"fmt"

"cosmossdk.io/math"
Expand All @@ -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)
Expand Down Expand Up @@ -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")
}
deividaspetraitis marked this conversation as resolved.
Show resolved Hide resolved

// GetTokenOutDenom implements RoutablePool.
func (r *routableConcentratedPoolImpl) GetTokenOutDenom() string {
return r.TokenOutDenom
Expand All @@ -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{}
}
deividaspetraitis marked this conversation as resolved.
Show resolved Hide resolved

// SetTokenInDenom implements domain.RoutablePool.
func (r *routableConcentratedPoolImpl) SetTokenInDenom(tokenInDenom string) {
r.TokenInDenom = tokenInDenom
Expand Down
2 changes: 1 addition & 1 deletion router/usecase/pools/routable_concentrated_pool_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Loading
Loading