Skip to content

Commit

Permalink
fix: decimal places
Browse files Browse the repository at this point in the history
  • Loading branch information
crisdut committed Nov 18, 2023
1 parent 2d7ae05 commit 23f7fab
Show file tree
Hide file tree
Showing 23 changed files with 255 additions and 153 deletions.
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "bitmask-core"
version = "0.7.0-beta.8"
version = "0.7.0-beta.9"
authors = [
"Jose Diego Robles <[email protected]>",
"Hunter Trujillo <[email protected]>",
Expand Down
18 changes: 17 additions & 1 deletion lib/web/rgb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,22 @@ export const importUdaData = async (request: MediaRequest): Promise<boolean> =>
export const getMedia = async (mediaId: string): Promise<MediaMetadata> =>
JSON.parse(await BMC.get_media_metadata(mediaId));

export const contractAmount = (amount: bigint, precision: number): bigint =>
JSON.parse(BMC.contract_amount(amount, precision).toString());

export const contractAmountStr = (amount: bigint, precision: number): String =>
JSON.parse(BMC.contract_amount_str(amount, precision));

export const contractAmountParseStr = (amount: string, precision: number): String =>
JSON.parse(BMC.contract_amount_parse_str(amount, precision).toString());

export const contractAmountParseValue = (amount: string, precision: number): bigint =>
JSON.parse(BMC.contract_amount_parse_value(amount, precision).toString());

export const contractDecimalParseValue = (amount: string): bigint =>
JSON.parse(BMC.contract_amount_parse_decimal_value(amount).toString());


// Core type interfaces based on structs defined within the bitmask-core Rust crate:
// https://github.com/diba-io/bitmask-core/blob/development/src/structs.rs

Expand Down Expand Up @@ -368,7 +384,7 @@ export interface ImportResponse {
/// The user contract balance
balance: bigint;
/// The user contract balance
balance_normalized: number;
balanceNormalized: number;
/// The contract allocations
allocations: AllocationDetail[];
/// The contract state (multiple formats)
Expand Down
8 changes: 4 additions & 4 deletions src/rgb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ pub async fn issue_contract(sk: &str, request: IssueRequest) -> Result<IssueResp
_ => None,
};

let contract_amount = ContractAmount::with(supply, precision);
let contract_amount = ContractAmount::load(supply, precision);
let contract = create_contract(
&ticker,
&name,
Expand Down Expand Up @@ -470,7 +470,7 @@ async fn internal_create_invoice(

let contr_id = ContractId::from_str(&contract_id).map_err(|_| InvoiceError::NoContract)?;
let boilerplate = export_boilerplate(contr_id, stock).map_err(|_| InvoiceError::NoContract)?;
let invoice_amount = ContractAmount::from_raw(amount.to_string());
let invoice_amount = ContractAmount::from_decimal_str(amount.to_string());
if invoice_amount.precision != boilerplate.precision {
return Err(InvoiceError::WrongPrecision(
boilerplate.precision,
Expand Down Expand Up @@ -986,7 +986,7 @@ pub async fn create_seller_offer(
expire_at,
);

let contract_amount = ContractAmount::from_raw(contract_amount).to_string();
let contract_amount = ContractAmount::from_decimal_str(contract_amount).to_string();
let contract_amount =
f64::from_str(&contract_amount).map_err(|_| RgbSwapError::WrongValue(contract_amount))?;

Expand Down Expand Up @@ -1197,7 +1197,7 @@ pub async fn create_buyer_bid(
}
}

let invoice_amount = ContractAmount::with(asset_amount, asset_precision);
let invoice_amount = ContractAmount::load(asset_amount, asset_precision);
let invoice_req = InvoiceRequest {
iface,
contract_id: contract_id.to_string(),
Expand Down
8 changes: 4 additions & 4 deletions src/rgb/contract.rs
Original file line number Diff line number Diff line change
Expand Up @@ -225,9 +225,9 @@ where
.sum();
}

let balance_normalised = ContractAmount::with(balance, specs.precision.into()).to_string();
let balance_normalised = f64::from_str(&balance_normalised)
.map_err(|_| ExportContractError::WrongValue(contr_id.clone(), balance_normalised))?;
let balance_normalized = ContractAmount::load(balance, specs.precision.into()).to_string();
let balance_normalized = f64::from_str(&balance_normalized)
.map_err(|_| ExportContractError::WrongValue(contr_id.clone(), balance_normalized))?;

let mut supply = 0;
for (index, (_, global_assign)) in contract_bindle.genesis.assignments.iter().enumerate() {
Expand Down Expand Up @@ -336,7 +336,7 @@ where
precision: specs.precision.into(),
supply,
balance,
balance_normalised,
balance_normalized,
allocations,
created: created.into(),
contract: ContractFormats {
Expand Down
95 changes: 72 additions & 23 deletions src/rgb/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use bp::Txid;
use core::fmt::Display;
use rgb::{RgbWallet, TerminalPath};
use std::{
cmp::Ordering,
collections::{BTreeMap, HashMap},
str::FromStr,
};
Expand Down Expand Up @@ -45,6 +46,7 @@ impl FromStr for AddressAmount {
}
}

#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub struct ContractAmount {
pub int: u64,
pub fract: u64,
Expand All @@ -66,18 +68,59 @@ impl ContractAmount {
///
/// ```
/// // Define the initial value
/// let amount = CoinAmount::with(5, 2);
/// use bitmask_core::rgb::structs::ContractAmount;
/// let amount = ContractAmount::new(100, 2);
///
/// assert_eq!(amount.int, 5);
/// assert_eq!(amount.int, 100);
/// assert_eq!(amount.fract, 0);
/// assert_eq!(amount.to_value(), 500);
/// assert_eq!(amount.to_string(), "5");
/// assert_eq!(amount.clone().to_value(), 10000);
/// assert_eq!(amount.to_string(), "100.00");
/// ```
pub fn new(value: u64, precision: u8) -> Self {
let pow = 10_u64.pow(precision as u32);

let int = if value < pow { value } else { value / pow };
let fract = if value < pow { 0 } else { value - int * pow };
let int = match value.cmp(&pow) {
Ordering::Less | Ordering::Equal => value,
Ordering::Greater => value / pow,
};

let fract = match value.cmp(&pow) {
Ordering::Less | Ordering::Equal => 0,
Ordering::Greater => value - int * pow,
};

ContractAmount {
int,
fract,
precision,
}
}

/// Define a contract value.
///
/// A value ([`u64`]) combine with precision ([`u8`]), load
/// a contract value, compabitle with rgb contract value.
///
/// Remeber: All contract amounts are represents in [`u64`].
/// The [`ContractAmount`] abstract the calculation.
///
/// # Examples
///
/// Basic usage:
///
/// ```
/// // Define the initial value
/// use bitmask_core::rgb::structs::ContractAmount;
/// let amount = ContractAmount::with(2, 50, 2);
///
/// assert_eq!(amount.int, 2);
/// assert_eq!(amount.fract, 50);
/// assert_eq!(amount.clone().to_value(), 250);
/// assert_eq!(amount.to_string(), "2.50");
/// ```
pub fn with(value: u64, fract: u64, precision: u8) -> Self {

Check failure on line 121 in src/rgb/structs.rs

View workflow job for this annotation

GitHub Actions / lint

error: redundant redefinition of a binding --> src/rgb/structs.rs:121:29 | 121 | pub fn with(value: u64, fract: u64, precision: u8) -> Self { | ^^^^^ 122 | let int = value; 123 | let fract = fract; | ^^^^^^^^^^^^^^^^^^ | = help: remove the redefinition of `fract` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_locals = note: `#[deny(clippy::redundant_locals)]` on by default

Check failure on line 121 in src/rgb/structs.rs

View workflow job for this annotation

GitHub Actions / lint-wasm

error: redundant redefinition of a binding --> src/rgb/structs.rs:121:29 | 121 | pub fn with(value: u64, fract: u64, precision: u8) -> Self { | ^^^^^ 122 | let int = value; 123 | let fract = fract; | ^^^^^^^^^^^^^^^^^^ | = help: remove the redefinition of `fract` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_locals = note: `#[deny(clippy::redundant_locals)]` on by default
let int = value;
let fract = fract;

Check failure on line 123 in src/rgb/structs.rs

View workflow job for this annotation

GitHub Actions / lint

error: redundant redefinition of a binding --> src/rgb/structs.rs:121:29 | 121 | pub fn with(value: u64, fract: u64, precision: u8) -> Self { | ^^^^^ 122 | let int = value; 123 | let fract = fract; | ^^^^^^^^^^^^^^^^^^ | = help: remove the redefinition of `fract` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_locals = note: `#[deny(clippy::redundant_locals)]` on by default

Check failure on line 123 in src/rgb/structs.rs

View workflow job for this annotation

GitHub Actions / lint-wasm

error: redundant redefinition of a binding --> src/rgb/structs.rs:121:29 | 121 | pub fn with(value: u64, fract: u64, precision: u8) -> Self { | ^^^^^ 122 | let int = value; 123 | let fract = fract; | ^^^^^^^^^^^^^^^^^^ | = help: remove the redefinition of `fract` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#redundant_locals = note: `#[deny(clippy::redundant_locals)]` on by default
ContractAmount {
int,
fract,
Expand All @@ -99,14 +142,15 @@ impl ContractAmount {
///
/// ```
/// // Define the initial value
/// let amount = ContractAmount::with(1100, 3);
/// use bitmask_core::rgb::structs::ContractAmount;
/// let amount = ContractAmount::load(1100, 3);
///
/// assert_eq!(amount.int, 1);
/// assert_eq!(amount.fract, 100);
/// assert_eq!(amount.to_value(), 1100);
/// assert_eq!(amount.clone().to_value(), 1100);
/// assert_eq!(amount.to_string(), "1.100");
/// ```
pub fn with(value: u64, precision: u8) -> Self {
pub fn load(value: u64, precision: u8) -> Self {
let pow = 10_u64.pow(precision as u32);
let int = value / pow;
let fract = value - int * pow;
Expand All @@ -117,7 +161,7 @@ impl ContractAmount {
}
}

/// Convert a raw string representation of the
/// Convert a raw u64 representation of the
/// number in Contract Amount.
///
/// A value ([`String`]) return a contract value,
Expand All @@ -129,17 +173,19 @@ impl ContractAmount {
///
/// ```
/// // Define the initial value
/// let amount = ContractAmount::from("1.100");
/// use bitmask_core::rgb::structs::ContractAmount;
/// let amount = ContractAmount::from("1".to_string(), 3);
///
/// assert_eq!(amount.int, 1);
/// assert_eq!(amount.fract, 100);
/// assert_eq!(amount.to_value(), 1100);
/// assert_eq!(amount.to_string(), "1.100");
/// assert_eq!(amount.int.clone(), 1);
/// assert_eq!(amount.fract, 0);
/// assert_eq!(amount.clone().to_value(), 1000);
/// assert_eq!(amount.to_string().clone(), "1.000");
/// ```
pub fn from(value: String, precision: u8) -> Self {
let mut fract = 0;
let int;
let mut fract;

let int = if value.contains('.') {
if value.contains('.') {
let parts = value.split('.');
let collection: Vec<&str> = parts.collect();

Expand All @@ -150,9 +196,11 @@ impl ContractAmount {
fract *= pow;
}

collection[0].parse().unwrap()
int = collection[0].parse().unwrap();
} else {
value.parse().unwrap()
let unsafe_contract_amount = Self::load(value.parse().unwrap(), precision);
fract = unsafe_contract_amount.fract;
int = unsafe_contract_amount.int
};

ContractAmount {
Expand All @@ -162,7 +210,7 @@ impl ContractAmount {
}
}

/// Convert a raw string representation of the
/// Convert a decimal string representation of the
/// number in Contract Amount.
///
/// A value ([`String`]) return a contract value,
Expand All @@ -174,14 +222,15 @@ impl ContractAmount {
///
/// ```
/// // Define the initial value
/// let amount = ContractAmount::from_raw("1.100");
/// use bitmask_core::rgb::structs::ContractAmount;
/// let amount = ContractAmount::from_decimal_str("1.100".to_string());
///
/// assert_eq!(amount.int, 1);
/// assert_eq!(amount.fract, 100);
/// assert_eq!(amount.to_value(), 1100);
/// assert_eq!(amount.clone().to_value(), 1100);
/// assert_eq!(amount.to_string(), "1.100");
/// ```
pub fn from_raw(value: String) -> Self {
pub fn from_decimal_str(value: String) -> Self {
let mut fract = 0;
let mut precision = 0;

Expand Down
2 changes: 1 addition & 1 deletion src/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,7 @@ pub struct ContractResponse {
/// Current balance
pub balance: u64,
/// Current balance (Humanized)
pub balance_normalised: f64,
pub balance_normalized: f64,
/// The contract allocations
pub allocations: Vec<AllocationDetail>,
/// The contract state (multiple formats)
Expand Down
31 changes: 31 additions & 0 deletions src/web.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use serde::de::DeserializeOwned;
use wasm_bindgen::prelude::*;
use wasm_bindgen_futures::{future_to_promise, JsFuture};

use crate::rgb::structs::ContractAmount;
use crate::structs::{
AcceptRequest, FullRgbTransferRequest, ImportRequest, InvoiceRequest, IssueRequest,
MediaRequest, PsbtRequest, PublishPsbtRequest, ReIssueRequest, RgbBidRequest, RgbOfferRequest,
Expand Down Expand Up @@ -995,6 +996,36 @@ pub mod rgb {
}
})
}

#[wasm_bindgen]
pub fn contract_amount(amount: u64, precision: u8) -> u64 {
set_panic_hook();
ContractAmount::new(amount, precision).to_value()
}

#[wasm_bindgen]
pub fn contract_amount_str(amount: u64, precision: u8) -> String {
set_panic_hook();
ContractAmount::new(amount, precision).to_string()
}

#[wasm_bindgen]
pub fn contract_amount_parse_str(amount: String, precision: u8) -> String {
set_panic_hook();
ContractAmount::from(amount, precision).to_string()
}

#[wasm_bindgen]
pub fn contract_amount_parse_value(amount: String, precision: u8) -> u64 {
set_panic_hook();
ContractAmount::from(amount, precision).to_value()
}

#[wasm_bindgen]
pub fn contract_amount_parse_decimal_value(amount: String) -> u64 {
set_panic_hook();
ContractAmount::from_decimal_str(amount).to_value()
}
}

pub mod lightning {
Expand Down
1 change: 1 addition & 0 deletions tests/rgb.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
mod rgb {

mod unit {
mod amount;
mod invoice;
mod issue;
mod psbt;
Expand Down
4 changes: 2 additions & 2 deletions tests/rgb/integration/accept.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ pub async fn allow_save_read_remove_transfers() -> Result<()> {
let owner_invoice = &create_new_invoice(
&issuer_resp.contract_id,
&issuer_resp.iface,
2.00,
ContractAmount::with(2, 0, issuer_resp.precision),
owner_keys.clone(),
None,
Some(issuer_resp.clone().contract.strict),
Expand Down Expand Up @@ -229,7 +229,7 @@ pub async fn accept_all_transfers() -> Result<()> {
let owner_invoice = &create_new_invoice(
&issuer_resp.contract_id,
&issuer_resp.iface,
2.00,
ContractAmount::with(2, 0, issuer_resp.precision),
owner_keys.clone(),
None,
Some(issuer_resp.clone().contract.strict),
Expand Down
Loading

0 comments on commit 23f7fab

Please sign in to comment.