11 releases (4 breaking)
0.4.1 | Jun 21, 2024 |
---|---|
0.4.0 | Jun 5, 2024 |
0.3.2 | Apr 4, 2024 |
0.2.0 | Mar 26, 2024 |
0.0.1 | Aug 13, 2023 |
#3 in #dollar
487 downloads per month
74KB
1.5K
SLoC
💰 currencies
This crate provides a generic Currency
and corresponding Amount
type that can handle basic
arithmetic operations and formatting of arbitrary currencies and cryptocurrencies. Main
features include:
Features
- Built-in support for all ISO-4217 currencies with proper precision and formatting
- Support for a variety of cryptocurrencies, also with proper underlying data types and
formatting. Accurate implementations for
ETH
,BTC
,DOT
, and a variety of other cryptocurrencies are included. - The ability to specify whether an
Amount
is forced to only make use of unchecked math, or not, at compile-time. Normally this is impossible to control since thecore:ops
operators are set up such that the checked operators require their unchecked counterparts to be implemented on the host type, however I have gone out of my way to make it possible to implement unchecked math only, and control it easily with aAmount<ETH, Checked>
-style switch. This is extremely desirable for scenarios where panicking could cause a catastrophic issue, and the way it is set up, programmers are forced to consume theOption
returned by the checked ops. - An easy-to-use macro,
define_currency!
that can define new currencies on-the-fly. - A painstakingly wrapped version of
primitive_types::U256
that implements many more usefulnum-traits
andnum-integer
traits than what Parity includes with thenum-traits
feature, and are often required when working with amounts of a currency. - All provided currencies implement most useful
num-traits
andnum-integer
traits. - Thorough testing of all of the above.
Examples
#[test]
fn show_off_currency_math() {
use currency::*;
let apple_cost = amt!(USD, "$3.24");
let orange_cost = Amount::<USD>::from_raw(7_97);
assert!(apple_cost < orange_cost);
assert!(apple_cost + orange_cost > orange_cost);
assert_eq!(format!("{}", apple_cost * orange_cost), "$25.82");
assert_eq!(format!("{}", apple_cost * 3), "$9.72");
let mut total = amt!(DOT, "57622449841.0000000004 DOT");
total -= amt!(DOT, "1000.0 DOT");
total *= Amount::from_raw(2_0000000000u64.into());
assert_eq!(format!("{}", total), "115244897682.0000000008 DOT");
}
#[test]
fn show_off_checked_math() {
use currency::*;
use safety::*;
// When using currency amounts with `Safety = Checked`, the Amount struct has been specially set
// up so that only checked math will be allowed, and you can still use the normal
// operator-based syntax. Thus currency amounts like this should never panic and are
// suitable for use in critical/infallible environments.
let drink_cost = amt_checked!(USD, "$6.29");
let movie_cost = Amount::<USD, Checked>::from_raw(24_99);
let Some(outing_cost) = drink_cost + movie_cost else {
unimplemented!("compiler forces you to handle this!")
};
assert_eq!(format!("{}", outing_cost), "$31.28");
}
Future Work
- Additional macros for defining an
Amount
via a decimal literal - Currency conversion facilities, possibly including an online data source
- Add
Signedness
support toAmount
- Additional testing
- Support for negative amounts via an additional const generic defaulting to
Positive
Dependencies
~3.5–5MB
~95K SLoC