1pub use checked::*;
6use serde::{Deserialize, Serialize};
7
8use crate::{base_types::ObjectID, gas_model::gas_v2::PerObjectStorage};
9
10#[sui_macros::with_checked_arithmetic]
11pub mod checked {
12
13 use crate::gas::GasUsageReport;
14 use crate::gas_model::gas_predicates::check_for_gas_price_too_high;
15 use crate::gas_model::gas_v2::PerObjectStorage;
16 use crate::{
17 ObjectID,
18 effects::{TransactionEffects, TransactionEffectsAPI},
19 error::{ExecutionError, SuiResult, UserInputError, UserInputResult},
20 gas_model::{
21 gas_v2::SuiGasStatus as SuiGasStatusV2, gas_v3::SuiGasStatus as SuiGasStatusV3,
22 tables::GasStatus,
23 },
24 object::Object,
25 sui_serde::{BigInt, Readable},
26 transaction::ObjectReadResult,
27 };
28 use enum_dispatch::enum_dispatch;
29 use itertools::MultiUnzip;
30 use schemars::JsonSchema;
31 use serde::{Deserialize, Serialize};
32 use serde_with::serde_as;
33 use sui_protocol_config::ProtocolConfig;
34
35 #[enum_dispatch]
36 pub trait SuiGasStatusAPI {
37 fn is_unmetered(&self) -> bool;
38 fn move_gas_status(&self) -> &GasStatus;
39 fn move_gas_status_mut(&mut self) -> &mut GasStatus;
40 fn bucketize_computation(&mut self, aborted: Option<bool>) -> Result<(), ExecutionError>;
41 fn summary(&self) -> GasCostSummary;
42 fn gas_budget(&self) -> u64;
43 fn gas_price(&self) -> u64;
44 fn reference_gas_price(&self) -> u64;
45 fn storage_gas_units(&self) -> u64;
46 fn storage_rebate(&self) -> u64;
47 fn unmetered_storage_rebate(&self) -> u64;
48 fn gas_used(&self) -> u64;
49 fn reset_storage_cost_and_rebate(&mut self);
50 fn charge_storage_read(&mut self, size: usize) -> Result<(), ExecutionError>;
51 fn charge_publish_package(&mut self, size: usize) -> Result<(), ExecutionError>;
52 fn track_storage_mutation(
53 &mut self,
54 object_id: ObjectID,
55 new_size: usize,
56 storage_rebate: u64,
57 ) -> Option<u64>;
58 fn charge_storage_and_rebate(&mut self) -> Result<(), ExecutionError>;
59 fn adjust_computation_on_out_of_gas(&mut self);
60 fn gas_usage_report(&self) -> GasUsageReport;
61 fn check_gas_balance(
62 &self,
63 gas_objs: &[&ObjectReadResult],
64 gas_budget: u64,
65 available_address_balance_gas: u64,
66 ) -> UserInputResult;
67 fn check_gas_objects(&self, gas_objs: &[&ObjectReadResult]) -> UserInputResult;
68 fn per_object_storage(&self) -> &Vec<(ObjectID, PerObjectStorage)>;
69 }
70
71 #[enum_dispatch(SuiGasStatusAPI)]
74 #[derive(Debug)]
75 pub enum SuiGasStatus {
76 V2(SuiGasStatusV2),
79 V3(SuiGasStatusV3),
80 }
81
82 impl SuiGasStatus {
83 pub fn new(
84 gas_budget: u64,
85 gas_price: u64,
86 reference_gas_price: u64,
87 config: &ProtocolConfig,
88 ) -> SuiResult<Self> {
89 if gas_price < reference_gas_price {
94 return Err(UserInputError::GasPriceUnderRGP {
95 gas_price,
96 reference_gas_price,
97 }
98 .into());
99 }
100 if check_for_gas_price_too_high(config.gas_model_version())
101 && gas_price >= config.max_gas_price()
102 {
103 return Err(UserInputError::GasPriceTooHigh {
104 max_gas_price: config.max_gas_price(),
105 }
106 .into());
107 }
108
109 if config.gas_model_version() >= 15 {
112 Ok(Self::V3(SuiGasStatusV3::new_with_budget(
113 gas_budget,
114 gas_price,
115 reference_gas_price,
116 config,
117 )))
118 } else {
119 Ok(Self::V2(SuiGasStatusV2::new_with_budget(
120 gas_budget,
121 gas_price,
122 reference_gas_price,
123 config,
124 )))
125 }
126 }
127
128 pub fn new_unmetered(config: &ProtocolConfig) -> Self {
129 if config.gas_model_version() >= 15 {
132 Self::V3(SuiGasStatusV3::new_unmetered())
133 } else {
134 Self::V2(SuiGasStatusV2::new_unmetered())
135 }
136 }
137 }
138
139 #[serde_as]
164 #[derive(Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
165 #[serde(rename_all = "camelCase")]
166 pub struct GasCostSummary {
167 #[schemars(with = "BigInt<u64>")]
169 #[serde_as(as = "Readable<BigInt<u64>, _>")]
170 pub computation_cost: u64,
171 #[schemars(with = "BigInt<u64>")]
173 #[serde_as(as = "Readable<BigInt<u64>, _>")]
174 pub storage_cost: u64,
175 #[schemars(with = "BigInt<u64>")]
178 #[serde_as(as = "Readable<BigInt<u64>, _>")]
179 pub storage_rebate: u64,
180 #[schemars(with = "BigInt<u64>")]
182 #[serde_as(as = "Readable<BigInt<u64>, _>")]
183 pub non_refundable_storage_fee: u64,
184 }
185
186 impl GasCostSummary {
187 pub fn new(
188 computation_cost: u64,
189 storage_cost: u64,
190 storage_rebate: u64,
191 non_refundable_storage_fee: u64,
192 ) -> GasCostSummary {
193 GasCostSummary {
194 computation_cost,
195 storage_cost,
196 storage_rebate,
197 non_refundable_storage_fee,
198 }
199 }
200
201 pub fn gas_used(&self) -> u64 {
202 self.computation_cost + self.storage_cost
203 }
204
205 pub fn sender_rebate(&self, storage_rebate_rate: u64) -> u64 {
208 const BASIS_POINTS: u128 = 10000;
211 (((self.storage_rebate as u128 * storage_rebate_rate as u128)
212 + (BASIS_POINTS / 2)) / BASIS_POINTS) as u64
214 }
215
216 pub fn net_gas_usage(&self) -> i64 {
218 self.gas_used() as i64 - self.storage_rebate as i64
219 }
220
221 pub fn new_from_txn_effects<'a>(
222 transactions: impl Iterator<Item = &'a TransactionEffects>,
223 ) -> GasCostSummary {
224 let (storage_costs, computation_costs, storage_rebates, non_refundable_storage_fee): (
225 Vec<u64>,
226 Vec<u64>,
227 Vec<u64>,
228 Vec<u64>,
229 ) = transactions
230 .map(|e| {
231 (
232 e.gas_cost_summary().storage_cost,
233 e.gas_cost_summary().computation_cost,
234 e.gas_cost_summary().storage_rebate,
235 e.gas_cost_summary().non_refundable_storage_fee,
236 )
237 })
238 .multiunzip();
239
240 GasCostSummary {
241 storage_cost: storage_costs.iter().sum(),
242 computation_cost: computation_costs.iter().sum(),
243 storage_rebate: storage_rebates.iter().sum(),
244 non_refundable_storage_fee: non_refundable_storage_fee.iter().sum(),
245 }
246 }
247 }
248
249 impl std::fmt::Display for GasCostSummary {
250 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
251 write!(
252 f,
253 "computation_cost: {}, storage_cost: {}, storage_rebate: {}, non_refundable_storage_fee: {}",
254 self.computation_cost,
255 self.storage_cost,
256 self.storage_rebate,
257 self.non_refundable_storage_fee,
258 )
259 }
260 }
261
262 impl std::ops::AddAssign<&Self> for GasCostSummary {
263 fn add_assign(&mut self, other: &Self) {
264 self.computation_cost += other.computation_cost;
265 self.storage_cost += other.storage_cost;
266 self.storage_rebate += other.storage_rebate;
267 self.non_refundable_storage_fee += other.non_refundable_storage_fee;
268 }
269 }
270
271 impl std::ops::AddAssign<Self> for GasCostSummary {
272 fn add_assign(&mut self, other: Self) {
273 self.add_assign(&other)
274 }
275 }
276
277 pub fn deduct_gas(gas_object: &mut Object, charge_or_rebate: i64) {
282 let gas_coin = gas_object.data.try_as_move_mut().unwrap();
284 let balance = gas_coin.get_coin_value_unsafe();
285 let new_balance = if charge_or_rebate < 0 {
286 balance + (-charge_or_rebate as u64)
287 } else {
288 assert!(balance >= charge_or_rebate as u64);
289 balance - charge_or_rebate as u64
290 };
291 gas_coin.set_coin_value_unsafe(new_balance)
292 }
293
294 pub fn get_gas_balance(gas_object: &Object) -> UserInputResult<u64> {
295 if let Some(move_obj) = gas_object.data.try_as_move() {
296 if !move_obj.type_().is_gas_coin() {
297 return Err(UserInputError::InvalidGasObject {
298 object_id: gas_object.id(),
299 });
300 }
301 Ok(move_obj.get_coin_value_unsafe())
302 } else {
303 Err(UserInputError::InvalidGasObject {
304 object_id: gas_object.id(),
305 })
306 }
307 }
308}
309
310#[derive(Debug, Clone, Serialize, Deserialize)]
311pub struct GasUsageReport {
312 pub cost_summary: GasCostSummary,
313 pub gas_used: u64,
314 pub gas_budget: u64,
315 pub gas_price: u64,
316 pub reference_gas_price: u64,
317 pub storage_gas_price: u64,
318 pub rebate_rate: u64,
319 pub per_object_storage: Vec<(ObjectID, PerObjectStorage)>,
320}