sui_types/
balance_change.rs1use crate::base_types::SuiAddress;
5use crate::coin::Coin;
6use crate::effects::TransactionEffects;
7use crate::effects::TransactionEffectsAPI;
8use crate::full_checkpoint_content::ObjectSet;
9use crate::object::Object;
10use crate::object::Owner;
11use crate::storage::ObjectKey;
12use move_core_types::language_storage::TypeTag;
13
14#[derive(Clone, serde::Serialize, serde::Deserialize, PartialEq, Eq, PartialOrd, Ord)]
15pub struct BalanceChange {
16 pub address: SuiAddress,
18
19 pub coin_type: TypeTag,
21
22 pub amount: i128,
26}
27
28impl std::fmt::Debug for BalanceChange {
29 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
30 f.debug_struct("BalanceChange")
31 .field("address", &self.address)
32 .field("coin_type", &self.coin_type.to_canonical_string(true))
33 .field("amount", &self.amount)
34 .finish()
35 }
36}
37
38fn coins(objects: &[Object]) -> impl Iterator<Item = (&SuiAddress, TypeTag, u64)> + '_ {
39 objects.iter().filter_map(|object| {
40 let address = match object.owner() {
41 Owner::AddressOwner(sui_address)
42 | Owner::ObjectOwner(sui_address)
43 | Owner::ConsensusAddressOwner {
44 owner: sui_address, ..
45 } => sui_address,
46 Owner::Shared { .. } | Owner::Immutable => return None,
47 };
48 let (coin_type, balance) = Coin::extract_balance_if_coin(object).ok().flatten()?;
49 Some((address, coin_type, balance))
50 })
51}
52
53pub fn derive_balance_changes(
54 _effects: &TransactionEffects,
55 input_objects: &[Object],
56 output_objects: &[Object],
57) -> Vec<BalanceChange> {
58 let balances = coins(input_objects).fold(
60 std::collections::BTreeMap::<_, i128>::new(),
61 |mut acc, (address, coin_type, balance)| {
62 *acc.entry((address, coin_type)).or_default() -= balance as i128;
63 acc
64 },
65 );
66
67 let balances =
69 coins(output_objects).fold(balances, |mut acc, (address, coin_type, balance)| {
70 *acc.entry((address, coin_type)).or_default() += balance as i128;
71 acc
72 });
73
74 balances
75 .into_iter()
76 .filter_map(|((address, coin_type), amount)| {
77 if amount == 0 {
78 return None;
79 }
80
81 Some(BalanceChange {
82 address: *address,
83 coin_type,
84 amount,
85 })
86 })
87 .collect()
88}
89
90pub fn derive_balance_changes_2(
91 effects: &TransactionEffects,
92 objects: &ObjectSet,
93) -> Vec<BalanceChange> {
94 let input_objects = effects
95 .modified_at_versions()
96 .into_iter()
97 .filter_map(|(object_id, version)| objects.get(&ObjectKey(object_id, version)).cloned())
98 .collect::<Vec<_>>();
99 let output_objects = effects
100 .all_changed_objects()
101 .into_iter()
102 .filter_map(|(object_ref, _owner, _kind)| objects.get(&object_ref.into()).cloned())
103 .collect::<Vec<_>>();
104
105 let balances = coins(&input_objects).fold(
107 std::collections::BTreeMap::<_, i128>::new(),
108 |mut acc, (address, coin_type, balance)| {
109 *acc.entry((address, coin_type)).or_default() -= balance as i128;
110 acc
111 },
112 );
113
114 let balances =
116 coins(&output_objects).fold(balances, |mut acc, (address, coin_type, balance)| {
117 *acc.entry((address, coin_type)).or_default() += balance as i128;
118 acc
119 });
120
121 balances
122 .into_iter()
123 .filter_map(|((address, coin_type), amount)| {
124 if amount == 0 {
125 return None;
126 }
127
128 Some(BalanceChange {
129 address: *address,
130 coin_type,
131 amount,
132 })
133 })
134 .collect()
135}