create-sia-app 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (112) hide show
  1. package/dist/index.d.ts +1 -0
  2. package/dist/index.js +179 -0
  3. package/package.json +29 -0
  4. package/template/CLAUDE.md +160 -0
  5. package/template/README.md +102 -0
  6. package/template/_gitignore +5 -0
  7. package/template/biome.json +40 -0
  8. package/template/index.html +13 -0
  9. package/template/package.json +30 -0
  10. package/template/rust/README.md +16 -0
  11. package/template/rust/sia-sdk-rs/.changeset/added_cancel_function_to_cancel_inflight_packed_uploads.md +6 -0
  12. package/template/rust/sia-sdk-rs/.changeset/check_if_we_have_enough_hosts_prior_to_encoding_in_upload_slabs.md +16 -0
  13. package/template/rust/sia-sdk-rs/.changeset/fix_slab_length_in_packed_object.md +5 -0
  14. package/template/rust/sia-sdk-rs/.changeset/fix_upload_racing_race_conditon.md +13 -0
  15. package/template/rust/sia-sdk-rs/.changeset/improved_parallelism_of_packed_uploads.md +5 -0
  16. package/template/rust/sia-sdk-rs/.changeset/progress_callback_will_now_be_called_as_expected_for_packed_uploads.md +5 -0
  17. package/template/rust/sia-sdk-rs/.github/dependabot.yml +10 -0
  18. package/template/rust/sia-sdk-rs/.github/workflows/main.yml +36 -0
  19. package/template/rust/sia-sdk-rs/.github/workflows/prepare-release.yml +34 -0
  20. package/template/rust/sia-sdk-rs/.github/workflows/release.yml +30 -0
  21. package/template/rust/sia-sdk-rs/.rustfmt.toml +4 -0
  22. package/template/rust/sia-sdk-rs/Cargo.lock +4127 -0
  23. package/template/rust/sia-sdk-rs/Cargo.toml +3 -0
  24. package/template/rust/sia-sdk-rs/LICENSE +21 -0
  25. package/template/rust/sia-sdk-rs/README.md +30 -0
  26. package/template/rust/sia-sdk-rs/indexd/CHANGELOG.md +79 -0
  27. package/template/rust/sia-sdk-rs/indexd/Cargo.toml +79 -0
  28. package/template/rust/sia-sdk-rs/indexd/benches/upload.rs +258 -0
  29. package/template/rust/sia-sdk-rs/indexd/src/app_client.rs +1710 -0
  30. package/template/rust/sia-sdk-rs/indexd/src/builder.rs +354 -0
  31. package/template/rust/sia-sdk-rs/indexd/src/download.rs +379 -0
  32. package/template/rust/sia-sdk-rs/indexd/src/hosts.rs +659 -0
  33. package/template/rust/sia-sdk-rs/indexd/src/lib.rs +827 -0
  34. package/template/rust/sia-sdk-rs/indexd/src/mock.rs +162 -0
  35. package/template/rust/sia-sdk-rs/indexd/src/object_encryption.rs +125 -0
  36. package/template/rust/sia-sdk-rs/indexd/src/quic.rs +575 -0
  37. package/template/rust/sia-sdk-rs/indexd/src/rhp4.rs +52 -0
  38. package/template/rust/sia-sdk-rs/indexd/src/slabs.rs +497 -0
  39. package/template/rust/sia-sdk-rs/indexd/src/upload.rs +629 -0
  40. package/template/rust/sia-sdk-rs/indexd/src/wasm_time.rs +41 -0
  41. package/template/rust/sia-sdk-rs/indexd/src/web_transport.rs +398 -0
  42. package/template/rust/sia-sdk-rs/indexd_ffi/CHANGELOG.md +76 -0
  43. package/template/rust/sia-sdk-rs/indexd_ffi/Cargo.toml +47 -0
  44. package/template/rust/sia-sdk-rs/indexd_ffi/examples/python/README.md +10 -0
  45. package/template/rust/sia-sdk-rs/indexd_ffi/examples/python/example.py +130 -0
  46. package/template/rust/sia-sdk-rs/indexd_ffi/src/bin/uniffi-bindgen.rs +3 -0
  47. package/template/rust/sia-sdk-rs/indexd_ffi/src/builder.rs +377 -0
  48. package/template/rust/sia-sdk-rs/indexd_ffi/src/io.rs +155 -0
  49. package/template/rust/sia-sdk-rs/indexd_ffi/src/lib.rs +1039 -0
  50. package/template/rust/sia-sdk-rs/indexd_ffi/src/logging.rs +58 -0
  51. package/template/rust/sia-sdk-rs/indexd_ffi/src/tls.rs +23 -0
  52. package/template/rust/sia-sdk-rs/indexd_wasm/Cargo.toml +33 -0
  53. package/template/rust/sia-sdk-rs/indexd_wasm/src/lib.rs +818 -0
  54. package/template/rust/sia-sdk-rs/knope.toml +54 -0
  55. package/template/rust/sia-sdk-rs/sia_derive/CHANGELOG.md +38 -0
  56. package/template/rust/sia-sdk-rs/sia_derive/Cargo.toml +19 -0
  57. package/template/rust/sia-sdk-rs/sia_derive/src/lib.rs +278 -0
  58. package/template/rust/sia-sdk-rs/sia_sdk/CHANGELOG.md +91 -0
  59. package/template/rust/sia-sdk-rs/sia_sdk/Cargo.toml +59 -0
  60. package/template/rust/sia-sdk-rs/sia_sdk/benches/merkle_root.rs +12 -0
  61. package/template/rust/sia-sdk-rs/sia_sdk/src/blake2.rs +22 -0
  62. package/template/rust/sia-sdk-rs/sia_sdk/src/consensus.rs +767 -0
  63. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding/v1.rs +257 -0
  64. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding/v2.rs +291 -0
  65. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding.rs +26 -0
  66. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding_async/v2.rs +367 -0
  67. package/template/rust/sia-sdk-rs/sia_sdk/src/encoding_async.rs +6 -0
  68. package/template/rust/sia-sdk-rs/sia_sdk/src/encryption.rs +303 -0
  69. package/template/rust/sia-sdk-rs/sia_sdk/src/erasure_coding.rs +347 -0
  70. package/template/rust/sia-sdk-rs/sia_sdk/src/lib.rs +15 -0
  71. package/template/rust/sia-sdk-rs/sia_sdk/src/macros.rs +435 -0
  72. package/template/rust/sia-sdk-rs/sia_sdk/src/merkle.rs +112 -0
  73. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/merkle.rs +357 -0
  74. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/rpc.rs +1507 -0
  75. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp/types.rs +146 -0
  76. package/template/rust/sia-sdk-rs/sia_sdk/src/rhp.rs +7 -0
  77. package/template/rust/sia-sdk-rs/sia_sdk/src/seed.rs +278 -0
  78. package/template/rust/sia-sdk-rs/sia_sdk/src/signing.rs +236 -0
  79. package/template/rust/sia-sdk-rs/sia_sdk/src/types/common.rs +677 -0
  80. package/template/rust/sia-sdk-rs/sia_sdk/src/types/currency.rs +450 -0
  81. package/template/rust/sia-sdk-rs/sia_sdk/src/types/specifier.rs +110 -0
  82. package/template/rust/sia-sdk-rs/sia_sdk/src/types/spendpolicy.rs +778 -0
  83. package/template/rust/sia-sdk-rs/sia_sdk/src/types/utils.rs +117 -0
  84. package/template/rust/sia-sdk-rs/sia_sdk/src/types/v1.rs +1737 -0
  85. package/template/rust/sia-sdk-rs/sia_sdk/src/types/v2.rs +1726 -0
  86. package/template/rust/sia-sdk-rs/sia_sdk/src/types/work.rs +59 -0
  87. package/template/rust/sia-sdk-rs/sia_sdk/src/types.rs +16 -0
  88. package/template/scripts/setup-rust.js +29 -0
  89. package/template/src/App.tsx +13 -0
  90. package/template/src/components/DevNote.tsx +21 -0
  91. package/template/src/components/auth/ApproveScreen.tsx +84 -0
  92. package/template/src/components/auth/AuthFlow.tsx +77 -0
  93. package/template/src/components/auth/ConnectScreen.tsx +214 -0
  94. package/template/src/components/auth/LoadingScreen.tsx +8 -0
  95. package/template/src/components/auth/RecoveryScreen.tsx +182 -0
  96. package/template/src/components/upload/UploadZone.tsx +314 -0
  97. package/template/src/index.css +9 -0
  98. package/template/src/lib/constants.ts +8 -0
  99. package/template/src/lib/format.ts +35 -0
  100. package/template/src/lib/hex.ts +13 -0
  101. package/template/src/lib/sdk.ts +25 -0
  102. package/template/src/lib/wasm-env.ts +5 -0
  103. package/template/src/main.tsx +12 -0
  104. package/template/src/stores/auth.ts +86 -0
  105. package/template/tsconfig.app.json +31 -0
  106. package/template/tsconfig.json +7 -0
  107. package/template/tsconfig.node.json +26 -0
  108. package/template/vite.config.ts +18 -0
  109. package/template/wasm/indexd_wasm/indexd_wasm.d.ts +309 -0
  110. package/template/wasm/indexd_wasm/indexd_wasm.js +1507 -0
  111. package/template/wasm/indexd_wasm/indexd_wasm_bg.wasm +0 -0
  112. package/template/wasm/indexd_wasm/package.json +31 -0
