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