From df7223f1143813c3cc5e347662bf55d20e710784 Mon Sep 17 00:00:00 2001 From: Antonio Pitasi Date: Tue, 14 Jun 2022 16:04:05 +0200 Subject: [PATCH 01/14] chore: cleanup unused `cached` pkg and old liquidity module The liquidity module conflicts with the new Crescent gRPC. --- api/cached/api.go | 140 ------------------------------------------- api/router/router.go | 5 -- docs/swagger_gen.go | 1 - 3 files changed, 146 deletions(-) delete mode 100644 api/cached/api.go diff --git a/api/cached/api.go b/api/cached/api.go deleted file mode 100644 index e5f3e1e7..00000000 --- a/api/cached/api.go +++ /dev/null @@ -1,140 +0,0 @@ -package cached - -import ( - "fmt" - "net/http" - - "github.com/emerishq/demeris-api-server/api/database" - "github.com/emerishq/demeris-api-server/lib/apierrors" - "github.com/emerishq/emeris-utils/store" - "github.com/gin-gonic/gin" - _ "github.com/gravity-devs/liquidity/x/liquidity/types" -) - -func Register(router *gin.Engine, db *database.Database, s *store.Store) { - group := router.Group("/cached/cosmos") - - group.GET("/liquidity/v1beta1/pools", getPools(db, s)) - group.GET("/liquidity/v1beta1/params", getParams(db, s)) - group.GET("/bank/v1beta1/supply", getSupply(db, s)) - group.GET("/node_info", getNodeInfo(db, s)) -} - -// getPools returns the of all pools. -// @Summary Gets pools info. -// @Tags pools -// @ID pools -// @Description Gets info of all pools.`10 -// @Produce json -// @Success 200 {object} types.QueryLiquidityPoolsResponse -// @Failure 500,403 {object} apierrors.UserFacingError -// @Router /cosmos/liquidity/v1beta1/pools [get] -func getPools(db *database.Database, s *store.Store) gin.HandlerFunc { - return func(c *gin.Context) { - res, err := s.GetPools() - if err != nil { - e := apierrors.New( - "pools", - fmt.Sprintf("cannot retrieve pools"), - http.StatusBadRequest, - ).WithLogContext( - fmt.Errorf("cannot query pools: %w", err), - ) - _ = c.Error(e) - - return - } - - c.Data(http.StatusOK, gin.MIMEJSON, res) - } -} - -// getParams returns the params of liquidity module. -// @Summary Gets params of liquidity module. -// @Tags params -// @ID params -// @Description Gets params of liquidity module. -// @Produce json -// @Success 200 {object} types.QueryParamsResponse -// @Failure 500,403 {object} apierrors.UserFacingError -// @Router /cosmos/liquidity/v1beta1/params [get] -func getParams(db *database.Database, s *store.Store) gin.HandlerFunc { - return func(c *gin.Context) { - - res, err := s.GetParams() - if err != nil { - e := apierrors.New( - "params", - fmt.Sprintf("cannot retrieve params"), - http.StatusBadRequest, - ).WithLogContext( - fmt.Errorf("cannot retrieve params: %w", err), - ) - _ = c.Error(e) - - return - } - - c.Data(http.StatusOK, gin.MIMEJSON, res) - } -} - -// getSupply returns the total supply. -// @Summary Gets total supply of cosmos-hub -// @Tags supply -// @ID total-supply -// @Description Gets total supply of cosmos hub. -// @Produce json -// @Success 200 {object} types.QueryTotalSupplyResponse -// @Failure 500,403 {object} apierrors.UserFacingError -// @Router / [get] -func getSupply(db *database.Database, s *store.Store) gin.HandlerFunc { - return func(c *gin.Context) { - - res, err := s.GetSupply() - if err != nil { - e := apierrors.New( - "supply", - fmt.Sprintf("cannot retrieve total supply"), - http.StatusBadRequest, - ).WithLogContext( - fmt.Errorf("cannot retrieve total supply: %w", err), - ) - _ = c.Error(e) - - return - } - - c.Data(http.StatusOK, gin.MIMEJSON, res) - } -} - -// getNodeInfo returns output of Cosmos's /node_info endpoint. -// @Summary returns output of Cosmos's /node_info endpoint -// @Tags nodeinfo -// @ID node_info -// @Description returns output of Cosmos's /node_info endpoint -// @Produce json -// @Success 200 {object} types.QueryTotalSupplyResponse -// @Failure 500,403 {object} apierrors.UserFacingError -// @Router / [get] -func getNodeInfo(db *database.Database, s *store.Store) gin.HandlerFunc { - return func(c *gin.Context) { - - res, err := s.GetNodeInfo() - if err != nil { - e := apierrors.New( - "node_info", - fmt.Sprintf("cannot retrieve node_info"), - http.StatusBadRequest, - ).WithLogContext( - fmt.Errorf("cannot retrieve node_info: %w", err), - ) - _ = c.Error(e) - - return - } - - c.Data(http.StatusOK, gin.MIMEJSON, res) - } -} diff --git a/api/router/router.go b/api/router/router.go index 839a51aa..2eefcc61 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -5,7 +5,6 @@ import ( "fmt" "github.com/emerishq/demeris-api-server/api/block" - "github.com/emerishq/demeris-api-server/api/cached" "github.com/emerishq/demeris-api-server/api/liquidity" "github.com/emerishq/demeris-api-server/lib/apierrors" "github.com/emerishq/demeris-api-server/lib/stringcache" @@ -161,8 +160,4 @@ func registerRoutes(engine *gin.Engine, db *database.Database, s *store.Store, // @tag.name liquidity // @tag.description pool-related endpoints liquidity.Register(engine, db, s) - - // @tag.name cached - // @tag.description cached data endpoints - cached.Register(engine, db, s) } diff --git a/docs/swagger_gen.go b/docs/swagger_gen.go index f6e53890..289ee1e1 100644 --- a/docs/swagger_gen.go +++ b/docs/swagger_gen.go @@ -24,7 +24,6 @@ import ( _ "github.com/emerishq/demeris-backend-models/tracelistener" _ "github.com/emerishq/emeris-utils/exported/sdktypes" _ "github.com/emerishq/emeris-utils/store" - _ "github.com/gravity-devs/liquidity/x/liquidity/types" _ "github.com/swaggo/swag" _ "github.com/tendermint/tendermint/proto/tendermint/version" _ "github.com/tendermint/tendermint/rpc/core/types" From 7bf53b52bce016f75df8d9916e5cdf0725166596 Mon Sep 17 00:00:00 2001 From: Antonio Pitasi Date: Tue, 14 Jun 2022 16:22:06 +0200 Subject: [PATCH 02/14] fix: use `Int` and `Dec` from `cosmos-sdk` pkg --- api/chains/chains_test.go | 5 ++--- api/chains/ports.go | 2 +- api/chains/ports_mocks_test.go | 6 +++--- usecase/sdkservice.go | 2 +- usecase/staking.go | 2 +- 5 files changed, 8 insertions(+), 9 deletions(-) diff --git a/api/chains/chains_test.go b/api/chains/chains_test.go index 878f492d..c29b3c70 100644 --- a/api/chains/chains_test.go +++ b/api/chains/chains_test.go @@ -12,14 +12,13 @@ import ( "testing" "time" + sdktypes "github.com/cosmos/cosmos-sdk/types" "github.com/emerishq/demeris-api-server/api/chains" utils "github.com/emerishq/demeris-api-server/api/test_utils" "github.com/emerishq/demeris-api-server/lib/stringcache" - "github.com/emerishq/emeris-utils/exported/sdktypes" + "github.com/emerishq/demeris-backend-models/cns" "github.com/emerishq/emeris-utils/logging" "github.com/gin-gonic/gin" - - "github.com/emerishq/demeris-backend-models/cns" "github.com/google/go-cmp/cmp" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/api/chains/ports.go b/api/chains/ports.go index 3614ac38..df19e8f9 100644 --- a/api/chains/ports.go +++ b/api/chains/ports.go @@ -4,8 +4,8 @@ import ( "context" "time" + sdktypes "github.com/cosmos/cosmos-sdk/types" "github.com/emerishq/demeris-backend-models/cns" - "github.com/emerishq/emeris-utils/exported/sdktypes" ) //go:generate mockgen -package chains_test -source ports.go -destination ports_mocks_test.go diff --git a/api/chains/ports_mocks_test.go b/api/chains/ports_mocks_test.go index 10d51864..41167019 100644 --- a/api/chains/ports_mocks_test.go +++ b/api/chains/ports_mocks_test.go @@ -9,8 +9,8 @@ import ( reflect "reflect" time "time" + types "github.com/cosmos/cosmos-sdk/types" cns "github.com/emerishq/demeris-backend-models/cns" - sdktypes "github.com/emerishq/emeris-utils/exported/sdktypes" gomock "github.com/golang/mock/gomock" ) @@ -38,10 +38,10 @@ func (m *MockApp) EXPECT() *MockAppMockRecorder { } // StakingAPR mocks base method. -func (m *MockApp) StakingAPR(ctx context.Context, chain cns.Chain) (sdktypes.Dec, error) { +func (m *MockApp) StakingAPR(ctx context.Context, chain cns.Chain) (types.Dec, error) { m.ctrl.T.Helper() ret := m.ctrl.Call(m, "StakingAPR", ctx, chain) - ret0, _ := ret[0].(sdktypes.Dec) + ret0, _ := ret[0].(types.Dec) ret1, _ := ret[1].(error) return ret0, ret1 } diff --git a/usecase/sdkservice.go b/usecase/sdkservice.go index e17f506e..93e7c793 100644 --- a/usecase/sdkservice.go +++ b/usecase/sdkservice.go @@ -5,7 +5,7 @@ import ( "encoding/json" "time" - "github.com/emerishq/emeris-utils/exported/sdktypes" + sdktypes "github.com/cosmos/cosmos-sdk/types" sdkutilities "github.com/emerishq/sdk-service-meta/gen/sdk_utilities" ) diff --git a/usecase/staking.go b/usecase/staking.go index fecbc485..9e24b851 100644 --- a/usecase/staking.go +++ b/usecase/staking.go @@ -7,9 +7,9 @@ import ( "strings" "time" + sdktypes "github.com/cosmos/cosmos-sdk/types" "github.com/emerishq/demeris-api-server/lib/apierrors" "github.com/emerishq/demeris-backend-models/cns" - "github.com/emerishq/emeris-utils/exported/sdktypes" sdkutilities "github.com/emerishq/sdk-service-meta/gen/sdk_utilities" ) From 102c9f25c179b66cce3dc582d2f82bca13d39c0d Mon Sep 17 00:00:00 2001 From: Antonio Pitasi Date: Tue, 14 Jun 2022 16:23:39 +0200 Subject: [PATCH 03/14] feat(poclient): use `context` in http requests --- lib/poclient/poclient.go | 19 ++++++++++++++++--- lib/poclient/poclient_test.go | 2 +- 2 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/poclient/poclient.go b/lib/poclient/poclient.go index 9c19d395..894bc522 100644 --- a/lib/poclient/poclient.go +++ b/lib/poclient/poclient.go @@ -1,7 +1,9 @@ package poclient import ( + "context" "encoding/json" + "errors" "fmt" "io/ioutil" "net/http" @@ -30,6 +32,7 @@ type PricesResponse struct { type POClient struct { PriceOracleBaseURL string cache *gocache.Cache + httpClient http.Client } func NewPOClient(priceOracleBaseURL string) POClient { @@ -37,11 +40,14 @@ func NewPOClient(priceOracleBaseURL string) POClient { return POClient{ PriceOracleBaseURL: priceOracleBaseURL, cache: cache, + httpClient: http.Client{ + Timeout: time.Second * 5, + }, } } // GetPrice returns price of token or fiat based on symbol -func (c POClient) GetPrice(symbol string) (Price, error) { +func (c POClient) GetPrice(ctx context.Context, symbol string) (Price, error) { // converting symbol to uppercase symbol = strings.ToUpper(symbol) @@ -54,7 +60,12 @@ func (c POClient) GetPrice(symbol string) (Price, error) { return price, nil } - resp, err := http.Get(fmt.Sprintf(allPricesURL, c.PriceOracleBaseURL)) + req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf(allPricesURL, c.PriceOracleBaseURL), nil) + if err != nil { + return Price{}, err + } + + resp, err := c.httpClient.Do(req) if err != nil { return Price{}, err } @@ -99,5 +110,7 @@ func (c POClient) GetPrice(symbol string) (Price, error) { } } - return Price{}, fmt.Errorf("cannot get price for given symbol: %s", symbol) + return Price{}, fmt.Errorf("%w: %s", ErrSymbolNotFound, symbol) } + +var ErrSymbolNotFound = errors.New("symbol not found") diff --git a/lib/poclient/poclient_test.go b/lib/poclient/poclient_test.go index c7d4e63c..62eaef9d 100644 --- a/lib/poclient/poclient_test.go +++ b/lib/poclient/poclient_test.go @@ -72,7 +72,7 @@ func TestGetPrice(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { client := poclient.NewPOClient(tt.poBaseURL) - price, err := client.GetPrice(tt.symbol) + price, err := client.GetPrice(context.Background(), tt.symbol) if tt.success { require.NoError(t, err) require.NotEmpty(t, price) From 0685939d615f38640a717b497d5c5a3fb242eca7 Mon Sep 17 00:00:00 2001 From: Antonio Pitasi Date: Tue, 14 Jun 2022 16:24:27 +0200 Subject: [PATCH 04/14] feat(database): add method to list all enabled denoms --- api/database/chains.go | 24 ++++++++++++++++++++++++ api/database/chains_test.go | 6 ++++++ 2 files changed, 30 insertions(+) diff --git a/api/database/chains.go b/api/database/chains.go index a4ebb4c1..ff45db0b 100644 --- a/api/database/chains.go +++ b/api/database/chains.go @@ -356,3 +356,27 @@ func (d *Database) ChainsOnlineStatuses(ctx context.Context) ([]ChainOnlineStatu return rows, nil } + +func (d *Database) Denoms(ctx context.Context) ([]cns.Denom, error) { + defer sentry.StartSpan(ctx, "db.DenomTicker").Finish() + + q := ` + SELECT denoms + FROM cns.chains + WHERE enabled + ` + + var rows []struct { + Denoms cns.DenomList `db:"denoms"` + } + if err := d.dbi.DB.SelectContext(ctx, &rows, q); err != nil { + return nil, err + } + + var ret []cns.Denom + for _, r := range rows { + ret = append(ret, r.Denoms...) + } + + return ret, nil +} diff --git a/api/database/chains_test.go b/api/database/chains_test.go index e727353d..514d4c1c 100644 --- a/api/database/chains_test.go +++ b/api/database/chains_test.go @@ -380,3 +380,9 @@ func (s *TestSuite) TestChainsOnlineStatuses() { }) } } + +func (s *TestSuite) TestDenoms() { + denoms, err := s.ctx.Router.DB.Denoms(context.Background()) + s.Require().NoError(err) + s.Require().Greater(len(denoms), 0) +} From 11550e54898127c30cb7fd8f6cc9691ef424e5f5 Mon Sep 17 00:00:00 2001 From: Antonio Pitasi Date: Tue, 14 Jun 2022 16:51:54 +0200 Subject: [PATCH 05/14] feat(options): introduce an `options` pkg It basically holds a Value and an Error. This option type is json serializable. --- lib/options/options.go | 47 +++++++++++++++++++++++++++++++++++++ lib/options/options_test.go | 47 +++++++++++++++++++++++++++++++++++++ 2 files changed, 94 insertions(+) create mode 100644 lib/options/options.go create mode 100644 lib/options/options_test.go diff --git a/lib/options/options.go b/lib/options/options.go new file mode 100644 index 00000000..9b61c7a6 --- /dev/null +++ b/lib/options/options.go @@ -0,0 +1,47 @@ +// Package options provides a generic option type that can be used for wrapping +// results and errors together in a single struct. +package options + +import ( + "encoding/json" +) + +type O[T any] struct { + Value T + Err error +} + +func Wrap[T any](v T, err error) O[T] { + if err != nil { + return FromError[T](err) + } + return FromValue(v) +} + +func FromValue[T any](v T) O[T] { + return O[T]{Value: v} +} + +func FromError[T any](e error) O[T] { + return O[T]{Err: e} +} + +// MarshalJSON implements json.Marshaler. It returns one of the two possible +// json values: +// { "error": "error message" } +// or +// { "value": } +func (o O[T]) MarshalJSON() ([]byte, error) { + if o.Err != nil { + return json.Marshal(errJson{Error: o.Err.Error()}) + } + return json.Marshal(valueJson[T]{Value: o.Value}) +} + +type errJson struct { + Error string `json:"error"` +} + +type valueJson[T any] struct { + Value T `json:"value"` +} diff --git a/lib/options/options_test.go b/lib/options/options_test.go new file mode 100644 index 00000000..295c2b0b --- /dev/null +++ b/lib/options/options_test.go @@ -0,0 +1,47 @@ +package options + +import ( + "encoding/json" + "fmt" + "testing" + + "github.com/stretchr/testify/require" +) + +func TestX(t *testing.T) { + require := require.New(t) + optionV := Wrap(struct{ SomeKey string }{"somevalue"}, nil) + j, err := json.Marshal(optionV) + require.NoError(err) + require.EqualValues("{\"value\":{\"SomeKey\":\"somevalue\"}}", j) +} + +func TestMarshalError(t *testing.T) { + table := []struct { + name string + err error + expected string + }{ + { + name: "string error", + err: fmt.Errorf("something bad happened"), + expected: `{"error":"something bad happened"}`, + }, + { + name: "string error with quotes", + err: fmt.Errorf("something \"bad\" happened"), + expected: `{"error":"something \"bad\" happened"}`, + }, + } + + for _, test := range table { + t.Run(test.name, func(t *testing.T) { + require := require.New(t) + optionV := Wrap(struct{ SomeKey string }{"somevalue"}, test.err) + j, err := json.Marshal(optionV) + require.NoError(err) + require.Equal(test.expected, string(j)) + }) + } + +} From 5b8caeec0dd103b1347f57d99d1892ab6bc2c717 Mon Sep 17 00:00:00 2001 From: Antonio Pitasi Date: Tue, 14 Jun 2022 16:54:37 +0200 Subject: [PATCH 06/14] feat: introduce `DenomPricer` port --- usecase/ports.go | 6 +++ usecase/prices.go | 117 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 123 insertions(+) create mode 100644 usecase/prices.go diff --git a/usecase/ports.go b/usecase/ports.go index 9816c432..0ed7df90 100644 --- a/usecase/ports.go +++ b/usecase/ports.go @@ -3,6 +3,7 @@ package usecase import ( "context" + sdktypes "github.com/cosmos/cosmos-sdk/types" sdkutilities "github.com/emerishq/sdk-service-meta/gen/sdk_utilities" ) @@ -34,3 +35,8 @@ type SDKServiceClient interface { BudgetParams(context.Context, *sdkutilities.BudgetParamsPayload) (res *sdkutilities.BudgetParams2, err error) DistributionParams(context.Context, *sdkutilities.DistributionParamsPayload) (res *sdkutilities.DistributionParams2, err error) } + +// DenomPrices returns the USD price of a denom of the given chain. +type DenomPricer interface { + DenomPrice(ctx context.Context, chainName, denom string) (sdktypes.Dec, error) +} diff --git a/usecase/prices.go b/usecase/prices.go new file mode 100644 index 00000000..a75c5afa --- /dev/null +++ b/usecase/prices.go @@ -0,0 +1,117 @@ +package usecase + +import ( + "context" + "database/sql" + "errors" + "fmt" + "math" + + sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/emerishq/demeris-api-server/api/database" + "github.com/emerishq/demeris-api-server/lib/poclient" + "github.com/emerishq/demeris-backend-models/cns" + "github.com/emerishq/demeris-backend-models/tracelistener" +) + +// PriceOracle returns the price of a certain "symbol". A symbol is a string in +// the form "TICK1TICK2" where TICK1 and TICK2 are the ticker of the requested +// denoms pair. It's common to use "USDT" as the second ticker to get the price +// in USD. +type PriceOracle interface { + GetPrice(ctx context.Context, symbol string) (poclient.Price, error) +} + +type DenomTracer interface { + DenomTrace(ctx context.Context, chainName, traceHash string) (tracelistener.IBCDenomTraceRow, error) +} + +type Denomer interface { + Denom(ctx context.Context, denomName string) (cns.Denom, error) +} + +type DatabaseDenomer struct { + db *database.Database +} + +var _ Denomer = &DatabaseDenomer{} + +func NewDatabaseDenomer(db *database.Database) *DatabaseDenomer { + return &DatabaseDenomer{ + db: db, + } +} + +func (d *DatabaseDenomer) Denom(ctx context.Context, denomName string) (cns.Denom, error) { + denoms, err := d.db.Denoms(ctx) + if err != nil { + return cns.Denom{}, fmt.Errorf("getting denoms: %w", err) + } + + for _, denom := range denoms { + if denom.Name == denomName { + return denom, nil + } + } + + return cns.Denom{}, ErrDenomNotFound +} + +var ErrDenomNotFound = errors.New("denom not found") + +// PriceOracleDenomPricer implements the DenomPricer interface. +type PriceOracleDenomPricer struct { + denomer Denomer + denomTracer DenomTracer + priceOracle PriceOracle +} + +var _ DenomPricer = &PriceOracleDenomPricer{} + +func NewPriceOracleDenomPricer(denomer Denomer, denomTracer DenomTracer, priceOracle PriceOracle) *PriceOracleDenomPricer { + return &PriceOracleDenomPricer{ + denomer: denomer, + denomTracer: denomTracer, + priceOracle: priceOracle, + } +} + +func (p *PriceOracleDenomPricer) DenomPrice(ctx context.Context, chainName, denomName string) (sdktypes.Dec, error) { + // resolve IBC denoms + if denomName[:4] == "ibc/" { + trace, err := p.denomTracer.DenomTrace(ctx, chainName, denomName[4:]) + if errors.Is(err, sql.ErrNoRows) { + return sdktypes.Dec{}, fmt.Errorf("trace %s: %w", denomName, ErrIBCTraceNotFound) + } + if err != nil { + return sdktypes.Dec{}, fmt.Errorf("resolving denom trace %s: %w", denomName, err) + } + denomName = trace.BaseDenom + // TODO: should we also check if the trace is verified? how we do that? + } + + // get denom + denom, err := p.denomer.Denom(ctx, denomName) + if err != nil { + return sdktypes.Dec{}, fmt.Errorf("resolving denom ticker %s: %w", denomName, err) + } + + // get price in USDT + poPrice, err := p.priceOracle.GetPrice(ctx, denom.Ticker+"USDT") + if err != nil { + return sdktypes.Dec{}, fmt.Errorf("getting price for denom %s: %w", denom.Ticker, err) + } + + // convert float into sdk.Dec + poPrecision := math.Pow10(6) + intPrice := int64(poPrice.Price * poPrecision) + price := sdktypes.NewDec(intPrice).QuoInt64(int64(poPrecision)) + + // get price for a single denom + denomPrecision := math.Pow10(int(denom.Precision)) + price = price.QuoInt64(int64(denomPrecision)) + + return price, nil +} + +var ErrIBCTraceNotFound = errors.New("ibc trace not found") From 4d8aba1b8598de7f765f2ee27fc43fd1aed19315 Mon Sep 17 00:00:00 2001 From: Antonio Pitasi Date: Tue, 14 Jun 2022 16:55:33 +0200 Subject: [PATCH 07/14] feat: introduce ports for Crescent and Osmosis gRPC interfaces --- usecase/crescent.go | 60 +++++++++++++++++++++++++++++++++++++ usecase/osmosis.go | 73 +++++++++++++++++++++++++++++++++++++++++++++ usecase/ports.go | 10 +++++++ 3 files changed, 143 insertions(+) create mode 100644 usecase/crescent.go create mode 100644 usecase/osmosis.go diff --git a/usecase/crescent.go b/usecase/crescent.go new file mode 100644 index 00000000..2ab10211 --- /dev/null +++ b/usecase/crescent.go @@ -0,0 +1,60 @@ +package usecase + +import ( + "context" + "fmt" + + sdkquery "github.com/cosmos/cosmos-sdk/types/query" + cretypes "github.com/crescent-network/crescent/x/liquidity/types" + "google.golang.org/grpc" +) + +type CrescentGrpcClient struct { + URL string +} + +var _ CrescentClient = &CrescentGrpcClient{} + +func NewCrescentGrpcClient(url string) *CrescentGrpcClient { + return &CrescentGrpcClient{ + URL: url, + } +} + +func (c *CrescentGrpcClient) Pools(ctx context.Context) ([]cretypes.PoolResponse, error) { + // init gRPC osmosis client + // TODO: when we have a service mesh reuse a single connection instead of + // opening a new one every time + grpcConn, err := grpc.Dial(c.URL, grpc.WithInsecure()) + if err != nil { + return nil, fmt.Errorf("cannot connect to crescent, %w", err) + } + defer func() { + _ = grpcConn.Close() + }() + + creq := cretypes.NewQueryClient(grpcConn) + + var ( + pools []cretypes.PoolResponse + nextKey []byte + ) + for { + res, err := creq.Pools(ctx, &cretypes.QueryPoolsRequest{ + Pagination: &sdkquery.PageRequest{ + Key: nextKey, + }, + }) + if err != nil { + return nil, fmt.Errorf("cannot get pools, %w", err) + } + pools = append(pools, res.Pools...) + + if len(res.Pagination.NextKey) == 0 { + break + } + nextKey = res.Pagination.NextKey + } + + return pools, nil +} diff --git a/usecase/osmosis.go b/usecase/osmosis.go new file mode 100644 index 00000000..f5dbee6e --- /dev/null +++ b/usecase/osmosis.go @@ -0,0 +1,73 @@ +package usecase + +import ( + "context" + "fmt" + + sdkcodec "github.com/cosmos/cosmos-sdk/codec/types" + sdkquery "github.com/cosmos/cosmos-sdk/types/query" + gammbalancer "github.com/osmosis-labs/osmosis/v7/x/gamm/pool-models/balancer" + gamm "github.com/osmosis-labs/osmosis/v7/x/gamm/types" + "google.golang.org/grpc" +) + +type OsmosisGrpcClient struct { + URL string +} + +var _ OsmosisClient = &OsmosisGrpcClient{} + +func NewOsmosisGrpcClient(url string) *OsmosisGrpcClient { + return &OsmosisGrpcClient{ + URL: url, + } +} + +func (c *OsmosisGrpcClient) Pools(ctx context.Context) ([]gammbalancer.Pool, error) { + // init gRPC osmosis client + // TODO: when we have a service mesh reuse a single connection instead of + // opening a new one every time + grpcConn, err := grpc.Dial(c.URL, grpc.WithInsecure()) + if err != nil { + return nil, err + } + defer func() { + _ = grpcConn.Close() + }() + gq := gamm.NewQueryClient(grpcConn) + + // list pools (paginated) + var ( + untypedPools []*sdkcodec.Any + nextKey []byte + ) + for { + res, err := gq.Pools(ctx, &gamm.QueryPoolsRequest{ + Pagination: &sdkquery.PageRequest{ + Key: nextKey, + }, + }) + if err != nil { + return nil, fmt.Errorf("cannot get pools: %w", err) + } + untypedPools = append(untypedPools, res.Pools...) + + if len(res.Pagination.NextKey) == 0 { + break + } + nextKey = res.Pagination.NextKey + } + + // convert pools to gammbalancer.Pool + pools := make([]gammbalancer.Pool, 0, len(untypedPools)) + for _, p := range untypedPools { + var pool gammbalancer.Pool + err := pool.Unmarshal(p.Value) + if err != nil { + return nil, fmt.Errorf("cannot unmarshal pool: %w", err) + } + pools = append(pools, pool) + } + + return pools, nil +} diff --git a/usecase/ports.go b/usecase/ports.go index 0ed7df90..1c7cabb0 100644 --- a/usecase/ports.go +++ b/usecase/ports.go @@ -4,7 +4,9 @@ import ( "context" sdktypes "github.com/cosmos/cosmos-sdk/types" + cretypes "github.com/crescent-network/crescent/x/liquidity/types" sdkutilities "github.com/emerishq/sdk-service-meta/gen/sdk_utilities" + gammbalancer "github.com/osmosis-labs/osmosis/v7/x/gamm/pool-models/balancer" ) //go:generate mockgen -package usecase_test -source ports.go -destination ports_mocks_test.go @@ -36,6 +38,14 @@ type SDKServiceClient interface { DistributionParams(context.Context, *sdkutilities.DistributionParamsPayload) (res *sdkutilities.DistributionParams2, err error) } +type OsmosisClient interface { + Pools(ctx context.Context) ([]gammbalancer.Pool, error) +} + +type CrescentClient interface { + Pools(ctx context.Context) ([]cretypes.PoolResponse, error) +} + // DenomPrices returns the USD price of a denom of the given chain. type DenomPricer interface { DenomPrice(ctx context.Context, chainName, denom string) (sdktypes.Dec, error) From ae81aea7cd5d261701d1697352970d2ed6c12a2b Mon Sep 17 00:00:00 2001 From: Antonio Pitasi Date: Tue, 14 Jun 2022 16:58:04 +0200 Subject: [PATCH 08/14] feat: add new ports to `App` struct --- usecase/ports_mocks_test.go | 117 ++++++++++++++++++++++++++++++++++++ usecase/usecase.go | 13 +++- usecase/usecase_test.go | 6 +- 3 files changed, 134 insertions(+), 2 deletions(-) diff --git a/usecase/ports_mocks_test.go b/usecase/ports_mocks_test.go index fe20b607..ea69632c 100644 --- a/usecase/ports_mocks_test.go +++ b/usecase/ports_mocks_test.go @@ -8,8 +8,11 @@ import ( context "context" reflect "reflect" + types "github.com/cosmos/cosmos-sdk/types" + types0 "github.com/crescent-network/crescent/x/liquidity/types" sdkutilities "github.com/emerishq/sdk-service-meta/gen/sdk_utilities" gomock "github.com/golang/mock/gomock" + balancer "github.com/osmosis-labs/osmosis/v7/x/gamm/pool-models/balancer" ) // MockSDKServiceClients is a mock of SDKServiceClients interface. @@ -372,3 +375,117 @@ func (mr *MockSDKServiceClientMockRecorder) TxMetadata(arg0, arg1 interface{}) * mr.mock.ctrl.T.Helper() return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "TxMetadata", reflect.TypeOf((*MockSDKServiceClient)(nil).TxMetadata), arg0, arg1) } + +// MockOsmosisClient is a mock of OsmosisClient interface. +type MockOsmosisClient struct { + ctrl *gomock.Controller + recorder *MockOsmosisClientMockRecorder +} + +// MockOsmosisClientMockRecorder is the mock recorder for MockOsmosisClient. +type MockOsmosisClientMockRecorder struct { + mock *MockOsmosisClient +} + +// NewMockOsmosisClient creates a new mock instance. +func NewMockOsmosisClient(ctrl *gomock.Controller) *MockOsmosisClient { + mock := &MockOsmosisClient{ctrl: ctrl} + mock.recorder = &MockOsmosisClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockOsmosisClient) EXPECT() *MockOsmosisClientMockRecorder { + return m.recorder +} + +// Pools mocks base method. +func (m *MockOsmosisClient) Pools(ctx context.Context) ([]balancer.Pool, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Pools", ctx) + ret0, _ := ret[0].([]balancer.Pool) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Pools indicates an expected call of Pools. +func (mr *MockOsmosisClientMockRecorder) Pools(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Pools", reflect.TypeOf((*MockOsmosisClient)(nil).Pools), ctx) +} + +// MockCrescentClient is a mock of CrescentClient interface. +type MockCrescentClient struct { + ctrl *gomock.Controller + recorder *MockCrescentClientMockRecorder +} + +// MockCrescentClientMockRecorder is the mock recorder for MockCrescentClient. +type MockCrescentClientMockRecorder struct { + mock *MockCrescentClient +} + +// NewMockCrescentClient creates a new mock instance. +func NewMockCrescentClient(ctrl *gomock.Controller) *MockCrescentClient { + mock := &MockCrescentClient{ctrl: ctrl} + mock.recorder = &MockCrescentClientMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockCrescentClient) EXPECT() *MockCrescentClientMockRecorder { + return m.recorder +} + +// Pools mocks base method. +func (m *MockCrescentClient) Pools(ctx context.Context) ([]types0.PoolResponse, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "Pools", ctx) + ret0, _ := ret[0].([]types0.PoolResponse) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// Pools indicates an expected call of Pools. +func (mr *MockCrescentClientMockRecorder) Pools(ctx interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Pools", reflect.TypeOf((*MockCrescentClient)(nil).Pools), ctx) +} + +// MockDenomPricer is a mock of DenomPricer interface. +type MockDenomPricer struct { + ctrl *gomock.Controller + recorder *MockDenomPricerMockRecorder +} + +// MockDenomPricerMockRecorder is the mock recorder for MockDenomPricer. +type MockDenomPricerMockRecorder struct { + mock *MockDenomPricer +} + +// NewMockDenomPricer creates a new mock instance. +func NewMockDenomPricer(ctrl *gomock.Controller) *MockDenomPricer { + mock := &MockDenomPricer{ctrl: ctrl} + mock.recorder = &MockDenomPricerMockRecorder{mock} + return mock +} + +// EXPECT returns an object that allows the caller to indicate expected use. +func (m *MockDenomPricer) EXPECT() *MockDenomPricerMockRecorder { + return m.recorder +} + +// DenomPrice mocks base method. +func (m *MockDenomPricer) DenomPrice(ctx context.Context, chainName, denom string) (types.Dec, error) { + m.ctrl.T.Helper() + ret := m.ctrl.Call(m, "DenomPrice", ctx, chainName, denom) + ret0, _ := ret[0].(types.Dec) + ret1, _ := ret[1].(error) + return ret0, ret1 +} + +// DenomPrice indicates an expected call of DenomPrice. +func (mr *MockDenomPricerMockRecorder) DenomPrice(ctx, chainName, denom interface{}) *gomock.Call { + mr.mock.ctrl.T.Helper() + return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "DenomPrice", reflect.TypeOf((*MockDenomPricer)(nil).DenomPrice), ctx, chainName, denom) +} diff --git a/usecase/usecase.go b/usecase/usecase.go index 93114f0c..f31630bc 100644 --- a/usecase/usecase.go +++ b/usecase/usecase.go @@ -7,10 +7,21 @@ const ( type App struct { sdkServiceClients SDKServiceClients + osmosisClient OsmosisClient + crescentClient CrescentClient + denomPricer DenomPricer } -func NewApp(sdk SDKServiceClients) *App { +func NewApp( + sdk SDKServiceClients, + osmosisClient OsmosisClient, + crescentClient CrescentClient, + denomPricer DenomPricer, +) *App { return &App{ sdkServiceClients: sdk, + osmosisClient: osmosisClient, + crescentClient: crescentClient, + denomPricer: denomPricer, } } diff --git a/usecase/usecase_test.go b/usecase/usecase_test.go index dcde5acb..90917727 100644 --- a/usecase/usecase_test.go +++ b/usecase/usecase_test.go @@ -12,6 +12,9 @@ type mocks struct { t *testing.T sdkServiceClients *MockSDKServiceClients sdkServiceClient *MockSDKServiceClient + osmosisClient *MockOsmosisClient + crescentClient *MockCrescentClient + denomPricer *MockDenomPricer } func newApp(t *testing.T, setup func(mocks)) *usecase.App { @@ -20,6 +23,7 @@ func newApp(t *testing.T, setup func(mocks)) *usecase.App { t: t, sdkServiceClients: NewMockSDKServiceClients(ctrl), sdkServiceClient: NewMockSDKServiceClient(ctrl), + denomPricer: NewMockDenomPricer(ctrl), } // Pre-setup expectations on sdkServiceClients @@ -33,5 +37,5 @@ func newApp(t *testing.T, setup func(mocks)) *usecase.App { if setup != nil { setup(m) } - return usecase.NewApp(m.sdkServiceClients) + return usecase.NewApp(m.sdkServiceClients, m.osmosisClient, m.crescentClient, m.denomPricer) } From 25a1211e59a26058ba80886ce70d882676ab26a6 Mon Sep 17 00:00:00 2001 From: Antonio Pitasi Date: Tue, 14 Jun 2022 17:05:27 +0200 Subject: [PATCH 09/14] feat: add business logic for getting prices of the pools --- usecase/lpools.go | 127 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 usecase/lpools.go diff --git a/usecase/lpools.go b/usecase/lpools.go new file mode 100644 index 00000000..952fb78a --- /dev/null +++ b/usecase/lpools.go @@ -0,0 +1,127 @@ +package usecase + +import ( + "context" + "errors" + "fmt" + + sdktypes "github.com/cosmos/cosmos-sdk/types" + "github.com/emerishq/demeris-api-server/lib/options" +) + +type StrategyID string + +// PoolsStrategy is the interface that defines how to retrieve a list of +// supported liquidity pools. +// +// Each strategy must have a unique ID so the pools can be easily categorized. +type PoolsStrategy interface { + ID() StrategyID + Pools(ctx context.Context) ([]LiquidityPool, error) +} + +// PoolPricesResult is a map containing the results of multiple PoolsStrategies. +// +// Example: +// "osmosis" -> [ {pool1}, {error pool2}, {pool3} ] +// "crescent" -> [ {pool1}, {pool2}, {pool3} ] +// "chainx" -> {error chainx} +type PoolPricesResult map[StrategyID]options.O[[]options.O[PoolPrice]] + +func (a *App) PoolPrices(ctx context.Context) PoolPricesResult { + strategies := []PoolsStrategy{} + + result := make(PoolPricesResult, len(strategies)) + for _, strategy := range strategies { + result[strategy.ID()] = options.Wrap(a.execPoolsStrategy(ctx, strategy)) + } + + return result +} + +func (a *App) execPoolsStrategy(ctx context.Context, s PoolsStrategy) ([]options.O[PoolPrice], error) { + // list pools + pools, err := s.Pools(ctx) + if err != nil { + err = fmt.Errorf("error in pools strategy %s: %w", s.ID(), err) + return nil, err + } + + // get prices for each pool + res := make([]options.O[PoolPrice], 0, len(pools)) + for _, pool := range pools { + poolPrice, err := NewPoolPrice(ctx, pool, a.denomPricer) + res = append(res, options.Wrap(poolPrice, err)) + } + + // ignore pools with "expected" errors + res = filterByError(res, ErrDenomNotFound) + res = filterByError(res, ErrIBCTraceNotFound) + + return res, nil +} + +// filterByError takes a slice of options and returns a new slice of options +// filtering out all the options that matched the given error kind. +func filterByError[T any](in []options.O[T], err error) []options.O[T] { + result := make([]options.O[T], 0, len(in)) + for _, o := range in { + if o.Err != nil && errors.Is(o.Err, err) { + continue + } + result = append(result, o) + } + return result +} + +// PoolPrice holds the informations of a certain Pool together with its total +// price. +type PoolPrice struct { + Pool LiquidityPool `json:"pool"` + TotalPrice sdktypes.Dec `json:"total_price"` +} + +func NewPoolPrice(ctx context.Context, pool LiquidityPool, denomPricer DenomPricer) (PoolPrice, error) { + price, err := pool.Price(ctx, denomPricer) + if err != nil { + return PoolPrice{}, fmt.Errorf("getting price for pool: %w", err) + } + return PoolPrice{ + Pool: pool, + TotalPrice: price, + }, nil +} + +// LiquidityPool is a generic representation of a liquidity pool. A pool has a +// price (in USD) that represents the total value of assets present inside the +// pool. +type LiquidityPool interface { + Price(ctx context.Context, denomPricer DenomPricer) (sdktypes.Dec, error) +} + +// MultiCoinPool represents a liquidity pool composed by one or more coins +// (denoms). +type MultiCoinPool struct { + coins []sdktypes.Coin +} + +var _ LiquidityPool = (*MultiCoinPool)(nil) + +func NewMultiCoinPool() *MultiCoinPool { return &MultiCoinPool{} } + +func (p *MultiCoinPool) AddCoin(coin sdktypes.Coin) { + p.coins = append(p.coins, coin) +} + +func (p *MultiCoinPool) Price(ctx context.Context, denomPricer DenomPricer) (sdktypes.Dec, error) { + poolTVL := sdktypes.NewDec(0) + for _, coin := range p.coins { + denomPrice, err := denomPricer.DenomPrice(ctx, osmosisChainName, coin.Denom) + if err != nil { + return sdktypes.Dec{}, fmt.Errorf("getting denom price: %w", err) + } + poolTVL = poolTVL.Add(denomPrice.MulInt(coin.Amount)) + } + + return poolTVL, nil +} From 1e0f2363c36a7615e4c6c4f13d74dd6f42d5a8cc Mon Sep 17 00:00:00 2001 From: Antonio Pitasi Date: Tue, 14 Jun 2022 17:05:51 +0200 Subject: [PATCH 10/14] feat: introduce Osmosis and Crescent LP strategies --- usecase/lpools.go | 8 ++- usecase/lpools_crescent.go | 100 +++++++++++++++++++++++++++++++++++++ usecase/lpools_osmosis.go | 58 +++++++++++++++++++++ 3 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 usecase/lpools_crescent.go create mode 100644 usecase/lpools_osmosis.go diff --git a/usecase/lpools.go b/usecase/lpools.go index 952fb78a..6c9ddc38 100644 --- a/usecase/lpools.go +++ b/usecase/lpools.go @@ -29,7 +29,13 @@ type PoolsStrategy interface { type PoolPricesResult map[StrategyID]options.O[[]options.O[PoolPrice]] func (a *App) PoolPrices(ctx context.Context) PoolPricesResult { - strategies := []PoolsStrategy{} + crescent := NewCrescentPoolsStrategy(a.crescentClient, a.sdkServiceClients) + osmosis := NewOsmosisPoolsStrategy(a.osmosisClient) + + strategies := []PoolsStrategy{ + crescent, + osmosis, + } result := make(PoolPricesResult, len(strategies)) for _, strategy := range strategies { diff --git a/usecase/lpools_crescent.go b/usecase/lpools_crescent.go new file mode 100644 index 00000000..965b2f48 --- /dev/null +++ b/usecase/lpools_crescent.go @@ -0,0 +1,100 @@ +package usecase + +import ( + "context" + "fmt" + + sdktypes "github.com/cosmos/cosmos-sdk/types" + cretypes "github.com/crescent-network/crescent/x/liquidity/types" + sdkutilities "github.com/emerishq/sdk-service-meta/gen/sdk_utilities" +) + +type CrescentPoolsStrategy struct { + client CrescentClient + sdkServiceClients SDKServiceClients +} + +var _ PoolsStrategy = (*CrescentPoolsStrategy)(nil) + +func NewCrescentPoolsStrategy( + client CrescentClient, + sdkServiceClients SDKServiceClients, +) *CrescentPoolsStrategy { + return &CrescentPoolsStrategy{ + client: client, + sdkServiceClients: sdkServiceClients, + } +} + +func (s *CrescentPoolsStrategy) ID() StrategyID { return crescentChainName } + +func (s *CrescentPoolsStrategy) Pools(ctx context.Context) ([]LiquidityPool, error) { + pools, err := s.client.Pools(ctx) + if err != nil { + return nil, fmt.Errorf("listing pools: %w", err) + } + + // list supplies (paginated) + v := "44" // crescent version, TODO: fetch this from cns/database + sdkService, err := s.sdkServiceClients.GetSDKServiceClient(v) + if err != nil { + return nil, fmt.Errorf("cannot get SDK service client, %w", err) + } + var ( + nextSupplyKey *string + supplies = make(map[string]string) + ) + for { + res, err := sdkService.Supply(ctx, &sdkutilities.SupplyPayload{ + ChainName: "crescent", + PaginationKey: nextSupplyKey, + }) + if err != nil { + return nil, fmt.Errorf("cannot get pools, %w", err) + } + for _, s := range res.Coins { + supplies[s.Denom] = s.Amount + } + + if res.Pagination.NextKey == nil || len(*res.Pagination.NextKey) == 0 { + break + } + nextSupplyKey = res.Pagination.NextKey + } + + // get prices for each pool + res := make([]LiquidityPool, 0, len(pools)) + for _, p := range pools { + res = append(res, NewCrescentPool(p, supplies)) + } + + return res, nil +} + +type CrescentPool struct { + *MultiCoinPool + + Denom string `json:"denom"` + TotalSupply string `json:"total_supply"` +} + +var _ LiquidityPool = CrescentPool{} + +func NewCrescentPool(p cretypes.PoolResponse, supplies map[string]string) *CrescentPool { + totalSupply, found := supplies[p.PoolCoinDenom] + if !found { + totalSupply = "0" + } + + pool := &CrescentPool{ + MultiCoinPool: NewMultiCoinPool(), + Denom: p.PoolCoinDenom, + TotalSupply: totalSupply, + } + + for _, b := range p.Balances { + pool.AddCoin(sdktypes.NewCoin(b.Denom, b.Amount)) + } + + return pool +} diff --git a/usecase/lpools_osmosis.go b/usecase/lpools_osmosis.go new file mode 100644 index 00000000..37b17db4 --- /dev/null +++ b/usecase/lpools_osmosis.go @@ -0,0 +1,58 @@ +package usecase + +import ( + "context" + "fmt" + + sdktypes "github.com/cosmos/cosmos-sdk/types" + gammbalancer "github.com/osmosis-labs/osmosis/v7/x/gamm/pool-models/balancer" +) + +type OsmosisPoolsStrategy struct { + client OsmosisClient +} + +var _ PoolsStrategy = (*OsmosisPoolsStrategy)(nil) + +func NewOsmosisPoolsStrategy(client OsmosisClient) *OsmosisPoolsStrategy { + return &OsmosisPoolsStrategy{ + client: client, + } +} + +func (s *OsmosisPoolsStrategy) ID() StrategyID { return osmosisChainName } + +func (s *OsmosisPoolsStrategy) Pools(ctx context.Context) ([]LiquidityPool, error) { + pools, err := s.client.Pools(ctx) + if err != nil { + return nil, fmt.Errorf("getting pools: %w", err) + } + + // get prices for each pool + res := make([]LiquidityPool, 0, len(pools)) + for _, p := range pools { + res = append(res, NewOsmosisPool(p)) + } + return res, nil +} + +type OsmosisPool struct { + *MultiCoinPool + + Denom string `json:"denom"` + TotalSupply sdktypes.Int `json:"total_supply"` +} + +var _ LiquidityPool = OsmosisPool{} + +func NewOsmosisPool(p gammbalancer.Pool) *OsmosisPool { + pool := &OsmosisPool{ + MultiCoinPool: NewMultiCoinPool(), + Denom: p.TotalShares.Denom, + TotalSupply: p.TotalShares.Amount, + } + for _, asset := range p.PoolAssets { + pool.AddCoin(asset.Token) + } + return pool +} From 02103a90d785912fa21a7a1fff8d6820c9b8ed46 Mon Sep 17 00:00:00 2001 From: Antonio Pitasi Date: Tue, 14 Jun 2022 17:17:18 +0200 Subject: [PATCH 11/14] feat: add new `/prices/pools` endpoint --- api/prices/ports.go | 11 +++++++++ api/prices/prices.go | 55 ++++++++++++++++++++++++++++++++++++++++++++ api/router/router.go | 5 ++++ 3 files changed, 71 insertions(+) create mode 100644 api/prices/ports.go create mode 100644 api/prices/prices.go diff --git a/api/prices/ports.go b/api/prices/ports.go new file mode 100644 index 00000000..f3c06adf --- /dev/null +++ b/api/prices/ports.go @@ -0,0 +1,11 @@ +package prices + +import ( + "context" + + "github.com/emerishq/demeris-api-server/usecase" +) + +type App interface { + PoolPrices(ctx context.Context) usecase.PoolPricesResult +} diff --git a/api/prices/prices.go b/api/prices/prices.go new file mode 100644 index 00000000..226b690c --- /dev/null +++ b/api/prices/prices.go @@ -0,0 +1,55 @@ +package prices + +import ( + "net/http" + + "github.com/emerishq/demeris-api-server/lib/ginutils" + "github.com/emerishq/demeris-api-server/usecase" + "github.com/emerishq/emeris-utils/logging" + "github.com/gin-gonic/gin" + "go.uber.org/zap" +) + +type PricesAPI struct { + app App +} + +func New(app App) *PricesAPI { + return &PricesAPI{ + app: app, + } +} + +func Register(router *gin.Engine, app App) { + api := New(app) + + router.Group("/prices"). + GET("/pools", api.GetPoolsPrices) +} + +type GetPoolsPricesResponse struct { + PoolResults usecase.PoolPricesResult `json:"pool_results"` +} + +// GetPoolsPrices returns the list of available pools and their associated token's price. +// @Summary Get pools' tokens prices +// @Tags Price +// @ID get-pools-prices +// @Produce json +// @Success 200 {object} GetPoolsPricesResponse +// @Failure 500 {object} apierrors.UserFacingError +// @Router /prices/pools [get] +func (api *PricesAPI) GetPoolsPrices(c *gin.Context) { + prices := api.app.PoolPrices(c.Request.Context()) + + logger := ginutils.GetValue[*zap.SugaredLogger](c, logging.LoggerKey) + for _, opt := range prices { + if opt.Err != nil { + logger.Errorw("fetching pool price", "err", opt.Err) + } + } + + c.JSON(http.StatusOK, GetPoolsPricesResponse{ + PoolResults: prices, + }) +} diff --git a/api/router/router.go b/api/router/router.go index 2eefcc61..5ce5cc30 100644 --- a/api/router/router.go +++ b/api/router/router.go @@ -6,6 +6,7 @@ import ( "github.com/emerishq/demeris-api-server/api/block" "github.com/emerishq/demeris-api-server/api/liquidity" + "github.com/emerishq/demeris-api-server/api/prices" "github.com/emerishq/demeris-api-server/lib/apierrors" "github.com/emerishq/demeris-api-server/lib/stringcache" "github.com/emerishq/demeris-api-server/sdkservice" @@ -160,4 +161,8 @@ func registerRoutes(engine *gin.Engine, db *database.Database, s *store.Store, // @tag.name liquidity // @tag.description pool-related endpoints liquidity.Register(engine, db, s) + + // @tag.name prices + // @tag.description prices related endpoints + prices.Register(engine, app) } From 09a1f44dd98a61fc01a39546e18c784b13aa0593 Mon Sep 17 00:00:00 2001 From: Antonio Pitasi Date: Tue, 14 Jun 2022 17:17:28 +0200 Subject: [PATCH 12/14] feat: initialize new App dependencies --- cmd/api-server/main.go | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/cmd/api-server/main.go b/cmd/api-server/main.go index e1fe4790..49b9cd23 100644 --- a/cmd/api-server/main.go +++ b/cmd/api-server/main.go @@ -12,6 +12,7 @@ import ( "github.com/emerishq/demeris-api-server/api/database" "github.com/emerishq/demeris-api-server/api/router" "github.com/emerishq/demeris-api-server/lib/fflag" + "github.com/emerishq/demeris-api-server/lib/poclient" "github.com/emerishq/demeris-api-server/sdkservice" "github.com/emerishq/demeris-api-server/usecase" "github.com/emerishq/emeris-utils/k8s" @@ -108,7 +109,17 @@ func main() { l.Panicw("cannot initialize sdk-service clients", "error", err) } - app := usecase.NewApp(sdkServiceClients) + osmosisClient := usecase.NewOsmosisGrpcClient("osmosis:9090") + + crescentClient := usecase.NewCrescentGrpcClient("crescent:9090") + + priceOracle := poclient.NewPOClient("http://price-oracle:8000") + + denomer := usecase.NewDatabaseDenomer(dbi) + + denomPricer := usecase.NewPriceOracleDenomPricer(denomer, dbi, priceOracle) + + app := usecase.NewApp(sdkServiceClients, osmosisClient, crescentClient, denomPricer) r := router.New( dbi, From 43df220cd4de6e572c07721b26f1f7b6c618a7a3 Mon Sep 17 00:00:00 2001 From: Antonio Pitasi Date: Tue, 14 Jun 2022 17:17:38 +0200 Subject: [PATCH 13/14] chore: go mod tidy --- go.mod | 15 ++++++++------- go.sum | 54 +++++++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/go.mod b/go.mod index 50fc8257..324c62f8 100644 --- a/go.mod +++ b/go.mod @@ -14,6 +14,7 @@ require ( github.com/allinbits/starport-operator v0.0.1-alpha.45 github.com/cockroachdb/cockroach-go/v2 v2.2.8 github.com/cosmos/cosmos-sdk v0.45.3 + github.com/crescent-network/crescent v1.1.0 github.com/emerishq/demeris-backend-models v1.5.0 github.com/emerishq/emeris-cns-server v0.0.0-20220422070001-a18e063b6374 github.com/emerishq/emeris-price-oracle v1.0.0 @@ -27,9 +28,9 @@ require ( github.com/gofrs/uuid v4.2.0+incompatible github.com/golang/mock v1.6.0 github.com/google/go-cmp v0.5.7 - github.com/gravity-devs/liquidity v1.5.0 github.com/jmoiron/sqlx v1.3.3 github.com/lib/pq v1.10.4 + github.com/osmosis-labs/osmosis/v7 v7.3.0 github.com/patrickmn/go-cache v2.1.0+incompatible github.com/stretchr/testify v1.7.1 github.com/swaggo/swag v1.8.0 @@ -72,7 +73,7 @@ require ( github.com/danieljoos/wincred v1.0.2 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.1 // indirect - github.com/dgraph-io/badger/v2 v2.2007.2 // indirect + github.com/dgraph-io/badger/v2 v2.2007.3 // indirect github.com/dgraph-io/ristretto v0.0.3 // indirect github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect @@ -107,7 +108,7 @@ require ( github.com/gorilla/websocket v1.5.0 // indirect github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 // indirect github.com/grpc-ecosystem/grpc-gateway v1.16.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.8.0 // indirect github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c // indirect github.com/gtank/merlin v0.1.1 // indirect github.com/gtank/ristretto255 v0.1.2 // indirect @@ -132,7 +133,7 @@ require ( github.com/keybase/go-keychain v0.0.0-20190712205309-48d3d31d256d // indirect github.com/leodido/go-urn v1.2.1 // indirect github.com/libp2p/go-buffer-pool v0.0.2 // indirect - github.com/magiconair/properties v1.8.5 // indirect + github.com/magiconair/properties v1.8.6 // indirect github.com/mailru/easyjson v0.7.6 // indirect github.com/mattn/go-isatty v0.0.14 // indirect github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect @@ -149,7 +150,7 @@ require ( github.com/pmezard/go-difflib v1.0.0 // indirect github.com/prometheus/client_golang v1.12.1 // indirect github.com/prometheus/client_model v0.2.0 // indirect - github.com/prometheus/common v0.32.1 // indirect + github.com/prometheus/common v0.33.0 // indirect github.com/prometheus/procfs v0.7.3 // indirect github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 // indirect github.com/regen-network/cosmos-proto v0.3.1 // indirect @@ -184,7 +185,7 @@ require ( golang.org/x/crypto v0.0.0-20220214200702-86341886e292 // indirect golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3 // indirect golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 // indirect - golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 // indirect + golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect golang.org/x/sys v0.0.0-20220422013727-9388b58f7150 // indirect golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect golang.org/x/text v0.3.7 // indirect @@ -206,5 +207,5 @@ require ( k8s.io/kube-openapi v0.0.0-20210527164424-3c818078ee3d // indirect k8s.io/utils v0.0.0-20210527160623-6fdb442a123b // indirect sigs.k8s.io/structured-merge-diff/v4 v4.1.0 // indirect - sigs.k8s.io/yaml v1.2.0 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 56c8ac58..dcff741a 100644 --- a/go.sum +++ b/go.sum @@ -42,6 +42,7 @@ cloud.google.com/go/bigtable v1.2.0/go.mod h1:JcVAOl45lrTmQfLj7T6TxyMzIN/3FGGcFm cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk= +cloud.google.com/go/firestore v1.6.0/go.mod h1:afJwI0vaXwAG54kI7A//lP/lSPDkQORQuMkv56TxEPU= cloud.google.com/go/firestore v1.6.1/go.mod h1:asNXNOzBdyVQmEU+ggO8UPodTkEVFW5Qx+rwHnAz+EY= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= @@ -101,6 +102,7 @@ github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZ github.com/Azure/go-autorest/tracing v0.5.0/go.mod h1:r/s2XiOKccPW3HrqB+W0TQzfbtp2fGCgRFtBroKn4Dk= github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I= github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= @@ -121,6 +123,7 @@ github.com/Joker/hpp v1.0.0/go.mod h1:8x5n+M1Hp5hC0g8okX3sR3vFQwynaX/UgSOM9MeBKz github.com/Knetic/govaluate v3.0.1-0.20171022003610-9aa49832a739+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww= github.com/Masterminds/semver/v3 v3.1.1 h1:hLg3sBzpNErnxhQtUy/mmLR2I9foDujNK030IGemrRc= github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/Microsoft/go-winio v0.4.14/go.mod h1:qXqCSQ3Xa7+6tgxaGTIe4Kpcdsi+P8jBhyzoq1bpyYA= @@ -158,6 +161,7 @@ github.com/Zilliqa/gozilliqa-sdk v1.2.1-0.20201201074141-dd0ecada1be6/go.mod h1: github.com/abraithwaite/sqlx v1.3.2-0.20210331022513-df9bf9884350 h1:YQgBUDZ2bVA8IGWk2R4Ib+PLmCyhNKvvYZhWINwBcOI= github.com/abraithwaite/sqlx v1.3.2-0.20210331022513-df9bf9884350/go.mod h1:2BljVx/86SuTyjE+aPYlHCTNvZrnJXghYGpNiXLBMCQ= github.com/adlio/schema v1.1.13/go.mod h1:L5Z7tw+7lRK1Fnpi/LT/ooCP1elkXn0krMWBQHUhEDE= +github.com/adlio/schema v1.1.14/go.mod h1:hQveFEMiDlG/M9yz9RAajnH5DzT6nAfqOG9YkEQU2pg= github.com/adlio/schema v1.3.0/go.mod h1:51QzxkpeFs6lRY11kPye26IaFPOV+HqEj01t5aXXKfs= github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/afex/hystrix-go v0.0.0-20180502004556-fa1af6a1f4f5/go.mod h1:SkGFH1ia65gfNATL8TAiHDNxPzPdmEL5uirI2Uyuz6c= @@ -317,6 +321,7 @@ github.com/cockroachdb/cockroach-go/v2 v2.2.8/go.mod h1:q4ZRgO6CQpwNyEvEwSxwNrOS github.com/cockroachdb/datadriven v0.0.0-20190809214429-80d97fb3cbaa/go.mod h1:zn76sxSg3SzpJ0PPJaLDCu+Bu0Lg3sKTORVIj19EIF8= github.com/codahale/hdrhistogram v0.0.0-20161010025455-3a0bb77429bd/go.mod h1:sE/e/2PUdi/liOCUjSTXgM1o87ZssimdTWN964YiIeI= github.com/codegangsta/inject v0.0.0-20150114235600-33e0aa1cb7c0/go.mod h1:4Zcjuz89kmFXt9morQgcfYZAYZ5n8WHjt81YYWIwtTM= +github.com/coinbase/rosetta-sdk-go v0.6.10/go.mod h1:J/JFMsfcePrjJZkwQFLh+hJErkAmdm9Iyy3D5Y0LfXo= github.com/coinbase/rosetta-sdk-go v0.7.0 h1:lmTO/JEpCvZgpbkOITL95rA80CPKb5CtMzLaqF2mCNg= github.com/coinbase/rosetta-sdk-go v0.7.0/go.mod h1:7nD3oBPIiHqhRprqvMgPoGxe/nyq3yftRmpsy29coWE= github.com/confio/ics23/go v0.0.0-20200817220745-f173e6211efb/go.mod h1:E45NqnlpxGnpfTWL/xauN7MRwEE28T4Dd4uraToOaKg= @@ -331,6 +336,7 @@ github.com/containerd/console v1.0.2/go.mod h1:ytZPjGgY2oeTkAONYafi2kSj0aYggsf8a github.com/containerd/containerd v1.3.0/go.mod h1:bC6axHOhabU15QhwfG7w5PipXdVtMXFTttgp+kVtyUA= github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6/go.mod h1:GL3xCUCBDV3CZiTSEKksMWbLE66hEyuu9qyDOOqM47Y= github.com/containerd/continuity v0.0.0-20200928162600-f2cc35102c2a/go.mod h1:W0qIOTD7mp2He++YVq+kgfXezRYqzP1uDuMVH1bITDY= +github.com/containerd/continuity v0.2.0/go.mod h1:wCYX+dRqZdImhGucXOqTQn05AhX6EUDaGEMUzTFFpLg= github.com/containerd/continuity v0.2.1/go.mod h1:wCYX+dRqZdImhGucXOqTQn05AhX6EUDaGEMUzTFFpLg= github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk= github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= @@ -353,7 +359,7 @@ github.com/cosmos/cosmos-sdk v0.41.0/go.mod h1:vlgqdPpUGSxgqSbZea6fjszoLkPKwCuiq github.com/cosmos/cosmos-sdk v0.41.3/go.mod h1:yKN705My5fGGT93DrdoeZ96GPFLCVZrzDBciGf8/Ldo= github.com/cosmos/cosmos-sdk v0.42.2/go.mod h1:xiLp1G8mumj82S5KLJGCAyeAlD+7VNomg/aRSJV12yk= github.com/cosmos/cosmos-sdk v0.42.4/go.mod h1:I1Zw1zmU4rA/NITaakTb71pXQnQrWyFBhqo3WSeg0vA= -github.com/cosmos/cosmos-sdk v0.45.1/go.mod h1:XXS/asyCqWNWkx2rW6pSuen+EVcpAFxq6khrhnZgHaQ= +github.com/cosmos/cosmos-sdk v0.44.5/go.mod h1:maUA6m2TBxOJZkbwl0eRtEBgTX37kcaiOWU5t1HEGaY= github.com/cosmos/cosmos-sdk v0.45.3 h1:PiVSU3IkNEDPhoxOZHk2lPnhwBBJgEYAtAR0jGXRN4g= github.com/cosmos/cosmos-sdk v0.45.3/go.mod h1:qYm5JEr0ZlbnmoP/Q3b+dYMOliHf4ddHirpILiwZzqg= github.com/cosmos/go-bip39 v0.0.0-20180819234021-555e2067c45d/go.mod h1:tSxLoYXyBmiFeKpvmq4dzayMdCjCnu8uqmCysIGBT2Y= @@ -368,6 +374,7 @@ github.com/cosmos/iavl v0.15.0-rc5/go.mod h1:WqoPL9yPTQ85QBMT45OOUzPxG/U/JcJoN7u github.com/cosmos/iavl v0.15.3/go.mod h1:OLjQiAQ4fGD2KDZooyJG9yz+p2ao2IAYSbke8mVvSA4= github.com/cosmos/iavl v0.17.3 h1:s2N819a2olOmiauVa0WAhoIJq9EhSXE9HDBAoR9k+8Y= github.com/cosmos/iavl v0.17.3/go.mod h1:prJoErZFABYZGDHka1R6Oay4z9PrNeFFiMKHDAMOi4w= +github.com/cosmos/ibc-go/v2 v2.0.2/go.mod h1:XUmW7wmubCRhIEAGtMGS+5IjiSSmcAwihoN/yPGd6Kk= github.com/cosmos/ledger-cosmos-go v0.11.1 h1:9JIYsGnXP613pb2vPjFeMMjBI5lEDsEaF6oYorTy6J4= github.com/cosmos/ledger-cosmos-go v0.11.1/go.mod h1:J8//BsAGTo3OC/vDLjMRFLW6q0WAaXvHnVc7ZmE8iUY= github.com/cosmos/ledger-go v0.9.2 h1:Nnao/dLwaVTk1Q5U9THldpUMMXU94BOTWPddSmVB6pI= @@ -382,6 +389,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.11/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/crescent-network/crescent v1.1.0 h1:/E5xFe2HnsEQT5E3i9tPfOknKO6U1bzZrJqIhoVN5R4= +github.com/crescent-network/crescent v1.1.0/go.mod h1:JoZkjZ9sQN7E8d5tezW4AOiQ8mBHtDSuneFtNW50Tsc= github.com/cyberdelia/templates v0.0.0-20141128023046-ca7fffd4298c/go.mod h1:GyV+0YP4qX0UQ7r2MoYZ+AvYDp12OF5yg4q8rGnyNh4= github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2G9VLXONKD9G7QGMM+4= github.com/danieljoos/wincred v1.0.2 h1:zf4bhty2iLuwgjgpraD2E9UbvO+fe54XXGJbOwe23fU= @@ -408,8 +417,9 @@ github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f h1:U5y3Y5UE0w7amNe github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f/go.mod h1:xH/i4TFMt8koVQZ6WFms69WAsDWr2XsYL3Hkl7jkoLE= github.com/dgraph-io/badger v1.6.0/go.mod h1:zwt7syl517jmP8s94KqSxTlM6IMsdhYy6psNgSztDR4= github.com/dgraph-io/badger/v2 v2.2007.1/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= -github.com/dgraph-io/badger/v2 v2.2007.2 h1:EjjK0KqwaFMlPin1ajhP943VPENHJdEz1KLIegjaI3k= github.com/dgraph-io/badger/v2 v2.2007.2/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= +github.com/dgraph-io/badger/v2 v2.2007.3 h1:Sl9tQWz92WCbVSe8pj04Tkqlm2boW+KAxd+XSs58SQI= +github.com/dgraph-io/badger/v2 v2.2007.3/go.mod h1:26P/7fbL4kUZVEVKLAKXkBXKOydDmM2p1e+NhhnBCAE= github.com/dgraph-io/ristretto v0.0.3-0.20200630154024-f66de99634de/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= github.com/dgraph-io/ristretto v0.0.3 h1:jh22xisGBjrEVnRZ1DVTpBVQm0Xndu8sMl0CWDzSIBI= github.com/dgraph-io/ristretto v0.0.3/go.mod h1:KPxhHT9ZxKefz+PCeOGsrHpl1qZ7i70dGTu2u+Ahh6E= @@ -531,6 +541,7 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI= github.com/gin-contrib/zap v0.0.2 h1:VnIucI+kUsxgzmcrX0gMk19a2I12KirTxi+ufuT2xZk= github.com/gin-contrib/zap v0.0.2/go.mod h1:2vZj8gTuOYOfottCirxZr9gNM/Q1yk2iSVn15SUVG5A= +github.com/gin-gonic/gin v1.5.0/go.mod h1:Nd6IXA8m5kNZdNEHMBd93KT+mdY3+bewLgRvmCsR2Do= github.com/gin-gonic/gin v1.6.3/go.mod h1:75u5sXoLsGZoRN5Sgbi1eraJ4GU3++wFwWzhwvtwp4M= github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs= github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U= @@ -630,9 +641,11 @@ github.com/go-openapi/validate v0.19.2/go.mod h1:1tRCw7m3jtI8eNWEEliiAqUIcBztB2K github.com/go-openapi/validate v0.19.5/go.mod h1:8DJv2CVJQ6kGNpFW6eV9N3JviE1C85nY1c2z52x1Gk4= github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-playground/locales v0.12.1/go.mod h1:IUMDtCfWo/w/mtMfIE/IG2K+Ey3ygWanZIBtBW0W2TM= github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8= github.com/go-playground/locales v0.14.0 h1:u50s323jtVGugKlcYeyzC0etD1HifMjqmJqb8WugfUU= github.com/go-playground/locales v0.14.0/go.mod h1:sawfccIbzZTqEDETgFXqTho0QybSa7l++s0DH+LDiLs= +github.com/go-playground/universal-translator v0.16.0/go.mod h1:1AnU7NaIRDWWzGEKwgtJRd2xk99HeFyHw3yid4rvQIY= github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA= github.com/go-playground/universal-translator v0.18.0 h1:82dyy6p4OuJq4/CByFNOn/jYrnRPArHwAcmLoJZxyho= github.com/go-playground/universal-translator v0.18.0/go.mod h1:UvRDBj+xPUEGrFYl+lu/H90nyDXpg0fqeB/AQUGNTVA= @@ -699,6 +712,7 @@ github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188/go.mod h1:vXjM/+ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/geo v0.0.0-20190916061304-5b978397cfec/go.mod h1:QZ0nwyI2jOfgRAoBvP+ab5aRr7c9x7lhGEJrKvBwjWI= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4= github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -838,8 +852,6 @@ github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/ad github.com/gotestyourself/gotestyourself v2.2.0+incompatible/go.mod h1:zZKM6oeNM8k+FRljX1mnzVYeS8wiGgQyvST1/GafPbY= github.com/graph-gophers/graphql-go v0.0.0-20191115155744-f33e81362277/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= github.com/graph-gophers/graphql-go v1.3.0/go.mod h1:9CQHMSxwO4MprSdzoIEobiHpoLtHm77vfxsvsIN5Vuc= -github.com/gravity-devs/liquidity v1.5.0 h1:QOMLCOBrvp6FYUDMbBPJ+K3Oi9UF/q74c1cp48SsCRs= -github.com/gravity-devs/liquidity v1.5.0/go.mod h1:67P0tk9OThjyIdXlIkxtBzCN+gTqNVc02uqLcrd7dT0= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= github.com/grpc-ecosystem/go-grpc-middleware v1.0.1-0.20190118093823-f849b5445de4/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs= @@ -855,8 +867,9 @@ github.com/grpc-ecosystem/grpc-gateway v1.14.7/go.mod h1:oYZKL012gGh6LMyg/xA7Q2y github.com/grpc-ecosystem/grpc-gateway v1.15.2/go.mod h1:vO11I9oWA+KsxmfFQPhLnnIb1VDE24M+pdxZFiuZcA8= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1 h1:X2vfSnm1WC8HEo0MBHZg2TcuDUHJj6kd1TmEAQncnSA= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.0.1/go.mod h1:oVMjMN64nzEcepv1kdZKgx1qNYt4Ro0Gqefiq2JWdis= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.6.0/go.mod h1:qrJPVzv9YlhsrxJc3P/Q85nr0w1lIRikTl4JlhdDH5w= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.8.0 h1:/57CcG6YAO31FWwTVUnNl+2RcDUAaT55HtI71KQAXVw= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.8.0/go.mod h1:/fckq3NE+vGiJsd4fDt4ge1XrK8cN+e5G5QWIzdg7Q8= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c h1:6rhixN/i8ZofjG1Y75iExal34USq5p+wiN1tpie8IrU= github.com/gsterjov/go-libsecret v0.0.0-20161001094733-a6f4afe4910c/go.mod h1:NMPJylDgVpX0MLRlPy15sqSwOFv/U1GZ2m21JhFfek0= github.com/gtank/merlin v0.1.1-0.20191105220539-8318aed1a79f/go.mod h1:T86dnYJhcGOh5BjZFCJWTDeTK7XW8uE+E21Cy/bIQ+s= @@ -1125,6 +1138,7 @@ github.com/labstack/echo/v4 v4.2.1/go.mod h1:AA49e0DZ8kk5jTOOCKNuPR6oTnBS0dYiM4F github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y= github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL7NoOu+k= github.com/leanovate/gopter v0.2.9/go.mod h1:U2L/78B+KVFIx2VmW6onHJQzXtFb+p5y3y2Sh+Jxxv8= +github.com/leodido/go-urn v1.1.0/go.mod h1:+cyI34gQWZcE1eQU7NVgKkkzdXDQHr1dBMtdAPozLkw= github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII= github.com/leodido/go-urn v1.2.1 h1:BqpAaACuzVSgi/VLzGZIobT2z4v53pjosyNd9Yv6n/w= github.com/leodido/go-urn v1.2.1/go.mod h1:zt4jvISO2HfUBqxjfIshjdMTYS56ZS/qv49ictyFfxY= @@ -1136,6 +1150,7 @@ github.com/lib/pq v1.3.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/lib/pq v1.9.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.0/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.3/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.4 h1:SO9z7FRPzA03QhHKJrH5BXA6HU1rS4V2nIVrrNC1iYk= github.com/lib/pq v1.10.4/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/libp2p/go-buffer-pool v0.0.2 h1:QNK2iAFa8gjAe1SPz6mHSMuCcjs+X1wlHzeOSqcmlfs= @@ -1152,8 +1167,9 @@ github.com/magefile/mage v1.11.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXq github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= github.com/magiconair/properties v1.8.4/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/magiconair/properties v1.8.5 h1:b6kJs+EmPFMYGkow9GiUyCyOvIwYetYJ3fSaWak/Gls= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo= +github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20180823135443-60711f1a8329/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190312143242-1de009706dbe/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -1363,6 +1379,8 @@ github.com/openzipkin/zipkin-go v0.2.2/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnh github.com/openzipkin/zipkin-go v0.2.5/go.mod h1:KpXfKdgRDnnhsxw4pNIH9Md5lyFqKUa4YDFlwRYAMyE= github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs= github.com/ory/dockertest/v3 v3.6.2/go.mod h1:EFLcVUOl8qCwp9NyDAcCDtq/QviLtYswW/VbWzUnTNE= +github.com/osmosis-labs/osmosis/v7 v7.3.0 h1:9CrUh2drxa0IKARgI5Cy6KzDHximeD3TxcNivN1TKLI= +github.com/osmosis-labs/osmosis/v7 v7.3.0/go.mod h1:uyrTFKD4w9hq0EY1PfvCmWNTtiO8X7c0ivRisBKrOA0= github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw= github.com/otiai10/copy v1.4.2/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E= github.com/otiai10/copy v1.6.0/go.mod h1:XWfuS3CrI0R6IE0FbgHsEazaXO8G0LpMp9o8tos0x4E= @@ -1451,8 +1469,9 @@ github.com/prometheus/common v0.15.0/go.mod h1:U+gB1OBLb1lF3O42bTCL+FK18tX9Oar16 github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc= github.com/prometheus/common v0.29.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= github.com/prometheus/common v0.30.0/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= -github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4= github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls= +github.com/prometheus/common v0.33.0 h1:rHgav/0a6+uYgGdNt3jwz8FNSesO/Hsang3O0T9A5SE= +github.com/prometheus/common v0.33.0/go.mod h1:gB3sOl7P0TvJabZpLY5uQMpUqRCPPCyRLCZYc7JZTNE= github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190117184657-bf6a532e95b1/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= @@ -1500,6 +1519,7 @@ github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XF github.com/rogpeppe/go-internal v1.8.1/go.mod h1:JeRgkft04UBgHMgCIwADu4Pn6Mtm5d4nPKWu0nJ5d+o= github.com/rs/cors v0.0.0-20160617231935-a62a804a8a00/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU= +github.com/rs/cors v1.8.0/go.mod h1:EBwu+T5AvHOcXwvZIkQFjUN6s8Czyqw12GL/Y0tUyRM= github.com/rs/cors v1.8.2 h1:KCooALfAYGs415Cwu5ABvv9n9509fSiG5SQJn/AQo4U= github.com/rs/cors v1.8.2/go.mod h1:XyqrcTp5zjWr1wsJ8PIRZssZ8b/WMcMf71DJnit4EMU= github.com/rs/xhandler v0.0.0-20160618193221-ed27b6fd6521/go.mod h1:RvLn4FgxWubrpZHtQLnOf6EwhN2hEMusxZOhcW9H3UQ= @@ -1519,6 +1539,7 @@ github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQD github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/ryanuber/columnize v2.1.0+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts= github.com/sabhiram/go-gitignore v0.0.0-20180611051255-d3107576ba94/go.mod h1:b18R55ulyQ/h3RaWyloPyER7fWQVZvimKKhnI5OfrJQ= +github.com/sagikazarmark/crypt v0.1.0/go.mod h1:B/mN0msZuINBtQ1zZLEQcegFJJf9vnYIR88KRMEuODE= github.com/sagikazarmark/crypt v0.3.0/go.mod h1:uD/D+6UF4SrIR1uGEv7bBNkNqLGqUr43MRiaGWX1Nig= github.com/sagikazarmark/crypt v0.4.0/go.mod h1:ALv2SRj7GxYV4HO9elxH9nS6M9gW+xDNxqmyJ6RfDFM= github.com/sahilm/fuzzy v0.1.0/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y= @@ -1602,6 +1623,7 @@ github.com/spf13/viper v1.4.0/go.mod h1:PTJ7Z/lr49W6bUbkmS1V3by4uWynFiR9p7+dSq/y github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/spf13/viper v1.9.0/go.mod h1:+i6ajR7OX2XaiBkrcZJFK21htRk7eDeLg7+O6bhUPP4= github.com/spf13/viper v1.10.0/go.mod h1:SoyBPwAtKDzypXNDFKN5kzH7ppppbGZtls1UpIy5AsM= github.com/spf13/viper v1.10.1 h1:nuJZuYpG7gTj/XqiUwg8bA0cp1+M2mC3J4g5luUYBKk= github.com/spf13/viper v1.10.1/go.mod h1:IGlFPqhNAPKRxohIzWpI5QEy4kuI7tcl5WvR+8qy1rU= @@ -1644,6 +1666,7 @@ github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45 github.com/tecbot/gorocksdb v0.0.0-20191217155057-f0fad39f321c/go.mod h1:ahpPrc7HpcfEWDQRZEmnXMzHY03mLDYMCxeDzy46i+8= github.com/tendermint/btcd v0.1.1 h1:0VcxPfflS2zZ3RiOAHkBiFUcPvbtRj5O7zHmcJWHV7s= github.com/tendermint/btcd v0.1.1/go.mod h1:DC6/m53jtQzr/NFmMNEu0rxf18/ktVoVtMrnDD5pN+U= +github.com/tendermint/budget v1.1.1/go.mod h1:6dU+Vcu1Uef+4STjANpbOq0Gxb6YJRjqsx1tJF45MRA= github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15 h1:hqAk8riJvK4RMWx1aInLzndwxKalgi5rTqgfXxOxbEI= github.com/tendermint/crypto v0.0.0-20191022145703-50d29ede1e15/go.mod h1:z4YtwM70uOnk8h0pjJYlj3zdYwi9l03By6iAIF5j/Pk= github.com/tendermint/go-amino v0.16.0 h1:GyhmgQKvqF82e2oZeuMSp9JTN0N09emoSZlb2lyGa2E= @@ -1662,6 +1685,7 @@ github.com/tendermint/tendermint v0.34.7/go.mod h1:JVuu3V1ZexOaZG8VJMRl8lnfrGw6h github.com/tendermint/tendermint v0.34.8/go.mod h1:JVuu3V1ZexOaZG8VJMRl8lnfrGw6hEB2TVnoUwKRbss= github.com/tendermint/tendermint v0.34.9/go.mod h1:kl4Z1JwGx1I+u1SXIzMDy7Z3T8LiMeCAOnzNn6AIMT4= github.com/tendermint/tendermint v0.34.14/go.mod h1:FrwVm3TvsVicI9Z7FlucHV6Znfd5KBc/Lpp69cCwtk0= +github.com/tendermint/tendermint v0.34.15/go.mod h1:/7EDAw02rD7GT8syC317cX9ZhZTCdaFVvYjU8W+yJSs= github.com/tendermint/tendermint v0.34.19 h1:y0P1qI5wSa9IRuhKnTDA6IUcOrLi1hXJuALR+R7HFEk= github.com/tendermint/tendermint v0.34.19/go.mod h1:R5+wgIwSxMdKQcmOaeudL0Cjkr3HDkhpcdum6VeU3R4= github.com/tendermint/tm-db v0.6.2/go.mod h1:GYtQ67SUvATOcoY8/+x6ylk8Qo02BQyLrAs+yAcLvGI= @@ -1984,10 +2008,13 @@ golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qx golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20210917221730-978cfadd31cf/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211005001312-d4b1ae081e3b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211008194852-3b03d305991f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20211208012354-db4efeb81f4b/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4 h1:HVyaeDAYux4pnY+D/SiwmLOR36ewZ4iGQIIrtnuCjFA= golang.org/x/net v0.0.0-20220425223048-2871e0cb64e4/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= @@ -2007,8 +2034,9 @@ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8 h1:RerP+noqYHUQ8CMRcPlC2nvTa4dcBIjegkuWdcUDuqg= golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b h1:clP8eMhB30EHdc0bd2Twtq6kgU7yl5ub2cQLSdrv1Dg= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -2147,6 +2175,7 @@ golang.org/x/sys v0.0.0-20210903071746-97244b99971b/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211004093028-2c5d950f24ef/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= @@ -2416,11 +2445,11 @@ google.golang.org/genproto v0.0.0-20211203200212-54befc351ae9/go.mod h1:5CzLGKJ6 google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= google.golang.org/genproto v0.0.0-20211223182754-3ac035c7e7cb/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220302033224-9aa15565e42a/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46 h1:G1IeWbjrqEq9ChWxEuRPJu6laA67+XgTFHVSAvepr38= google.golang.org/genproto v0.0.0-20220426171045-31bebdecfb46/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= google.golang.org/grpc v1.33.2 h1:EQyQC3sa8M+p6Ulc8yy9SWSS2GVwyRc83gAbG8lrl4o= google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.0.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -2452,6 +2481,7 @@ gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMy gopkg.in/gcfg.v1 v1.2.0/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= +gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/go-playground/validator.v9 v9.30.0/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= gopkg.in/h2non/gock.v1 v1.0.14 h1:fTeu9fcUvSnLNacYvYI54h+1/XEteDyHvrVCZEEEYNM= gopkg.in/h2non/gock.v1 v1.0.14/go.mod h1:sX4zAkdYX1TRGJ2JY156cFspQn4yRWn6p9EMdODlynE= @@ -2461,6 +2491,7 @@ gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.51.1/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/ini.v1 v1.63.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.2/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/ini.v1 v1.66.3 h1:jRskFVxYaMGAMUbN0UZ7niA9gzL9B49DOqE78vg0k3w= gopkg.in/ini.v1 v1.66.3/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -2579,6 +2610,7 @@ sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK sigs.k8s.io/structured-merge-diff/v4 v4.1.0 h1:C4r9BgJ98vrKnnVCjwCSXcWjWe0NKcUQkmzDXZXGwH8= sigs.k8s.io/structured-merge-diff/v4 v4.1.0/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= -sigs.k8s.io/yaml v1.2.0 h1:kr/MCeFWJWTwyaHoR9c8EjH9OumOmoF9YGiZd7lFm/Q= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= +sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8= sourcegraph.com/sourcegraph/appdash v0.0.0-20190731080439-ebfcffb1b5c0/go.mod h1:hI742Nqp5OhwiqlzhgfbWU4mW4yO10fP+LoT9WOswdU= From 5299a16bf70fea789bba05c21d036f1360ffe874 Mon Sep 17 00:00:00 2001 From: Antonio Pitasi Date: Wed, 15 Jun 2022 16:59:25 +0200 Subject: [PATCH 14/14] test: add basic tests for crescent strategy --- usecase/lpools_crescent.go | 2 +- usecase/lpools_crescent_test.go | 86 +++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 usecase/lpools_crescent_test.go diff --git a/usecase/lpools_crescent.go b/usecase/lpools_crescent.go index 965b2f48..b13be7ce 100644 --- a/usecase/lpools_crescent.go +++ b/usecase/lpools_crescent.go @@ -56,7 +56,7 @@ func (s *CrescentPoolsStrategy) Pools(ctx context.Context) ([]LiquidityPool, err supplies[s.Denom] = s.Amount } - if res.Pagination.NextKey == nil || len(*res.Pagination.NextKey) == 0 { + if res.Pagination == nil || res.Pagination.NextKey == nil || len(*res.Pagination.NextKey) == 0 { break } nextSupplyKey = res.Pagination.NextKey diff --git a/usecase/lpools_crescent_test.go b/usecase/lpools_crescent_test.go new file mode 100644 index 00000000..e65e3402 --- /dev/null +++ b/usecase/lpools_crescent_test.go @@ -0,0 +1,86 @@ +package usecase_test + +import ( + "context" + "testing" + + "github.com/cosmos/cosmos-sdk/types" + cretypes "github.com/crescent-network/crescent/x/liquidity/types" + "github.com/emerishq/demeris-api-server/usecase" + sdkutilities "github.com/emerishq/sdk-service-meta/gen/sdk_utilities" + "github.com/golang/mock/gomock" + "github.com/stretchr/testify/require" +) + +func TestCrescentPoolsStrategy_Pools(t *testing.T) { + ctx := context.Background() + type fields struct { + client *MockCrescentClient + sdkServiceClients *MockSDKServiceClients + } + tests := []struct { + name string + supplies []*sdkutilities.Coin + pools []cretypes.PoolResponse + }{ + { + name: "ok: no pools", + supplies: []*sdkutilities.Coin{ + { + Denom: "uatom", + Amount: "10", + }, + }, + pools: []cretypes.PoolResponse{}, + }, + { + name: "ok: pool with two denoms in it", + supplies: []*sdkutilities.Coin{ + { + Denom: "pool/1", + Amount: "12341234", + }, + }, + pools: []cretypes.PoolResponse{ + { + Id: 1, + Balances: []types.Coin{ + types.NewInt64Coin("uatom", 10), + types.NewInt64Coin("ucre", 5), + }, + PoolCoinDenom: "pool/1", + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + s := usecase.NewCrescentPoolsStrategy(makeArgs(t, ctx, tt.pools, tt.supplies)) + got, err := s.Pools(ctx) + require.NoError(t, err) + // TODO: this test table only checks that the len of returned + // LiquidityPools matched the len of Crescent pools returned by the + // Crecent client. + // I wasn't able to write a simple but more accurate test (e.g. + // checking that Amount and Denom are correct) because of the + // MultiDenomPool inner struct. + require.Len(t, got, len(tt.pools)) + }) + } +} + +func makeArgs(t *testing.T, ctx context.Context, pools []cretypes.PoolResponse, supplies []*sdkutilities.Coin) (*MockCrescentClient, *MockSDKServiceClients) { + ctrl := gomock.NewController(t) + + client := NewMockCrescentClient(ctrl) + client.EXPECT().Pools(gomock.Any()).Return(pools, nil) + + sdkService := NewMockSDKServiceClient(ctrl) + sdkService.EXPECT().Supply(ctx, gomock.Any()).Return(&sdkutilities.Supply2{ + Coins: supplies, + }, nil) + sdkServiceClients := NewMockSDKServiceClients(ctrl) + sdkServiceClients.EXPECT().GetSDKServiceClient(gomock.Any()).Return(sdkService, nil) + + return client, sdkServiceClients +}