Skip to content

Commit 5f9bbc8

Browse files
Merge pull request #17 from ckb-cell/feature/dynamic-fee-rate
feat: support dynamic CKB fee rate
2 parents fc5b5b8 + 03bd8ff commit 5f9bbc8

File tree

5 files changed

+80
-10
lines changed

5 files changed

+80
-10
lines changed

src/cli/deploy.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ impl Args {
7474
NetworkInfo::new(self.ckb.network, self.ckb.ckb_endpoint.as_str().to_owned());
7575
let configuration = {
7676
let mut tmp = TransactionBuilderConfiguration::new_with_network(network_info.clone())?;
77-
tmp.fee_rate = self.ckb.fee_rate;
77+
tmp.fee_rate = self.ckb.fee_rate()?;
7878
tmp
7979
};
8080

src/cli/init.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ impl Args {
134134
NetworkInfo::new(self.ckb.network, self.ckb.ckb_endpoint.as_str().to_owned());
135135
let configuration = {
136136
let mut tmp = TransactionBuilderConfiguration::new_with_network(network_info.clone())?;
137-
tmp.fee_rate = self.ckb.fee_rate;
137+
tmp.fee_rate = self.ckb.fee_rate()?;
138138
tmp
139139
};
140140

src/cli/mod.rs

+62-6
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
//! The command line argument.
22
33
use ckb_sdk::{rpc::CkbRpcClient, types::NetworkType};
4-
use clap::{Parser, Subcommand};
4+
use ckb_types::core::FeeRate;
5+
use clap::{Args, Parser, Subcommand};
56
use clap_verbosity_flag::{InfoLevel, Verbosity};
67
use url::Url;
78

89
use crate::{
910
components::BitcoinClient,
11+
prelude::*,
1012
result::Result,
1113
utilities::{value_parsers, Key256Bits},
1214
};
@@ -66,11 +68,10 @@ pub struct CkbArgs {
6668
value_parser = value_parsers::NetworkTypeValueParser,
6769
default_value = "testnet"
6870
)]
69-
pub network: NetworkType,
71+
pub(crate) network: NetworkType,
7072

71-
/// The fee rate for CKB transactions.
72-
#[arg(long = "ckb-fee-rate", default_value = "1000")]
73-
pub(crate) fee_rate: u64,
73+
#[command(flatten)]
74+
pub(crate) fee_rate: FeeRateArgs,
7475

7576
/// A binary file, which contains a secp256k1 private key.
7677
/// This private key will be used to provide all CKBytes.
@@ -92,7 +93,39 @@ pub struct CkbRoArgs {
9293
value_parser = value_parsers::NetworkTypeValueParser,
9394
default_value = "testnet"
9495
)]
95-
pub network: NetworkType,
96+
pub(crate) network: NetworkType,
97+
}
98+
99+
#[derive(Args)]
100+
#[group(multiple = false)]
101+
pub struct FeeRateArgs {
102+
/// The fixed fee rate for CKB transactions.
103+
#[arg(
104+
group = "fixed-fee-rate",
105+
conflicts_with = "dynamic-fee-rate",
106+
long = "ckb-fee-rate",
107+
default_value = "1000"
108+
)]
109+
fixed_value: u64,
110+
111+
/// [Experimental] Enable dynamic fee rate for CKB transactions.
112+
///
113+
/// The actual fee rate will be the `median` fee rate which is fetched through the CKB RPC method `get_fee_rate_statistics`.
114+
///
115+
/// For security, a hard limit is required.
116+
/// When the returned dynamic fee rate is larger than the hard limit, the hard limit will be used.
117+
///
118+
/// ### Warning
119+
///
120+
/// Users have to make sure the remote CKB node they used are trustsed.
121+
///
122+
/// Ref: <https://github.com/nervosnetwork/ckb/tree/v0.114.0/rpc#method-get_fee_rate_statistics>
123+
#[arg(
124+
group = "dynamic-fee-rate",
125+
conflicts_with = "fixed-fee-rate",
126+
long = "enable-dynamic-ckb-fee-rate-with-limit"
127+
)]
128+
limit_for_dynamic: Option<u64>,
96129
}
97130

