sui_core/accumulators/
balances.rs1use mysten_common::debug_fatal;
5use sui_types::{
6 TypeTag,
7 accumulator_metadata::AccumulatorOwner,
8 accumulator_root::AccumulatorValue,
9 balance::Balance,
10 base_types::{ObjectID, SuiAddress},
11 error::SuiResult,
12 storage::error::Error as StorageError,
13 storage::{ChildObjectResolver, DynamicFieldIteratorItem, DynamicFieldKey, RpcIndexes},
14};
15
16use crate::jsonrpc_index::IndexStoreTables;
17
18pub fn get_balance(
21 owner: SuiAddress,
22 child_object_resolver: &dyn ChildObjectResolver,
23 currency_type: TypeTag,
24) -> SuiResult<u64> {
25 let balance_type = Balance::type_tag(currency_type);
26 let address_balance =
27 AccumulatorValue::load(child_object_resolver, None, owner, &balance_type)?
28 .and_then(|b| b.as_u128())
29 .unwrap_or(0);
30
31 let u64_balance = if address_balance > u64::MAX as u128 {
32 tracing::warn!(
36 "address balance for {} {} is greater than u64::MAX",
37 owner,
38 balance_type.to_canonical_string(true)
39 );
40 u64::MAX
41 } else {
42 address_balance as u64
43 };
44
45 Ok(u64_balance)
46}
47
48pub trait GetDynamicFieldsIter {
49 fn get_dynamic_fields_iterator(
50 &self,
51 parent: ObjectID,
52 cursor: Option<ObjectID>,
53 ) -> Result<Box<dyn Iterator<Item = DynamicFieldIteratorItem> + '_>, StorageError>;
54}
55
56impl GetDynamicFieldsIter for &IndexStoreTables {
57 fn get_dynamic_fields_iterator(
58 &self,
59 parent: ObjectID,
60 cursor: Option<ObjectID>,
61 ) -> Result<Box<dyn Iterator<Item = DynamicFieldIteratorItem> + '_>, StorageError> {
62 Ok(Box::new(
63 IndexStoreTables::get_dynamic_fields_iterator(self, parent, cursor).map(
64 move |r| match r {
65 Ok((field_id, _)) => Ok(DynamicFieldKey { parent, field_id }),
66 Err(e) => Err(e),
67 },
68 ),
69 ))
70 }
71}
72
73impl<T: RpcIndexes> GetDynamicFieldsIter for T {
74 fn get_dynamic_fields_iterator(
75 &self,
76 parent: ObjectID,
77 cursor: Option<ObjectID>,
78 ) -> Result<Box<dyn Iterator<Item = DynamicFieldIteratorItem> + '_>, StorageError> {
79 Ok(Box::new(self.dynamic_field_iter(parent, cursor)?))
80 }
81}
82
83pub fn get_currency_types_for_owner(
85 owner: SuiAddress,
86 child_object_resolver: &dyn ChildObjectResolver,
87 index_tables: impl GetDynamicFieldsIter,
88 limit: usize,
89 cursor: Option<ObjectID>,
90) -> SuiResult<Vec<TypeTag>> {
91 let Some(owner_obj) = AccumulatorOwner::load_object(child_object_resolver, None, owner)? else {
92 return Ok(Vec::new());
93 };
94
95 let owner_version = owner_obj.version();
96
97 let accumulator_owner_obj = AccumulatorOwner::from_object(owner_obj)?;
98
99 if accumulator_owner_obj.owner != owner {
100 debug_fatal!("owner object owner does not match the requested owner");
101 return Ok(Vec::new());
102 };
103
104 let bag_id = accumulator_owner_obj.balances.id.object_id();
105
106 let accumulator_metadata: Vec<_> = index_tables
108 .get_dynamic_fields_iterator(*bag_id, cursor)?
109 .take(limit)
110 .collect();
111
112 let mut currency_types = Vec::new();
113 for result in accumulator_metadata {
114 let DynamicFieldKey { parent, field_id } = result?;
115 debug_assert_eq!(parent, *bag_id);
116
117 if let Some(object) =
118 child_object_resolver.read_child_object(bag_id, &field_id, owner_version)?
119 {
120 let ty = object
121 .data
122 .try_as_move()
123 .expect("accumulator metadata object is not a move object")
124 .type_();
125
126 let Some(currency_type) = ty.balance_accumulator_metadata_field_type_maybe() else {
127 debug_fatal!(
130 "accumulator metadata object is not a balance accumulator metadata field"
131 );
132 continue;
133 };
134
135 currency_types.push(currency_type);
136 }
137 }
138
139 Ok(currency_types)
140}
141
142pub fn get_all_balances_for_owner(
145 owner: SuiAddress,
146 child_object_resolver: &dyn ChildObjectResolver,
147 index_tables: impl GetDynamicFieldsIter,
148 limit: usize,
149 cursor: Option<ObjectID>,
150) -> SuiResult<Vec<(TypeTag, u64)>> {
151 let currency_types =
152 get_currency_types_for_owner(owner, child_object_resolver, index_tables, limit, cursor)?;
153 let mut balances = Vec::new();
154 for currency_type in currency_types {
155 let balance = get_balance(owner, child_object_resolver, currency_type.clone())?;
156 balances.push((currency_type, balance));
157 }
158 Ok(balances)
159}