Skip to main content

iota_sdk_types/
gas.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2025 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5/// Summary of gas charges.
6///
7/// Storage is charged independently of computation.
8/// There are 3 parts to the storage charges:
9/// - `storage_cost`: it is the charge of storage at the time the transaction is
10///   executed. The cost of storage is the number of bytes of the objects being
11///   mutated multiplied by a variable storage cost per byte
12/// - `storage_rebate`: this is the amount a user gets back when manipulating an
13///   object. The `storage_rebate` is the `storage_cost` for an object minus
14///   fees.
15/// - `non_refundable_storage_fee`: not all the value of the object storage cost
16///   is given back to user and there is a small fraction that is kept by the
17///   system. This value tracks that charge.
18///
19/// When looking at a gas cost summary the amount charged to the user is
20/// `computation_cost + storage_cost - storage_rebate`
21/// and that is the amount that is deducted from the gas coins.
22/// `non_refundable_storage_fee` is collected from the objects being
23/// mutated/deleted and it is tracked by the system in storage funds.
24///
25/// Objects deleted, including the older versions of objects mutated, have the
26/// storage field on the objects added up to a pool of "potential rebate". This
27/// rebate then is reduced by the "nonrefundable rate" such that:
28/// `potential_rebate(storage cost of deleted/mutated objects) =
29/// storage_rebate + non_refundable_storage_fee`
30///
31/// # BCS
32///
33/// The BCS serialized form for this type is defined by the following ABNF:
34///
35/// ```text
36/// gas-cost-summary = u64   ; computation-cost
37///                    u64   ; computation-cost-burned
38///                    u64   ; storage-cost
39///                    u64   ; storage-rebate
40///                    u64   ; non-refundable-storage-fee
41/// ```
42#[derive(Clone, Debug, Default, Eq, PartialEq)]
43#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
44#[cfg_attr(feature = "proptest", derive(test_strategy::Arbitrary))]
45#[cfg_attr(feature = "bcs-schema", derive(iota_bcs_schema::BcsSchema))]
46pub struct GasCostSummary {
47    /// Cost of computation/execution
48    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
49    pub computation_cost: u64,
50    /// The burned component of the computation/execution costs
51    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
52    pub computation_cost_burned: u64,
53    /// Storage cost, it's the sum of all storage cost for all objects created
54    /// or mutated.
55    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
56    pub storage_cost: u64,
57    /// The amount of storage cost refunded to the user for all objects deleted
58    /// or mutated in the transaction.
59    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
60    pub storage_rebate: u64,
61    /// The fee for the rebate. The portion of the storage rebate kept by the
62    /// system.
63    #[cfg_attr(feature = "serde", serde(with = "crate::_serde::ReadableDisplay"))]
64    pub non_refundable_storage_fee: u64,
65}
66
67impl GasCostSummary {
68    /// Create a new gas cost summary.
69    ///
70    /// # Arguments
71    /// * `computation_cost` - Cost of computation cost/execution.
72    /// * `storage_cost` - Storage cost, it's the sum of all storage cost for
73    ///   all objects created or mutated.
74    /// * `storage_rebate` - The amount of storage cost refunded to the user for
75    ///   all objects deleted or mutated in the transaction.
76    /// * `non_refundable_storage_fee` - The fee for the rebate. The portion of
77    ///   the storage rebate kept by the system.
78    pub fn new(
79        computation_cost: u64,
80        computation_cost_burned: u64,
81        storage_cost: u64,
82        storage_rebate: u64,
83        non_refundable_storage_fee: u64,
84    ) -> GasCostSummary {
85        GasCostSummary {
86            computation_cost,
87            computation_cost_burned,
88            storage_cost,
89            storage_rebate,
90            non_refundable_storage_fee,
91        }
92    }
93
94    /// The total gas used, which is the sum of computation and storage costs.
95    pub fn gas_used(&self) -> u64 {
96        self.computation_cost
97            .checked_add(self.storage_cost)
98            .expect("gas_used overflow")
99    }
100
101    /// The net gas usage, which is the total gas used minus the storage rebate.
102    /// A positive number means used gas; negative number means refund.
103    pub fn net_gas_usage(&self) -> i64 {
104        (self.gas_used() as i64)
105            .checked_sub(self.storage_rebate as i64)
106            .expect("net_gas_usage underflow")
107    }
108}
109
110impl std::ops::AddAssign<&Self> for GasCostSummary {
111    fn add_assign(&mut self, other: &Self) {
112        self.computation_cost = self
113            .computation_cost
114            .checked_add(other.computation_cost)
115            .expect("computation_cost overflow");
116        self.computation_cost_burned = self
117            .computation_cost_burned
118            .checked_add(other.computation_cost_burned)
119            .expect("computation_cost_burned overflow");
120        self.storage_cost = self
121            .storage_cost
122            .checked_add(other.storage_cost)
123            .expect("storage_cost overflow");
124        self.storage_rebate = self
125            .storage_rebate
126            .checked_add(other.storage_rebate)
127            .expect("storage_rebate overflow");
128        self.non_refundable_storage_fee = self
129            .non_refundable_storage_fee
130            .checked_add(other.non_refundable_storage_fee)
131            .expect("non_refundable_storage_fee overflow");
132    }
133}
134
135impl std::ops::SubAssign<&Self> for GasCostSummary {
136    fn sub_assign(&mut self, other: &Self) {
137        self.computation_cost = self
138            .computation_cost
139            .checked_sub(other.computation_cost)
140            .expect("computation_cost underflow");
141        self.computation_cost_burned = self
142            .computation_cost_burned
143            .checked_sub(other.computation_cost_burned)
144            .expect("computation_cost_burned underflow");
145        self.storage_cost = self
146            .storage_cost
147            .checked_sub(other.storage_cost)
148            .expect("storage_cost underflow");
149        self.storage_rebate = self
150            .storage_rebate
151            .checked_sub(other.storage_rebate)
152            .expect("storage_rebate underflow");
153        self.non_refundable_storage_fee = self
154            .non_refundable_storage_fee
155            .checked_sub(other.non_refundable_storage_fee)
156            .expect("non_refundable_storage_fee underflow");
157    }
158}
159
160impl std::ops::AddAssign<Self> for GasCostSummary {
161    fn add_assign(&mut self, other: Self) {
162        self.add_assign(&other);
163    }
164}
165
166impl std::ops::SubAssign<Self> for GasCostSummary {
167    fn sub_assign(&mut self, other: Self) {
168        self.sub_assign(&other);
169    }
170}
171
172impl crate::TreeDisplay for GasCostSummary {
173    fn fmt_tree(&self, w: &mut crate::TreeWriter<'_, '_>) -> std::fmt::Result {
174        w.header("Gas Cost Summary")?;
175        w.leaf("Computation Cost", &self.computation_cost, false)?;
176        w.leaf(
177            "Computation Cost Burned",
178            &self.computation_cost_burned,
179            false,
180        )?;
181        w.leaf("Storage Cost", &self.storage_cost, false)?;
182        w.leaf("Storage Rebate", &self.storage_rebate, false)?;
183        w.leaf(
184            "Non-Refundable Storage Fee",
185            &self.non_refundable_storage_fee,
186            true,
187        )
188    }
189}
190
191crate::impl_tree_display!(GasCostSummary);
192
193#[cfg(all(test, feature = "serde"))]
194mod tests {
195    #[cfg(target_arch = "wasm32")]
196    use wasm_bindgen_test::wasm_bindgen_test as test;
197
198    use super::*;
199
200    #[test]
201    fn formats() {
202        let actual = GasCostSummary {
203            computation_cost: 42,
204            computation_cost_burned: 24,
205            storage_cost: u64::MAX,
206            storage_rebate: 0,
207            non_refundable_storage_fee: 9,
208        };
209
210        println!("{}", serde_json::to_string(&actual).unwrap());
211        println!("{:?}", bcs::to_bytes(&actual).unwrap());
212    }
213}