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(
110 &self,
111 gas_objs: &[&ObjectReadResult],
112 gas_budget: u64,
113 ) -> UserInputResult {
114 match self {
115 Self::V2(status) => status.check_gas_balance(gas_objs, gas_budget),
116 }
117 }
118
119 pub fn gas_price(&self) -> u64 {
120 match self {
121 Self::V2(status) => status.gas_price(),
122 }
123 }
124 }
125
126 #[serde_as]
151 #[derive(Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
152 #[serde(rename_all = "camelCase")]
153 pub struct GasCostSummary {
154 #[schemars(with = "BigInt<u64>")]
156 #[serde_as(as = "Readable<BigInt<u64>, _>")]
157 pub computation_cost: u64,
158 #[schemars(with = "BigInt<u64>")]
160 #[serde_as(as = "Readable<BigInt<u64>, _>")]
161 pub storage_cost: u64,
162 #[schemars(with = "BigInt<u64>")]
165 #[serde_as(as = "Readable<BigInt<u64>, _>")]
166 pub storage_rebate: u64,
167 #[schemars(with = "BigInt<u64>")]
169 #[serde_as(as = "Readable<BigInt<u64>, _>")]
170 pub non_refundable_storage_fee: u64,
171 }
172
173 impl GasCostSummary {
174 pub fn new(
175 computation_cost: u64,
176 storage_cost: u64,
177 storage_rebate: u64,
178 non_refundable_storage_fee: u64,
179 ) -> GasCostSummary {
180 GasCostSummary {
181 computation_cost,
182 storage_cost,
183 storage_rebate,
184 non_refundable_storage_fee,
185 }
186 }
187
188 pub fn gas_used(&self) -> u64 {
189 self.computation_cost + self.storage_cost
190 }
191
192 pub fn sender_rebate(&self, storage_rebate_rate: u64) -> u64 {
195 const BASIS_POINTS: u128 = 10000;
198 (((self.storage_rebate as u128 * storage_rebate_rate as u128)
199 + (BASIS_POINTS / 2)) / BASIS_POINTS) as u64
201 }
202
203 pub fn net_gas_usage(&self) -> i64 {
205 self.gas_used() as i64 - self.storage_rebate as i64
206 }
207
208 pub fn new_from_txn_effects<'a>(
209 transactions: impl Iterator<Item = &'a TransactionEffects>,
210 ) -> GasCostSummary {
211 let (storage_costs, computation_costs, storage_rebates, non_refundable_storage_fee): (
212 Vec<u64>,
213 Vec<u64>,
214 Vec<u64>,
215 Vec<u64>,
216 ) = transactions
217 .map(|e| {
218 (
219 e.gas_cost_summary().storage_cost,
220 e.gas_cost_summary().computation_cost,
221 e.gas_cost_summary().storage_rebate,
222 e.gas_cost_summary().non_refundable_storage_fee,
223 )
224 })
225 .multiunzip();
226
227 GasCostSummary {
228 storage_cost: storage_costs.iter().sum(),
229 computation_cost: computation_costs.iter().sum(),
230 storage_rebate: storage_rebates.iter().sum(),
231 non_refundable_storage_fee: non_refundable_storage_fee.iter().sum(),
232 }
233 }
234 }
235
236 impl std::fmt::Display for GasCostSummary {
237 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
238 write!(
239 f,
240 "computation_cost: {}, storage_cost: {}, storage_rebate: {}, non_refundable_storage_fee: {}",
241 self.computation_cost,
242 self.storage_cost,
243 self.storage_rebate,
244 self.non_refundable_storage_fee,
245 )
246 }
247 }
248
249 impl std::ops::AddAssign<&Self> for GasCostSummary {
250 fn add_assign(&mut self, other: &Self) {
251 self.computation_cost += other.computation_cost;
252 self.storage_cost += other.storage_cost;
253 self.storage_rebate += other.storage_rebate;
254 self.non_refundable_storage_fee += other.non_refundable_storage_fee;
255 }
256 }
257
258 impl std::ops::AddAssign<Self> for GasCostSummary {
259 fn add_assign(&mut self, other: Self) {
260 self.add_assign(&other)
261 }
262 }
263
264 pub fn deduct_gas(gas_object: &mut Object, charge_or_rebate: i64) {
269 let gas_coin = gas_object.data.try_as_move_mut().unwrap();
271 let balance = gas_coin.get_coin_value_unsafe();
272 let new_balance = if charge_or_rebate < 0 {
273 balance + (-charge_or_rebate as u64)
274 } else {
275 assert!(balance >= charge_or_rebate as u64);
276 balance - charge_or_rebate as u64
277 };
278 gas_coin.set_coin_value_unsafe(new_balance)
279 }
280
281 pub fn get_gas_balance(gas_object: &Object) -> UserInputResult<u64> {
282 if let Some(move_obj) = gas_object.data.try_as_move() {
283 if !move_obj.type_().is_gas_coin() {
284 return Err(UserInputError::InvalidGasObject {
285 object_id: gas_object.id(),
286 });
287 }
288 Ok(move_obj.get_coin_value_unsafe())
289 } else {
290 Err(UserInputError::InvalidGasObject {
291 object_id: gas_object.id(),
292 })
293 }
294 }
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct GasUsageReport {
299 pub cost_summary: GasCostSummary,
300 pub gas_used: u64,
301 pub gas_budget: u64,
302 pub gas_price: u64,
303 pub reference_gas_price: u64,
304 pub storage_gas_price: u64,
305 pub rebate_rate: u64,
306 pub per_object_storage: Vec<(ObjectID, PerObjectStorage)>,
307}