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, SequenceNumber);
19
20 fn get_account_amount_at_version(
23 &self,
24 account_id: &AccumulatorObjId,
25 version: SequenceNumber,
26 ) -> u128;
27
28 fn check_amounts_available(
32 &self,
33 requested_amounts: &BTreeMap<AccumulatorObjId, (u64, TypeTag)>,
34 ) -> SuiResult {
35 for (object_id, (requested_amount, _type_tag)) in requested_amounts {
36 let (actual_amount, _) = self.get_latest_account_amount(object_id);
37
38 if actual_amount < *requested_amount as u128 {
39 return Err(SuiErrorKind::UserInputError {
40 error: UserInputError::InvalidWithdrawReservation {
41 error: format!(
42 "Available amount in account for object id {} is less than requested: {} < {}",
43 object_id, actual_amount, requested_amount
44 ),
45 },
46 }
47 .into());
48 }
49 }
50
51 Ok(())
52 }
53
54 fn check_remaining_amounts_after_withdrawal(
59 &self,
60 requested_amounts: &BTreeMap<AccumulatorObjId, (u64, TypeTag)>,
61 min_amounts: &BTreeMap<TypeTag, u64>,
62 ) -> SuiResult {
63 for (object_id, (requested_amount, type_tag)) in requested_amounts {
64 let (actual_amount, _) = self.get_latest_account_amount(object_id);
65 let remaining = actual_amount.saturating_sub(*requested_amount as u128);
66 if remaining == 0 {
67 continue;
68 }
69 let coin_type =
70 Balance::maybe_get_balance_type_param(type_tag).unwrap_or_else(|| type_tag.clone());
71 if let Some(&min_amount) = min_amounts.get(&coin_type)
72 && min_amount > 0
73 && remaining < min_amount as u128
74 {
75 return Err(SuiErrorKind::UserInputError {
76 error: UserInputError::InvalidWithdrawReservation {
77 error: format!(
78 "Invalid gasless withdrawal from {object_id}. \
79 Gasless transactions must either use the entire balance, \
80 or leave at least {min_amount} for token type {coin_type}. \
81 Remaining amount is {remaining}",
82 ),
83 },
84 }
85 .into());
86 }
87 }
88
89 Ok(())
90 }
91}