iota_types/
balance.rs

1// Copyright (c) Mysten Labs, Inc.
2// Modifications Copyright (c) 2024 IOTA Stiftung
3// SPDX-License-Identifier: Apache-2.0
4
5use iota_sdk_types::{Identifier, StructTag, TypeTag};
6use move_core_types::{
7    annotated_value::{MoveFieldLayout, MoveStructLayout, MoveTypeLayout},
8    ident_str,
9};
10use serde::{Deserialize, Serialize};
11use serde_with::serde_as;
12
13use crate::{
14    error::{ExecutionError, ExecutionErrorKind},
15    iota_sdk_types_conversions::{struct_tag_core_to_sdk, struct_tag_sdk_to_core},
16    iota_serde::{BigInt, Readable},
17};
18pub const BALANCE_CREATE_REWARDS_FUNCTION_NAME: Identifier =
19    Identifier::from_static("create_staking_rewards");
20pub const BALANCE_DESTROY_REBATES_FUNCTION_NAME: Identifier =
21    Identifier::from_static("destroy_storage_rebates");
22
23#[serde_as]
24#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
25pub struct Supply {
26    #[serde_as(as = "Readable<BigInt<u64>, _>")]
27    pub value: u64,
28}
29
30#[derive(Debug, Serialize, Deserialize, Clone, Eq, PartialEq)]
31pub struct Balance {
32    value: u64,
33}
34
35impl Balance {
36    pub fn new(value: u64) -> Self {
37        Self { value }
38    }
39
40    pub fn withdraw(&mut self, amount: u64) -> Result<(), ExecutionError> {
41        fp_ensure!(
42            self.value >= amount,
43            ExecutionError::new_with_source(
44                ExecutionErrorKind::InsufficientCoinBalance,
45                format!("balance: {} required: {}", self.value, amount)
46            )
47        );
48        self.value -= amount;
49        Ok(())
50    }
51
52    pub fn deposit_for_safe_mode(&mut self, amount: u64) {
53        self.value += amount;
54    }
55
56    pub fn value(&self) -> u64 {
57        self.value
58    }
59
60    pub fn to_bcs_bytes(&self) -> Vec<u8> {
61        bcs::to_bytes(&self).unwrap()
62    }
63
64    pub fn layout(type_param: TypeTag) -> MoveStructLayout {
65        MoveStructLayout {
66            type_: struct_tag_sdk_to_core(&StructTag::new_balance(type_param)),
67            fields: vec![MoveFieldLayout::new(
68                ident_str!("value").to_owned(),
69                MoveTypeLayout::U64,
70            )],
71        }
72    }
73
74    /// Check if a struct layout represents a `Balance<T>` type with the
75    /// expected field structure.
76    pub fn is_balance_layout(struct_layout: &MoveStructLayout) -> bool {
77        let ty = struct_tag_core_to_sdk(&struct_layout.type_);
78
79        if !ty.is_balance() {
80            return false;
81        }
82
83        if ty.type_params().len() != 1 {
84            return false;
85        }
86
87        if struct_layout.fields.len() != 1 {
88            return false;
89        }
90
91        let Some(field) = struct_layout.fields.first() else {
92            return false;
93        };
94
95        if field.name.as_str() != "value" {
96            return false;
97        }
98
99        if !matches!(field.layout, MoveTypeLayout::U64) {
100            return false;
101        }
102
103        true
104    }
105}