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::ObjectId;
11use move_core_types::annotated_value::MoveStructLayout;
12use serde::{Deserialize, Serialize};
13
14use crate::{
15    balance::Supply,
16    base_types::SequenceNumber,
17    coin::{Coin, TreasuryCap},
18    error::{ExecutionError, ExecutionErrorKind},
19    object::{Data, MoveObject, MoveObjectExt, Object},
20};
21
22/// The number of Nanos per IOTA token
23pub const NANOS_PER_IOTA: u64 = 1_000_000_000;
24
25/// Total supply in IOTA at genesis, after the migration from a Stardust ledger,
26/// before any inflation mechanism
27pub const STARDUST_TOTAL_SUPPLY_IOTA: u64 = 4_600_000_000;
28
29// Note: cannot use checked arithmetic here since `const unwrap` is still
30// unstable.
31/// Total supply at genesis denominated in Nanos, after the migration from a
32/// Stardust ledger, before any inflation mechanism
33pub const STARDUST_TOTAL_SUPPLY_NANOS: u64 = STARDUST_TOTAL_SUPPLY_IOTA * NANOS_PER_IOTA;
34
35pub use checked::*;
36
37#[iota_macros::with_checked_arithmetic]
38mod checked {
39    use iota_sdk_types::{StructTag, TypeTag};
40
41    use super::*;
42
43    pub struct GAS {}
44    impl GAS {
45        pub fn type_tag() -> TypeTag {
46            StructTag::new_gas().into()
47        }
48
49        pub fn is_gas_type(other: &TypeTag) -> bool {
50            match other {
51                TypeTag::Struct(s) => s.is_gas(),
52                _ => false,
53            }
54        }
55    }
56
57    /// Rust version of the Move iota::coin::Coin<Iota::iota::IOTA> type
58    #[derive(Clone, Debug, Serialize, Deserialize)]
59    pub struct GasCoin(pub Coin);
60
61    impl GasCoin {
62        pub fn new(id: ObjectId, value: u64) -> Self {
63            Self(Coin::new(id, value))
64        }
65
66        pub fn value(&self) -> u64 {
67            self.0.value()
68        }
69
70        /// Return `true` if `s` is the type of a gas balance (i.e.,
71        /// 0x2::balance::Balance<0x2::iota::IOTA>)
72        pub fn is_gas_balance(s: &StructTag) -> bool {
73            s.is_balance() && GAS::is_gas_type(&s.type_params()[0])
74        }
75
76        pub fn id(&self) -> &ObjectId {
77            self.0.id()
78        }
79
80        pub fn to_bcs_bytes(&self) -> Vec<u8> {
81            bcs::to_bytes(&self).unwrap()
82        }
83
84        pub fn to_object(&self, version: SequenceNumber) -> MoveObject {
85            MoveObject::new_gas_coin(version, *self.id(), self.value())
86        }
87
88        pub fn layout() -> MoveStructLayout {
89            Coin::layout(TypeTag::Struct(Box::new(StructTag::new_gas())))
90        }
91
92        pub fn new_for_testing(value: u64) -> Self {
93            Self::new(ObjectId::random(), value)
94        }
95
96        pub fn new_for_testing_with_id(id: ObjectId, value: u64) -> Self {
97            Self::new(id, value)
98        }
99    }
100
101    impl TryFrom<&MoveObject> for GasCoin {
102        type Error = ExecutionError;
103
104        fn try_from(value: &MoveObject) -> Result<GasCoin, ExecutionError> {
105            if !value.struct_tag().is_gas_coin() {
106                return Err(ExecutionError::new_with_source(
107                    ExecutionErrorKind::InvalidGasObject,
108                    format!("Gas object type is not a gas coin: {}", value.struct_tag()),
109                ));
110            }
111            let gas_coin: GasCoin = bcs::from_bytes(value.contents()).map_err(|err| {
112                ExecutionError::new_with_source(
113                    ExecutionErrorKind::InvalidGasObject,
114                    format!("Unable to deserialize gas object: {err:?}"),
115                )
116            })?;
117            Ok(gas_coin)
118        }
119    }
120
121    impl TryFrom<&Object> for GasCoin {
122        type Error = ExecutionError;
123
124        fn try_from(value: &Object) -> Result<GasCoin, ExecutionError> {
125            match &value.data {
126                Data::Struct(obj) => obj.try_into(),
127                Data::Package(_) => Err(ExecutionError::new_with_source(
128                    ExecutionErrorKind::InvalidGasObject,
129                    format!("Gas object type is not a gas coin: {value:?}"),
130                )),
131            }
132        }
133    }
134
135    impl Display for GasCoin {
136        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
137            write!(f, "Coin {{ id: {}, value: {} }}", self.id(), self.value())
138        }
139    }
140
141    // Rust version of the IotaTreasuryCap type
142    #[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
143    pub struct IotaTreasuryCap {
144        pub inner: TreasuryCap,
145    }
146
147    impl IotaTreasuryCap {
148        /// Returns the `TreasuryCap<IOTA>` object ID.
149        pub fn id(&self) -> &ObjectId {
150            self.inner.id.object_id()
151        }
152
153        /// Returns the total `Supply` of `Coin<IOTA>`.
154        pub fn total_supply(&self) -> &Supply {
155            &self.inner.total_supply
156        }
157    }
158}