1use 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 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}