Skip to content

Commit

Permalink
Generalize GetBaseCurrencyPriceEndpoint
Browse files Browse the repository at this point in the history
  • Loading branch information
diamondhands0 committed Dec 17, 2024
1 parent 03e2d04 commit 4cb450d
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 50 deletions.
16 changes: 8 additions & 8 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,11 @@ require (
github.com/spf13/cobra v1.8.1
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.9.0
github.com/stretchr/testify v1.10.0
github.com/tyler-smith/go-bip39 v1.1.0
golang.org/x/crypto v0.28.0
golang.org/x/crypto v0.29.0
golang.org/x/image v0.21.0
golang.org/x/sync v0.8.0
golang.org/x/sync v0.9.0
google.golang.org/api v0.201.0
gopkg.in/DataDog/dd-trace-go.v1 v1.69.0
)
Expand Down Expand Up @@ -123,7 +123,7 @@ require (
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
github.com/kevinburke/go-types v0.0.0-20240719050749-165e75e768f7 // indirect
github.com/kevinburke/rest v0.0.0-20240617045629-3ed0ad3487f0 // indirect
github.com/klauspost/compress v1.17.1 // indirect
github.com/klauspost/compress v1.17.11 // indirect
github.com/kyokomi/emoji/v2 v2.2.13 // indirect
github.com/magiconair/properties v1.8.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
Expand Down Expand Up @@ -181,11 +181,11 @@ require (
go.uber.org/multierr v1.11.0 // indirect
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c // indirect
golang.org/x/mod v0.21.0 // indirect
golang.org/x/net v0.30.0 // indirect
golang.org/x/net v0.31.0 // indirect
golang.org/x/oauth2 v0.23.0 // indirect
golang.org/x/sys v0.26.0 // indirect
golang.org/x/term v0.25.0 // indirect
golang.org/x/text v0.19.0 // indirect
golang.org/x/sys v0.27.0 // indirect
golang.org/x/term v0.26.0 // indirect
golang.org/x/text v0.20.0 // indirect
golang.org/x/time v0.7.0 // indirect
golang.org/x/tools v0.26.0 // indirect
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da // indirect
Expand Down
8 changes: 8 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6
github.com/klauspost/compress v1.12.3/go.mod h1:8dP1Hq4DHOhN9w426knH3Rhby4rFm6D8eO+e+Dq5Gzg=
github.com/klauspost/compress v1.17.1 h1:NE3C767s2ak2bweCZo3+rdP4U/HoyVXLv/X9f2gPS5g=
github.com/klauspost/compress v1.17.1/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
Expand Down Expand Up @@ -463,6 +464,7 @@ github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/syndtr/goleveldb v1.0.1-0.20210819022825-2ae1ddf74ef7/go.mod h1:q4W45IWZaF22tdD+VEXcAWRA037jwmWEB5VWYORlTpc=
Expand Down Expand Up @@ -538,6 +540,7 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPh
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
golang.org/x/crypto v0.29.0/go.mod h1:+F4F4N5hv6v38hfeYwTdx20oUvLLc+QfrE9Ax9HtgRg=
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c h1:7dEasQXItcW1xKJ2+gg5VOiBnqWrJc+rq0DPKyvvdbY=
golang.org/x/exp v0.0.0-20241009180824-f66d83c29e7c/go.mod h1:NQtJDoLvd6faHhE7m4T/1IY708gDefGGjR/iUW8yQQ8=
Expand Down Expand Up @@ -574,6 +577,7 @@ golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
golang.org/x/net v0.9.0/go.mod h1:d48xBJpPfHeWQsugry2m+kC02ZBRGRgulfHnEXEuWns=
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
golang.org/x/net v0.31.0/go.mod h1:P4fl1q7dY2hnZFxEk4pPSkDHF+QqjitcnDjUQyMM+pM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs=
golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
Expand All @@ -588,6 +592,7 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
Expand Down Expand Up @@ -621,6 +626,7 @@ golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.27.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
Expand All @@ -629,6 +635,7 @@ golang.org/x/term v0.7.0/go.mod h1:P32HKFT3hSsZrRxla30E9HqToFYAQPCMs/zFMBUFqPY=
golang.org/x/term v0.13.0/go.mod h1:LTmsnFJwVN6bCy1rVCoS+qHT1HhALEFxKncY3WNNh4U=
golang.org/x/term v0.25.0 h1:WtHI/ltw4NvSUig5KARz9h521QvRC8RmF/cuYqifU24=
golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M=
golang.org/x/term v0.26.0/go.mod h1:Si5m1o57C5nBNQo5z1iq+XDijt21BDBDp2bK0QI8e3E=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
Expand All @@ -638,6 +645,7 @@ golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
golang.org/x/text v0.19.0 h1:kTxAhCbGbxhK0IwgSKiMO5awPoDQ0RpfiVYBfK860YM=
golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4=
golang.org/x/time v0.7.0 h1:ntUhktv3OPE6TgYxXWv9vKvUSJyIFJlyohwbkEwPrKQ=
golang.org/x/time v0.7.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
Expand Down
106 changes: 64 additions & 42 deletions routes/dao_coin_exchange_with_fees.go
Original file line number Diff line number Diff line change
Expand Up @@ -592,7 +592,7 @@ func GetQuoteBasePkidFromBuyingSellingPkids(
}
}

type GetBaseCurrencyPriceRequest struct {
type BaseCurrencyPriceRequestEntry struct {
BaseCurrencyPublicKeyBase58Check string `safeForLogging:"true"`
// Only deso, focus, and usdc supported
QuoteCurrencyPublicKeyBase58Check string `safeForLogging:"true"`
Expand All @@ -601,7 +601,11 @@ type GetBaseCurrencyPriceRequest struct {
BaseCurrencyQuantityToSell float64 `safeForLogging:"true"`
}

type GetBaseCurrencyPriceResponse struct {
type GetBaseCurrencyPriceRequest struct {
Entries []*BaseCurrencyPriceRequestEntry `safeForLogging:"true"`
}

type BaseCurrencyPriceResponseEntry struct {
// It's useful to include the quote currency price used for USD conversions
QuoteCurrencyPriceInUsd float64 `safeForLogging:"true"`

Expand All @@ -621,49 +625,41 @@ type GetBaseCurrencyPriceResponse struct {
ExecutionPriceInUsd float64 `safeForLogging:"true"`
}

func (fes *APIServer) GetBaseCurrencyPriceEndpoint(ww http.ResponseWriter, req *http.Request) {
decoder := json.NewDecoder(io.LimitReader(req.Body, MaxRequestBodySizeBytes))
requestData := GetBaseCurrencyPriceRequest{}
if err := decoder.Decode(&requestData); err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice: Problem parsing request body: %v", err))
return
}
type GetBaseCurrencyPriceResponse struct {
Entries []*BaseCurrencyPriceResponseEntry `safeForLogging:"true"`
}

utxoView, err := fes.backendServer.GetMempool().GetAugmentedUniversalView()
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice: Error fetching mempool view: %v", err))
return
}
func (fes *APIServer) GetBaseCurrencyPriceForEntry(
utxoView *lib.UtxoView,
entry *BaseCurrencyPriceRequestEntry,
) (*BaseCurrencyPriceResponseEntry, error) {

quotePkid, err := fes.getPKIDFromPublicKeyBase58CheckOrDESOString(
utxoView,
requestData.QuoteCurrencyPublicKeyBase58Check,
entry.QuoteCurrencyPublicKeyBase58Check,
)
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice: Error getting quote pkid: %v", err))
return
return nil, fmt.Errorf("Error getting quote pkid: %v", err)
}

basePkid, err := fes.getPKIDFromPublicKeyBase58CheckOrDESOString(
utxoView,
requestData.BaseCurrencyPublicKeyBase58Check,
entry.BaseCurrencyPublicKeyBase58Check,
)
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice: Error getting base pkid: %v", err))
return
return nil, fmt.Errorf("Error getting base pkid: %v", err)
}

// Super annoying, but it takes two fetches to get all the orders for a market
ordersSide1, err := utxoView.GetAllDAOCoinLimitOrdersForThisDAOCoinPair(
basePkid, quotePkid)
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice: Error getting limit orders: %v", err))
return
return nil, fmt.Errorf("Error getting limit orders: %v", err)
}
ordersSide2, err := utxoView.GetAllDAOCoinLimitOrdersForThisDAOCoinPair(
quotePkid, basePkid)
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice: Error getting limit orders: %v", err))
return
return nil, fmt.Errorf("Error getting limit orders: %v", err)
}
allOrders := append(ordersSide1, ordersSide2...)

Expand Down Expand Up @@ -692,8 +688,7 @@ func (fes *APIServer) GetBaseCurrencyPriceEndpoint(ww http.ResponseWriter, req *
lib.PkToString(order.SellingDAOCoinCreatorPKID[:], fes.Params),
order.ScaledExchangeRateCoinsToSellPerCoinToBuy)
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice for bid: Error calculating price: %v", err))
return
return nil, fmt.Errorf("GetBaseCurrencyPrice for bid: Error calculating price: %v", err)
}

