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