mirror of
https://github.com/alexgo-io/stacks-subnets.git
synced 2026-01-12 22:43:44 +08:00
use estimations array, address PR feedback, check minimum relay fees
This commit is contained in:
@@ -8,14 +8,18 @@
|
||||
"write_length": 1020
|
||||
},
|
||||
"estimated_cost_scalar": 14,
|
||||
"estimated_fee_rates": [
|
||||
1.2410714285714286,
|
||||
8.958333333333332,
|
||||
10
|
||||
],
|
||||
"estimated_fees": [
|
||||
17,
|
||||
125,
|
||||
140
|
||||
"estimations": [
|
||||
{
|
||||
"fee": 17,
|
||||
"fee_rate": 1.2410714285714286
|
||||
},
|
||||
{
|
||||
"fee": 125,
|
||||
"fee_rate": 8.958333333333332
|
||||
},
|
||||
{
|
||||
"fee": 140,
|
||||
"fee_rate": 10
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -24,16 +24,18 @@
|
||||
"write_length": { "type": "integer" }
|
||||
}
|
||||
},
|
||||
"estimated_fee_rates": {
|
||||
"estimations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "number"
|
||||
}
|
||||
},
|
||||
"estimated_fees": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "integer"
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"fee_rate": {
|
||||
"type": "number"
|
||||
},
|
||||
"fee": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -282,7 +282,7 @@ paths:
|
||||
tags:
|
||||
- Fees
|
||||
description: |
|
||||
Get an estimated fee for that supplied transaction. This
|
||||
Get an estimated fee for the supplied transaction. This
|
||||
estimates the execution cost of the transaction, the current
|
||||
fee rate of the network, and returns estimates for fee
|
||||
amounts.
|
||||
@@ -290,30 +290,53 @@ paths:
|
||||
* `transaction_payload` is a hex-encoded serialization of
|
||||
the TransactionPayload for the transaction.
|
||||
* `estimated_len` is an optional argument that provides the
|
||||
endpoint
|
||||
endpoint with an estimation of the final length (in bytes)
|
||||
of the transaction, including any post-conditions and
|
||||
signatures
|
||||
|
||||
If the node cannot provide an estimate for the transaction
|
||||
(e.g., if the node has never seen a contract-call for the
|
||||
given contract and function) or if estimation is not
|
||||
configured on this node, a 400 response is returned.
|
||||
The 400 response will be a JSON error containing a `reason`
|
||||
field which can be one of the following:
|
||||
|
||||
* `DatabaseError` - this Stacks node has had an internal
|
||||
database error while trying to estimate the costs of the
|
||||
supplied transaction.
|
||||
* `NoEstimateAvailable` - this Stacks node has not seen this
|
||||
kind of contract-call before, and it cannot provide an
|
||||
estimate yet.
|
||||
* `CostEstimationDisabled` - this Stacks node does not perform
|
||||
fee or cost estimation, and it cannot respond on this
|
||||
endpoint.
|
||||
|
||||
The 200 response contains the following data:
|
||||
|
||||
* `estimated_cost` - the estimated multi-dimensional cost of
|
||||
executing the Clarity VM on the provided transaction.
|
||||
* `estimated_cost_scalar` - an integer that captures the total
|
||||
proportion of the block limit that a transaction would be
|
||||
estimated to consume. This value is multiplied by a fee rate
|
||||
to suggest the total fee amount to be paid by the node. This
|
||||
* `estimated_cost_scalar` - a unitless integer that captures
|
||||
the total proportion of the block limit that a transaction
|
||||
would be estimated to consume. In order to compute an
|
||||
estimate of total fee amount for the transaction, this value
|
||||
is multiplied by the estimated fee rate. This value
|
||||
incorporates the estimated transaction length.
|
||||
* `cost_scalar_change_by_byte` - a float value that indicates how
|
||||
much the `estimated_cost_scalar` value would increase for every
|
||||
additional byte in the final transaction.
|
||||
* `estimated_fee_rates` - three estimated values for the current
|
||||
fee rates in the network
|
||||
* `estimated_fees` - three estimated values for the total fee that
|
||||
the given transaction should pay. These values are the result of
|
||||
computing: `estimated_fee_rates` x `estimated_cost_scalar`
|
||||
* `estimations` - an array of estimated fee rates and total fees to
|
||||
pay in microSTX for the transaction. This array provides a range of
|
||||
estimates (default: 3) that may be used. Each element of the array
|
||||
contains the following fields:
|
||||
* `fee_rate` - the estimated value for the current fee
|
||||
rates in the network
|
||||
* `fee` - the estimated value for the total fee in
|
||||
microSTX that the given transaction should pay. These
|
||||
values are the result of computing:
|
||||
`fee_rate` x `estimated_cost_scalar`.
|
||||
If the estimated fees are less than the minimum relay
|
||||
fee `(1 ustx x estimated_len)`, then that minimum relay
|
||||
fee will be returned here instead.
|
||||
|
||||
|
||||
Note: If the final transaction's byte size is larger than
|
||||
|
||||
@@ -192,7 +192,7 @@ impl EstimatorError {
|
||||
Some(json!({"message": self.to_string()})),
|
||||
),
|
||||
EstimatorError::SqliteError(_) => {
|
||||
("DatavaseError", Some(json!({"message": self.to_string()})))
|
||||
("DatabaseError", Some(json!({"message": self.to_string()})))
|
||||
}
|
||||
};
|
||||
let mut result = json!({
|
||||
|
||||
@@ -1029,23 +1029,27 @@ pub struct RPCPoxInfoData {
|
||||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
|
||||
pub struct RPCFeeEstimate {
|
||||
pub high: u64,
|
||||
pub middle: u64,
|
||||
pub low: u64,
|
||||
pub fee_rate: f64,
|
||||
pub fee: u64,
|
||||
}
|
||||
|
||||
impl RPCFeeEstimate {
|
||||
pub fn estimate_fees(scalar: u64, fee_rates: FeeRateEstimate) -> RPCFeeEstimate {
|
||||
let estimated_fees_f64 = fee_rates * (scalar as f64);
|
||||
RPCFeeEstimate {
|
||||
high: estimated_fees_f64.high as u64,
|
||||
middle: estimated_fees_f64.middle as u64,
|
||||
low: estimated_fees_f64.low as u64,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_vec(self) -> Vec<u64> {
|
||||
vec![self.low, self.middle, self.high]
|
||||
pub fn estimate_fees(scalar: u64, fee_rates: FeeRateEstimate) -> Vec<RPCFeeEstimate> {
|
||||
let estimated_fees_f64 = fee_rates.clone() * (scalar as f64);
|
||||
vec![
|
||||
RPCFeeEstimate {
|
||||
fee: estimated_fees_f64.low as u64,
|
||||
fee_rate: fee_rates.low,
|
||||
},
|
||||
RPCFeeEstimate {
|
||||
fee: estimated_fees_f64.middle as u64,
|
||||
fee_rate: fee_rates.middle,
|
||||
},
|
||||
RPCFeeEstimate {
|
||||
fee: estimated_fees_f64.high as u64,
|
||||
fee_rate: fee_rates.high,
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1053,8 +1057,7 @@ impl RPCFeeEstimate {
|
||||
pub struct RPCFeeEstimateResponse {
|
||||
pub estimated_cost: ExecutionCost,
|
||||
pub estimated_cost_scalar: u64,
|
||||
pub estimated_fees: Vec<u64>,
|
||||
pub estimated_fee_rates: Vec<f64>,
|
||||
pub estimations: Vec<RPCFeeEstimate>,
|
||||
pub cost_scalar_change_by_byte: f64,
|
||||
}
|
||||
|
||||
|
||||
@@ -1631,16 +1631,22 @@ impl ConversationHttp {
|
||||
}
|
||||
};
|
||||
|
||||
let estimated_fees =
|
||||
RPCFeeEstimate::estimate_fees(scalar_cost, fee_rates.clone()).to_vec();
|
||||
let mut estimations = RPCFeeEstimate::estimate_fees(scalar_cost, fee_rates).to_vec();
|
||||
|
||||
let minimum_fee = estimated_len * MINIMUM_TX_FEE_RATE_PER_BYTE;
|
||||
|
||||
for estimate in estimations.iter_mut() {
|
||||
if estimate.fee < minimum_fee {
|
||||
estimate.fee = minimum_fee;
|
||||
}
|
||||
}
|
||||
|
||||
let response = HttpResponseType::TransactionFeeEstimation(
|
||||
response_metadata,
|
||||
RPCFeeEstimateResponse {
|
||||
estimated_cost,
|
||||
estimated_fees,
|
||||
estimations,
|
||||
estimated_cost_scalar: scalar_cost,
|
||||
estimated_fee_rates: fee_rates.to_vec(),
|
||||
cost_scalar_change_by_byte: metric.change_per_byte(),
|
||||
},
|
||||
);
|
||||
|
||||
@@ -5,6 +5,7 @@ use std::sync::Mutex;
|
||||
use reqwest;
|
||||
|
||||
use stacks::burnchains::Address;
|
||||
use stacks::chainstate::stacks::db::blocks::MINIMUM_TX_FEE_RATE_PER_BYTE;
|
||||
use stacks::chainstate::stacks::{
|
||||
db::blocks::MemPoolRejection, db::StacksChainState, StacksPrivateKey, StacksTransaction,
|
||||
};
|
||||
@@ -857,13 +858,19 @@ fn integration_test_get_info() {
|
||||
// the estimated scalar should still be non-zero, because the length of the tx goes into this field.
|
||||
assert!(res.get("estimated_cost_scalar").unwrap().as_u64().unwrap() > 0);
|
||||
|
||||
let estimated_fee_rates = res.get("estimated_fee_rates").expect("Should have an estimated_fee_rates field")
|
||||
.as_array()
|
||||
.expect("Fees should be array");
|
||||
let estimated_fees = res.get("estimated_fees").expect("Should have an estimated_fees field")
|
||||
let estimations = res.get("estimations").expect("Should have an estimations field")
|
||||
.as_array()
|
||||
.expect("Fees should be array");
|
||||
|
||||
let estimated_fee_rates: Vec<_> = estimations
|
||||
.iter()
|
||||
.map(|x| x.get("fee_rate").expect("Should have fee_rate field"))
|
||||
.collect();
|
||||
let estimated_fees: Vec<_> = estimations
|
||||
.iter()
|
||||
.map(|x| x.get("fee").expect("Should have fee field"))
|
||||
.collect();
|
||||
|
||||
assert!(estimated_fee_rates.len() == 3, "Fee rates should be length 3 array");
|
||||
assert!(estimated_fees.len() == 3, "Fees should be length 3 array");
|
||||
|
||||
@@ -902,13 +909,19 @@ fn integration_test_get_info() {
|
||||
let estimated_cost_scalar = res.get("estimated_cost_scalar").unwrap().as_u64().unwrap();
|
||||
assert!(estimated_cost_scalar > 0);
|
||||
|
||||
let estimated_fee_rates = res.get("estimated_fee_rates").expect("Should have an estimated_fee_rates field")
|
||||
.as_array()
|
||||
.expect("Fees should be array");
|
||||
let estimated_fees = res.get("estimated_fees").expect("Should have an estimated_fees field")
|
||||
let estimations = res.get("estimations").expect("Should have an estimations field")
|
||||
.as_array()
|
||||
.expect("Fees should be array");
|
||||
|
||||
let estimated_fee_rates: Vec<_> = estimations
|
||||
.iter()
|
||||
.map(|x| x.get("fee_rate").expect("Should have fee_rate field"))
|
||||
.collect();
|
||||
let estimated_fees: Vec<_> = estimations
|
||||
.iter()
|
||||
.map(|x| x.get("fee").expect("Should have fee field"))
|
||||
.collect();
|
||||
|
||||
assert!(estimated_fee_rates.len() == 3, "Fee rates should be length 3 array");
|
||||
assert!(estimated_fees.len() == 3, "Fees should be length 3 array");
|
||||
|
||||
@@ -922,7 +935,8 @@ fn integration_test_get_info() {
|
||||
let payload_data = tx_payload.serialize_to_vec();
|
||||
let payload_hex = to_hex(&payload_data);
|
||||
|
||||
let body = json!({ "transaction_payload": payload_hex.clone(), "estimated_len": 1550 });
|
||||
let estimated_len = 1550;
|
||||
let body = json!({ "transaction_payload": payload_hex.clone(), "estimated_len": estimated_len });
|
||||
info!("POST body\n {}", body);
|
||||
|
||||
let res = client.post(&path)
|
||||
@@ -947,16 +961,27 @@ fn integration_test_get_info() {
|
||||
assert!(estimated_cost_scalar > 0);
|
||||
assert!(new_estimated_cost_scalar > estimated_cost_scalar, "New scalar estimate should be higher because of the tx length increase");
|
||||
|
||||
let new_estimated_fees = res.get("estimated_fees").expect("Should have an estimated_fees field")
|
||||
let new_estimations = res.get("estimations").expect("Should have an estimations field")
|
||||
.as_array()
|
||||
.expect("Fees should be array");
|
||||
|
||||
let new_estimated_fees: Vec<_> = new_estimations
|
||||
.iter()
|
||||
.map(|x| x.get("fee").expect("Should have fee field"))
|
||||
.collect();
|
||||
|
||||
let minimum_relay_fee = estimated_len * MINIMUM_TX_FEE_RATE_PER_BYTE;
|
||||
|
||||
assert!(new_estimated_fees[2].as_u64().unwrap() >= estimated_fees[2].as_u64().unwrap(),
|
||||
"Supplying an estimated tx length should increase the estimated fees");
|
||||
assert!(new_estimated_fees[0].as_u64().unwrap() >= estimated_fees[0].as_u64().unwrap(),
|
||||
"Supplying an estimated tx length should increase the estimated fees");
|
||||
assert!(new_estimated_fees[1].as_u64().unwrap() >= estimated_fees[1].as_u64().unwrap(),
|
||||
"Supplying an estimated tx length should increase the estimated fees");
|
||||
for estimate in new_estimated_fees.iter() {
|
||||
assert!(estimate.as_u64().unwrap() >= minimum_relay_fee,
|
||||
"The estimated fees must always be greater than minimum_relay_fee");
|
||||
}
|
||||
},
|
||||
_ => {},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user