sui_types/
gas_coin.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use move_core_types::{
5    annotated_value::MoveStructLayout,
6    ident_str,
7    identifier::IdentStr,
8    language_storage::{StructTag, TypeTag},
9};
10use serde::{Deserialize, Serialize};
11use std::convert::{TryFrom, TryInto};
12use std::fmt::{Display, Formatter};
13
14use crate::{
15    SUI_FRAMEWORK_ADDRESS,
16    balance::Balance,
17    base_types::{ObjectID, SequenceNumber},
18    coin::Coin,
19    error::{ExecutionError, ExecutionErrorKind},
20    object::{Data, MoveObject, Object},
21};
22
23/// The number of Mist per Sui token
24pub const MIST_PER_SUI: u64 = 1_000_000_000;
25
26/// Total supply denominated in Sui
27pub const TOTAL_SUPPLY_SUI: u64 = 10_000_000_000;
28
29// Note: cannot use checked arithmetic here since `const unwrap` is still unstable.
30/// Total supply denominated in Mist
31pub const TOTAL_SUPPLY_MIST: u64 = TOTAL_SUPPLY_SUI * MIST_PER_SUI;
32
33pub const GAS_MODULE_NAME: &IdentStr = ident_str!("sui");
34pub const GAS_STRUCT_NAME: &IdentStr = ident_str!("SUI");
35
36pub use checked::*;
37
38#[sui_macros::with_checked_arithmetic]
39mod checked {
40    use super::*;
41
42    pub struct GAS {}
43    impl GAS {
44        pub fn type_() -> StructTag {
45            StructTag {
46                address: SUI_FRAMEWORK_ADDRESS,
47                name: GAS_STRUCT_NAME.to_owned(),
48                module: GAS_MODULE_NAME.to_owned(),
49                type_params: Vec::new(),
50            }
51        }
52
53        pub fn type_tag() -> TypeTag {
54            TypeTag::Struct(Box::new(Self::type_()))
55        }
56
57        pub fn is_gas(other: &StructTag) -> bool {
58            &Self::type_() == other
59        }
60
61        pub fn is_gas_type(other: &TypeTag) -> bool {
62            match other {
63                TypeTag::Struct(s) => Self::is_gas(s),
64                _ => false,
65            }
66        }
67    }
68
69    /// Rust version of the Move sui::coin::Coin<Sui::sui::SUI> type
70    #[derive(Clone, Debug, Serialize, Deserialize)]
71    pub struct GasCoin(pub Coin);
72
73    impl GasCoin {
74        pub fn new(id: ObjectID, value: u64) -> Self {
75            Self(Coin::new(id, value))
76        }
77
78        pub fn value(&self) -> u64 {
79            self.0.value()
80        }
81
82        pub fn type_() -> StructTag {
83            Coin::type_(TypeTag::Struct(Box::new(GAS::type_())))
84        }
85
86        /// Return `true` if `s` is the type of a gas coin (i.e., 0x2::coin::Coin<0x2::sui::SUI>)
87        pub fn is_gas_coin(s: &StructTag) -> bool {
88            Coin::is_coin(s) && s.type_params.len() == 1 && GAS::is_gas_type(&s.type_params[0])
89        }
90
91        /// Return `true` if `s` is the type of a gas balance (i.e., 0x2::balance::Balance<0x2::sui::SUI>)
92        pub fn is_gas_balance(s: &StructTag) -> bool {
93            Balance::is_balance(s)
94                && s.type_params.len() == 1
95                && GAS::is_gas_type(&s.type_params[0])
96        }
97
98        pub fn id(&self) -> &ObjectID {
99            self.0.id()
100        }
101
102        pub fn to_bcs_bytes(&self) -> Vec<u8> {
103            bcs::to_bytes(&self).unwrap()
104        }
105
106        pub fn to_object(&self, version: SequenceNumber) -> MoveObject {
107            MoveObject::new_gas_coin(version, *self.id(), self.value())
108        }
109
110        pub fn layout() -> MoveStructLayout {
111            Coin::layout(TypeTag::Struct(Box::new(GAS::type_())))
112        }
113
114        pub fn new_for_testing(value: u64) -> Self {
115            Self::new(ObjectID::random(), value)
116        }
117
118        pub fn new_for_testing_with_id(id: ObjectID, value: u64) -> Self {
119            Self::new(id, value)
120        }
121    }
122
123    impl TryFrom<&MoveObject> for GasCoin {
124        type Error = ExecutionError;
125
126        fn try_from(value: &MoveObject) -> Result<GasCoin, ExecutionError> {
127            if !value.type_().is_gas_coin() {
128                return Err(ExecutionError::new_with_source(
129                    ExecutionErrorKind::InvalidGasObject,
130                    format!("Gas object type is not a gas coin: {}", value.type_()),
131                ));
132            }
133            let gas_coin: GasCoin = bcs::from_bytes(value.contents()).map_err(|err| {
134                ExecutionError::new_with_source(
135                    ExecutionErrorKind::InvalidGasObject,
136                    format!("Unable to deserialize gas object: {:?}", err),
137                )
138            })?;
139            Ok(gas_coin)
140        }
141    }
142
143    impl TryFrom<&Object> for GasCoin {
144        type Error = ExecutionError;
145
146        fn try_from(value: &Object) -> Result<GasCoin, ExecutionError> {
147            match &value.data {
148                Data::Move(obj) => obj.try_into(),
149                Data::Package(_) => Err(ExecutionError::new_with_source(
150                    ExecutionErrorKind::InvalidGasObject,
151                    format!("Gas object type is not a gas coin: {:?}", value),
152                )),
153            }
154        }
155    }
156
157    impl Display for GasCoin {
158        fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
159            write!(f, "Coin {{ id: {}, value: {} }}", self.id(), self.value())
160        }
161    }
162}