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    id::UID,
25    object::{Data, MoveObject, Object},
26};
27
28/// The number of Nanos per IOTA token
29pub const NANOS_PER_IOTA: u64 = 1_000_000_000;
30
31/// Total supply in IOTA at genesis, after the migration from a Stardust ledger,
32/// before any inflation mechanism
33pub const STARDUST_TOTAL_SUPPLY_IOTA: u64 = 4_600_000_000;
34
35// Note: cannot use checked arithmetic here since `const unwrap` is still
36// unstable.
37/// Total supply at genesis denominated in Nanos, after the migration from a
38/// Stardust ledger, before any inflation mechanism
39pub 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    /// Rust version of the Move iota::coin::Coin<Iota::iota::IOTA> type
79    #[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        /// Return `true` if `s` is the type of a gas coin (i.e.,
96        /// 0x2::coin::Coin<0x2::iota::IOTA>)
97        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        /// Return `true` if `s` is the type of a gas balance (i.e.,
102        /// 0x2::balance::Balance<0x2::iota::IOTA>)
103        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    // Rust version of the IotaTreasuryCap type
175    #[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        /// Returns the `TreasuryCap<IOTA>` object ID.
191        pub fn id(&self) -> &ObjectID {
192            self.inner.id.object_id()
193        }
194
195        /// Returns the total `Supply` of `Coin<IOTA>`.
196        pub fn total_supply(&self) -> &Supply {
197            &self.inner.total_supply
198        }
199    }
200}