Skip to content
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

feat: support dynamic CKB fee rate #17

Merged
merged 1 commit into from
Apr 11, 2024
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
2 changes: 1 addition & 1 deletion src/cli/deploy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ impl Args {
NetworkInfo::new(self.ckb.network, self.ckb.ckb_endpoint.as_str().to_owned());
let configuration = {
let mut tmp = TransactionBuilderConfiguration::new_with_network(network_info.clone())?;
tmp.fee_rate = self.ckb.fee_rate;
tmp.fee_rate = self.ckb.fee_rate()?;
tmp
};

Expand Down
2 changes: 1 addition & 1 deletion src/cli/init.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ impl Args {
NetworkInfo::new(self.ckb.network, self.ckb.ckb_endpoint.as_str().to_owned());
let configuration = {
let mut tmp = TransactionBuilderConfiguration::new_with_network(network_info.clone())?;
tmp.fee_rate = self.ckb.fee_rate;
tmp.fee_rate = self.ckb.fee_rate()?;
tmp
};

Expand Down
68 changes: 62 additions & 6 deletions src/cli/mod.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
//! The command line argument.

use ckb_sdk::{rpc::CkbRpcClient, types::NetworkType};
use clap::{Parser, Subcommand};
use ckb_types::core::FeeRate;
use clap::{Args, Parser, Subcommand};
use clap_verbosity_flag::{InfoLevel, Verbosity};
use url::Url;

use crate::{
components::BitcoinClient,
prelude::*,
result::Result,
utilities::{value_parsers, Key256Bits},
};
Expand Down Expand Up @@ -66,11 +68,10 @@ pub struct CkbArgs {
value_parser = value_parsers::NetworkTypeValueParser,
default_value = "testnet"
)]
pub network: NetworkType,
pub(crate) network: NetworkType,

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

/// A binary file, which contains a secp256k1 private key.
/// This private key will be used to provide all CKBytes.
Expand All @@ -92,7 +93,39 @@ pub struct CkbRoArgs {
value_parser = value_parsers::NetworkTypeValueParser,
default_value = "testnet"
)]
pub network: NetworkType,
pub(crate) network: NetworkType,
}

#[derive(Args)]
#[group(multiple = false)]
pub struct FeeRateArgs {
/// The fixed fee rate for CKB transactions.
#[arg(
group = "fixed-fee-rate",
conflicts_with = "dynamic-fee-rate",
long = "ckb-fee-rate",
default_value = "1000"
)]
fixed_value: u64,

/// [Experimental] Enable dynamic fee rate for CKB transactions.
///
/// The actual fee rate will be the `median` fee rate which is fetched through the CKB RPC method `get_fee_rate_statistics`.
///
/// For security, a hard limit is required.
/// When the returned dynamic fee rate is larger than the hard limit, the hard limit will be used.
///
/// ### Warning
///
/// Users have to make sure the remote CKB node they used are trustsed.
///
/// Ref: <https://github.com/nervosnetwork/ckb/tree/v0.114.0/rpc#method-get_fee_rate_statistics>
#[arg(
group = "dynamic-fee-rate",
conflicts_with = "fixed-fee-rate",
long = "enable-dynamic-ckb-fee-rate-with-limit"
)]
limit_for_dynamic: Option<u64>,
}

#[derive(Parser)]
Expand Down Expand Up @@ -160,6 +193,29 @@ impl CkbArgs {
pub fn client(&self) -> CkbRpcClient {
CkbRpcClient::new(self.ckb_endpoint.as_str())
}

pub fn fee_rate(&self) -> Result<u64> {
let value = if let Some(limit) = self.fee_rate.limit_for_dynamic {
let dynamic = self.client().dynamic_fee_rate()?;
log::info!("CKB fee rate: {} (dynamic)", FeeRate(dynamic));
if dynamic > limit {
log::warn!(
"dynamic CKB fee rate {} is too large, it seems unreasonable;\
so the upper limit {} will be used",
FeeRate(dynamic),
FeeRate(limit)
);
limit
} else {
dynamic
}
} else {
let fixed = self.fee_rate.fixed_value;
log::info!("CKB fee rate: {} (fixed)", FeeRate(fixed));
fixed
};
Ok(value)
}
}

impl CkbRoArgs {
Expand Down
4 changes: 2 additions & 2 deletions src/cli/serve.rs
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ impl Args {
NetworkInfo::new(self.ckb.network, self.ckb.ckb_endpoint.as_str().to_owned());
let configuration = {
let mut tmp = TransactionBuilderConfiguration::new_with_network(network_info.clone())?;
tmp.fee_rate = self.ckb.fee_rate;
tmp.fee_rate = self.ckb.fee_rate()?;
tmp
};

Expand Down Expand Up @@ -357,7 +357,7 @@ impl Args {
NetworkInfo::new(self.ckb.network, self.ckb.ckb_endpoint.as_str().to_owned());
let configuration = {
let mut tmp = TransactionBuilderConfiguration::new_with_network(network_info.clone())?;
tmp.fee_rate = self.ckb.fee_rate;
tmp.fee_rate = self.ckb.fee_rate()?;
tmp
};

Expand Down
14 changes: 14 additions & 0 deletions src/components/ckb_client.rs
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ impl SpvInfoCell {
}

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

Expand Down Expand Up @@ -109,6 +110,17 @@ pub trait CkbRpcClientExtension {
}

impl CkbRpcClientExtension for CkbRpcClient {
fn dynamic_fee_rate(&self) -> Result<u64> {
self.get_fee_rate_statistics(None)?
.ok_or_else(|| {
let msg = "remote server replied null for \
RPC method get_fee_rate_statistics(null)";
Error::other(msg)
})
.map(|resp| resp.median)
.map(Into::into)
}

fn send_transaction_ext(&self, tx_json: TransactionView, dry_run: bool) -> Result<H256> {
if log::log_enabled!(log::Level::Trace) {
match serde_json::to_string_pretty(&tx_json) {
Expand Down Expand Up @@ -147,6 +159,8 @@ impl CkbRpcClientExtension for CkbRpcClient {
})?
.unpack();

log::trace!("the type script of SPV cell is {spv_type_script}");

let query = CellQueryOptions::new(spv_type_script, PrimaryScriptType::Type);
let order = Order::Desc;
let search_key = SearchKey::from(query);
Expand Down