sui_types/
balance.rs

1// Copyright (c) Mysten Labs, Inc.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::SUI_FRAMEWORK_ADDRESS;
5use crate::sui_serde::BigInt;
6use crate::sui_serde::Readable;
7use crate::{error::ExecutionError, execution_status::ExecutionErrorKind};
8use move_core_types::account_address::AccountAddress;
9use move_core_types::annotated_value::{MoveFieldLayout, MoveStructLayout, MoveTypeLayout};
10use move_core_types::ident_str;
11use move_core_types::identifier::IdentStr;
12use move_core_types::language_storage::{StructTag, TypeTag};
13use schemars::JsonSchema;
14use serde::Deserialize;
15use serde::Serialize;
16use serde_with::serde_as;
17
18pub const SUI_MODULE_NAME: &IdentStr = ident_str!("sui");
19pub const BALANCE_MODULE_NAME: &IdentStr = ident_str!("balance");
20pub const BALANCE_STRUCT_NAME: &IdentStr = ident_str!("Balance");
21pub const RESOLVED_BALANCE_STRUCT: (&AccountAddress, &IdentStr, &IdentStr) = (
22    &SUI_FRAMEWORK_ADDRESS,
23    BALANCE_MODULE_NAME,
24    BALANCE_STRUCT_NAME,
25);
26pub const BALANCE_CREATE_REWARDS_FUNCTION_NAME: &IdentStr = ident_str!("create_staking_rewards");
27pub const BALANCE_DESTROY_REBATES_FUNCTION_NAME: &IdentStr = ident_str!("destroy_storage_rebates");
28
29pub const BALANCE_REDEEM_FUNDS_FUNCTION_NAME: &IdentStr = ident_str!("redeem_funds");
30pub const BALANCE_SEND_FUNDS_FUNCTION_NAME: &IdentStr = ident_str!("send_funds");
31pub const BALANCE_SPLIT_FUNCTION_NAME: &IdentStr = ident_str!("split");
32pub const BALANCE_ZERO_FUNCTION_NAME: &IdentStr = ident_str!("zero");
33
34#[serde_as]
35#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq, JsonSchema)]
36pub struct Supply {
37    #[schemars(with = "BigInt<u64>")]
38    #[serde_as(as = "Readable<BigInt<u64>, _>")]
39    pub value: u64,
40}
41
42#[derive(Debug, Serialize, Deserialize, Clone, JsonSchema, Eq, PartialEq)]
43pub struct Balance {
44    value: u64,
45}
46
47impl Balance {
48    pub fn new(value: u64) -> Self {
49        Self { value }
50    }
51
52    pub fn type_(type_param: TypeTag) -> StructTag {
53        StructTag {
54            address: SUI_FRAMEWORK_ADDRESS,
55            module: BALANCE_MODULE_NAME.to_owned(),
56            name: BALANCE_STRUCT_NAME.to_owned(),
57            type_params: vec![type_param],
58        }
59    }
60
61    pub fn type_tag(type_param: TypeTag) -> TypeTag {
62        TypeTag::Struct(Box::new(Self::type_(type_param)))
63    }
64
65    pub fn is_balance(s: &StructTag) -> bool {
66        s.address == SUI_FRAMEWORK_ADDRESS
67            && s.module.as_ident_str() == BALANCE_MODULE_NAME
68            && s.name.as_ident_str() == BALANCE_STRUCT_NAME
69            && s.type_params.len() == 1
70    }
71
72    pub fn is_balance_type(type_param: &TypeTag) -> bool {
73        if let TypeTag::Struct(struct_tag) = type_param {
74            Self::is_balance(struct_tag)
75        } else {
76            false
77        }
78    }
79
80    /// If the given type is `Balance<T>`, return `Some(T)`.
81    pub fn maybe_get_balance_type_param(ty: &TypeTag) -> Option<TypeTag> {
82        if let TypeTag::Struct(struct_tag) = ty
83            && Self::is_balance(struct_tag)
84        {
85            assert_eq!(struct_tag.type_params.len(), 1);
86            return Some(struct_tag.type_params[0].clone());
87        }
88        None
89    }
90
91    pub fn withdraw(&mut self, amount: u64) -> Result<(), ExecutionError> {
92        fp_ensure!(
93            self.value >= amount,
94            ExecutionError::new_with_source(
95                ExecutionErrorKind::InsufficientCoinBalance,
96                format!("balance: {} required: {}", self.value, amount)
97            )
98        );
99        self.value -= amount;
100        Ok(())
101    }
102
103    pub fn deposit_for_safe_mode(&mut self, amount: u64) {
104        self.value += amount;
105    }
106
107    pub fn value(&self) -> u64 {
108        self.value
109    }
110
111    pub fn to_bcs_bytes(&self) -> Vec<u8> {
112        bcs::to_bytes(&self).unwrap()
113    }
114
115    pub fn layout(type_param: TypeTag) -> MoveStructLayout {
116        MoveStructLayout {
117            type_: Self::type_(type_param),
118            fields: vec![MoveFieldLayout::new(
119                ident_str!("value").to_owned(),
120                MoveTypeLayout::U64,
121            )],
122        }
123    }
124
125    /// Check if a struct layout represents a `Balance<T>` type with the expected field structure.
126    pub fn is_balance_layout(struct_layout: &MoveStructLayout) -> bool {
127        let ty = &struct_layout.type_;
128
129        if !Self::is_balance(ty) {
130            return false;
131        }
132
133        if ty.type_params.len() != 1 {
134            return false;
135        }
136
137        if struct_layout.fields.len() != 1 {
138            return false;
139        }
140
141        let Some(field) = struct_layout.fields.first() else {
142            return false;
143        };
144
145        if field.name.as_str() != "value" {
146            return false;
147        }
148
149        if !matches!(field.layout, MoveTypeLayout::U64) {
150            return false;
151        }
152
153        true
154    }
155}