• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ergoplatform / sigma-rust / 14311715155

07 Apr 2025 02:22PM UTC coverage: 78.24% (-0.03%) from 78.273%
14311715155

Pull #822

github

web-flow
Merge e753f1bba into f2e64bb7f
Pull Request #822: fix: updated the transactionhintsbag implementation

13 of 15 new or added lines in 1 file covered. (86.67%)

7 existing lines in 3 files now uncovered.

11517 of 14720 relevant lines covered (78.24%)

3.03 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

72.88
/ergotree-ir/src/chain/token.rs
1
//! Token related types
2

3
use crate::serialization::SigmaSerializeResult;
4
use crate::serialization::{
5
    sigma_byte_reader::SigmaByteRead, sigma_byte_writer::SigmaByteWrite, SigmaParsingError,
6
    SigmaSerializable,
7
};
8
use core::convert::TryFrom;
9

10
use super::ergo_box::BoxId;
11
use alloc::string::String;
12
use alloc::vec::Vec;
13
use derive_more::From;
14
use derive_more::FromStr;
15
use derive_more::Into;
16
use ergo_chain_types::{Digest32, DigestNError};
17
use sigma_ser::ScorexSerializable;
18
use thiserror::Error;
19

20
/// newtype for token id
21
#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone, From, FromStr, Into)]
22
#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
23
pub struct TokenId(Digest32);
24

25
impl TokenId {
26
    /// token id size in bytes
27
    pub const SIZE: usize = Digest32::SIZE;
28

29
    /// Parse TokenId from base64 encoded string
30
    pub fn from_base64(s: &str) -> Result<TokenId, DigestNError> {
×
31
        Digest32::from_base64(s).map(Into::into)
×
32
    }
33
}
34

35
impl From<BoxId> for TokenId {
36
    fn from(i: BoxId) -> Self {
1✔
37
        TokenId(i.into())
1✔
38
    }
39
}
40

41
impl From<TokenId> for Vec<i8> {
UNCOV
42
    fn from(v: TokenId) -> Self {
×
UNCOV
43
        v.0.into()
×
44
    }
45
}
46

47
impl From<TokenId> for Vec<u8> {
48
    fn from(v: TokenId) -> Self {
×
49
        v.0.into()
×
50
    }
51
}
52

53
impl AsRef<[u8]> for TokenId {
54
    fn as_ref(&self) -> &[u8] {
×
55
        self.0.as_ref()
×
56
    }
57
}
58

59
impl From<TokenId> for String {
60
    fn from(v: TokenId) -> Self {
×
61
        v.0.into()
×
62
    }
63
}
64

65
impl SigmaSerializable for TokenId {
66
    fn sigma_serialize<W: SigmaByteWrite>(&self, w: &mut W) -> SigmaSerializeResult {
6✔
67
        Ok(self.0.scorex_serialize(w)?)
6✔
68
    }
69
    fn sigma_parse<R: SigmaByteRead>(r: &mut R) -> Result<Self, SigmaParsingError> {
4✔
70
        Ok(Self(Digest32::scorex_parse(r)?))
4✔
71
    }
72
}
73

74
/// Token amount with bound checks
75
#[cfg(not(feature = "json"))]
76
#[derive(PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord)]
77
pub struct TokenAmount(u64);
78

79
/// Token amount with bound checks
80
#[cfg(feature = "json")]
81
#[derive(
82
    serde::Serialize, serde::Deserialize, PartialEq, Eq, Hash, Debug, Clone, Copy, PartialOrd, Ord,
83
)]
84
#[serde(
85
    try_from = "crate::chain::json::token::TokenAmountJson",
86
    into = "crate::chain::json::token::TokenAmountJson"
87
)]
88
pub struct TokenAmount(pub(crate) u64);
89

90
impl TokenAmount {
91
    /// minimal allowed value
92
    pub const MIN_RAW: u64 = 1;
93
    /// maximal allowed value
94
    pub const MAX_RAW: u64 = i64::MAX as u64;
95

96
    /// minimal allowed value
97
    pub const MIN: TokenAmount = TokenAmount(Self::MIN_RAW);
98

99
    /// Addition with overflow check
100
    pub fn checked_add(&self, rhs: &Self) -> Result<Self, TokenAmountError> {
1✔
101
        let raw = self
3✔
102
            .0
103
            .checked_add(rhs.0)
1✔
104
            .ok_or(TokenAmountError::Overflow)?;
1✔
105
        if raw > Self::MAX_RAW {
2✔
106
            Err(TokenAmountError::OutOfBounds(raw as i64))
×
107
        } else {
108
            Ok(Self(raw))
1✔
109
        }
110
    }
111

112
    /// Subtraction with overflow and bounds check
113
    pub fn checked_sub(&self, rhs: &Self) -> Result<Self, TokenAmountError> {
1✔
114
        let raw = self
3✔
115
            .0
116
            .checked_sub(rhs.0)
1✔
117
            .ok_or(TokenAmountError::Overflow)?;
1✔
118
        if raw < Self::MIN_RAW {
2✔
119
            Err(TokenAmountError::OutOfBounds(raw as i64))
×
120
        } else {
121
            Ok(Self(raw))
1✔
122
        }
123
    }
124

125
    /// Get the value as u64
126
    pub fn as_u64(&self) -> &u64 {
1✔
127
        &self.0
128
    }
129
}
130

131
/// BoxValue errors
132
#[derive(Error, Eq, PartialEq, Debug, Clone)]
133
pub enum TokenAmountError {
134
    /// Value is out of bounds
135
    #[error("Token amount is out of bounds: {0}")]
136
    OutOfBounds(i64),
137
    /// Overflow
138
    #[error("Overflow")]
139
    Overflow,
140
}
141

