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