153 lines
3.4 KiB
Go
153 lines
3.4 KiB
Go
package client
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
|
|
"github.com/gorilla/websocket"
|
|
)
|
|
|
|
const (
|
|
API_URL = "https://mempool.space/api/v1/ws"
|
|
)
|
|
|
|
type MempoolInfo struct {
|
|
Size int `json:"size"`
|
|
Bytes int `json:"bytes"`
|
|
}
|
|
|
|
type Block struct {
|
|
ID string `json:"id"`
|
|
Height int `json:"height"`
|
|
TxCount int `json:"tx_count"`
|
|
Size int `json:"size"`
|
|
Time int `json:"timestamp"`
|
|
Weight int `json:"weight"`
|
|
FeeRange []float64 `json:"feeRange"`
|
|
MedianFee float64 `json:"medianFee"`
|
|
}
|
|
|
|
type MempoolBlock struct {
|
|
BlockSize int `json:"blockSize"`
|
|
BlockWeight float64 `json:"blockVSize"`
|
|
NTx int `json:"nTx"`
|
|
MinWeigthFee float64 `json:"minWeigthFee"`
|
|
MaxWeigthFee float64 `json:"maxWeigthFee"`
|
|
MedianFee float64 `json:"medianFee"`
|
|
FeeRange []float64 `json:"feeRange"`
|
|
HasMyTx bool `json:"hasMytx"`
|
|
}
|
|
|
|
type TrackTx struct {
|
|
Tracking bool `json:"tracking"`
|
|
BlockHeight int `json:"blockHeight"`
|
|
Message string `json:"message"`
|
|
TX struct {
|
|
Status struct {
|
|
Confirmed bool
|
|
}
|
|
} `json:"tx"`
|
|
}
|
|
|
|
type Response struct {
|
|
MempoolInfo *MempoolInfo `json:"mempoolInfo"`
|
|
|
|
Block *Block `json:"block"`
|
|
Blocks []Block `json:"blocks"`
|
|
|
|
MempoolBlocks []MempoolBlock `json:"mempool-blocks"`
|
|
TrackTx TrackTx `json:"track-tx"`
|
|
|
|
TxPerSecond float64 `json:"txPerSecond"`
|
|
VBytesPerSecond int `json:"vBytesPerSecond"`
|
|
Conversions struct {
|
|
BTC float64 `json:"BTC"`
|
|
USD float64 `json:"USD"`
|
|
} `json:"conversions"`
|
|
}
|
|
|
|
type Client struct {
|
|
conn *websocket.Conn
|
|
}
|
|
|
|
func New() (*Client, error) {
|
|
dialer := websocket.Dialer{}
|
|
conn, _, err := dialer.Dial("wss://mempool.space/api/v1/ws", nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
conn.WriteMessage(websocket.TextMessage, []byte(
|
|
`{"action": "init"}`,
|
|
))
|
|
|
|
return &Client{conn: conn}, nil
|
|
}
|
|
|
|
func (c *Client) Read() (*Response, error) {
|
|
var resp Response
|
|
if err := c.conn.ReadJSON(&resp); err != nil {
|
|
return nil, err
|
|
}
|
|
return &resp, nil
|
|
}
|
|
|
|
func (c *Client) Want() error {
|
|
return c.conn.WriteMessage(websocket.TextMessage, []byte(
|
|
`{"action":"want","data":["stats","blocks","mempool-blocks"]}`,
|
|
))
|
|
}
|
|
|
|
func (c *Client) Track(txId string) error {
|
|
return c.conn.WriteMessage(websocket.TextMessage, []byte(
|
|
fmt.Sprintf(`{"action":"track-tx","txId":"%s"}`, txId),
|
|
))
|
|
}
|
|
|
|
type Fees []struct {
|
|
FPV float64 `json:"fpv"`
|
|
}
|
|
|
|
func (f Fees) Len() int { return len(f) }
|
|
func (f Fees) Less(i, j int) bool { return f[i].FPV < f[j].FPV }
|
|
func (f Fees) Swap(i, j int) { f[i], f[j] = f[j], f[i] }
|
|
|
|
func Get(ctx context.Context, path string, v interface{}) error {
|
|
req, err := http.NewRequest("GET", API_URL+path, nil)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
req = req.WithContext(ctx)
|
|
|
|
r, err := http.DefaultClient.Do(req)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
defer r.Body.Close()
|
|
|
|
if s := r.StatusCode; s != 200 {
|
|
return fmt.Errorf("status %d", s)
|
|
}
|
|
|
|
return json.NewDecoder(r.Body).Decode(v)
|
|
}
|
|
|
|
func GetMempoolFee(ctx context.Context, n int) (Fees, error) {
|
|
var fees Fees
|
|
if err := Get(ctx, fmt.Sprintf("transactions/mempool/%d", n), &fees); err != nil {
|
|
return nil, err
|
|
}
|
|
return fees, nil
|
|
}
|
|
|
|
func GetBlockFee(ctx context.Context, n int) (Fees, error) {
|
|
var fees Fees
|
|
if err := Get(ctx, fmt.Sprintf("transactions/height/%d", n), &fees); err != nil {
|
|
return nil, err
|
|
}
|
|
return fees, nil
|
|
}
|