// The quantity is tricky. If we had a bid order then the quantity is just the
Expand All @@ -706,8 +701,7 @@ func (fes *APIServer) GetBaseCurrencyPriceEndpoint(ww http.ResponseWriter, req *
DAOCoinLimitOrderOperationTypeString(order.OperationType.String()),
order.QuantityToFillInBaseUnits)
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice: Error calculating quantity: %v", err))
return
return nil, fmt.Errorf("GetBaseCurrencyPrice: Error calculating quantity: %v", err)
}
if order.OperationType == lib.DAOCoinLimitOrderOperationTypeASK {
// If the order is an ask, then the quantity is the amount of quote currency
Expand All @@ -731,13 +725,11 @@ func (fes *APIServer) GetBaseCurrencyPriceEndpoint(ww http.ResponseWriter, req *
lib.PkToString(order.SellingDAOCoinCreatorPKID[:], fes.Params),
order.ScaledExchangeRateCoinsToSellPerCoinToBuy)
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice for ask: Error calculating price: %v", err))
return
return nil, fmt.Errorf("GetBaseCurrencyPrice for ask: Error calculating price: %v", err)
}
if priceFloat == 0.0 {
// We should never see an order with a zero price so error if we see one.
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice: Zero price order: %v", order))
return
return nil, fmt.Errorf("GetBaseCurrencyPrice: Zero price order: %v", order)
}
priceFloat = 1.0 / priceFloat

