everscale_types/models/
currency.rs1use crate::cell::*;
4use crate::dict::{AugDictExtra, Dict};
5use crate::error::Error;
6use crate::num::{Tokens, VarUint248};
7
8#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
10#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
11#[must_use]
12pub struct CurrencyCollection {
13 pub tokens: Tokens,
15 pub other: ExtraCurrencyCollection,
17}
18
19impl Default for CurrencyCollection {
20 #[inline]
21 fn default() -> Self {
22 Self::ZERO
23 }
24}
25
26impl CurrencyCollection {
27 pub const ZERO: Self = Self {
30 tokens: Tokens::ZERO,
31 other: ExtraCurrencyCollection::new(),
32 };
33
34 pub const fn new(tokens: u128) -> Self {
37 Self {
38 tokens: Tokens::new(tokens),
39 other: ExtraCurrencyCollection::new(),
40 }
41 }
42
43 pub const fn bit_len(&self) -> u16 {
45 self.tokens.unwrap_bit_len() + 1
46 }
47
48 pub fn checked_add(&self, other: &Self) -> Result<Self, Error> {
52 Ok(Self {
53 tokens: match self.tokens.checked_add(other.tokens) {
54 Some(value) => value,
55 None => return Err(Error::IntOverflow),
56 },
57 other: ok!(self.other.checked_add(&other.other)),
58 })
59 }
60
61 pub fn checked_sub(&self, other: &Self) -> Result<Self, Error> {
65 Ok(Self {
66 tokens: match self.tokens.checked_sub(other.tokens) {
67 Some(value) => value,
68 None => return Err(Error::IntOverflow),
69 },
70 other: ok!(self.other.checked_sub(&other.other)),
71 })
72 }
73
74 pub fn try_add_assign_tokens(&mut self, other: Tokens) -> Result<(), Error> {
76 match self.tokens.checked_add(other) {
77 Some(value) => {
78 self.tokens = value;
79 Ok(())
80 }
81 None => Err(Error::IntOverflow),
82 }
83 }
84
85 pub fn try_sub_assign_tokens(&mut self, other: Tokens) -> Result<(), Error> {
87 match self.tokens.checked_sub(other) {
88 Some(value) => {
89 self.tokens = value;
90 Ok(())
91 }
92 None => Err(Error::IntOverflow),
93 }
94 }
95
96 pub fn try_add_assign(&mut self, other: &Self) -> Result<(), Error> {
98 *self = ok!(self.checked_add(other));
99 Ok(())
100 }
101
102 pub fn try_sub_assign(&mut self, other: &Self) -> Result<(), Error> {
104 *self = ok!(self.checked_sub(other));
105 Ok(())
106 }
107}
108
109impl From<Tokens> for CurrencyCollection {
110 #[inline]
111 fn from(tokens: Tokens) -> Self {
112 Self {
113 tokens,
114 other: ExtraCurrencyCollection::new(),
115 }
116 }
117}
118
119impl ExactSize for CurrencyCollection {
120 #[inline]
121 fn exact_size(&self) -> Size {
122 self.tokens.exact_size() + self.other.exact_size()
123 }
124}
125
126impl AugDictExtra for CurrencyCollection {
127 fn comp_add(
128 left: &mut CellSlice,
129 right: &mut CellSlice,
130 b: &mut CellBuilder,
131 cx: &mut dyn CellContext,
132 ) -> Result<(), Error> {
133 let left = ok!(Self::load_from(left));
134 let right = ok!(Self::load_from(right));
135 ok!(left.checked_add(&right)).store_into(b, cx)
136 }
137}
138
139#[derive(Debug, Clone, Eq, PartialEq, Store, Load)]
141#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
142#[must_use]
143#[repr(transparent)]
144pub struct ExtraCurrencyCollection(Dict<u32, VarUint248>);
145
146impl Default for ExtraCurrencyCollection {
147 #[inline]
148 fn default() -> Self {
149 Self(Dict::new())
150 }
151}
152
153impl ExtraCurrencyCollection {
154 pub const fn new() -> Self {
156 Self(Dict::new())
157 }
158
159 pub const fn from_raw(dict: Option<Cell>) -> Self {
161 Self(Dict::from_raw(dict))
162 }
163
164 pub const fn is_empty(&self) -> bool {
166 self.0.is_empty()
167 }
168
169 pub const fn as_dict(&self) -> &Dict<u32, VarUint248> {
171 &self.0
172 }
173
174 pub fn as_dict_mut(&mut self) -> &mut Dict<u32, VarUint248> {
176 &mut self.0
177 }
178
179 pub fn checked_add(&self, other: &Self) -> Result<Self, Error> {
183 let mut result = self.clone();
184 for entry in other.0.iter() {
185 let (currency_id, other) = ok!(entry);
186
187 let existing = ok!(result.as_dict().get(currency_id)).unwrap_or_default();
188 match existing.checked_add(&other) {
189 Some(ref value) => ok!(result.0.set(currency_id, value)),
190 None => return Err(Error::IntOverflow),
191 };
192 }
193 Ok(result)
194 }
195
196 pub fn checked_sub(&self, other: &Self) -> Result<Self, Error> {
200 let mut result = self.clone();
201 for entry in other.0.iter() {
202 let (currency_id, other) = ok!(entry);
203
204 let existing = ok!(result.as_dict().get(currency_id)).unwrap_or_default();
205 match existing.checked_sub(&other) {
206 Some(ref value) => ok!(result.0.set(currency_id, value)),
207 None => return Err(Error::IntOverflow),
208 };
209 }
210 Ok(result)
211 }
212}
213
214impl From<Dict<u32, VarUint248>> for ExtraCurrencyCollection {
215 #[inline]
216 fn from(value: Dict<u32, VarUint248>) -> Self {
217 Self(value)
218 }
219}
220
221impl ExactSize for ExtraCurrencyCollection {
222 #[inline]
223 fn exact_size(&self) -> Size {
224 self.0.exact_size()
225 }
226}
227
228#[cfg(test)]
229mod tests {
230 use super::*;
231
232 fn _cc_must_use() -> anyhow::Result<()> {
233 #[expect(unused_must_use)]
234 {
235 CurrencyCollection::new(10).checked_add(&CurrencyCollection::ZERO)?;
236 }
237
238 #[expect(unused_must_use)]
239 {
240 ExtraCurrencyCollection::new().checked_add(&ExtraCurrencyCollection::new())?;
241 }
242
243 Ok(())
244 }
245}