#currency #money #cryptocurrency #dollar #arithmetic-operations

no-std currencies

Allows for generic manipulation of currencies (both real-world and cryptocurrencies) with optionally compile-time enforced checked math and support for all ISO-4217 currencies

9 unstable releases (3 breaking)

0.3.2 Apr 4, 2024
0.3.1 Apr 4, 2024
0.2.0 Mar 26, 2024
0.1.3 Mar 6, 2024
0.0.1 Aug 13, 2023

#317 in Magic Beans

Download history 13/week @ 2024-01-04 35/week @ 2024-01-11 59/week @ 2024-01-18 52/week @ 2024-01-25 9/week @ 2024-02-15 19/week @ 2024-02-22 228/week @ 2024-02-29 179/week @ 2024-03-07 115/week @ 2024-03-14 167/week @ 2024-03-21 92/week @ 2024-03-28 440/week @ 2024-04-04 107/week @ 2024-04-11 209/week @ 2024-04-18

875 downloads per month

MIT license

68KB
1.5K SLoC

💰 currencies

Crates.io docs.rs Build Status MIT License

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, KSM and AAVE 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 the core: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 a Amount<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 the Option 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 useful num-traits and num-integer traits than what Parity includes with the num-traits feature, and are often required when working with amounts of a currency.
  • All provided currencies implement most useful num-traits and num-integer traits.
  • Thorough testing of all of the above.

Examples

#[test]
fn show_off_currency_math() {
    use currency::*;

    let apple_cost = Amount::<USD>::from_raw(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 = Amount::<AAVE>::from_raw(5762244984_100000000000000004u128.into());
    total -= Amount::from_raw(1000_000000000000000000u128.into());
    total *= Amount::from_raw(2_000000000000000000u64.into());
    assert_eq!(format!("{}", total), "11524487968.200000000000000008 AAVE");
}

#[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 = Amount::<USD, Checked>::from_raw(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 to Amount
  • Additional testing
  • Support for negative amounts via an additional const generic defaulting to Positive

Dependencies

~0.4–1.3MB
~23K SLoC