Expand All @@ -751,8 +743,7 @@ func (fes *APIServer) GetBaseCurrencyPriceEndpoint(ww http.ResponseWriter, req *
DAOCoinLimitOrderOperationTypeString(order.OperationType.String()),
order.QuantityToFillInBaseUnits)
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice: Error calculating quantity: %v", err))
return
return nil, fmt.Errorf("GetBaseCurrencyPrice: Error calculating quantity: %v", err)
}
if order.OperationType == lib.DAOCoinLimitOrderOperationTypeBID {
// If the order is an bid, then the quantity is the amount of quote currency
Expand Down Expand Up @@ -792,7 +783,7 @@ func (fes *APIServer) GetBaseCurrencyPriceEndpoint(ww http.ResponseWriter, req *

// Iterate through the bids "filling" orders until we hit the base currency
// quantity we're looking for.
baseCurrencyToFill := big.NewFloat(requestData.BaseCurrencyQuantityToSell)
baseCurrencyToFill := big.NewFloat(entry.BaseCurrencyQuantityToSell)
for _, bid := range simpleBidOrders {
// If the amount filled plus the amount we're about to fill is greater
// than the amount we're looking to fill, then we just partially fill
Expand Down Expand Up @@ -826,15 +817,13 @@ func (fes *APIServer) GetBaseCurrencyPriceEndpoint(ww http.ResponseWriter, req *

// Get the price of the quote currency in usd. Use the mid price
quoteCurrencyPriceInUsdStr, _, _, err := fes.GetQuoteCurrencyPriceInUsd(
requestData.QuoteCurrencyPublicKeyBase58Check)
entry.QuoteCurrencyPublicKeyBase58Check)
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice: Problem getting quote currency price in usd: %v", err))
return
return nil, fmt.Errorf("Problem getting quote currency price in usd: %v", err)
}
quoteCurrencyPriceInUsd, err := strconv.ParseFloat(quoteCurrencyPriceInUsdStr, 64)
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice: Problem parsing quote currency price in usd: %v", err))
return
return nil, fmt.Errorf("Problem parsing quote currency price in usd: %v", err)
}

// If any of these are too big for the Float64() it's better to best-effort
Expand All @@ -846,7 +835,9 @@ func (fes *APIServer) GetBaseCurrencyPriceEndpoint(ww http.ResponseWriter, req *
priceInQuoteCurrencyFloat, _ := priceInQuoteCurrency.Float64()
executionPriceInUsdFloat, _ := big.NewFloat(0).Mul(
priceInQuoteCurrency, big.NewFloat(quoteCurrencyPriceInUsd)).Float64()
res := &GetBaseCurrencyPriceResponse{

// Useful for computing "cashout" values on the wallet page
responseEntry := &BaseCurrencyPriceResponseEntry{
QuoteCurrencyPriceInUsd: quoteCurrencyPriceInUsd,

// Traditional price values
Expand All @@ -864,7 +855,38 @@ func (fes *APIServer) GetBaseCurrencyPriceEndpoint(ww http.ResponseWriter, req *
ExecutionPriceInQuoteCurrency: priceInQuoteCurrencyFloat,
ExecutionPriceInUsd: executionPriceInUsdFloat,
}
if err := json.NewEncoder(ww).Encode(res); err != nil {

return responseEntry, nil
}

func (fes *APIServer) GetBaseCurrencyPriceEndpoint(ww http.ResponseWriter, req *http.Request) {
decoder := json.NewDecoder(io.LimitReader(req.Body, MaxRequestBodySizeBytes))
requestData := GetBaseCurrencyPriceRequest{}
if err := decoder.Decode(&requestData); err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice: Problem parsing request body: %v", err))
return
}

utxoView, err := fes.backendServer.GetMempool().GetAugmentedUniversalView()
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice: Error fetching mempool view: %v", err))
return
}

response := &GetBaseCurrencyPriceResponse{
Entries: []*BaseCurrencyPriceResponseEntry{},
}

for _, entry := range requestData.Entries {
respEntry, err := fes.GetBaseCurrencyPriceForEntry(utxoView, entry)
if err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetBaseCurrencyPrice: %v", err))
return
}
response.Entries = append(response.Entries, respEntry)
}

if err := json.NewEncoder(ww).Encode(response); err != nil {
_AddBadRequestError(ww, fmt.Sprintf("GetQuoteCurrencyPriceInUsd: Problem encoding response: %v", err))
return
}
Expand Down

0 comments on commit 4cb450d

Please sign in to comment.