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