ibc_app_transfer_types/
amount.rs1use core::ops::Deref;
3use core::str::FromStr;
4
5use derive_more::{Display, From, Into};
6use ibc_core::host::types::error::DecodingError;
7use ibc_core::primitives::prelude::*;
8#[cfg(feature = "serde")]
9use ibc_core::primitives::serializers;
10use primitive_types::U256;
11
12#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
14#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
15#[cfg_attr(feature = "schema", derive(schemars::JsonSchema))]
16#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Display, From, Into)]
17pub struct Amount(
18 #[cfg_attr(feature = "arbitrary", arbitrary(with = arb_u256))]
19 #[cfg_attr(feature = "schema", schemars(with = "String"))]
20 #[cfg_attr(feature = "serde", serde(serialize_with = "serializers::serialize"))]
21 #[cfg_attr(feature = "serde", serde(deserialize_with = "deserialize"))]
22 U256,
23);
24
25#[cfg(feature = "parity-scale-codec")]
26impl parity_scale_codec::WrapperTypeDecode for Amount {
27 type Wrapped = [u64; 4];
28}
29
30#[cfg(feature = "parity-scale-codec")]
31impl parity_scale_codec::WrapperTypeEncode for Amount {}
32
33#[cfg(feature = "borsh")]
34impl borsh::BorshSerialize for Amount {
35 fn serialize<W: borsh::io::Write>(&self, writer: &mut W) -> borsh::io::Result<()> {
36 let words = self.as_slice();
38 let bytes: Vec<u8> = words.iter().flat_map(|word| word.to_be_bytes()).collect();
39
40 writer.write_all(&bytes)
41 }
42}
43#[cfg(feature = "borsh")]
44impl borsh::BorshDeserialize for Amount {
45 fn deserialize_reader<R: borsh::io::Read>(reader: &mut R) -> borsh::io::Result<Self> {
46 const NUM_BYTES_IN_U64: usize = 8;
47 const NUM_WORDS_IN_U256: usize = 4;
48
49 let mut buf = [0; 32];
50 let bytes_read = reader.read(&mut buf)?;
51 if bytes_read != 32 {
52 return Err(borsh::io::Error::new(
53 borsh::io::ErrorKind::InvalidInput,
54 format!("Expected to read 32 bytes, read {bytes_read}"),
55 ));
56 }
57
58 let words: Vec<u64> = buf
59 .chunks_exact(NUM_BYTES_IN_U64)
60 .map(|word| {
61 let word: [u8; NUM_BYTES_IN_U64] = word
62 .try_into()
63 .expect("exact chunks of 8 bytes are expected to be 8 bytes");
64 u64::from_be_bytes(word)
65 })
66 .collect();
67
68 let four_words: [u64; NUM_WORDS_IN_U256] = words
69 .try_into()
70 .expect("U256 is always 4 four words, and we confirmed that we read 32 bytes");
71
72 Ok(four_words.into())
73 }
74}
75
76impl Deref for Amount {
77 type Target = [u64; 4];
78
79 fn deref(&self) -> &Self::Target {
80 &self.0 .0
81 }
82}
83
84impl From<[u64; 4]> for Amount {
85 fn from(value: [u64; 4]) -> Self {
86 Self(U256(value))
87 }
88}
89
90impl Amount {
91 pub fn checked_add(self, rhs: Self) -> Option<Self> {
92 self.0.checked_add(rhs.0).map(Self)
93 }
94
95 pub fn checked_sub(self, rhs: Self) -> Option<Self> {
96 self.0.checked_sub(rhs.0).map(Self)
97 }
98}
99
100impl AsRef<U256> for Amount {
101 fn as_ref(&self) -> &U256 {
102 &self.0
103 }
104}
105
106impl FromStr for Amount {
107 type Err = DecodingError;
108
109 fn from_str(s: &str) -> Result<Self, Self::Err> {
110 let amount = U256::from_dec_str(s).map_err(|e| {
111 DecodingError::invalid_raw_data(format!("amount could not be parsed as a U256: {e}"))
112 })?;
113
114 Ok(Self(amount))
115 }
116}
117
118impl From<u64> for Amount {
119 fn from(v: u64) -> Self {
120 Self(v.into())
121 }
122}
123
124#[cfg(feature = "serde")]
125fn deserialize<'de, D>(deserializer: D) -> Result<U256, D::Error>
126where
127 D: serde::Deserializer<'de>,
128{
129 use serde::Deserialize;
130 U256::from_dec_str(<String>::deserialize(deserializer)?.as_str())
131 .map_err(serde::de::Error::custom)
132}
133
134#[cfg(feature = "arbitrary")]
135fn arb_u256(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<U256> {
136 let parts: [u8; 32] = arbitrary::Arbitrary::arbitrary(u)?;
137 Ok(U256::from_big_endian(&parts))
138}
139
140#[cfg(test)]
141mod tests {
142 use super::Amount;
143
144 #[cfg(feature = "serde")]
145 #[test]
146 fn serde_amount() {
147 let value = Amount::from(42);
148 let string = serde_json::to_string(&value).expect("can serde string");
149 assert_eq!(string, "\"42\"");
150 let binary = serde_json::to_vec(&value).expect("can serde binary");
151 let de: Amount = serde_json::from_slice(binary.as_ref()).expect("can deserialize");
152 assert_eq!(de, value);
153 }
154
155 #[cfg(feature = "borsh")]
156 #[test]
157 fn borsh_amount() {
158 let value = Amount::from(42);
159 let serialized = borsh::to_vec(&value).unwrap();
160
161 assert_eq!(serialized.len(), 32);
163
164 let value_deserialized = borsh::from_slice::<Amount>(&serialized).unwrap();
165
166 assert_eq!(value, value_deserialized);
167 }
168}