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