Skip to content

Commit d5ef3c6

Browse files
committed
feat(rpc): add z_getstandardfee RPC returning the ZIP-317 marginal fee
Add a parameterless `z_getstandardfee` JSON-RPC method that returns the recommended standard fee per logical action. This is the static interface placeholder (version 0) from the draft ZIP "Dynamic Fee Estimation via z_getstandardfee": it returns the current ZIP-317 marginal fee (5000 zatoshis) so wallets can integrate against a stable method signature now, while a future change replaces the value with a dynamic estimate and increments the version field without altering the result shape. The result object is { standard_fee, version, height }, matching the draft ZIP's specification.
1 parent 990dcb6 commit d5ef3c6

5 files changed

Lines changed: 96 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,13 @@ and this project adheres to [Semantic Versioning](https://semver.org).
77

88
## [Unreleased]
99

10+
### Added
11+
12+
- Added the `z_getstandardfee` RPC, a parameterless method returning the
13+
recommended standard fee per logical action (the ZIP-317 marginal fee, 5000
14+
zatoshis) with a `version` field for future dynamic fee estimation
15+
([#10717](https://github.com/ZcashFoundation/zebra/pull/10717))
16+
1017
## [Zebra 5.1.1](https://github.com/ZcashFoundation/zebra/releases/tag/v5.1.1) - 2026-06-11
1118

1219
This release reduces Zebra's end-of-support window ahead of the NU7 network upgrade

zebra-rpc/src/methods.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ use types::{
133133
transaction::TransactionObject,
134134
unified_address::ZListUnifiedReceiversResponse,
135135
validate_address::ValidateAddressResponse,
136+
z_getstandardfee::ZGetStandardFeeResponse,
136137
z_validate_address::ZValidateAddressResponse,
137138
};
138139

@@ -647,6 +648,16 @@ pub trait Rpc {
647648
#[method(name = "z_validateaddress")]
648649
async fn z_validate_address(&self, address: String) -> Result<ZValidateAddressResponse>;
649650

651+
/// Returns the recommended standard fee per logical action, in zatoshis.
652+
///
653+
/// Currently returns a static fee with `version` 0; this will be replaced by
654+
/// a dynamic estimate without changing the parameters or result shape.
655+
///
656+
/// method: post
657+
/// tags: fees
658+
#[method(name = "z_getstandardfee")]
659+
async fn z_getstandardfee(&self) -> Result<ZGetStandardFeeResponse>;
660+
650661
/// Returns the block subsidy reward of the block at `height`, taking into account the mining slow start.
651662
/// Returns an error if `height` is less than the height of the first halving for the current network.
652663
///
@@ -2792,6 +2803,23 @@ where
27922803
z_validate_address(network, raw_address)
27932804
}
27942805

2806+
async fn z_getstandardfee(&self) -> Result<ZGetStandardFeeResponse> {
2807+
// The current standard fee: 5000 zatoshis per logical action.
2808+
const MARGINAL_FEE_ZATOSHIS: u64 = 5000;
2809+
const VERSION: u32 = 0;
2810+
2811+
let height = self
2812+
.latest_chain_tip
2813+
.best_tip_height()
2814+
.ok_or_misc_error("no chain tip available, wait until a block is committed")?;
2815+
2816+
Ok(ZGetStandardFeeResponse::new(
2817+
MARGINAL_FEE_ZATOSHIS,
2818+
VERSION,
2819+
height.0,
2820+
))
2821+
}
2822+
27952823
async fn get_block_subsidy(&self, height: Option<u32>) -> Result<GetBlockSubsidyResponse> {
27962824
let net = self.network.clone();
27972825

zebra-rpc/src/methods/tests/vectors.rs

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3190,3 +3190,43 @@ async fn rpc_gettxout() {
31903190
let rpc_tx_queue_task_result = rpc_tx_queue.now_or_never();
31913191
assert!(rpc_tx_queue_task_result.is_none());
31923192
}
3193+
3194+
#[tokio::test(flavor = "multi_thread")]
3195+
async fn rpc_z_getstandardfee() {
3196+
let _init_guard = zebra_test::init();
3197+
3198+
let mempool: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests();
3199+
let state: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests();
3200+
let read_state: MockService<_, _, _, BoxError> = MockService::build().for_unit_tests();
3201+
3202+
let (tip, tip_sender) = MockChainTip::new();
3203+
tip_sender.send_best_tip_height(Height(2_750_000));
3204+
3205+
let (_tx, rx) = tokio::sync::watch::channel(None);
3206+
let (rpc, _rpc_tx_queue) = RpcImpl::new(
3207+
Mainnet,
3208+
Default::default(),
3209+
Default::default(),
3210+
"0.0.1",
3211+
"RPC test",
3212+
Buffer::new(mempool.clone(), 1),
3213+
Buffer::new(state.clone(), 1),
3214+
Buffer::new(read_state.clone(), 1),
3215+
MockService::build().for_unit_tests(),
3216+
MockSyncStatus::default(),
3217+
tip,
3218+
MockAddressBookPeers::default(),
3219+
rx,
3220+
None,
3221+
);
3222+
3223+
let response = rpc
3224+
.z_getstandardfee()
3225+
.await
3226+
.expect("z_getstandardfee should succeed with a chain tip");
3227+
3228+
// Static v0 placeholder: the ZIP-317 marginal fee, version 0, and the tip height.
3229+
assert_eq!(response.standard_fee(), 5000);
3230+
assert_eq!(response.version(), 0);
3231+
assert_eq!(response.height(), 2_750_000);
3232+
}

zebra-rpc/src/methods/types.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ pub mod subsidy;
1414
pub mod transaction;
1515
pub mod unified_address;
1616
pub mod validate_address;
17+
pub mod z_getstandardfee;
1718
pub mod z_validate_address;
1819
pub mod zec;
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
//! Types for the `z_getstandardfee` RPC.
2+
3+
use derive_getters::Getters;
4+
use derive_new::new;
5+
6+
/// A response to a `z_getstandardfee` RPC request.
7+
#[derive(Clone, Debug, Eq, PartialEq, serde::Serialize, serde::Deserialize, Getters, new)]
8+
pub struct ZGetStandardFeeResponse {
9+
/// Recommended fee per logical action, in zatoshis.
10+
#[getter(copy)]
11+
pub(crate) standard_fee: u64,
12+
13+
/// Estimator version identifier.
14+
#[getter(copy)]
15+
pub(crate) version: u32,
16+
17+
/// The chain tip height at the time of computation.
18+
#[getter(copy)]
19+
pub(crate) height: u32,
20+
}

0 commit comments

Comments
 (0)