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