1use move_core_types::{
5 annotated_value::MoveStructLayout,
6 ident_str,
7 identifier::IdentStr,
8 language_storage::{StructTag, TypeTag},
9};
10use serde::{Deserialize, Serialize};
11use std::convert::{TryFrom, TryInto};
12use std::fmt::{Display, Formatter};
13
14use crate::{
15 SUI_FRAMEWORK_ADDRESS,
16 balance::Balance,
17 base_types::{ObjectID, SequenceNumber},
18 coin::Coin,
19 error::ExecutionError,
20 execution_status::ExecutionErrorKind,
21 object::{Data, MoveObject, Object},
22};
23
24pub const MIST_PER_SUI: u64 = 1_000_000_000;
26
27pub const TOTAL_SUPPLY_SUI: u64 = 10_000_000_000;
29
30pub const TOTAL_SUPPLY_MIST: u64 = TOTAL_SUPPLY_SUI * MIST_PER_SUI;
33
34pub const GAS_MODULE_NAME: &IdentStr = ident_str!("sui");
35pub const GAS_STRUCT_NAME: &IdentStr = ident_str!("SUI");
36
37pub use checked::*;
38
39#[sui_macros::with_checked_arithmetic]
40mod checked {
41 use super::*;
42
43 pub struct GAS {}
44 impl GAS {
45 pub fn type_() -> StructTag {
46 StructTag {
47 address: SUI_FRAMEWORK_ADDRESS,
48 name: GAS_STRUCT_NAME.to_owned(),
49 module: GAS_MODULE_NAME.to_owned(),
50 type_params: Vec::new(),
51 }
52 }
53
54 pub fn type_tag() -> TypeTag {
55 TypeTag::Struct(Box::new(Self::type_()))
56 }
57
58 pub fn is_gas(other: &StructTag) -> bool {
59 &Self::type_() == other
60 }
61
62 pub fn is_gas_type(other: &TypeTag) -> bool {
63 match other {
64 TypeTag::Struct(s) => Self::is_gas(s),
65 _ => false,
66 }
67 }
68 }
69
70 #[derive(Clone, Debug, Serialize, Deserialize)]
72 pub struct GasCoin(pub Coin);
73
74 impl GasCoin {
75 pub fn new(id: ObjectID, value: u64) -> Self {
76 Self(Coin::new(id, value))
77 }
78
79 pub fn value(&self) -> u64 {
80 self.0.value()
81 }
82
83 pub fn type_() -> StructTag {
84 Coin::type_(TypeTag::Struct(Box::new(GAS::type_())))
85 }
86
87 pub fn is_gas_coin(s: &StructTag) -> bool {
89 Coin::is_coin(s) && s.type_params.len() == 1 && GAS::is_gas_type(&s.type_params[0])
90 }
91
92 pub fn is_gas_balance(s: &StructTag) -> bool {
94 Balance::is_balance(s)
95 && s.type_params.len() == 1
96 && GAS::is_gas_type(&s.type_params[0])
97 }
98
99 pub fn is_gas_balance_type(type_param: &TypeTag) -> bool {
100 if let TypeTag::Struct(s) = type_param {
101 Self::is_gas_balance(s)
102 } else {
103 false
104 }
105 }
106
107 pub fn id(&self) -> &ObjectID {
108 self.0.id()
109 }
110
111 pub fn to_bcs_bytes(&self) -> Vec<u8> {
112 bcs::to_bytes(&self).unwrap()
113 }
114
115 pub fn to_object(&self, version: SequenceNumber) -> MoveObject {
116 MoveObject::new_gas_coin(version, *self.id(), self.value())
117 }
118
119 pub fn layout() -> MoveStructLayout {
120 Coin::layout(TypeTag::Struct(Box::new(GAS::type_())))
121 }
122
123 pub fn new_for_testing(value: u64) -> Self {
124 Self::new(ObjectID::random(), value)
125 }
126
127 pub fn new_for_testing_with_id(id: ObjectID, value: u64) -> Self {
128 Self::new(id, value)
129 }
130 }
131
132 impl TryFrom<&MoveObject> for GasCoin {
133 type Error = ExecutionError;
134
135 fn try_from(value: &MoveObject) -> Result<GasCoin, ExecutionError> {
136 if !value.type_().is_gas_coin() {
137 return Err(ExecutionError::new_with_source(
138 ExecutionErrorKind::InvalidGasObject,
139 format!("Gas object type is not a gas coin: {}", value.type_()),
140 ));
141 }
142 let gas_coin: GasCoin = bcs::from_bytes(value.contents()).map_err(|err| {
143 ExecutionError::new_with_source(
144 ExecutionErrorKind::InvalidGasObject,
145 format!("Unable to deserialize gas object: {:?}", err),
146 )
147 })?;
148 Ok(gas_coin)
149 }
150 }
151
152 impl TryFrom<&Object> for GasCoin {
153 type Error = ExecutionError;
154
155 fn try_from(value: &Object) -> Result<GasCoin, ExecutionError> {
156 match &value.data {
157 Data::Move(obj) => obj.try_into(),
158 Data::Package(_) => Err(ExecutionError::new_with_source(
159 ExecutionErrorKind::InvalidGasObject,
160 format!("Gas object type is not a gas coin: {:?}", value),
161 )),
162 }
163 }
164 }
165
166 impl Display for GasCoin {
167 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
168 write!(f, "Coin {{ id: {}, value: {} }}", self.id(), self.value())
169 }
170 }
171}