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