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 type_tag() -> TypeTag {
66 StructTag::new_gas().into()
67 }
68
69 pub fn is_gas_type(other: &TypeTag) -> bool {
70 match other {
71 TypeTag::Struct(s) => s.is_gas(),
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 is_gas_balance(s: &StructTag) -> bool {
93 s.is_balance() && GAS::is_gas_type(&s.type_params()[0])
94 }
95
96 pub fn id(&self) -> &ObjectId {
97 self.0.id()
98 }
99
100 pub fn to_bcs_bytes(&self) -> Vec<u8> {
101 bcs::to_bytes(&self).unwrap()
102 }
103
104 pub fn to_object(&self, version: Version) -> MoveStruct {
105 MoveStruct::new_gas_coin(version, *self.id(), self.value())
106 }
107
108 pub fn layout() -> MoveStructLayout {
109 Coin::layout(TypeTag::Struct(Box::new(StructTag::new_gas())))
110 }
111
112 pub fn new_for_testing(value: u64) -> Self {
113 Self::new(ObjectId::random(), value)
114 }
115
116 pub fn new_for_testing_with_id(id: ObjectId, value: u64) -> Self {
117 Self::new(id, value)
118 }
119 }
120
121 impl TryFrom<&MoveStruct> for GasCoin {
122 type Error = ExecutionError;
123
124 fn try_from(value: &MoveStruct) -> Result<GasCoin, ExecutionError> {
125 if !value.struct_tag().is_gas_coin() {
126 return Err(ExecutionError::new_with_source(
127 ExecutionErrorKind::InvalidGasObject,
128 format!("Gas object type is not a gas coin: {}", value.struct_tag()),
129 ));
130 }
131 let gas_coin: GasCoin = bcs::from_bytes(value.contents()).map_err(|err| {
132 ExecutionError::new_with_source(
133 ExecutionErrorKind::InvalidGasObject,
134 format!("Unable to deserialize gas object: {err:?}"),
135 )
136 })?;
137 Ok(gas_coin)
138 }
139 }
140
141 impl TryFrom<&Object> for GasCoin {
142 type Error = ExecutionError;
143
144 fn try_from(value: &Object) -> Result<GasCoin, ExecutionError> {
145 match &value.data {
146 ObjectData::Struct(obj) => obj.try_into(),
147 ObjectData::Package(_) => Err(ExecutionError::new_with_source(
148 ExecutionErrorKind::InvalidGasObject,
149 format!("Gas object type is not a gas coin: {value:?}"),
150 )),
151 }
152 }
153 }
154
155 impl Display for GasCoin {
156 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
157 write!(f, "Coin {{ id: {}, value: {} }}", self.id(), self.value())
158 }
159 }
160
161 #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
163 pub struct IotaTreasuryCap {
164 pub inner: TreasuryCap,
165 }
166
167 impl IotaTreasuryCap {
168 pub fn id(&self) -> &ObjectId {
170 self.inner.id.object_id()
171 }
172
173 pub fn total_supply(&self) -> &Supply {
175 &self.inner.total_supply
176 }
177 }
178}