Skip to content

Commit

Permalink
BE-695 | Implement PrepareResultPoolsInGivenOut API
Browse files Browse the repository at this point in the history
Implements `PrepareResultPoolsInGivenOut` API for computing exact
amount out quotes.

This is smaller chunk of bigger PR-607 ( #607 ) implementing computing exact
amount out quotes.
  • Loading branch information
deividaspetraitis committed Feb 17, 2025
1 parent 0176860 commit 8991038
Show file tree
Hide file tree
Showing 3 changed files with 94 additions and 14 deletions.
29 changes: 15 additions & 14 deletions domain/mocks/route_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,15 +10,15 @@ import (
)

type RouteMock struct {
CalculateTokenOutByTokenInFunc func(ctx context.Context, tokenIn types.Coin) (types.Coin, error)
CalculateTokenInByTokenOutFunc func(ctx context.Context, tokenOut types.Coin) (types.Coin, error)
ContainsGeneralizedCosmWasmPoolFunc func() bool
GetPoolsFunc func() []domain.RoutablePool
GetTokenOutDenomFunc func() string
GetTokenInDenomFunc func() string
PrepareResultPoolsExactAmountInFunc func(ctx context.Context, tokenIn types.Coin, logger log.Logger) ([]domain.RoutablePool, math.LegacyDec, math.LegacyDec, error)
PrepareResultPoolsExactAmountOutFunc func(ctx context.Context, tokenOut types.Coin, logger log.Logger) ([]domain.RoutablePool, math.LegacyDec, math.LegacyDec, error)
StringFunc func() string
CalculateTokenOutByTokenInFunc func(ctx context.Context, tokenIn types.Coin) (types.Coin, error)
CalculateTokenInByTokenOutFunc func(ctx context.Context, tokenOut types.Coin) (types.Coin, error)
ContainsGeneralizedCosmWasmPoolFunc func() bool
GetPoolsFunc func() []domain.RoutablePool
GetTokenOutDenomFunc func() string
GetTokenInDenomFunc func() string
PrepareResultPoolsOutGivenInFunc func(ctx context.Context, tokenIn types.Coin, logger log.Logger) ([]domain.RoutablePool, math.LegacyDec, math.LegacyDec, error)
PrepareResultPoolsInGivenOutFunc func(ctx context.Context, tokenOut types.Coin, logger log.Logger) ([]domain.RoutablePool, math.LegacyDec, math.LegacyDec, error)
StringFunc func() string

GetAmountInFunc func() math.Int
GetAmountOutFunc func() math.Int
Expand Down Expand Up @@ -80,15 +80,16 @@ func (r *RouteMock) GetTokenInDenom() string {

// PrepareResultPoolsOutGivenIn implements domain.Route.
func (r *RouteMock) PrepareResultPoolsOutGivenIn(ctx context.Context, tokenIn types.Coin, logger log.Logger) ([]domain.RoutablePool, math.LegacyDec, math.LegacyDec, error) {
if r.PrepareResultPoolsExactAmountInFunc != nil {
return r.PrepareResultPoolsExactAmountInFunc(ctx, tokenIn, logger)
if r.PrepareResultPoolsOutGivenInFunc != nil {
return r.PrepareResultPoolsOutGivenInFunc(ctx, tokenIn, logger)
}

panic("unimplemented")
}
func (r *RouteMock) PrepareResultPoolsExactAmountOut(ctx context.Context, tokenIn types.Coin, logger log.Logger) ([]domain.RoutablePool, math.LegacyDec, math.LegacyDec, error) {
if r.PrepareResultPoolsExactAmountOutFunc != nil {
return r.PrepareResultPoolsExactAmountOutFunc(ctx, tokenIn, logger)

func (r *RouteMock) PrepareResultPoolsInGivenOut(ctx context.Context, tokenIn types.Coin, logger log.Logger) ([]domain.RoutablePool, math.LegacyDec, math.LegacyDec, error) {
if r.PrepareResultPoolsInGivenOutFunc != nil {
return r.PrepareResultPoolsInGivenOutFunc(ctx, tokenIn, logger)
}

panic("unimplemented")
Expand Down
10 changes: 10 additions & 0 deletions domain/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,16 @@ type Route interface {
// The token in is the base token and the token out is the quote token.
PrepareResultPoolsOutGivenIn(ctx context.Context, tokenIn sdk.Coin, logger log.Logger) ([]RoutablePool, osmomath.Dec, osmomath.Dec, error)

// PrepareResultPoolsInGivenOut strips away unnecessary fields
// from each pool in the route,
// leaving only the data needed by client
// Runs the quote logic one final time to compute the effective spot price.
// Note that it mutates the route.
// Computes the spot price of the route.
// Returns the spot price before swap and effective spot price.
// The token in is the base token and the token out is the quote token.
PrepareResultPoolsInGivenOut(ctx context.Context, tokenOut sdk.Coin, logger log.Logger) ([]RoutablePool, osmomath.Dec, osmomath.Dec, error)

String() string
}

Expand Down
69 changes: 69 additions & 0 deletions router/usecase/route/route.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,75 @@ func (r RouteImpl) PrepareResultPoolsOutGivenIn(ctx context.Context, tokenIn sdk
return newPools, routeSpotPriceInBaseOutQuote, effectiveSpotPriceInBaseOutQuote, nil
}

// PrepareResultPoolsOutGivenIn implements domain.Route.
// Strips away unnecessary fields from each pool in the route,
// leaving only the data needed by client
// The following are the list of fields that are returned to the client in each pool:
// - ID
// - Type
// - Balances
// - Spread Factor
// - Token Out Denom
// - Taker Fee
// Note that it mutates the route.
// Returns spot price before swap and the effective spot price
// with token out as base and token in as quote.
func (r RouteImpl) PrepareResultPoolsInGivenOut(ctx context.Context, tokenOut sdk.Coin, logger log.Logger) ([]domain.RoutablePool, osmomath.Dec, osmomath.Dec, error) {
var (
routeSpotPriceOutBaseInQuote = osmomath.OneDec()
effectiveSpotPriceOutBaseInQuote = osmomath.OneDec()
)

newPools := make([]domain.RoutablePool, 0, len(r.Pools))

for _, pool := range r.Pools {
// Compute spot price before swap.
spotPriceOutBaseInQuote, err := pool.CalcSpotPrice(ctx, tokenOut.Denom, pool.GetTokenInDenom())
if err != nil {
logger.Error("failed to calculate spot price for pool", zap.Error(err))

// We don't want to fail the entire quote if one pool fails to calculate spot price.
// This might cause miestimaions downsream but we a
spotPriceOutBaseInQuote = osmomath.ZeroBigDec()

// Increment the counter for the error
spotPriceErrorResultCounter.WithLabelValues(
tokenOut.Denom,
pool.GetTokenOutDenom(),
r.Pools[len(r.Pools)-1].GetTokenOutDenom(),
).Inc()
}

tokenIn, err := pool.CalculateTokenInByTokenOut(ctx, tokenOut)
if err != nil {
return nil, osmomath.Dec{}, osmomath.Dec{}, err
}

// Charge taker fee
tokenIn = pool.ChargeTakerFeeExactOut(tokenIn)

// Update effective spot price
effectiveSpotPriceOutBaseInQuote.MulMut(tokenIn.Amount.ToLegacyDec().QuoMut(tokenOut.Amount.ToLegacyDec()))

// Note, in the future we may want to increase the precision of the spot price
routeSpotPriceOutBaseInQuote.MulMut(spotPriceOutBaseInQuote.Dec())

newPool := pools.NewExactAmountOutRoutableResultPool(
pool.GetId(),
pool.GetType(),
pool.GetSpreadFactor(),
pool.GetTokenInDenom(),
pool.GetTakerFee(),
pool.GetCodeID(),
)

newPools = append(newPools, newPool)

tokenOut = tokenIn
}
return newPools, routeSpotPriceOutBaseInQuote, effectiveSpotPriceOutBaseInQuote, nil
}

// GetPools implements Route.
func (r *RouteImpl) GetPools() []domain.RoutablePool {
return r.Pools
Expand Down

0 comments on commit 8991038

Please sign in to comment.