Skip to content

Commit

Permalink
feat: format spot price in quotes (#100)
Browse files Browse the repository at this point in the history
  • Loading branch information
p0mvn authored Mar 9, 2024
1 parent e0b75a8 commit 06fdcc6
Show file tree
Hide file tree
Showing 7 changed files with 70 additions and 16 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,10 @@ Ref: https://keepachangelog.com/en/1.0.0/

# Changelog

## v0.7.2

* [#100](https://github.com/osmosis-labs/sqs/pull/100) Format in over out spot price in quotes.

## v0.7.0

* [#99](https://github.com/osmosis-labs/sqs/pull/99) Move candidate routes cache out of Redis. Remove route overwrite
Expand Down
3 changes: 3 additions & 0 deletions domain/mvc/tokens.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ type TokensUsecase interface {
// A clone should be made for any mutative operation.
GetChainScalingFactorByDenomMut(ctx context.Context, denom string) (osmomath.Dec, error)

// GetSpotPriceScalingFactorByDenomMut returns the scaling factor for spot price.
GetSpotPriceScalingFactorByDenom(ctx context.Context, baseDenom, quoteDenom string) (osmomath.Dec, error)

// GetPrices returns prices for all given base and quote denoms given a pricing strategy or, otherwise, error, if any.
// The outer map consists of base denoms as keys.
// The inner map consists of quote denoms as keys.
Expand Down
5 changes: 4 additions & 1 deletion domain/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,10 @@ type Quote interface {

// PrepareResult mutates the quote to prepare
// it with the data formatted for output to the client.
PrepareResult(ctx context.Context) ([]SplitRoute, osmomath.Dec)
// scalingFactor is the spot price scaling factor according to chain precision.
// scalingFactor of zero is a valid value. It might occur if we do not have precision information
// for the tokens. In that case, we invalidate spot price by setting it to zero.
PrepareResult(ctx context.Context, scalingFactor osmomath.Dec) ([]SplitRoute, osmomath.Dec)

String() string
}
Expand Down
30 changes: 26 additions & 4 deletions router/delivery/http/router_handler.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package http

import (
"context"
"errors"
"net/http"
"strconv"
Expand All @@ -9,6 +10,7 @@ import (

sdk "github.com/cosmos/cosmos-sdk/types"

"github.com/osmosis-labs/osmosis/osmomath"
"github.com/osmosis-labs/sqs/domain"
"github.com/osmosis-labs/sqs/domain/mvc"
"github.com/osmosis-labs/sqs/log"
Expand Down Expand Up @@ -92,7 +94,9 @@ func (a *RouterHandler) GetOptimalQuote(c echo.Context) (err error) {
return c.JSON(domain.GetStatusCode(err), domain.ResponseError{Message: err.Error()})
}

quote.PrepareResult(ctx)
scalingFactor := a.getSpotPriceScalingFactor(ctx, tokenInDenom, tokenOutDenom)

quote.PrepareResult(ctx, scalingFactor)

return c.JSON(http.StatusOK, quote)
}
Expand All @@ -111,7 +115,9 @@ func (a *RouterHandler) GetBestSingleRouteQuote(c echo.Context) error {
return c.JSON(domain.GetStatusCode(err), domain.ResponseError{Message: err.Error()})
}

quote.PrepareResult(ctx)
scalingFactor := a.getSpotPriceScalingFactor(ctx, tokenIn.Denom, tokenOutDenom)

quote.PrepareResult(ctx, scalingFactor)

return c.JSON(http.StatusOK, quote)
}
Expand Down Expand Up @@ -142,7 +148,9 @@ func (a *RouterHandler) GetCustomQuote(c echo.Context) error {
return c.JSON(domain.GetStatusCode(err), domain.ResponseError{Message: err.Error()})
}

quote.PrepareResult(ctx)
scalingFactor := a.getSpotPriceScalingFactor(ctx, tokenIn.Denom, tokenOutDenom)

quote.PrepareResult(ctx, scalingFactor)

return c.JSON(http.StatusOK, quote)
}
Expand Down Expand Up @@ -173,7 +181,9 @@ func (a *RouterHandler) GetDirectCustomQuote(c echo.Context) error {
return c.JSON(domain.GetStatusCode(err), domain.ResponseError{Message: err.Error()})
}

quote.PrepareResult(ctx)
scalingFactor := a.getSpotPriceScalingFactor(ctx, tokenIn.Denom, tokenOutDenom)

quote.PrepareResult(ctx, scalingFactor)

return c.JSON(http.StatusOK, quote)
}
Expand Down Expand Up @@ -344,6 +354,18 @@ func getValidRoutingParameters(c echo.Context) (string, sdk.Coin, error) {
return tokenOutStr, tokenIn, nil
}

// getSpotPriceScalingFactor returns the spot price scaling factor for a given tokenIn and tokenOutDenom.
func (a *RouterHandler) getSpotPriceScalingFactor(ctx context.Context, tokenInDenom, tokenOutDenom string) osmomath.Dec {
scalingFactor, err := a.TUsecase.GetSpotPriceScalingFactorByDenom(ctx, tokenOutDenom, tokenInDenom)
if err != nil {
// Note that we do not fail the quote if scaling factor fetching fails.
// Instead, we simply set it to zero to validate future calculations downstream.
scalingFactor = sdk.ZeroDec()
}

return scalingFactor
}

func getValidTokenInStr(c echo.Context) (string, error) {
tokenInStr := c.QueryParam("tokenIn")

Expand Down
14 changes: 8 additions & 6 deletions router/usecase/quote.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,12 @@ import (
)

type quoteImpl struct {
AmountIn sdk.Coin "json:\"amount_in\""
AmountOut osmomath.Int "json:\"amount_out\""
Route []domain.SplitRoute "json:\"route\""
EffectiveFee osmomath.Dec "json:\"effective_fee\""
PriceImpact osmomath.Dec "json:\"price_impact\""
AmountIn sdk.Coin "json:\"amount_in\""
AmountOut osmomath.Int "json:\"amount_out\""
Route []domain.SplitRoute "json:\"route\""
EffectiveFee osmomath.Dec "json:\"effective_fee\""
PriceImpact osmomath.Dec "json:\"price_impact\""
InOutSpotPrice osmomath.Dec "json:\"in_out_spot_price\""
}

var (
Expand All @@ -35,7 +36,7 @@ var _ domain.Quote = &quoteImpl{}
// Computes an effective spread factor from all routes.
//
// Returns the updated route and the effective spread factor.
func (q *quoteImpl) PrepareResult(ctx context.Context) ([]domain.SplitRoute, osmomath.Dec) {
func (q *quoteImpl) PrepareResult(ctx context.Context, scalingFactor osmomath.Dec) ([]domain.SplitRoute, osmomath.Dec) {
totalAmountIn := q.AmountIn.Amount.ToLegacyDec()
totalFeeAcrossRoutes := osmomath.ZeroDec()

Expand Down Expand Up @@ -89,6 +90,7 @@ func (q *quoteImpl) PrepareResult(ctx context.Context) ([]domain.SplitRoute, osm

q.EffectiveFee = totalFeeAcrossRoutes
q.Route = resultRoutes
q.InOutSpotPrice = totalSpotPriceInOverOut.Mul(scalingFactor)

return q.Route, q.EffectiveFee
}
Expand Down
11 changes: 6 additions & 5 deletions router/usecase/quote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ import (
)

var (
defaultAmount = sdk.NewInt(100_000_00)
totalInAmount = defaultAmount
totalOutAmount = defaultAmount.MulRaw(4)
defaultAmount = sdk.NewInt(100_000_00)
totalInAmount = defaultAmount
totalOutAmount = defaultAmount.MulRaw(4)
defaultSpotPriceScalingFactor = osmomath.OneDec()
)

// TestPrepareResult prepares the result of the quote for output to the client.
Expand Down Expand Up @@ -226,7 +227,7 @@ func (s *RouterTestSuite) TestPrepareResult() {
expectedEffectiveSpreadFactor := expectedRouteOneFee.Add(expectedRouteTwoFee)

// System under test
routes, effectiveSpreadFactor := testQuote.PrepareResult(context.TODO())
routes, effectiveSpreadFactor := testQuote.PrepareResult(context.TODO(), defaultSpotPriceScalingFactor)

// Validate routes.
s.validateRoutes(expectedRoutes, routes)
Expand Down Expand Up @@ -301,7 +302,7 @@ func (s *RouterTestSuite) TestPrepareResult_PriceImpact() {
}

// System under test.
testQuote.PrepareResult(context.TODO())
testQuote.PrepareResult(context.TODO(), defaultSpotPriceScalingFactor)

// Validate price impact.
s.Require().Equal(expectedPriceImpact.String(), testQuote.GetPriceImpact().String())
Expand Down
19 changes: 19 additions & 0 deletions tokens/usecase/tokens_usecase.go
Original file line number Diff line number Diff line change
Expand Up @@ -326,3 +326,22 @@ func GetTokensFromChainRegistry(chainRegistryAssetsFileURL string) (map[string]d

return tokensByChainDenom, nil
}

// GetSpotPriceScalingFactorByDenomMut implements mvc.TokensUsecase.
func (t *tokensUseCase) GetSpotPriceScalingFactorByDenom(ctx context.Context, baseDenom string, quoteDenom string) (osmomath.Dec, error) {
baseScalingFactor, err := t.GetChainScalingFactorByDenomMut(ctx, baseDenom)
if err != nil {
return osmomath.Dec{}, err
}

quoteScalingFactor, err := t.GetChainScalingFactorByDenomMut(ctx, quoteDenom)
if err != nil {
return osmomath.Dec{}, err
}

if quoteScalingFactor.IsZero() {
return osmomath.Dec{}, fmt.Errorf("scaling factor for quote denom (%s) is zero", quoteDenom)
}

return baseScalingFactor.Quo(quoteScalingFactor), nil
}

0 comments on commit 06fdcc6

Please sign in to comment.