142
impl TryFrom<u64> for TokenAmount {
143
    type Error = TokenAmountError;
144

145
    fn try_from(v: u64) -> Result<Self, Self::Error> {
2✔
146
        if (TokenAmount::MIN_RAW..=TokenAmount::MAX_RAW).contains(&v) {
2✔
147
            Ok(TokenAmount(v))
2✔
148
        } else {
149
            Err(TokenAmountError::OutOfBounds(v as i64))
×
150
        }
151
    }
152
}
153

154
impl From<TokenAmount> for u64 {
155
    fn from(ta: TokenAmount) -> Self {
6✔
156
        ta.0
157
    }
158
}
159

160
impl From<TokenAmount> for i64 {
UNCOV
161
    fn from(ta: TokenAmount) -> Self {
×
162
        ta.0 as i64
163
    }
164
}
165

166
impl From<Token> for (Vec<i8>, i64) {
UNCOV
167
    fn from(t: Token) -> Self {
×
UNCOV
168
        (t.token_id.into(), t.amount.into())
×
169
    }
170
}
171

172
/// Token represented with token id paired with it's amount
173
#[cfg_attr(feature = "json", derive(serde::Serialize, serde::Deserialize))]
174
#[derive(PartialEq, Eq, Debug, Copy, Clone)]
175
pub struct Token {
176
    /// token id
177
    #[cfg_attr(feature = "json", serde(rename = "tokenId"))]
178
    pub token_id: TokenId,
179
    /// token amount
180
    #[cfg_attr(feature = "json", serde(rename = "amount"))]
181
    pub amount: TokenAmount,
182
}
183

184
impl From<(TokenId, TokenAmount)> for Token {
185
    fn from(token_pair: (TokenId, TokenAmount)) -> Self {
5✔
186
        Token {
187
            token_id: token_pair.0,
5✔
188
            amount: token_pair.1,
5✔
189
        }
190
    }
191
}
192

193
/// Arbitrary
194
#[allow(clippy::unwrap_used)]
195
#[cfg(feature = "arbitrary")]
196
pub mod arbitrary {
197
    use ergo_chain_types::Digest32;
198
    use proptest::prelude::*;
199

200
    use super::Token;
201
    use super::TokenAmount;
202
    use super::TokenId;
203

204
    use core::convert::TryFrom;
205

206
    /// How to generate a token id
207
    #[derive(Default)]
208
    pub enum ArbTokenIdParam {
209
        /// From a list of predefined token ids
210
        #[default]
211
        Predef,
212
        /// Arbitrary token id
213
        Arbitrary,
214
    }
215

216
    impl Arbitrary for TokenId {
217
        type Parameters = ArbTokenIdParam;
218

219
        fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
5✔
220
            match args {
8✔
221
                ArbTokenIdParam::Predef => prop_oneof![
24✔
222
                    Just(TokenId::from(
5✔
223
                        Digest32::try_from(
8✔
224
                            "3130a82e45842aebb888742868e055e2f554ab7d92f233f2c828ed4a43793710"
5✔
225
                                .to_string()
226
                        )
227
                        .unwrap()
228
                    )),
229
                    Just(TokenId::from(
5✔
230
                        Digest32::try_from(
8✔
231
                            "e7321ffb4ec5d71deb3110eb1ac09612b9cf57445acab1e0e3b1222d5b5a6c60"
5✔
232
                                .to_string()
233
                        )
234
                        .unwrap()
235
                    )),
236
                    Just(TokenId::from(
5✔
237
                        Digest32::try_from(
8✔
238
                            "ad62f6dd92e7dc850bc406770dfac9a943dd221a7fb440b7b2bcc7d3149c1792"
5✔
239
                                .to_string()
240
                        )
241
                        .unwrap()
242
                    ))
243
                ]
244
                .boxed(),
245
                ArbTokenIdParam::Arbitrary => (any::<Digest32>()).prop_map_into().boxed(),
2✔
246
            }
247
        }
248

249
        type Strategy = BoxedStrategy<Self>;
250
    }
251

252
    impl Arbitrary for TokenAmount {
253
        type Parameters = ();
254

255
        fn arbitrary_with(_args: Self::Parameters) -> Self::Strategy {
5✔
256
            (TokenAmount::MIN_RAW..=TokenAmount::MAX_RAW / 100000)
8✔
257
                .prop_map(Self)
258
                .boxed()
259
        }
260
        type Strategy = BoxedStrategy<Self>;
261
    }
262

263
    impl Default for TokenAmount {
264
        fn default() -> Self {
265
            TokenAmount::MIN
266
        }
267
    }
268

269
    impl Arbitrary for Token {
270
        type Parameters = ArbTokenIdParam;
271

272
        fn arbitrary_with(args: Self::Parameters) -> Self::Strategy {
5✔
273
            (any_with::<TokenId>(args), any::<TokenAmount>())
8✔
274
                .prop_map(Self::from)
275
                .boxed()
276
        }
277
        type Strategy = BoxedStrategy<Self>;
278
    }
279
}
280

281
#[allow(clippy::panic)]
282
#[cfg(test)]
283
#[cfg(feature = "arbitrary")]
284
mod tests {
285

286
    use crate::chain::token::TokenId;
287
    use crate::serialization::sigma_serialize_roundtrip;
288

289
    use proptest::prelude::*;
290

291
    proptest! {
292

293
        #[test]
294
        fn token_id_roundtrip(v in any::<TokenId>()) {
295
            prop_assert_eq![sigma_serialize_roundtrip(&v), v];
296
        }
297
    }
298
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc