forest/shim/
gas.rs

1// Copyright 2019-2025 ChainSafe Systems
2// SPDX-License-Identifier: Apache-2.0, MIT
3use std::fmt::{Debug, Display};
4
5pub use super::fvm_latest::gas::{
6    Gas as Gas_latest, GasCharge as GasCharge_latest, GasDuration as GasDuration_latest,
7    GasOutputs as GasOutputs_latest,
8};
9use fvm2::gas::{
10    Gas as GasV2, GasCharge as GasChargeV2, PriceList as PriceListV2,
11    price_list_by_network_version as price_list_by_network_version_v2,
12};
13use fvm3::gas::{
14    Gas as GasV3, MILLIGAS_PRECISION,
15    price_list_by_network_version as price_list_by_network_version_v3,
16};
17pub use fvm3::gas::{GasCharge as GasChargeV3, GasTracker, PriceList as PriceListV3};
18use fvm4::gas::price_list_by_network_version as price_list_by_network_version_v4;
19pub use fvm4::gas::{
20    Gas as GasV4, GasCharge as GasChargeV4, GasDuration as GasDurationV4,
21    GasOutputs as GasOutputsV4, PriceList as PriceListV4,
22};
23
24use crate::shim::econ::TokenAmount;
25use crate::shim::version::NetworkVersion;
26
27#[derive(Hash, Eq, PartialEq, Ord, PartialOrd, Copy, Clone, Default)]
28pub struct Gas(Gas_latest);
29
30#[derive(Clone, Default)]
31pub struct GasDuration(GasDuration_latest);
32
33#[derive(Clone, Default)]
34pub struct GasOutputs(GasOutputs_latest);
35
36impl Debug for Gas {
37    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38        if self.0.as_milligas() == 0 {
39            f.debug_tuple("Gas").field(&0 as &dyn Debug).finish()
40        } else {
41            let integral = self.0.as_milligas() / MILLIGAS_PRECISION;
42            let fractional = self.0.as_milligas() % MILLIGAS_PRECISION;
43            f.debug_tuple("Gas")
44                .field(&format_args!("{integral}.{fractional:03}"))
45                .finish()
46        }
47    }
48}
49
50impl Display for Gas {
51    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
52        if self.0.as_milligas() == 0 {
53            f.write_str("0")
54        } else {
55            let integral = self.0.as_milligas() / MILLIGAS_PRECISION;
56            let fractional = self.0.as_milligas() % MILLIGAS_PRECISION;
57            write!(f, "{integral}.{fractional:03}")
58        }
59    }
60}
61
62impl Gas {
63    pub fn new(gas: u64) -> Self {
64        Self(Gas_latest::new(gas))
65    }
66
67    pub fn round_up(&self) -> u64 {
68        self.0.round_up()
69    }
70}
71
72impl GasDuration {
73    pub fn as_nanos(&self) -> u64 {
74        if let Some(duration) = self.0.get() {
75            duration.as_nanos().clamp(0, u64::MAX as u128) as u64
76        } else {
77            0
78        }
79    }
80}
81
82impl From<GasV2> for Gas {
83    fn from(value: GasV2) -> Self {
84        Gas(Gas_latest::from_milligas(value.as_milligas() as _))
85    }
86}
87
88impl From<Gas> for GasV2 {
89    fn from(value: Gas) -> Self {
90        GasV2::from_milligas(value.0.as_milligas() as _)
91    }
92}
93
94impl From<Gas> for GasV3 {
95    fn from(value: Gas) -> Self {
96        GasV3::from_milligas(value.0.as_milligas())
97    }
98}
99
100impl From<GasV3> for Gas {
101    fn from(value: GasV3) -> Self {
102        Gas(Gas_latest::from_milligas(value.as_milligas() as _))
103    }
104}
105
106impl From<Gas> for GasV4 {
107    fn from(value: Gas) -> Self {
108        GasV4::from_milligas(value.0.as_milligas())
109    }
110}
111
112impl From<GasV4> for Gas {
113    fn from(value: GasV4) -> Self {
114        Gas(value)
115    }
116}
117
118#[derive(Debug, Clone)]
119pub struct GasCharge(GasCharge_latest);
120
121impl GasCharge {
122    /// Calculates total gas charge (in `milligas`) by summing compute and
123    /// storage gas associated with this charge.
124    pub fn total(&self) -> Gas {
125        self.0.total().into()
126    }
127    pub fn name(&self) -> &str {
128        &self.0.name
129    }
130    pub fn compute_gas(&self) -> Gas {
131        self.0.compute_gas.into()
132    }
133    pub fn other_gas(&self) -> Gas {
134        self.0.other_gas.into()
135    }
136    pub fn elapsed(&self) -> GasDuration {
137        self.0.elapsed.clone().into()
138    }
139}
140
141impl From<GasChargeV2> for GasCharge {
142    fn from(value: GasChargeV2) -> Self {
143        GasChargeV3 {
144            name: value.name,
145            compute_gas: GasV3::from_milligas(value.compute_gas.as_milligas() as u64),
146            other_gas: GasV3::from_milligas(value.storage_gas.as_milligas() as u64),
147            elapsed: Default::default(),
148        }
149        .into()
150    }
151}
152
153impl From<GasChargeV3> for GasCharge {
154    fn from(value: GasChargeV3) -> Self {
155        GasChargeV4 {
156            name: value.name,
157            compute_gas: GasV4::from_milligas(value.compute_gas.as_milligas()),
158            other_gas: GasV4::from_milligas(value.other_gas.as_milligas()),
159            elapsed: value.elapsed.get().map(|&d| d.into()).unwrap_or_default(),
160        }
161        .into()
162    }
163}
164
165impl From<GasChargeV4> for GasCharge {
166    fn from(value: GasChargeV4) -> Self {
167        GasCharge(value)
168    }
169}
170
171impl From<GasCharge> for GasChargeV2 {
172    fn from(value: GasCharge) -> Self {
173        Self {
174            name: value.0.name,
175            compute_gas: GasV2::from_milligas(value.0.compute_gas.as_milligas() as _),
176            storage_gas: GasV2::from_milligas(value.0.other_gas.as_milligas() as _),
177        }
178    }
179}
180
181impl From<GasCharge> for GasChargeV3 {
182    fn from(value: GasCharge) -> Self {
183        Self {
184            name: value.0.name,
185            compute_gas: GasV3::from_milligas(value.0.compute_gas.as_milligas() as _),
186            other_gas: GasV3::from_milligas(value.0.other_gas.as_milligas() as _),
187            elapsed: Default::default(),
188        }
189    }
190}
191
192impl From<GasCharge> for GasChargeV4 {
193    fn from(value: GasCharge) -> Self {
194        value.0
195    }
196}
197
198impl From<GasDurationV4> for GasDuration {
199    fn from(value: GasDurationV4) -> Self {
200        GasDuration(value)
201    }
202}
203
204impl GasOutputs {
205    pub fn compute(
206        // In whole gas units.
207        gas_used: u64,
208        gas_limit: u64,
209        base_fee: &TokenAmount,
210        fee_cap: &TokenAmount,
211        gas_premium: &TokenAmount,
212    ) -> Self {
213        GasOutputsV4::compute(gas_used, gas_limit, base_fee, fee_cap, gas_premium).into()
214    }
215
216    pub fn total_spent(self) -> TokenAmount {
217        (self.0.base_fee_burn + self.0.miner_tip + self.0.over_estimation_burn).into()
218    }
219}
220
221impl From<GasOutputsV4> for GasOutputs {
222    fn from(value: GasOutputsV4) -> Self {
223        GasOutputs(value)
224    }
225}
226
227pub enum PriceList {
228    V2(&'static PriceListV2),
229    V3(&'static PriceListV3),
230    V4(&'static PriceListV4),
231}
232
233impl PriceList {
234    pub fn on_block_open_base(&self) -> GasCharge {
235        match self {
236            PriceList::V2(list) => list.on_block_open_base().into(),
237            PriceList::V3(list) => list.on_block_open_base().into(),
238            PriceList::V4(list) => list.on_block_open_base().into(),
239        }
240    }
241
242    pub fn on_block_link(&self, data_size: usize) -> GasCharge {
243        match self {
244            PriceList::V2(list) => list.on_block_link(data_size).into(),
245            PriceList::V3(list) => list
246                .on_block_link(fvm3::kernel::SupportedHashes::Blake2b256, data_size)
247                .into(),
248            PriceList::V4(list) => list
249                .on_block_link(fvm4::kernel::SupportedHashes::Blake2b256, data_size)
250                .into(),
251        }
252    }
253
254    pub fn on_chain_message(&self, msg_size: usize) -> GasCharge {
255        match self {
256            PriceList::V2(list) => list.on_chain_message(msg_size).into(),
257            PriceList::V3(list) => list.on_chain_message(msg_size).into(),
258            PriceList::V4(list) => list.on_chain_message(msg_size).into(),
259        }
260    }
261}
262
263impl From<&'static PriceListV2> for PriceList {
264    fn from(value: &'static PriceListV2) -> Self {
265        PriceList::V2(value)
266    }
267}
268
269impl From<&'static PriceListV3> for PriceList {
270    fn from(value: &'static PriceListV3) -> Self {
271        PriceList::V3(value)
272    }
273}
274
275impl From<&'static PriceListV4> for PriceList {
276    fn from(value: &'static PriceListV4) -> Self {
277        PriceList::V4(value)
278    }
279}
280
281pub fn price_list_by_network_version(network_version: NetworkVersion) -> PriceList {
282    if network_version < NetworkVersion::V18 {
283        price_list_by_network_version_v2(network_version.into()).into()
284    } else if network_version < NetworkVersion::V21 {
285        price_list_by_network_version_v3(network_version.into()).into()
286    } else {
287        price_list_by_network_version_v4(network_version.into()).into()
288    }
289}