sui_core/accumulators/
funds_read.rs1use std::collections::BTreeMap;
5
6use move_core_types::language_storage::TypeTag;
7use sui_types::{
8 accumulator_root::AccumulatorObjId,
9 balance::Balance,
10 base_types::SequenceNumber,
11 error::{SuiErrorKind, SuiResult, UserInputError},
12};
13
14pub trait AccountFundsRead: Send + Sync {
15 fn get_latest_account_amount(&self, account_id: &AccumulatorObjId) -> u128;
19
20 fn get_consistent_latest_account_amount_and_version(
26 &self,
27 account_id: &AccumulatorObjId,
28 ) -> (u128, SequenceNumber);
29
30 fn get_account_amount_at_version(
33 &self,
34 account_id: &AccumulatorObjId,
35 version: SequenceNumber,
36 ) -> u128;
37
38 fn check_amounts_available(
42 &self,
43 requested_amounts: &BTreeMap<AccumulatorObjId, (u64, TypeTag)>,
44 ) -> SuiResult {
45 for (object_id, (requested_amount, _type_tag)) in requested_amounts {
46 let actual_amount = self.get_latest_account_amount(object_id);
47
48 if actual_amount < *requested_amount as u128 {
49 return Err(SuiErrorKind::UserInputError {
50 error: UserInputError::InvalidWithdrawReservation {
51 error: format!(
52 "Available amount in account for object id {} is less than requested: {} < {}",
53 object_id, actual_amount, requested_amount
54 ),
55 },
56 }
57 .into());
58 }
59 }
60
61 Ok(())
62 }
63
64 fn check_remaining_amounts_after_withdrawal(
69 &self,
70 requested_amounts: &BTreeMap<AccumulatorObjId, (u64, TypeTag)>,
71 min_amounts: &BTreeMap<TypeTag, u64>,
72 ) -> SuiResult {
73 for (object_id, (requested_amount, type_tag)) in requested_amounts {
74 let actual_amount = self.get_latest_account_amount(object_id);
75 let remaining = actual_amount.saturating_sub(*requested_amount as u128);
76 if remaining == 0 {
77 continue;
78 }
79 let coin_type =
80 Balance::maybe_get_balance_type_param(type_tag).unwrap_or_else(|| type_tag.clone());
81 if let Some(&min_amount) = min_amounts.get(&coin_type)
82 && min_amount > 0
83 && remaining < min_amount as u128
84 {
85 return Err(SuiErrorKind::UserInputError {
86 error: UserInputError::InvalidWithdrawReservation {
87 error: format!(
88 "Invalid gasless withdrawal from {object_id}. \
89 Gasless transactions must either use the entire balance, \
90 or leave at least {min_amount} for token type {coin_type}. \
91 Remaining amount is {remaining}",
92 ),
93 },
94 }
95 .into());
96 }
97 }
98
99 Ok(())
100 }
101}