1use std::{
6 convert::{TryFrom, TryInto},
7 fmt::{Display, Formatter},
8};
9
10use move_core_types::annotated_value::MoveStructLayout;
11use serde::{Deserialize, Serialize};
12
13use crate::{
14 balance::Supply,
15 base_types::{ObjectID, SequenceNumber},
16 coin::{Coin, TreasuryCap},
17 error::{ExecutionError, ExecutionErrorKind},
18 object::{Data, MoveObject, MoveObjectExt, Object},
19};
20
21pub const NANOS_PER_IOTA: u64 = 1_000_000_000;
23
24pub const STARDUST_TOTAL_SUPPLY_IOTA: u64 = 4_600_000_000;
27
28pub const STARDUST_TOTAL_SUPPLY_NANOS: u64 = STARDUST_TOTAL_SUPPLY_IOTA * NANOS_PER_IOTA;
33
34pub use checked::*;
35
36#[iota_macros::with_checked_arithmetic]
37mod checked {
38 use iota_sdk_types::{StructTag, TypeTag};
39
40 use super::*;
41
42 pub struct GAS {}
43 impl GAS {
44 pub fn type_tag() -> TypeTag {
45 StructTag::new_gas().into()
46 }
47
48 pub fn is_gas_type(other: &TypeTag) -> bool {
49 match other {
50 TypeTag::Struct(s) => s.is_gas(),
51 _ => false,
52 }
53 }
54 }
55
56 #[derive(Clone, Debug, Serialize, Deserialize)]
58 pub struct GasCoin(pub Coin);
59
60 impl GasCoin {
61 pub fn new(id: ObjectID, value: u64) -> Self {
62 Self(Coin::new(id, value))
63 }
64
65 pub fn value(&self) -> u64 {
66 self.0.value()
67 }
68
69 pub fn is_gas_balance(s: &StructTag) -> bool {
72 s.is_balance() && GAS::is_gas_type(&s.type_params()[0])
73 }
74
75 pub fn id(&self) -> &ObjectID {
76 self.0.id()
77 }
78
79 pub fn to_bcs_bytes(&self) -> Vec<u8> {
80 bcs::to_bytes(&self).unwrap()
81 }
82
83 pub fn to_object(&self, version: SequenceNumber) -> MoveObject {
84 MoveObject::new_gas_coin(version, *self.id(), self.value())
85 }
86
87 pub fn layout() -> MoveStructLayout {
88 Coin::layout(TypeTag::Struct(Box::new(StructTag::new_gas())))
89 }
90
91 pub fn new_for_testing(value: u64) -> Self {
92 Self::new(ObjectID::random(), value)
93 }
94
95 pub fn new_for_testing_with_id(id: ObjectID, value: u64) -> Self {
96 Self::new(id, value)
97 }
98 }
99
100 impl TryFrom<&MoveObject> for GasCoin {
101 type Error = ExecutionError;
102
103 fn try_from(value: &MoveObject) -> Result<GasCoin, ExecutionError> {
104 if !value.struct_tag().is_gas_coin() {
105 return Err(ExecutionError::new_with_source(
106 ExecutionErrorKind::InvalidGasObject,
107 format!("Gas object type is not a gas coin: {}", value.struct_tag()),
108 ));
109 }
110 let gas_coin: GasCoin = bcs::from_bytes(value.contents()).map_err(|err| {
111 ExecutionError::new_with_source(
112 ExecutionErrorKind::InvalidGasObject,
113 format!("Unable to deserialize gas object: {err:?}"),
114 )
115 })?;
116 Ok(gas_coin)
117 }
118 }
119
120 impl TryFrom<&Object> for GasCoin {
121 type Error = ExecutionError;
122
123 fn try_from(value: &Object) -> Result<GasCoin, ExecutionError> {
124 match &value.data {
125 Data::Struct(obj) => obj.try_into(),
126 Data::Package(_) => Err(ExecutionError::new_with_source(
127 ExecutionErrorKind::InvalidGasObject,
128 format!("Gas object type is not a gas coin: {value:?}"),
129 )),
130 }
131 }
132 }
133
134 impl Display for GasCoin {
135 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
136 write!(f, "Coin {{ id: {}, value: {} }}", self.id(), self.value())
137 }
138 }
139
140 #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
142 pub struct IotaTreasuryCap {
143 pub inner: TreasuryCap,
144 }
145
146 impl IotaTreasuryCap {
147 pub fn id(&self) -> &ObjectID {
149 self.inner.id.object_id()
150 }
151
152 pub fn total_supply(&self) -> &Supply {
154 &self.inner.total_supply
155 }
156 }
157}