98131
#[derive(Parser)]
@@ -160,6 +193,29 @@ impl CkbArgs {
160193
pub fn client(&self) -> CkbRpcClient {
161194
CkbRpcClient::new(self.ckb_endpoint.as_str())
162195
}
196+
197+
pub fn fee_rate(&self) -> Result<u64> {
198+
let value = if let Some(limit) = self.fee_rate.limit_for_dynamic {
199+
let dynamic = self.client().dynamic_fee_rate()?;
200+
log::info!("CKB fee rate: {} (dynamic)", FeeRate(dynamic));
201+
if dynamic > limit {
202+
log::warn!(
203+
"dynamic CKB fee rate {} is too large, it seems unreasonable;\
204+
so the upper limit {} will be used",
205+
FeeRate(dynamic),
206+
FeeRate(limit)
207+
);
208+
limit
209+
} else {
210+
dynamic
211+
}
212+
} else {
213+
let fixed = self.fee_rate.fixed_value;
214+
log::info!("CKB fee rate: {} (fixed)", FeeRate(fixed));
215+
fixed
216+
};
217+
Ok(value)
218+
}
163219
}
164220

165221
impl CkbRoArgs {

src/cli/serve.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -185,7 +185,7 @@ impl Args {
185185
NetworkInfo::new(self.ckb.network, self.ckb.ckb_endpoint.as_str().to_owned());
186186
let configuration = {
187187
let mut tmp = TransactionBuilderConfiguration::new_with_network(network_info.clone())?;
188-
tmp.fee_rate = self.ckb.fee_rate;
188+
tmp.fee_rate = self.ckb.fee_rate()?;
189189
tmp
190190
};
191191

@@ -357,7 +357,7 @@ impl Args {
357357
NetworkInfo::new(self.ckb.network, self.ckb.ckb_endpoint.as_str().to_owned());
358358
let configuration = {
359359
let mut tmp = TransactionBuilderConfiguration::new_with_network(network_info.clone())?;
360-
tmp.fee_rate = self.ckb.fee_rate;
360+
tmp.fee_rate = self.ckb.fee_rate()?;
361361
tmp
362362
};
363363

src/components/ckb_client.rs

+14
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ impl SpvInfoCell {
6262
}
6363

6464
pub trait CkbRpcClientExtension {
65+
fn dynamic_fee_rate(&self) -> Result<u64>;
6566
fn send_transaction_ext(&self, tx_json: TransactionView, dry_run: bool) -> Result<H256>;
6667
fn find_raw_spv_cells(&self, spv_type_script: Script) -> Result<Vec<LiveCell>>;
6768

@@ -109,6 +110,17 @@ pub trait CkbRpcClientExtension {
109110
}
110111

111112
impl CkbRpcClientExtension for CkbRpcClient {
113+
fn dynamic_fee_rate(&self) -> Result<u64> {
114+
self.get_fee_rate_statistics(None)?
115+
.ok_or_else(|| {
116+
let msg = "remote server replied null for \
117+
RPC method get_fee_rate_statistics(null)";
118+
Error::other(msg)
119+
})
120+
.map(|resp| resp.median)
121+
.map(Into::into)
122+
}
123+
112124
fn send_transaction_ext(&self, tx_json: TransactionView, dry_run: bool) -> Result<H256> {
113125
if log::log_enabled!(log::Level::Trace) {
114126
match serde_json::to_string_pretty(&tx_json) {
@@ -147,6 +159,8 @@ impl CkbRpcClientExtension for CkbRpcClient {
147159
})?
148160
.unpack();
149161

162+
log::trace!("the type script of SPV cell is {spv_type_script}");
163+
150164
let query = CellQueryOptions::new(spv_type_script, PrimaryScriptType::Type);
151165
let order = Order::Desc;
152166
let search_key = SearchKey::from(query);

0 commit comments

Comments
 (0)