Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "wavesplatform"
version = "0.3.0"
version = "0.3.1"
authors = ["peterz <peterz@rambler.ru>", "DEADBLACKCLOVER <deadblackclover@protonmail.com>"]
description = "Library to work with Waves blockchain (https://waves.tech/)"
edition = "2018"
Expand All @@ -13,6 +13,7 @@ keywords = ["waves", "blockchain"]
[dependencies]
base58 = "0.2.0"
rand = "0.8.5"
regex = "1.6.0"
tiny-bip39 = "1.0.0"

blake2 = "0.9.2"
Expand Down
9 changes: 7 additions & 2 deletions examples/get_balance.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use wavesplatform::node::{Node, MAINNET_URL};
use wavesplatform::util::Amount;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
Expand All @@ -9,13 +10,17 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
.get_balance("3PEktVux2RhchSN63DsDo4b4mz4QqzKSeDv")
.await?;

println!("Balance: {} WAVES", result.balance());
let balance = Amount::from_wavelet(result.balance());

println!("Balance: {} WAVES", balance);

let result = node
.get_balance_details("3PEktVux2RhchSN63DsDo4b4mz4QqzKSeDv")
.await?;

println!("Regular balance: {} WAVES", result.regular());
let balance = Amount::from_wavelet(result.regular());

println!("Regular balance: {} WAVES", balance);

