package fleet import ( "bytes" "context" "encoding/json" "net/http" "time" ) // Markets counts the venues listed on a network's native DEX graph. type Markets interface { Count(ctx context.Context, network string) (int, bool) } // graphHost is the explorer that serves each network's DEX graph. var graphHost = map[string]string{ "mainnet": "explorer.lux.network", "testnet": "explorer.testnet.lux.network", "devnet": "explorer.devnet.lux.network", } // Graph reads the market count from the explorer's DEX GraphQL endpoint. // A network whose graph is not up simply reports no count. type Graph struct{ http *http.Client } // NewGraph returns a market counter with a short budget — the count is a // garnish on the overview, never a reason to hold it up. func NewGraph() *Graph { return &Graph{http: &http.Client{Timeout: 4 * time.Second}} } // Count returns the number of markets, and whether the graph answered. func (g *Graph) Count(ctx context.Context, network string) (int, bool) { host, ok := graphHost[network] if !ok { return 0, false } body := []byte(`{"query":"{ markets { id } }"}`) req, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://"+host+"/v1/graph/lux/dex/graphql", bytes.NewReader(body)) if err != nil { return 0, false } req.Header.Set("Content-Type", "application/json") resp, err := g.http.Do(req) if err != nil { return 0, false } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return 0, false } var out struct { Data struct { Markets []json.RawMessage `json:"markets"` } `json:"data"` } if json.NewDecoder(resp.Body).Decode(&out) != nil || out.Data.Markets == nil { return 0, false } return len(out.Data.Markets), true }