iota_types/
gas_coin.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use 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
27/// The number of Nanos per IOTA token
28pub const NANOS_PER_IOTA: u64 = 1_000_000_000;
29
30/// Total supply in IOTA at genesis, after the migration from a Stardust ledger,
31/// before any inflation mechanism
32pub const STARDUST_TOTAL_SUPPLY_IOTA: u64 = 4_600_000_000;
33
34// Note: cannot use checked arithmetic here since `const unwrap` is still
35// unstable.
36/// Total supply at genesis denominated in Nanos, after the migration from a
37/// Stardust ledger, before any inflation mechanism
38pub 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    /// Rust version of the Move iota::coin::Coin<Iota::iota::IOTA> type
78    #[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        /// Return `true` if `s` is the type of a gas coin (i.e.,
95        /// 0x2::coin::Coin<0x2::iota::IOTA>)
96        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        /// Return `true` if `s` is the type of a gas balance (i.e.,
101        /// 0x2::balance::Balance<0x2::iota::IOTA>)
102        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    // Rust version of the IotaTreasuryCap type
174    #[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        /// Returns the `TreasuryCap<IOTA>` object ID.
190        pub fn id(&self) -> &ObjectID {
191            self.inner.id.object_id()
192        }
193
194        /// Returns the total `Supply` of `Coin<IOTA>`.
195        pub fn total_supply(&self) -> &Supply {
196            &self.inner.total_supply
197        }
198    }
199}