From 706875629a1d0af719159692f82376c38b221926 Mon Sep 17 00:00:00 2001 From: kaladinlight <35275952+kaladinlight@users.noreply.github.com> Date: Tue, 5 Dec 2023 10:55:10 -0700 Subject: [PATCH] feat: add estimate fees endpoint for cosmos --- go/coinstacks/cosmos/api/api.go | 20 ++++++ go/coinstacks/cosmos/api/handler.go | 28 ++++++++ go/coinstacks/cosmos/api/swagger.json | 37 +++++++++++ go/pkg/cosmos/fees.go | 93 +++++++++++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 go/pkg/cosmos/fees.go diff --git a/go/coinstacks/cosmos/api/api.go b/go/coinstacks/cosmos/api/api.go index 35645eeae..399629462 100644 --- a/go/coinstacks/cosmos/api/api.go +++ b/go/coinstacks/cosmos/api/api.go @@ -119,6 +119,7 @@ func New(httpClient *cosmos.HTTPClient, grpcClient *cosmos.GRPCClient, wsClient v1Gas := v1.PathPrefix("/gas").Subrouter() v1Gas.HandleFunc("/estimate", a.EstimateGas).Methods("POST") + v1Gas.HandleFunc("/fees", a.Fees).Methods("GET") v1ValidatorsRoot := v1.PathPrefix("/validators").Subrouter() v1ValidatorsRoot.HandleFunc("", a.GetValidators).Methods("GET") @@ -206,3 +207,22 @@ func (a *API) ValidatorTxHistory(w http.ResponseWriter, r *http.Request) { api.HandleResponse(w, http.StatusOK, txHistory) } + +// swagger:route GET /api/v1/gas/fees v1 Fees +// +// Get current fees. +// +// responses: +// +// 200: Fees +// 400: BadRequestError +// 500: InternalServerError +func (a *API) Fees(w http.ResponseWriter, r *http.Request) { + fees, err := a.handler.GetFees() + if err != nil { + api.HandleError(w, http.StatusInternalServerError, err.Error()) + return + } + + api.HandleResponse(w, http.StatusOK, fees) +} diff --git a/go/coinstacks/cosmos/api/handler.go b/go/coinstacks/cosmos/api/handler.go index b3acb7b8e..cc62da695 100644 --- a/go/coinstacks/cosmos/api/handler.go +++ b/go/coinstacks/cosmos/api/handler.go @@ -234,3 +234,31 @@ func (h *Handler) getAPRData() (*APRData, error) { return aprData, nil } + +// Contains info about the current fees +// swagger:model Fees +type Fees map[string]string + +func (h *Handler) GetFees() (*Fees, error) { + globalMinGasPrices, err := h.HTTPClient.GetGlobalMinimumGasPrices() + if err != nil { + return nil, errors.Wrap(err, "failed to get global minimum gas prices") + } + + localMinGasPrice, err := h.HTTPClient.GetLocalMinimumGasPrices() + if err != nil { + return nil, errors.Wrap(err, "failed to get local minimum gas prices") + } + + minGasPrices := make(Fees) + for k, global := range globalMinGasPrices { + minGasPrices[k] = global.String() + if local, ok := localMinGasPrice[k]; ok { + if local.GT(global) { + minGasPrices[k] = local.String() + } + } + } + + return &minGasPrices, nil +} diff --git a/go/coinstacks/cosmos/api/swagger.json b/go/coinstacks/cosmos/api/swagger.json index 6d6df04c2..8fb9424e8 100644 --- a/go/coinstacks/cosmos/api/swagger.json +++ b/go/coinstacks/cosmos/api/swagger.json @@ -170,6 +170,35 @@ } } }, + "/api/v1/gas/fees": { + "get": { + "tags": [ + "v1" + ], + "summary": "Get current fees.", + "operationId": "Fees", + "responses": { + "200": { + "description": "Fees", + "schema": { + "$ref": "#/definitions/Fees" + } + }, + "400": { + "description": "BadRequestError", + "schema": { + "$ref": "#/definitions/BadRequestError" + } + }, + "500": { + "description": "InternalServerError", + "schema": { + "$ref": "#/definitions/InternalServerError" + } + } + } + } + }, "/api/v1/info": { "get": { "tags": [ @@ -678,6 +707,14 @@ }, "x-go-package": "github.com/shapeshift/unchained/pkg/cosmos" }, + "Fees": { + "description": "Contains info about the current fees", + "type": "object", + "additionalProperties": { + "type": "string" + }, + "x-go-package": "github.com/shapeshift/unchained/coinstacks/cosmos/api" + }, "GasAmount": { "type": "string", "x-go-package": "github.com/shapeshift/unchained/pkg/api" diff --git a/go/pkg/cosmos/fees.go b/go/pkg/cosmos/fees.go new file mode 100644 index 000000000..917e2ba5b --- /dev/null +++ b/go/pkg/cosmos/fees.go @@ -0,0 +1,93 @@ +package cosmos + +import ( + "encoding/json" + "fmt" + + sdk "github.com/cosmos/cosmos-sdk/types" + "github.com/pkg/errors" +) + +func (c *HTTPClient) GetGlobalMinimumGasPrices() (map[string]sdk.Dec, error) { + gasPrices := make(map[string]sdk.Dec) + + var res struct { + Param struct { + Amount string `json:"subspace"` + Key string `json:"key"` + Value string `json:"value"` + } `json:"param"` + } + + e := &ErrorResponse{} + + queryParams := map[string]string{ + "subspace": "globalfee", + "key": "MinimumGasPricesParam", + } + + r, err := c.LCD.R().SetResult(&res).SetError(e).SetQueryParams(queryParams).Get("/cosmos/params/v1beta1/params") + if err != nil { + return gasPrices, errors.Wrap(err, "failed to get globalfee params") + } + + if r.Error() != nil { + return gasPrices, errors.Errorf("failed to get globalfee params: %s", e.Msg) + } + + values := []struct { + Denom string `json:"denom"` + Amount string `json:"amount"` + }{} + + err = json.Unmarshal([]byte(res.Param.Value), &values) + if err != nil { + return gasPrices, errors.Wrapf(err, "failed to unmarshal value: %s", res.Param.Value) + } + + for _, value := range values { + coinStr := fmt.Sprintf("%s%s", value.Amount, value.Denom) + + coin, err := sdk.ParseDecCoin(coinStr) + if err != nil { + logger.Errorf("failed to parse dec coin: %s: %v", coinStr, err) + continue + } + + gasPrices[coin.GetDenom()] = coin.Amount + } + + return gasPrices, nil +} + +func (c *HTTPClient) GetLocalMinimumGasPrices() (map[string]sdk.Dec, error) { + gasPrices := make(map[string]sdk.Dec) + + var res struct { + MinimumGasPrice string `json:"minimum_gas_price"` + } + + e := &ErrorResponse{} + + r, err := c.LCD.R().SetResult(&res).SetError(e).Get("/cosmos/base/node/v1beta1/config") + if err != nil { + return gasPrices, errors.Wrap(err, "failed to get base node config") + } + + if r.Error() != nil { + return gasPrices, errors.Errorf("failed to get base node config: %s", e.Msg) + } + + coins, err := sdk.ParseDecCoins(res.MinimumGasPrice) + if err != nil { + return gasPrices, errors.Wrap(err, "failed to parse coins") + } + + for _, coin := range coins { + gasPrices[coin.GetDenom()] = coin.Amount + } + + gasPrices["uatom"] = sdk.NewDec(1) + + return gasPrices, nil +}