Skip to main content

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 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
23/// The number of Nanos per IOTA token
24pub const NANOS_PER_IOTA: u64 = 1_000_000_000;
25
26/// Total supply in IOTA at genesis, after the migration from a Stardust ledger,
27/// before any inflation mechanism
28pub const STARDUST_TOTAL_SUPPLY_IOTA: u64 = 4_600_000_000;
29
30// Note: cannot use checked arithmetic here since `const unwrap` is still
31// unstable.
32/// Total supply at genesis denominated in Nanos, after the migration from a
33/// Stardust ledger, before any inflation mechanism
34pub const STARDUST_TOTAL_SUPPLY_NANOS: u64 = STARDUST_TOTAL_SUPPLY_IOTA * NANOS_PER_IOTA;
35
36/// Value of the mock gas coin minted for a gasless transaction in dev-inspect,
37/// dry-run, and offline simulation when no gas coin is provided.
38pub const SIMULATION_GAS_COIN_VALUE: u64 = 1_000_000_000 * NANOS_PER_IOTA; // 1B IOTA
39
40/// Mint the one-shot mock gas coin that simulation paths use for a transaction
41/// carrying no gas payment: a fresh coin at [`ObjectId::MAX`] owned by `owner`
42/// and funded with [`SIMULATION_GAS_COIN_VALUE`].
43pub 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    /// 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        /// Return `true` if `s` is the type of a gas balance (i.e.,
91        /// 0x2::balance::Balance<0x2::iota::IOTA>)
92        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    // Rust version of the IotaTreasuryCap type
162    #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
163    pub struct IotaTreasuryCap {
164        pub inner: TreasuryCap,
165    }
166
167    impl IotaTreasuryCap {
168        /// Returns the `TreasuryCap<IOTA>` object ID.
169        pub fn id(&self) -> &ObjectId {
170            self.inner.id.object_id()
171        }
172
173        /// Returns the total `Supply` of `Coin<IOTA>`.
174        pub fn total_supply(&self) -> &Supply {
175            &self.inner.total_supply
176        }
177    }
178}