diff --git a/routes/base.go b/routes/base.go index 7a9ff1e8..742701d7 100644 --- a/routes/base.go +++ b/routes/base.go @@ -130,10 +130,12 @@ func (fes *APIServer) GetExchangeRate(ww http.ResponseWriter, rr *http.Request) } func (fes *APIServer) GetExchangeDeSoPrice() uint64 { - if fes.UsdCentsPerDeSoExchangeRate > fes.USDCentsToDESOReserveExchangeRate { - return fes.UsdCentsPerDeSoExchangeRate - } - return fes.USDCentsToDESOReserveExchangeRate + // We no longer observe a reserve rate. + return fes.MostRecentDesoDexPriceUSDCents + //if fes.UsdCentsPerDeSoExchangeRate > fes.USDCentsToDESOReserveExchangeRate { + // return fes.UsdCentsPerDeSoExchangeRate + //} + //return fes.USDCentsToDESOReserveExchangeRate } type BlockchainDeSoTickerResponse struct { @@ -261,14 +263,14 @@ type GateTickerResponse struct { Low24H string `json:"low_24h"` } -var currencyPair string +type currencyPair string const ( - GATE_DESO_USDT = "deso_usdt" - GATE_USDT_USD = "usdt_usd" + GateDesoUsdt currencyPair = "deso_usdt" + GateUsdtUsd currencyPair = "usdt_usd" ) -func getTickerResponseFromGate(currencyPair string) (*GateTickerResponse, error) { +func getTickerResponseFromGate(currencyPair currencyPair) (*GateTickerResponse, error) { httpClient := &http.Client{} url := fmt.Sprintf("https://api.gateio.ws/api/v4/spot/tickers?currency_pair=%v", currencyPair) req, err := http.NewRequest("GET", url, nil) @@ -302,12 +304,12 @@ func getTickerResponseFromGate(currencyPair string) (*GateTickerResponse, error) } func (fes *APIServer) GetGateExchangeRate() (_exchangeRate float64, _err error) { - desoToUSDTTickerResponse, err := getTickerResponseFromGate(GATE_DESO_USDT) + desoToUSDTTickerResponse, err := getTickerResponseFromGate(GateDesoUsdt) if err != nil { glog.Errorf("GetGateExchangeRate: Problem fetching exchange rate from gate: %v", err) return 0, err } - usdtToUSDTickerResponse, err := getTickerResponseFromGate(GATE_USDT_USD) + usdtToUSDTickerResponse, err := getTickerResponseFromGate(GateUsdtUsd) if err != nil { glog.Errorf("GetGateExchangeRate: Problem fetching exchange rate from gate: %v", err) return 0, err @@ -333,6 +335,25 @@ func (fes *APIServer) GetGateExchangeRate() (_exchangeRate float64, _err error) return usdCentsToDESOExchangePrice, nil } +func (fes *APIServer) GetExchangeRateFromDeSoDex() (float64, error) { + utxoView, err := fes.backendServer.GetMempool().GetAugmentedUniversalView() + if err != nil { + return 0, err + } + usdcProfileEntry := utxoView.GetProfileEntryForUsername([]byte("dusdc_")) + if usdcProfileEntry == nil { + return 0, fmt.Errorf("GetExchangeRateFromDeSoDex: Could not find profile entry for dusdc_") + } + + usdcPKID := utxoView.GetPKIDForPublicKey(usdcProfileEntry.PublicKey) + + midPriceDESO, _, _, err := fes.GetHighestBidAndLowestAskPriceFromPKIDs(&lib.ZeroPKID, usdcPKID.PKID, utxoView, 0) + if err != nil { + return 0, err + } + return midPriceDESO, nil +} + // UpdateUSDCentsToDeSoExchangeRate updates app state's USD Cents per DeSo value func (fes *APIServer) UpdateUSDCentsToDeSoExchangeRate() { glog.V(2).Info("Refreshing exchange rate...") @@ -358,8 +379,14 @@ func (fes *APIServer) UpdateUSDCentsToDeSoExchangeRate() { glog.Errorf("UpdateUSDCentsToDeSoExchangeRate: Error fetching exchange rate from gate: %v", err) } + desoDexPrice, err := fes.GetExchangeRateFromDeSoDex() + glog.V(2).Infof("DeSoDex price (USD Cents): %v", desoDexPrice) + if err != nil { + glog.Errorf("UpdateUSDCentsToDeSoExchangeRate: Error fetching exchange rate from DeSoDex: %v", err) + } + // Take the max - lastTradePrice, err := stats.Max([]float64{blockchainDotComPrice, gatePrice}) + lastTradePrice, err := stats.Max([]float64{blockchainDotComPrice, gatePrice, desoDexPrice}) // store the most recent exchange prices // For now, we are using gate.io as the primary exchange @@ -368,6 +395,7 @@ func (fes *APIServer) UpdateUSDCentsToDeSoExchangeRate() { fes.MostRecentCoinbasePriceUSDCents = uint64(gatePrice) fes.MostRecentBlockchainDotComPriceUSDCents = uint64(blockchainDotComPrice) fes.MostRecentGatePriceUSDCents = uint64(gatePrice) + fes.MostRecentDesoDexPriceUSDCents = uint64(desoDexPrice) // Get the current timestamp and append the current last trade price to the LastTradeDeSoPriceHistory slice timestamp := uint64(time.Now().UnixNano()) diff --git a/routes/dao_coin_exchange_with_fees.go b/routes/dao_coin_exchange_with_fees.go index 7e41b01e..d24bc171 100644 --- a/routes/dao_coin_exchange_with_fees.go +++ b/routes/dao_coin_exchange_with_fees.go @@ -954,54 +954,24 @@ func (fes *APIServer) GetQuoteCurrencyPriceInUsd( if desoUsdCents == 0 { return "", "", "", fmt.Errorf("GetQuoteCurrencyPriceInUsd: Coinbase DESO price is zero") } + pkid := utxoView.GetPKIDForPublicKey(pkBytes) if pkid == nil { return "", "", "", fmt.Errorf("GetQuoteCurrencyPriceInUsd: Error getting pkid for public key %v", quoteCurrencyPublicKey) } - ordersBuyingCoin1, err := utxoView.GetAllDAOCoinLimitOrdersForThisDAOCoinPair( - &lib.ZeroPKID, pkid.PKID) - if err != nil { - return "", "", "", fmt.Errorf("GetDAOCoinLimitOrders: Error getting limit orders: %v", err) - } - ordersBuyingCoin2, err := utxoView.GetAllDAOCoinLimitOrdersForThisDAOCoinPair( - pkid.PKID, &lib.ZeroPKID) - if err != nil { - return "", "", "", fmt.Errorf("GetDAOCoinLimitOrders: Error getting limit orders: %v", err) - } - allOrders := append(ordersBuyingCoin1, ordersBuyingCoin2...) // Find the highest bid price and the lowest ask price highestBidPrice := float64(0.0) if lowerUsername == "focus" { highestBidPrice = float64(FOCUS_FLOOR_PRICE_DESO_NANOS) / float64(lib.NanosPerUnit) } - lowestAskPrice := math.MaxFloat64 - for _, order := range allOrders { - priceStr, err := CalculatePriceStringFromScaledExchangeRate( - lib.PkToString(order.BuyingDAOCoinCreatorPKID[:], fes.Params), - lib.PkToString(order.SellingDAOCoinCreatorPKID[:], fes.Params), - order.ScaledExchangeRateCoinsToSellPerCoinToBuy, - DAOCoinLimitOrderOperationTypeString(order.OperationType.String())) - if err != nil { - return "", "", "", fmt.Errorf("GetQuoteCurrencyPriceInUsd: Error calculating price: %v", err) - } - priceFloat, err := strconv.ParseFloat(priceStr, 64) - if err != nil { - return "", "", "", fmt.Errorf("GetQuoteCurrencyPriceInUsd: Error parsing price: %v", err) - } - if order.OperationType == lib.DAOCoinLimitOrderOperationTypeBID && - priceFloat > highestBidPrice { - - highestBidPrice = priceFloat - } - if order.OperationType == lib.DAOCoinLimitOrderOperationTypeASK && - priceFloat < lowestAskPrice { - - lowestAskPrice = priceFloat - } + var lowestAskPrice, midPriceDeso float64 + midPriceDeso, highestBidPrice, lowestAskPrice, err = fes.GetHighestBidAndLowestAskPriceFromPKIDs( + pkid.PKID, &lib.ZeroPKID, utxoView, highestBidPrice) + if err != nil { + return "", "", "", fmt.Errorf("GetQuoteCurrencyPriceInUsd: Error getting price: %v", err) } if highestBidPrice != 0.0 && lowestAskPrice != math.MaxFloat64 { - midPriceDeso := (highestBidPrice + lowestAskPrice) / 2.0 midPriceUsd := midPriceDeso * float64(desoUsdCents) / 100 return fmt.Sprintf("%0.9f", midPriceUsd), @@ -1018,6 +988,58 @@ func (fes *APIServer) GetQuoteCurrencyPriceInUsd( quoteCurrencyPublicKey) } +func (fes *APIServer) GetHighestBidAndLowestAskPriceFromPKIDs( + coin1PKID *lib.PKID, + coin2PKID *lib.PKID, + utxoView *lib.UtxoView, + initialHighestBidPrice float64, +) (float64, float64, float64, error) { + ordersBuyingCoin1, err := utxoView.GetAllDAOCoinLimitOrdersForThisDAOCoinPair( + coin1PKID, coin2PKID) + if err != nil { + return 0, 0, 0, fmt.Errorf("GetDAOCoinLimitOrders: Error getting limit orders: %v", err) + } + ordersBuyingCoin2, err := utxoView.GetAllDAOCoinLimitOrdersForThisDAOCoinPair( + coin2PKID, coin1PKID) + if err != nil { + return 0, 0, 0, fmt.Errorf("GetDAOCoinLimitOrders: Error getting limit orders: %v", err) + } + allOrders := append(ordersBuyingCoin1, ordersBuyingCoin2...) + // Find the highest bid price and the lowest ask price + highestBidPrice := initialHighestBidPrice + lowestAskPrice := math.MaxFloat64 + for _, order := range allOrders { + priceStr, err := CalculatePriceStringFromScaledExchangeRate( + lib.PkToString(order.BuyingDAOCoinCreatorPKID[:], fes.Params), + lib.PkToString(order.SellingDAOCoinCreatorPKID[:], fes.Params), + order.ScaledExchangeRateCoinsToSellPerCoinToBuy, + DAOCoinLimitOrderOperationTypeString(order.OperationType.String())) + if err != nil { + return 0, 0, 0, fmt.Errorf("GetQuoteCurrencyPriceInUsd: Error calculating price: %v", err) + } + priceFloat, err := strconv.ParseFloat(priceStr, 64) + if err != nil { + return 0, 0, 0, fmt.Errorf("GetQuoteCurrencyPriceInUsd: Error parsing price: %v", err) + } + if order.OperationType == lib.DAOCoinLimitOrderOperationTypeBID && + priceFloat > highestBidPrice { + + highestBidPrice = priceFloat + } + if order.OperationType == lib.DAOCoinLimitOrderOperationTypeASK && + priceFloat < lowestAskPrice { + + lowestAskPrice = priceFloat + } + } + if highestBidPrice != 0.0 && lowestAskPrice != math.MaxFloat64 { + midPrice := (highestBidPrice + lowestAskPrice) / 2.0 + + return midPrice, highestBidPrice, lowestAskPrice, nil + } + return 0, 0, 0, fmt.Errorf("GetQuoteCurrencyPriceInUsd: Error calculating price") +} + func (fes *APIServer) CreateMarketOrLimitOrder( isMarketOrder bool, request *DAOCoinLimitOrderCreationRequest, diff --git a/routes/server.go b/routes/server.go index 7611e70b..b6384640 100644 --- a/routes/server.go +++ b/routes/server.go @@ -401,6 +401,7 @@ type APIServer struct { MostRecentCoinbasePriceUSDCents uint64 MostRecentBlockchainDotComPriceUSDCents uint64 MostRecentGatePriceUSDCents uint64 + MostRecentDesoDexPriceUSDCents uint64 // Base-58 prefix to check for to determine if a string could be a public key. PublicKeyBase58Prefix string