@@ -0,0 +1,450 @@
1
+ use core::num::ParseIntError;
2
+ use core::ops::{Add, Deref, DerefMut, Div, Mul, Rem, Sub};
3
+ use std::fmt::Display;
4
+ use std::io::Write;
5
+ use std::iter::Sum;
6
+
7
+ use num_traits::AsPrimitive;
8
+ use serde::{Deserialize, Serialize};
9
+
10
+ use crate::encoding::{self, SiaDecodable, SiaEncodable, V1SiaDecodable, V1SiaEncodable};
11
+ use crate::encoding_async::{self, AsyncEncoder, AsyncSiaDecodable, AsyncSiaEncodable};
12
+
13
+ // I miss untyped constants
14
+ const SIACOIN_PRECISION_I32: i32 = 24;
15
+ const SIACOIN_PRECISION_U32: u32 = 24;
16
+
17
+ // Currency represents a quantity of Siacoins as Hastings.
18
+ #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
19
+ pub struct Currency(u128);
20
+
21
+ impl Serialize for Currency {
22
+ fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
23
+ serializer.serialize_str(&self.to_string())
24
+ }
25
+ }
26
+
27
+ impl<'de> Deserialize<'de> for Currency {
28
+ fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
29
+ struct CurrencyVisitor;
30
+
31
+ impl serde::de::Visitor<'_> for CurrencyVisitor {
32
+ type Value = Currency;
33
+
34
+ fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result {
35
+ formatter.write_str("a string or numeric representing a currency value")
36
+ }
37
+
38
+ fn visit_str<E: serde::de::Error>(self, s: &str) -> Result<Self::Value, E> {
39
+ s.parse()
40
+ .map_err(|e| serde::de::Error::custom(format!("{e:?}")))
41
+ }
42
+
43
+ fn visit_i32<E: serde::de::Error>(self, value: i32) -> Result<Self::Value, E> {
44
+ if value < 0 {
45
+ return Err(serde::de::Error::custom("currency value must be positive"));
46
+ }
47
+ Ok(Currency::new(value as u128))
48
+ }
49
+
50
+ fn visit_i64<E: serde::de::Error>(self, value: i64) -> Result<Self::Value, E> {
51
+ if value < 0 {
52
+ return Err(serde::de::Error::custom("currency value must be positive"));
53
+ }
54
+ Ok(Currency::new(value as u128))
55
+ }
56
+
57
+ fn visit_i128<E: serde::de::Error>(self, value: i128) -> Result<Self::Value, E> {
58
+ if value < 0 {
59
+ return Err(serde::de::Error::custom("currency value must be positive"));
60
+ }
61
+ Ok(Currency::new(value as u128))
62
+ }
63
+
64
+ fn visit_u32<E: serde::de::Error>(self, value: u32) -> Result<Self::Value, E> {
65
+ Ok(Currency::new(value as u128))
66
+ }
67
+
68
+ fn visit_u64<E: serde::de::Error>(self, value: u64) -> Result<Self::Value, E> {
69
+ Ok(Currency::new(value as u128))
70
+ }
71
+
72
+ fn visit_u128<E: serde::de::Error>(self, value: u128) -> Result<Self::Value, E> {
73
+ Ok(Currency::new(value))
74
+ }
75
+ }
76
+
77
+ deserializer.deserialize_any(CurrencyVisitor)
78
+ }
79
+ }
80
+
81
+ impl V1SiaEncodable for Currency {
82
+ fn encode_v1<W: Write>(&self, w: &mut W) -> encoding::Result<()> {
83
+ let currency_buf = self.to_be_bytes();
84
+ let i = currency_buf
85
+ .iter()
86
+ .enumerate()
87
+ .find(|&(_index, &value)| value != 0)
88
+ .map_or(16, |(index, _value)| index); // 16 if all bytes are 0
89
+ currency_buf[i..].encode_v1(w)
90
+ }
91
+ }
92
+
93
+ impl V1SiaDecodable for Currency {
94
+ fn decode_v1<R: std::io::Read>(r: &mut R) -> encoding::Result<Self> {
95
+ let len = usize::decode_v1(r)?;
96
+ if len > 16 {
97
+ return Err(encoding::Error::InvalidLength(len));
98
+ }
99
+ let mut buf = [0u8; 16];
100
+ r.read_exact(&mut buf[16 - len..])?;
101
+ Ok(Currency(u128::from_be_bytes(buf)))
102
+ }
103
+ }
104
+
105
+ impl SiaEncodable for Currency {
106
+ fn encode<W: Write>(&self, w: &mut W) -> encoding::Result<()> {
107
+ w.write_all(&self.0.to_le_bytes())?;
108
+ Ok(())
109
+ }
110
+ }
111
+
112
+ impl SiaDecodable for Currency {
113
+ fn decode<R: std::io::Read>(r: &mut R) -> encoding::Result<Self> {
114
+ let mut buf = [0u8; 16];
115
+ r.read_exact(&mut buf)?;
116
+ Ok(Currency(u128::from_le_bytes(buf)))
117
+ }
118
+ }
119
+
120
+ impl AsyncSiaEncodable for Currency {
121
+ async fn encode_async<E: AsyncEncoder>(&self, e: &mut E) -> Result<(), E::Error> {
122
+ e.encode_buf(&self.0.to_le_bytes()).await?;
123
+ Ok(())
124
+ }
125
+ }
126
+
127
+ impl AsyncSiaDecodable for Currency {
128
+ async fn decode_async<D: encoding_async::AsyncDecoder>(d: &mut D) -> Result<Self, D::Error> {
129
+ let mut buf = [0u8; 16];
130
+ d.decode_buf(&mut buf).await?;
131
+ Ok(Currency(u128::from_le_bytes(buf)))
132
+ }
133
+ }
134
+
135
+ // Implement Deref and DerefMut to be able to implicitly use Currency as a u128
136
+ // This gives us all the traits that u128 already implements for free.
137
+ impl Deref for Currency {
138
+ type Target = u128;
139
+ fn deref(&self) -> &u128 {
140
+ &self.0
141
+ }
142
+ }
143
+
144
+ impl DerefMut for Currency {
145
+ fn deref_mut(&mut self) -> &mut u128 {
146
+ &mut self.0
147
+ }
148
+ }
149
+
150
+ impl TryInto<u64> for Currency {
151
+ type Error = core::num::TryFromIntError;
152
+ fn try_into(self) -> Result<u64, Self::Error> {
153
+ self.0.try_into()
154
+ }
155
+ }
156
+
157
+ impl<T> From<T> for Currency
158
+ where
159
+ T: AsPrimitive<u128> + Copy + 'static,
160
+ {
161
+ fn from(value: T) -> Self {
162
+ Currency::new(value.as_())
163
+ }
164
+ }
165
+
166
+ // Implement AsRef as well to be able to implicitly obtain a &u128 from a Currency as well.
167
+ impl<T> AsRef<T> for Currency
168
+ where
169
+ T: ?Sized,
170
+ <Currency as Deref>::Target: AsRef<T>,
171
+ {
172
+ fn as_ref(&self) -> &T {
173
+ self.deref().as_ref()
174
+ }
175
+ }
176
+
177
+ impl Display for Currency {
178
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
179
+ write!(f, "{}", self.0)
180
+ }
181
+ }
182
+
183
+ impl Currency {
184
+ pub const fn new(value: u128) -> Self {
185
+ Currency(value)
186
+ }
187
+
188
+ pub const fn zero() -> Self {
189
+ Currency(0)
190
+ }
191
+
192
+ /// Converts a given amount of Siacoins into the `Currency` type.
193
+ ///
194
+ /// This function takes the amount of Siacoins as a `u64` and converts it into
195
+ /// the `Currency` type, which internally represents the value in Hastings where
196
+ /// 1 SC = 10^24 H.
197
+ ///
198
+ /// # Arguments
199
+ ///
200
+ /// * `n` - The amount of Siacoins to be converted into `Currency`.
201
+ ///
202
+ /// # Returns
203
+ ///
204
+ /// Returns a `Currency` instance representing the specified amount of Siacoins.
205
+ pub const fn siacoins(n: u64) -> Self {
206
+ Currency::new((n as u128) * 10u128.pow(SIACOIN_PRECISION_U32))
207
+ }
208
+
209
+ pub fn checked_add(self, other: Currency) -> Option<Self> {
210
+ let v = self.0.checked_add(other.0)?;
211
+ Some(Currency(v))
212
+ }
213
+
214
+ pub fn checked_sub(self, other: Currency) -> Option<Self> {
215
+ let v = self.0.checked_sub(other.0)?;
216
+ Some(Currency(v))
217
+ }
218
+
219
+ pub fn checked_mul(self, other: Currency) -> Option<Self> {
220
+ let v = self.0.checked_mul(other.0)?;
221
+ Some(Currency(v))
222
+ }
223
+
224
+ pub fn checked_div(self, other: Currency) -> Option<Self> {
225
+ let v = self.0.checked_div(other.0)?;
226
+ Some(Currency(v))
227
+ }
228
+ }
229
+
230
+ impl std::str::FromStr for Currency {
231
+ type Err = crate::types::CurrencyParseError;
232
+
233
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
234
+ let i = s
235
+ .find(|c: char| !c.is_ascii_digit() && c != '.')
236
+ .unwrap_or(s.len());
237
+ let (value, unit) = s.split_at(i);
238
+ let value = value.trim();
239
+ let unit = unit.trim();
240
+
241
+ if unit.is_empty() || unit == "H" {
242
+ let value = value.parse::<u128>()?;
243
+ return Ok(Currency::new(value));
244
+ }
245
+
246
+ let scaling_factor: i32 = match unit {
247
+ "pS" => -12,
248
+ "nS" => -9,
249
+ "uS" => -6,
250
+ "mS" => -3,
251
+ "SC" => 0,
252
+ "KS" => 3,
253
+ "MS" => 6,
254
+ "GS" => 9,
255
+ "TS" => 12,
256
+ &_ => return Err(CurrencyParseError::InvalidUnit(unit.to_string())),
257
+ };
258
+
259
+ let parts: Vec<&str> = value.split('.').collect();
260
+ if parts.len() > 2 {
261
+ return Err(CurrencyParseError::InvalidFormat(
262
+ "too many decimal points".to_string(),
263
+ ));
264
+ }
265
+
266
+ let integer_part = parts[0]
267
+ .parse::<u128>()
268
+ .map_err(|_| CurrencyParseError::InvalidFormat("invalid integer part".to_string()))?;
269
+ let fraction_part = if parts.len() == 2 {
270
+ parts[1].parse::<u128>().map_err(|_| {
271
+ CurrencyParseError::InvalidFormat("invalid integer part".to_string())
272
+ })?
273
+ } else {
274
+ 0
275
+ };
276
+
277
+ let frac_digits = parts.get(1).map_or(0, |frac| frac.len() as i32);
278
+ let integer = integer_part * 10u128.pow((SIACOIN_PRECISION_I32 + scaling_factor) as u32);
279
+ let fraction = fraction_part
280
+ * 10u128.pow((SIACOIN_PRECISION_I32 - frac_digits + scaling_factor) as u32);
281
+
282
+ Ok(Currency::new(integer + fraction))
283
+ }
284
+ }
285
+
286
+ impl Add for Currency {
287
+ type Output = Self;
288
+
289
+ fn add(self, other: Self) -> Self {
290
+ Self(self.0 + other.0)
291
+ }
292
+ }
293
+
294
+ impl Sub for Currency {
295
+ type Output = Self;
296
+
297
+ fn sub(self, other: Self) -> Self {
298
+ Self(self.0 - other.0)
299
+ }
300
+ }
301
+
302
+ impl Mul for Currency {
303
+ type Output = Self;
304
+
305
+ fn mul(self, other: Self) -> Self {
306
+ Self(self.0 * other.0)
307
+ }
308
+ }
309
+
310
+ impl Div for Currency {
311
+ type Output = Self;
312
+
313
+ fn div(self, other: Self) -> Self {
314
+ Self(self.0 / other.0)
315
+ }
316
+ }
317
+
318
+ impl Sum for Currency {
319
+ fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
320
+ iter.fold(Currency::new(0), Add::add)
321
+ }
322
+ }
323
+
324
+ impl Rem for Currency {
325
+ type Output = Self;
326
+
327
+ fn rem(self, rhs: Self) -> Self::Output {
328
+ Self(self.0 % rhs.0)
329
+ }
330
+ }
331
+
332
+ #[derive(Debug, PartialEq)]
333
+ pub enum CurrencyParseError {
334
+ ParseIntErr(ParseIntError),
335
+ InvalidUnit(String),
336
+ InvalidFormat(String),
337
+ }
338
+
339
+ impl From<ParseIntError> for CurrencyParseError {
340
+ fn from(err: ParseIntError) -> Self {
341
+ CurrencyParseError::ParseIntErr(err)
342
+ }
343
+ }
344
+
345
+ #[cfg(test)]
346
+ mod tests {
347
+ use super::*;
348
+
349
+ #[test]
350
+ fn test_from_str() {
351
+ let test_cases = vec![
352
+ ("1 H", Currency::new(1)),
353
+ ("100 H", Currency::new(100)),
354
+ ("1001 H", Currency::new(1001)),
355
+ ("10000 H", Currency::new(10000)),
356
+ (
357
+ "1 pS",
358
+ Currency::siacoins(1) / Currency::new(1_000_000_000_000),
359
+ ),
360
+ (
361
+ "151.212312 uS",
362
+ Currency::siacoins(151212312) / Currency::new(1_000_000_000_000),
363
+ ),
364
+ ("500 mS", Currency::siacoins(1) / Currency::new(2)),
365
+ ("1 SC", Currency::siacoins(1)),
366
+ ("10 SC", Currency::siacoins(10)),
367
+ ("100 SC", Currency::siacoins(100)),
368
+ ("1 KS", Currency::siacoins(1000)),
369
+ ("10 KS", Currency::siacoins(10000)),
370
+ ("65.535 KS", Currency::siacoins(u16::MAX as u64)),
371
+ ("100KS", Currency::siacoins(100000)),
372
+ ("1 MS", Currency::siacoins(1000000)),
373
+ ("10 MS", Currency::siacoins(10000000)),
374
+ ("100 MS", Currency::siacoins(100000000)),
375
+ ("1 GS", Currency::siacoins(1000000000)),
376
+ ("4.294967295GS", Currency::siacoins(u32::MAX as u64)),
377
+ ("10 GS", Currency::siacoins(10000000000)),
378
+ ("100 GS", Currency::siacoins(100000000000)),
379
+ ("1 TS", Currency::siacoins(1000000000000)),
380
+ ("10 TS", Currency::siacoins(10000000000000)),
381
+ ("100 TS", Currency::siacoins(100000000000000)),
382
+ (
383
+ "9.999999999999999999999999 SC",
384
+ Currency::siacoins(10) - Currency::new(1),
385
+ ),
386
+ ("50.587566 SC", Currency::new(50587566000000000000000000)),
387
+ (
388
+ "2.529378333356156158367 mS",
389
+ Currency::new(2529378333356156158367),
390
+ ),
391
+ (
392
+ "340.282366920938463463374607431768211455 TS",
393
+ Currency::new(u128::MAX),
394
+ ),
395
+ ];
396
+ for (input, expected) in test_cases {
397
+ assert_eq!(input.parse::<Currency>().unwrap(), expected);
398
+ }
399
+ }
400
+
401
+ #[test]
402
+ fn test_encode_v1() {
403
+ let test_cases = vec![
404
+ (Currency::new(0), vec![0, 0, 0, 0, 0, 0, 0, 0]),
405
+ (Currency::new(10000), vec![2, 0, 0, 0, 0, 0, 0, 0, 39, 16]),
406
+ (
407
+ Currency::new(50587566000000000000000000),
408
+ vec![
409
+ 11, 0, 0, 0, 0, 0, 0, 0, 41, 216, 85, 114, 169, 108, 83, 1, 248, 0, 0,
410
+ ],
411
+ ),
412
+ (
413
+ Currency::new(2529378333356156158367),
414
+ vec![
415
+ 9, 0, 0, 0, 0, 0, 0, 0, 137, 30, 45, 0, 247, 119, 66, 129, 159,
416
+ ],
417
+ ),
418
+ (
419
+ Currency::new(340282366920938463463374607431768211455),
420
+ vec![
421
+ 16, 0, 0, 0, 0, 0, 0, 0, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255,
422
+ 255, 255, 255, 255, 255,
423
+ ],
424
+ ),
425
+ ];
426
+
427
+ for (currency, expected) in test_cases {
428
+ let mut serialized_currency = Vec::new();
429
+ currency
430
+ .encode_v1(&mut serialized_currency)
431
+ .unwrap_or_else(|e| panic!("failed to encode: {e:?}"));
432
+ assert_eq!(serialized_currency, expected, "failed for {currency:?}");
433
+ let deserialized_currency = Currency::decode_v1(&mut &serialized_currency[..])
434
+ .unwrap_or_else(|e| panic!("failed to decode: {e:?}"));
435
+ assert_eq!(deserialized_currency, currency, "failed for {currency:?}");
436
+ }
437
+ }
438
+
439
+ #[test]
440
+ fn test_json_serialize_currency() {
441
+ let currency_num = 120282366920938463463374607431768211455;
442
+ let currency = Currency::new(currency_num);
443
+
444
+ // json
445
+ let currency_serialized = serde_json::to_string(&currency).unwrap();
446
+ let currency_deserialized: Currency = serde_json::from_str(&currency_serialized).unwrap();
447
+ assert_eq!(currency_serialized, format!("\"{currency_num}\""));
448
+ assert_eq!(currency_deserialized, currency);
449
+ }
450
+ }
@@ -0,0 +1,110 @@
1
+ use crate::encoding::{
2
+ SiaDecodable, SiaDecode, SiaEncodable, SiaEncode, V1SiaDecodable, V1SiaDecode, V1SiaEncodable,
3
+ V1SiaEncode,
4
+ };
5
+ use crate::encoding_async::{AsyncSiaDecodable, AsyncSiaDecode, AsyncSiaEncodable, AsyncSiaEncode};
6
+ use core::{fmt, str};
7
+ use serde::{Deserialize, Serialize};
8
+
9
+ pub const SPECIFIER_SIZE: usize = 16;
10
+
11
+ #[derive(
12
+ Debug,
13
+ Clone,
14
+ Copy,
15
+ PartialEq,
16
+ Serialize,
17
+ Deserialize,
18
+ AsyncSiaDecode,
19
+ AsyncSiaEncode,
20
+ SiaEncode,
21
+ SiaDecode,
22
+ V1SiaEncode,
23
+ V1SiaDecode,
24
+ )]
25
+ pub struct Specifier([u8; SPECIFIER_SIZE]);
26
+
27
+ impl Specifier {
28
+ pub const fn new(buf: [u8; SPECIFIER_SIZE]) -> Self {
29
+ Self(buf)
30
+ }
31
+ }
32
+
33
+ impl AsRef<[u8]> for Specifier {
34
+ fn as_ref(&self) -> &[u8] {
35
+ &self.0
36
+ }
37
+ }
38
+
39
+ impl From<&str> for Specifier {
40
+ fn from(s: &str) -> Self {
41
+ let mut buf = [0; SPECIFIER_SIZE];
42
+ let src = s.as_bytes();
43
+ let len = src.len();
44
+ let index = len.min(SPECIFIER_SIZE);
45
+ buf[..index].copy_from_slice(&src[..index]);
46
+ Self(buf)
47
+ }
48
+ }
49
+
50
+ impl fmt::Display for Specifier {
51
+ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
52
+ // get the last non-zero byte or 0 if all bytes are 0
53
+ let index = self
54
+ .0
55
+ .iter()
56
+ .rev()
57
+ .position(|&x| x != 0)
58
+ .map_or(0, |pos| SPECIFIER_SIZE - pos);
59
+ let str = str::from_utf8(&self.0[..index]).map_err(|_| fmt::Error)?;
60
+ write!(f, "{str}")
61
+ }
62
+ }
63
+
64
+ macro_rules! specifier {
65
+ ($text:expr) => {{
66
+ let src = $text.as_bytes();
67
+ let len = src.len();
68
+ assert!(len <= $crate::types::SPECIFIER_SIZE, "specifier too long");
69
+ let mut buf = [0; 16];
70
+ let mut index: usize = 0;
71
+ while index < len {
72
+ buf[index] = src[index];
73
+ index += 1;
74
+ }
75
+ $crate::types::Specifier::new(buf)
76
+ }};
77
+ }
78
+ pub(crate) use specifier;
79
+
80
+ #[cfg(test)]
81
+ mod tests {
82
+ use super::*;
83
+
84
+ #[test]
85
+ fn test_specifier() {
86
+ let spec = specifier!["hello"];
87
+ let expected = Specifier([
88
+ b'h', b'e', b'l', b'l', b'o', 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
89
+ ]);
90
+ assert_eq!(spec, expected);
91
+ }
92
+
93
+ #[test]
94
+ fn test_specifier_string() {
95
+ let test_cases = vec![
96
+ (specifier!["hello world"], "hello world"),
97
+ (specifier!["hello"], "hello"),
98
+ (
99
+ Specifier::new([
100
+ b'h', b'e', b'l', b'l', b'o', 0, b'w', b'o', b'r', b'l', b'd', 0, 0, 0, 0, 0,
101
+ ]),
102
+ "hello\0world",
103
+ ),
104
+ (Specifier::new([0; 16]), ""),
105
+ ];
106
+ for (specifier, expected) in test_cases {
107
+ assert_eq!(specifier.to_string(), expected);
108
+ }
109
+ }
110
+ }