Ok(())
}
10 changes: 8 additions & 2 deletions src/node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ impl<'a> Node<'a> {
/// Get the regular balance in WAVES at a given address
/// ```no_run
/// use wavesplatform::node::{Node, MAINNET_URL};
/// use wavesplatform::util::Amount;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
Expand All @@ -43,7 +44,9 @@ impl<'a> Node<'a> {
/// .get_balance("3PEktVux2RhchSN63DsDo4b4mz4QqzKSeDv")
/// .await?;
///
/// println!("Balance: {} WAVES", result.balance());
/// let balance = Amount::from_wavelet(result.balance());
///
/// println!("Balance: {} WAVES", balance);
///
/// Ok(())
/// }
Expand All @@ -62,6 +65,7 @@ impl<'a> Node<'a> {
/// Get the available, regular, generating, and effective balance
/// ```no_run
/// use wavesplatform::node::{Node, MAINNET_URL};
/// use wavesplatform::util::Amount;
///
/// #[tokio::main]
/// async fn main() -> Result<(), Box<dyn std::error::Error>> {
Expand All @@ -71,7 +75,9 @@ impl<'a> Node<'a> {
/// .get_balance_details("3PEktVux2RhchSN63DsDo4b4mz4QqzKSeDv")
/// .await?;
///
/// println!("Regular balance: {} WAVES", result.regular());
/// let balance = Amount::from_wavelet(result.regular());
///
/// println!("Regular balance: {} WAVES", balance);
///
/// Ok(())
/// }
Expand Down
6 changes: 6 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
mod alias;
mod amount;

use curve25519_dalek::montgomery::MontgomeryPoint;
use ed25519_dalek::*;

pub use alias::*;
pub use amount::*;

/// Signature verify function
pub fn sig_verify(
message: &[u8],
Expand Down
93 changes: 93 additions & 0 deletions src/util/alias.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use regex::Regex;
use std::fmt;

/// Regular expression for the definition of alias
const REGEXP: &str = "^[-.0-9@_a-z]{4,30}$";
/// Regular expression for the definition of alias with prefix
const REGEXP_WITH_PREFIX: &str = "^alias:[A-Z]{1}:[-.0-9@_a-z]{4,30}$";

/// List of errors in processing [`Alias`]
#[derive(Debug, PartialEq, Eq)]
pub enum AliasError {
InvalidAlias,
RegexError,
}

/// The [`Alias`] type on working with aliases in the Waves blockchain and presenting it in a format with or without the network prefix
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Alias(String);

impl Alias {
/// Create an [`Alias`] from the string
pub fn new(alias: &str) -> Result<Alias, AliasError> {
if Self::is_valid(REGEXP, alias)? {
Ok(Alias(format!("{}", alias)))
} else if Self::is_valid(REGEXP_WITH_PREFIX, alias)? {
let value = Self::replace_prefix(alias)?;
Ok(Alias(value))
} else {
Err(AliasError::InvalidAlias)
}
}

/// Representing [`Alias`] as a string with a prefix
pub fn to_string_with_prefix(&self, chain_id: u8) -> String {
format!("alias:{}:{}", chain_id as char, self.0)
}

/// Validation of alias using regular expressions
fn is_valid(regexp: &str, alias: &str) -> Result<bool, AliasError> {
match Regex::new(regexp) {
Ok(r) => Ok(r.is_match(alias)),
Err(_) => Err(AliasError::RegexError),
}
}

/// Removing the prefix with regular expressions
fn replace_prefix(alias: &str) -> Result<String, AliasError> {
match Regex::new("^alias:[A-Z]{1}:") {
Ok(r) => Ok(r.replace(alias, "").to_string()),
Err(_) => Err(AliasError::RegexError),
}
}
}

impl fmt::Display for Alias {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}

#[cfg(test)]
mod tests {
use super::*;
use crate::account::TESTNET;

#[test]
fn test_alias() {
let result = Alias::new("test");
assert!(result.is_ok());

let alias = result.unwrap();

assert_eq!(alias.to_string(), "test");
assert_eq!(alias.to_string_with_prefix(TESTNET), "alias:T:test");

let result = Alias::new("a");
assert_eq!(result, Err(AliasError::InvalidAlias));

let result = Alias::new("3MzGEv9wnaqrYFYujAXSH5RQfHaVKNQvx3D");
assert_eq!(result, Err(AliasError::InvalidAlias));
}

#[test]
fn test_alias_with_prefix() {
let result = Alias::new("alias:T:test");
assert!(result.is_ok());

let alias = result.unwrap();

assert_eq!(alias.to_string(), "test");
assert_eq!(alias.to_string_with_prefix(TESTNET), "alias:T:test");
}
}
93 changes: 93 additions & 0 deletions src/util/amount.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
use std::fmt;
use std::str::FromStr;

/// The number of decimal places (decimals) for WAVES is 8.
const DECIMALS: usize = 8;

/// The [`Amount`] type can be used to express Waves amounts that supports conversion to various denominations.
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Amount(u64);

impl Amount {
/// The zero amount.
pub const ZERO: Amount = Amount(0);
/// Exactly one WAVELET.
pub const ONE_WAVELET: Amount = Amount(1);
/// Exactly one WAVES.
pub const ONE_WAVES: Amount = Amount(100_000_000);

/// Create an [`Amount`] with WAVELET precision and the given number of WAVELET.
pub fn from_wavelet(wavelet: u64) -> Amount {
Amount(wavelet)
}

/// The maximum value of an [`Amount`].
pub fn max_value() -> Amount {
Amount(u64::max_value())
}

/// The minimum value of an [`Amount`].
pub fn min_value() -> Amount {
Amount(u64::min_value())
}

/// Get the number of WAVELET in this [`Amount`].
pub fn as_wavelet(self) -> u64 {
self.0
}

/// Express this [`Amount`] as a floating-point value in WAVES.
pub fn as_waves(self) -> f64 {
let real = format!("{:0width$}", self.0, width = DECIMALS);

if real.len() == DECIMALS {
let result = format!("0.{}", &real[real.len() - DECIMALS..]);
f64::from_str(&result).unwrap()
} else {
let result = format!(
"{}.{}",
&real[0..(real.len() - DECIMALS)],
&real[real.len() - DECIMALS..]
);
f64::from_str(&result).unwrap()
}
}
}

impl Default for Amount {
fn default() -> Self {
Amount::ZERO
}
}

impl fmt::Display for Amount {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.as_waves())
}
}

#[cfg(test)]
mod tests {
use super::*;

#[test]
fn test() {
let wavelet = 1_000;
let balance = Amount::from_wavelet(wavelet);
assert_eq!(balance.as_waves(), 0.00001);

let wavelet = 1_000_000;
let balance = Amount::from_wavelet(wavelet);
assert_eq!(balance.as_waves(), 0.01);

let wavelet = 1_000_000_000;
let balance = Amount::from_wavelet(wavelet);
assert_eq!(balance.as_waves(), 10.0);
}

#[test]
fn test_one_waves() {
let one_waves = Amount::ONE_WAVES;
assert_eq!(one_waves.as_waves(), 1.0);
}
}