soroban_sdk/testutils/cost_estimate.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
use soroban_env_host::{fees::FeeConfiguration, FeeEstimate, InvocationResources};
use crate::{testutils::budget::Budget, Env};
pub struct CostEstimate {
env: Env,
}
impl CostEstimate {
pub(crate) fn new(env: Env) -> Self {
Self { env }
}
/// Returns the resources metered during the last top level contract
/// invocation.
/// Take the return value with a grain of salt. The returned resources mostly
/// correspond only to the operations that have happened during the host
/// invocation, i.e. this won't try to simulate the work that happens in
/// production scenarios (e.g. certain XDR rountrips). This also doesn't try
/// to model resources related to the transaction size.
///
/// The returned value is as useful as the preceding setup, e.g. if a test
/// contract is used instead of a Wasm contract, all the costs related to
/// VM instantiation and execution, as well as Wasm reads/rent bumps will be
/// missed.
pub fn resources(&self) -> InvocationResources {
if let Some(res) = self.env.host().get_last_invocation_resources() {
res
} else {
panic!("Invocation cost estimate is not available. Make sure invocation cost metering is enabled in the EnvTestConfig and this is called after an invocation.")
}
}
/// Estimates the fee for the last invocation's resources, i.e. the
/// resources returned by `resources()`.
///
/// The fees are computed using the snapshot of the Stellar Pubnet fees made
/// on 2024-12-11.
///
/// Take the return value with a grain of salt as both the resource estimate
/// and the fee rates may be imprecise.
///
/// The returned value is as useful as the preceding setup, e.g. if a test
/// contract is used instead of a Wasm contract, all the costs related to
/// VM instantiation and execution, as well as Wasm reads/rent bumps will be
/// missed.
pub fn fee(&self) -> FeeEstimate {
// This is a snapshot of the fees as of 2024-12-11.
let pubnet_fee_config = FeeConfiguration {
fee_per_instruction_increment: 25,
fee_per_read_entry: 6250,
fee_per_write_entry: 10000,
fee_per_read_1kb: 1786,
// This is a bit higher than the current network fee, it's an
// overestimate for the sake of providing a bit more conservative
// results in case if the state grows.
fee_per_write_1kb: 12000,
fee_per_historical_1kb: 16235,
fee_per_contract_event_1kb: 10000,
fee_per_transaction_size_1kb: 1624,
};
let pubnet_persistent_rent_rate_denominator = 2103;
let pubnet_temp_rent_rate_denominator = 4206;
self.resources().estimate_fees(
&pubnet_fee_config,
pubnet_persistent_rent_rate_denominator,
pubnet_temp_rent_rate_denominator,
)
}
/// Returns the budget object that provides the detailed CPU and memory
/// metering information recorded thus far.
///
/// The budget metering resets before every top-level contract level
/// invocation.
///
/// budget() may also be used to adjust the CPU and memory limits via the
/// `reset_` methods.
///
/// Note, that unlike `resources()`/`fee()` this will always return some
/// value. If there was no contract call, then the resulting value will
/// correspond to metering any environment setup that has been made thus
/// far.
pub fn budget(&self) -> Budget {
Budget::new(self.env.host().budget_cloned())
}
}