8000 Add longTermFeeRate websocket endpoint by grdddj · Pull Request #1262 · trezor/blockbook · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

Add longTermFeeRate websocket endpoint #1262

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jun 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions api/types.go
Open in desktop
Original file line number Diff line number Diff line change
Expand Up @@ -620,3 +620,8 @@ type Eip1559Fees struct {
PriorityFeeTrend string `json:"priorityFeeTrend,omitempty" ts_type:"'up' | 'down'"`
BaseFeeTrend string `json:"baseFeeTrend,omitempty" ts_type:"'up' | 'down'"`
}

type LongTermFeeRate struct {
FeePerUnit string `json:"feePerUnit" ts_doc:"Long term fee rate (in sat/kByte)."`
Blocks uint64 `json:"blocks" ts_doc:"Amount of blocks used for the long term fee rate estimation."`
}
5 changes: 5 additions & 0 deletions bchain/basechain.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ func (b *BaseChain) GetMempoolEntry(txid string) (*MempoolEntry, error) {
return nil, errors.New("GetMempoolEntry: not supported")
}

// LongTermFeeRate returns smallest fee rate from historic blocks.
func (b *BaseChain) LongTermFeeRate() (*LongTermFeeRate, error) {
return nil, errors.New("not supported")
}

// EthereumTypeGetBalance is not supported
func (b *BaseChain) EthereumTypeGetBalance(addrDesc AddressDescriptor) (*big.Int, error) {
return nil, errors.New("not supported")
Expand Down
5 changes: 5 additions & 0 deletions bchain/coins/blockchain.go
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,11 @@ func (c *blockChainWithMetrics) EstimateFee(blocks int) (v big.Int, err error) {
return c.b.EstimateFee(blocks)
}

func (c *blockChainWithMetrics) LongTermFeeRate() (v *bchain.LongTermFeeRate, err error) {
defer func(s time.Time) { c.observeRPCLatency("LongTermFeeRate", s, err) }(time.Now())
return c.b.LongTermFeeRate()
}

func (c *blockChainWithMetrics) SendRawTransaction(tx string) (v string, err error) {
defer func(s time.Time) { c.observeRPCLatency("SendRawTransaction", s, err) }(time.Now())
return c.b.SendRawTransaction(tx)
Expand Down
15 changes: 15 additions & 0 deletions bchain/coins/btc/bitcoinrpc.go 10000
Original file line number Diff line number Diff line change
Expand Up @@ -873,6 +873,21 @@ func (b *BitcoinRPC) EstimateFee(blocks int) (big.Int, error) {
return r, nil
}

// LongTermFeeRate returns smallest fee rate from historic blocks.
func (b *BitcoinRPC) LongTermFeeRate() (*bchain.LongTermFeeRate, error) {
blocks := 1008 // ~7 days of blocks, highest number estimatesmartfee supports
glog.V(1).Info("rpc: estimatesmartfee (long term fee rate) - ", blocks)
// Going for the ECONOMICAL mode, to get the lowest fee rate
feePerUnit, err := b.blockchainEstimateSmartFee(blocks, false)
if err != nil {
return nil, err
}
return &bchain.LongTermFeeRate{
Blocks: uint64(blocks),
FeePerUnit: feePerUnit,
}, nil
}

// SendRawTransaction sends raw transaction
func (b *BitcoinRPC) SendRawTransaction(tx string) (string, error) {
glog.V(1).Info("rpc: sendrawtransaction")
Expand Down
7 changes: 7 additions & 0 deletions bchain/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,12 @@ type ChainInfo struct {
Consensus interface{} `json:"consensus,omitempty" ts_doc:"Additional consensus details, structure depends on chain."`
}

// LongTermFeeRate gets information about the fee rate over longer period of time.
type LongTermFeeRate struct {
FeePerUnit big.Int `json:"feePerUnit" ts_doc:"Long term fee rate (in sat/kByte)."`
Blocks uint64 `json:"blocks" ts_doc:"Amount of blocks used for the long term fee rate estimation."`
}

// RPCError defines rpc error returned by backend
type RPCError struct {
Code int `json:"code" ts_doc:"Error code returned by the backend RPC."`
Expand Down Expand Up @@ -324,6 +330,7 @@ type BlockChain interface {
GetTransactionSpecific(tx *Tx) (json.RawMessage, error)
EstimateSmartFee(blocks int, conservative bool) (big.Int, error)
EstimateFee(blocks int) (big.Int, error)
LongTermFeeRate() (*LongTermFeeRate, error)
SendRawTransaction(tx string) (string, error)
GetMempoolEntry(txid string) (*MempoolEntry, error)
GetContractInfo(contractDesc AddressDescriptor) (*ContractInfo, error)
Expand Down
6 changes: 6 additions & 0 deletions blockbook-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -745,6 +745,12 @@ export interface WsEstimateFeeRes {
feeLimit?: string;
eip1559?: Eip1559Fees;
}
export interface WsLongTermFeeRateRes {
/** Long term fee rate (in sat/kByte). */
feePerUnit: string;
/** Amount of blocks used for the long term fee rate estimation. */
blocks: number;
}
export interface WsSendTransactionReq {
/** Hex-encoded transaction data to broadcast. */
hex: string;
Expand Down
1 change: 1 addition & 0 deletions build/tools/typescriptify/typescriptify.go
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ func main() {
t.Add(server.WsTransactionSpecificReq{})
t.Add(server.WsEstimateFeeReq{})
t.Add(server.WsEstimateFeeRes{})
t.Add(server.WsLongTermFeeRateRes{})
t.Add(server.WsSendTransactionReq{})
t.Add(server.WsSubscribeAddressesReq{})
t.Add(server.WsSubscribeFiatRatesReq{})
Expand Down
14 changes: 14 additions & 0 deletions server/websocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,9 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *WsRe
"estimateFee": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
return s.estimateFee(req.Params)
},
"longTermFeeRate": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
return s.longTermFeeRate()
},
"sendTransaction": func(s *WebsocketServer, c *websocketChannel, req *WsReq) (rv interface{}, err error) {
r := WsSendTransactionReq{}
err = json.Unmarshal(req.Params, &r)
Expand Down Expand Up @@ -737,6 +740,17 @@ func (s *WebsocketServer) estimateFee(params []byte) (interface{}, error) {
return res, nil
}

func (s *WebsocketServer) longTermFeeRate() (res interface{}, err error) {
feeRate, err := s.chain.LongTermFeeRate()
if err != nil {
return nil, err
}
return WsLongTermFeeRateRes{
FeePerUnit: feeRate.FeePerUnit.String(),
Blocks: feeRate.Blocks,
}, nil
}

func (s *WebsocketServer) sendTransaction(tx string) (res resultSendTransaction, err error) {
txid, err := s.chain.SendRawTransaction(tx)
if err != nil {
Expand Down
6 changes: 6 additions & 0 deletions server/ws_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,12 @@ type WsEstimateFeeRes struct {
Eip1559 *api.Eip1559Fees `json:"eip1559,omitempty"`
}

// WsLongTermFeeRateRes is returned in response to a long term fee rate request.
type WsLongTermFeeRateRes struct {
FeePerUnit string `json:"feePerUnit" ts_doc:"Long term fee rate (in sat/kByte)."`
Blocks uint64 `json:"blocks" ts_doc:"Amount of blocks used for the long term fee rate estimation."`
}

// WsSendTransactionReq is used to broadcast a transaction to the network.
type WsSendTransactionReq struct {
Hex string `json:"hex" ts_doc:"Hex-encoded transaction data to broadcast."`
Expand Down
27 changes: 27 additions & 0 deletions static/test-websocket.html
Original file line number Diff line number Diff line change
Expand Up @@ -289,6 +289,19 @@
}
}

function longTermFeeRate() {
try {
const method = 'longTermFeeRate';
send(method, {}, function (result) {
document.getElementById('longTermFeeRateResult').innerText = JSON.stringify(
result,
).replace(/,/g, ', ');
});
} catch (e) {
document.getElementById('longTermFeeRateResult').innerText = e;
}
}

function sendTransaction() {
var hex = document.getElementById('sendTransactionHex').value.trim();
const method = 'sendTransaction';
Expand Down Expand Up @@ -892,6 +905,20 @@ <h1>Blockbook Websocket Test Page</h1>
<div class="row">
<div class="col" id="estimateFeeResult"></div>
</div>
<div class="row">
<div class="col">
<input
class="btn btn-secondary"
type="button"
value="longTermFeeRate"
>
/>
</div>
<div class="col"></div>
</div>
<div class="row">
<div class="col" id="longTermFeeRateResult"></div>
</div>
<div class="row">
<div class="col">
<input
Expand Down
0