Make wallet denom dynamic
Making denom dynamic enables coin transfers on other nets than the default sandbox net, such as mainnet.
This commit is contained in:
Generated
+2
-1
@@ -3669,6 +3669,7 @@ dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"hex-literal",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"url",
|
||||
]
|
||||
|
||||
@@ -8248,4 +8249,4 @@ checksum = "615120c7a2431d16cf1cf979e7fc31ba7a5b5e5707b29c8a99e5dbf8a8392a33"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
]
|
||||
]
|
||||
|
||||
@@ -24,7 +24,7 @@ where
|
||||
pub fn connect_with_signer<U>(
|
||||
endpoint: U,
|
||||
signer: DirectSecp256k1HdWallet,
|
||||
gas_price: Option<GasPrice>,
|
||||
gas_price: GasPrice,
|
||||
) -> Result<signing_client::Client, NymdError>
|
||||
where
|
||||
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
|
||||
|
||||
@@ -644,7 +644,7 @@ impl Client {
|
||||
pub fn connect_with_signer<U>(
|
||||
endpoint: U,
|
||||
signer: DirectSecp256k1HdWallet,
|
||||
gas_price: Option<GasPrice>,
|
||||
gas_price: GasPrice,
|
||||
) -> Result<Self, NymdError>
|
||||
where
|
||||
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
|
||||
@@ -653,7 +653,7 @@ impl Client {
|
||||
Ok(Client {
|
||||
rpc_client,
|
||||
signer,
|
||||
gas_price: gas_price.unwrap_or_default(),
|
||||
gas_price,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,11 +71,9 @@ impl FromStr for GasPrice {
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GasPrice {
|
||||
fn default() -> Self {
|
||||
format!("{}{}", defaults::GAS_PRICE_AMOUNT, defaults::DENOM)
|
||||
.parse()
|
||||
.unwrap()
|
||||
impl GasPrice {
|
||||
pub fn new_with_default_price(denom: String) -> Result<Self, NymdError> {
|
||||
format!("{}{}", defaults::GAS_PRICE_AMOUNT, denom).parse()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,7 +83,8 @@ mod tests {
|
||||
|
||||
#[test]
|
||||
fn default_gas_price_is_valid() {
|
||||
let _ = GasPrice::default();
|
||||
let denom = "unym".parse().unwrap();
|
||||
let _ = GasPrice::new_with_default_price(denom);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
@@ -74,7 +74,7 @@ impl NymdClient<QueryNymdClient> {
|
||||
vesting_contract_address,
|
||||
erc20_bridge_contract_address,
|
||||
client_address: None,
|
||||
custom_gas_limits: Default::default(),
|
||||
custom_gas_limits: HashMap::default(),
|
||||
simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER,
|
||||
})
|
||||
}
|
||||
@@ -83,6 +83,7 @@ impl NymdClient<QueryNymdClient> {
|
||||
impl NymdClient<SigningNymdClient> {
|
||||
// maybe the wallet could be made into a generic, but for now, let's just have this one implementation
|
||||
pub fn connect_with_signer<U>(
|
||||
network: config::defaults::all::Network,
|
||||
endpoint: U,
|
||||
mixnet_contract_address: Option<AccountId>,
|
||||
vesting_contract_address: Option<AccountId>,
|
||||
@@ -93,11 +94,13 @@ impl NymdClient<SigningNymdClient> {
|
||||
where
|
||||
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
|
||||
{
|
||||
let denom = network.denom();
|
||||
let client_address = signer
|
||||
.try_derive_accounts()?
|
||||
.into_iter()
|
||||
.map(|account| account.address)
|
||||
.collect();
|
||||
let gas_price = gas_price.unwrap_or(GasPrice::new_with_default_price(denom)?);
|
||||
|
||||
Ok(NymdClient {
|
||||
client: SigningNymdClient::connect_with_signer(endpoint, signer, gas_price)?,
|
||||
@@ -105,7 +108,7 @@ impl NymdClient<SigningNymdClient> {
|
||||
vesting_contract_address,
|
||||
erc20_bridge_contract_address,
|
||||
client_address: Some(client_address),
|
||||
custom_gas_limits: Default::default(),
|
||||
custom_gas_limits: HashMap::default(),
|
||||
simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER,
|
||||
})
|
||||
}
|
||||
@@ -123,12 +126,14 @@ impl NymdClient<SigningNymdClient> {
|
||||
U: TryInto<HttpClientUrl, Error = TendermintRpcError>,
|
||||
{
|
||||
let prefix = network.bech32_prefix();
|
||||
let denom = network.denom();
|
||||
let wallet = DirectSecp256k1HdWallet::from_mnemonic(prefix, mnemonic)?;
|
||||
let client_address = wallet
|
||||
.try_derive_accounts()?
|
||||
.into_iter()
|
||||
.map(|account| account.address)
|
||||
.collect();
|
||||
let gas_price = gas_price.unwrap_or(GasPrice::new_with_default_price(denom)?);
|
||||
|
||||
Ok(NymdClient {
|
||||
client: SigningNymdClient::connect_with_signer(endpoint, wallet, gas_price)?,
|
||||
@@ -136,7 +141,7 @@ impl NymdClient<SigningNymdClient> {
|
||||
vesting_contract_address,
|
||||
erc20_bridge_contract_address,
|
||||
client_address: Some(client_address),
|
||||
custom_gas_limits: Default::default(),
|
||||
custom_gas_limits: HashMap::default(),
|
||||
simulated_gas_multiplier: DEFAULT_SIMULATED_GAS_MULTIPLIER,
|
||||
})
|
||||
}
|
||||
@@ -161,13 +166,6 @@ impl<C> NymdClient<C> {
|
||||
.ok_or(NymdError::NoContractAddressAvailable)
|
||||
}
|
||||
|
||||
// now the question is as follows: will denom always be in the format of `u{prefix}`?
|
||||
pub fn denom(&self) -> Result<Denom, NymdError> {
|
||||
Ok(format!("u{}", self.mixnet_contract_address()?.prefix())
|
||||
.parse()
|
||||
.unwrap())
|
||||
}
|
||||
|
||||
pub fn address(&self) -> &AccountId
|
||||
where
|
||||
C: SigningCosmWasmClient,
|
||||
|
||||
@@ -57,15 +57,13 @@ pub struct DirectSecp256k1HdWallet {
|
||||
}
|
||||
|
||||
impl DirectSecp256k1HdWallet {
|
||||
pub fn builder() -> DirectSecp256k1HdWalletBuilder {
|
||||
DirectSecp256k1HdWalletBuilder::default()
|
||||
pub fn builder(prefix: String) -> DirectSecp256k1HdWalletBuilder {
|
||||
DirectSecp256k1HdWalletBuilder::new(prefix)
|
||||
}
|
||||
|
||||
/// Restores a wallet from the given BIP39 mnemonic using default options.
|
||||
pub fn from_mnemonic(prefix: String, mnemonic: bip39::Mnemonic) -> Result<Self, NymdError> {
|
||||
DirectSecp256k1HdWalletBuilder::new()
|
||||
.with_prefix(prefix)
|
||||
.build(mnemonic)
|
||||
DirectSecp256k1HdWalletBuilder::new(prefix).build(mnemonic)
|
||||
}
|
||||
|
||||
pub fn generate(prefix: String, word_count: usize) -> Result<Self, NymdError> {
|
||||
@@ -147,20 +145,14 @@ pub struct DirectSecp256k1HdWalletBuilder {
|
||||
prefix: String,
|
||||
}
|
||||
|
||||
impl Default for DirectSecp256k1HdWalletBuilder {
|
||||
fn default() -> Self {
|
||||
impl DirectSecp256k1HdWalletBuilder {
|
||||
pub fn new(prefix: String) -> Self {
|
||||
DirectSecp256k1HdWalletBuilder {
|
||||
bip39_password: String::new(),
|
||||
hd_paths: vec![defaults::COSMOS_DERIVATION_PATH.parse().unwrap()],
|
||||
prefix: defaults::BECH32_PREFIX.to_string(),
|
||||
prefix,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl DirectSecp256k1HdWalletBuilder {
|
||||
pub fn new() -> Self {
|
||||
Default::default()
|
||||
}
|
||||
|
||||
pub fn with_bip39_password<S: Into<String>>(mut self, password: S) -> Self {
|
||||
self.bip39_password = password.into();
|
||||
|
||||
@@ -10,4 +10,5 @@ edition = "2021"
|
||||
cfg-if = "1.0.0"
|
||||
hex-literal = "0.3.3"
|
||||
serde = {version = "1.0", features = ["derive"]}
|
||||
thiserror = "1.0"
|
||||
url = "2.2"
|
||||
|
||||
@@ -2,10 +2,18 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::{collections::HashMap, fmt, str::FromStr};
|
||||
|
||||
use crate::{mainnet, qa, sandbox, ValidatorDetails};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum NetworkDefaultsError {
|
||||
#[error("The provided network was invalid")]
|
||||
MalformedNetworkProvided(String),
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
|
||||
pub enum Network {
|
||||
QA,
|
||||
@@ -21,6 +29,39 @@ impl Network {
|
||||
Self::MAINNET => String::from(mainnet::BECH32_PREFIX),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn denom(&self) -> String {
|
||||
match self {
|
||||
Self::QA => String::from(qa::DENOM),
|
||||
Self::SANDBOX => String::from(sandbox::DENOM),
|
||||
Self::MAINNET => String::from(mainnet::DENOM),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Network {
|
||||
type Err = NetworkDefaultsError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
match s.to_lowercase().as_str() {
|
||||
"qa" => Ok(Network::QA),
|
||||
"sandbox" => Ok(Network::SANDBOX),
|
||||
"mainnet" => Ok(Network::MAINNET),
|
||||
_ => Err(NetworkDefaultsError::MalformedNetworkProvided(
|
||||
s.to_string(),
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Display for Network {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
Network::QA => f.write_str("QA"),
|
||||
Network::SANDBOX => f.write_str("Sandbox"),
|
||||
Network::MAINNET => f.write_str("Mainnet"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Deserialize, Serialize)]
|
||||
|
||||
Generated
+1
@@ -882,6 +882,7 @@ dependencies = [
|
||||
"cfg-if",
|
||||
"hex-literal",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"url",
|
||||
]
|
||||
|
||||
|
||||
Generated
+1
@@ -2767,6 +2767,7 @@ dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"hex-literal",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"url",
|
||||
]
|
||||
|
||||
|
||||
@@ -2,14 +2,12 @@
|
||||
|
||||
use crate::error::BackendError;
|
||||
use crate::network::Network;
|
||||
use ::config::defaults::DENOM;
|
||||
use cosmrs::Decimal;
|
||||
use cosmrs::Denom as CosmosDenom;
|
||||
use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
use cosmwasm_std::Uint128;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryFrom;
|
||||
use std::fmt;
|
||||
use std::ops::{Add, Sub};
|
||||
use std::str::FromStr;
|
||||
use strum::IntoEnumIterator;
|
||||
@@ -24,15 +22,6 @@ pub enum Denom {
|
||||
|
||||
const MINOR_IN_MAJOR: f64 = 1_000_000.;
|
||||
|
||||
impl fmt::Display for Denom {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match *self {
|
||||
Denom::Major => f.write_str(&DENOM[1..]),
|
||||
Denom::Minor => f.write_str(DENOM),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl FromStr for Denom {
|
||||
type Err = BackendError;
|
||||
|
||||
@@ -65,19 +54,14 @@ pub struct Coin {
|
||||
denom: Denom,
|
||||
}
|
||||
|
||||
// TODO convert to TryFrom
|
||||
impl From<GasPrice> for Coin {
|
||||
fn from(g: GasPrice) -> Coin {
|
||||
Coin {
|
||||
amount: g.amount.to_string(),
|
||||
denom: Denom::from_str(&g.denom.to_string()).unwrap(),
|
||||
}
|
||||
}
|
||||
}
|
||||
impl TryFrom<GasPrice> for Coin {
|
||||
type Error = BackendError;
|
||||
|
||||
impl fmt::Display for Coin {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(&format!("{} {}", self.amount, self.denom))
|
||||
fn try_from(g: GasPrice) -> Result<Self, Self::Error> {
|
||||
Ok(Coin {
|
||||
amount: g.amount.to_string(),
|
||||
denom: Denom::from_str(&g.denom.to_string())?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,6 +110,7 @@ impl Sub for Coin {
|
||||
}
|
||||
|
||||
impl Coin {
|
||||
#[allow(unused)]
|
||||
pub fn major<T: ToString>(amount: T) -> Coin {
|
||||
Coin {
|
||||
amount: amount.to_string(),
|
||||
@@ -133,6 +118,7 @@ impl Coin {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn minor<T: ToString>(amount: T) -> Coin {
|
||||
Coin {
|
||||
amount: amount.to_string(),
|
||||
@@ -171,34 +157,45 @@ impl Coin {
|
||||
self.amount.clone()
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
pub fn denom(&self) -> Denom {
|
||||
self.denom.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Coin> for CosmWasmCoin {
|
||||
type Error = BackendError;
|
||||
// Helper function that returns the local denom in terms of the specified network denom.
|
||||
fn denom_as_string(&self, network_denom: &CosmosDenom) -> Result<String, BackendError> {
|
||||
// Currently there is the widespread assumption that network denomination is always in
|
||||
// `Denom::Minor`, and starts with 'u'.
|
||||
let network_denom = network_denom.to_string();
|
||||
if !network_denom.starts_with('u') {
|
||||
return Err(BackendError::InvalidNetworkDenom(network_denom));
|
||||
}
|
||||
|
||||
fn try_from(coin: Coin) -> Result<CosmWasmCoin, Self::Error> {
|
||||
Ok(CosmWasmCoin::new(
|
||||
Uint128::try_from(coin.amount.as_str()).unwrap().u128(),
|
||||
coin.denom.to_string(),
|
||||
))
|
||||
Ok(match &self.denom {
|
||||
Denom::Minor => network_denom,
|
||||
Denom::Major => network_denom[1..].to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl TryFrom<Coin> for CosmosCoin {
|
||||
type Error = BackendError;
|
||||
|
||||
fn try_from(coin: Coin) -> Result<CosmosCoin, BackendError> {
|
||||
match Decimal::from_str(&coin.amount) {
|
||||
Ok(d) => Ok(CosmosCoin {
|
||||
amount: d,
|
||||
denom: CosmosDenom::from_str(&coin.denom.to_string())?,
|
||||
pub fn into_cosmos_coin(self, network_denom: &CosmosDenom) -> Result<CosmosCoin, BackendError> {
|
||||
match Decimal::from_str(&self.amount) {
|
||||
Ok(amount) => Ok(CosmosCoin {
|
||||
amount,
|
||||
denom: CosmosDenom::from_str(&self.denom_as_string(network_denom)?)?,
|
||||
}),
|
||||
Err(e) => Err(e.into()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_cosmwasm_coin(
|
||||
self,
|
||||
network_denom: &CosmosDenom,
|
||||
) -> Result<CosmWasmCoin, BackendError> {
|
||||
Ok(CosmWasmCoin {
|
||||
denom: self.denom_as_string(network_denom)?,
|
||||
amount: Uint128::try_from(self.amount.as_str())?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<CosmosCoin> for Coin {
|
||||
@@ -221,13 +218,14 @@ impl From<CosmWasmCoin> for Coin {
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use crate::coin::{Coin, Denom};
|
||||
use super::*;
|
||||
use crate::error::BackendError;
|
||||
use cosmrs::Coin as CosmosCoin;
|
||||
use cosmrs::Decimal;
|
||||
use cosmrs::Denom as CosmosDenom;
|
||||
use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
use serde_json::json;
|
||||
use std::convert::{TryFrom, TryInto};
|
||||
use std::convert::TryFrom;
|
||||
use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
@@ -252,6 +250,19 @@ mod test {
|
||||
assert_eq!(major_coin, test_major_coin);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn denom_from_str() {
|
||||
assert_eq!(Denom::from_str("unym").unwrap(), Denom::Minor);
|
||||
assert_eq!(Denom::from_str("nym").unwrap(), Denom::Major);
|
||||
assert_eq!(Denom::from_str("minor").unwrap(), Denom::Minor);
|
||||
assert_eq!(Denom::from_str("major").unwrap(), Denom::Major);
|
||||
|
||||
assert!(matches!(
|
||||
Denom::from_str("foo").unwrap_err(),
|
||||
BackendError::InvalidDenom { .. },
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn denom_conversions() {
|
||||
let minor = Coin::minor("1");
|
||||
@@ -263,6 +274,66 @@ mod test {
|
||||
assert_eq!(minor, Coin::minor("1"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn network_denom_is_assumed_to_be_in_minor_denom() {
|
||||
let network_denom = CosmosDenom::from_str("nym").unwrap();
|
||||
assert!(matches!(
|
||||
Coin::minor("42")
|
||||
.denom_as_string(&network_denom)
|
||||
.unwrap_err(),
|
||||
BackendError::InvalidNetworkDenom { .. }
|
||||
));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn local_denom_to_interpreted_using_network_denom() {
|
||||
let network_denom = CosmosDenom::from_str("unym").unwrap();
|
||||
assert_eq!(
|
||||
Coin::minor("42").denom_as_string(&network_denom).unwrap(),
|
||||
"unym",
|
||||
);
|
||||
assert_eq!(
|
||||
Coin::major("42").denom_as_string(&network_denom).unwrap(),
|
||||
"nym",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coin_to_coin_minor() {
|
||||
let network_denom = CosmosDenom::from_str("unym").unwrap();
|
||||
let coin = Coin::minor("42");
|
||||
|
||||
let cosmoswasm_coin = coin.clone().into_cosmwasm_coin(&network_denom).unwrap();
|
||||
assert_eq!(cosmoswasm_coin, CosmWasmCoin::new(42, "unym"),);
|
||||
|
||||
let cosmos_coin = coin.into_cosmos_coin(&network_denom).unwrap();
|
||||
assert_eq!(
|
||||
cosmos_coin,
|
||||
CosmosCoin {
|
||||
denom: CosmosDenom::from_str("unym").unwrap(),
|
||||
amount: Decimal::from_str("42").unwrap(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coin_to_coin_major() {
|
||||
let network_denom = CosmosDenom::from_str("unym").unwrap();
|
||||
let coin = Coin::major("52");
|
||||
|
||||
let cosmoswasm_coin = coin.clone().into_cosmwasm_coin(&network_denom).unwrap();
|
||||
assert_eq!(cosmoswasm_coin, CosmWasmCoin::new(52, "nym"),);
|
||||
|
||||
let cosmos_coin = coin.into_cosmos_coin(&network_denom).unwrap();
|
||||
assert_eq!(
|
||||
cosmos_coin,
|
||||
CosmosCoin {
|
||||
denom: CosmosDenom::from_str("nym").unwrap(),
|
||||
amount: Decimal::from_str("52").unwrap(),
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
fn amounts() -> Vec<&'static str> {
|
||||
vec![
|
||||
"1",
|
||||
@@ -288,23 +359,24 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn coin_to_cosmoswasm() {
|
||||
let network_denom = CosmosDenom::from_str("unym").unwrap();
|
||||
for amount in amounts() {
|
||||
let coin: Coin = Coin::minor(amount).into();
|
||||
let cosmoswasm_coin: CosmWasmCoin = coin.try_into().unwrap();
|
||||
let coin = Coin::minor(amount);
|
||||
let cosmoswasm_coin: CosmWasmCoin = coin.into_cosmwasm_coin(&network_denom).unwrap();
|
||||
assert_eq!(
|
||||
cosmoswasm_coin,
|
||||
CosmWasmCoin::new(amount.parse::<u128>().unwrap(), Denom::Minor.to_string())
|
||||
CosmWasmCoin::new(amount.parse::<u128>().unwrap(), "unym")
|
||||
);
|
||||
assert_eq!(
|
||||
Coin::try_from(cosmoswasm_coin).unwrap(),
|
||||
Coin::minor(amount)
|
||||
);
|
||||
|
||||
let coin: Coin = Coin::major(amount).into();
|
||||
let cosmoswasm_coin: CosmWasmCoin = coin.try_into().unwrap();
|
||||
let coin = Coin::major(amount);
|
||||
let cosmoswasm_coin: CosmWasmCoin = coin.into_cosmwasm_coin(&network_denom).unwrap();
|
||||
assert_eq!(
|
||||
cosmoswasm_coin,
|
||||
CosmWasmCoin::new(amount.parse::<u128>().unwrap(), Denom::Major.to_string())
|
||||
CosmWasmCoin::new(amount.parse::<u128>().unwrap(), "nym")
|
||||
);
|
||||
assert_eq!(
|
||||
Coin::try_from(cosmoswasm_coin).unwrap(),
|
||||
@@ -315,25 +387,26 @@ mod test {
|
||||
|
||||
#[test]
|
||||
fn coin_to_cosmos() {
|
||||
let network_denom = CosmosDenom::from_str("unym").unwrap();
|
||||
for amount in amounts() {
|
||||
let coin: Coin = Coin::minor(amount).into();
|
||||
let cosmos_coin: CosmosCoin = coin.try_into().unwrap();
|
||||
let coin = Coin::minor(amount);
|
||||
let cosmos_coin: CosmosCoin = coin.into_cosmos_coin(&network_denom).unwrap();
|
||||
assert_eq!(
|
||||
cosmos_coin,
|
||||
CosmosCoin {
|
||||
amount: Decimal::from_str(amount).unwrap(),
|
||||
denom: CosmosDenom::from_str(&Denom::Minor.to_string()).unwrap()
|
||||
denom: CosmosDenom::from_str("unym").unwrap()
|
||||
}
|
||||
);
|
||||
assert_eq!(Coin::try_from(cosmos_coin).unwrap(), Coin::minor(amount));
|
||||
|
||||
let coin: Coin = Coin::major(amount).into();
|
||||
let cosmos_coin: CosmosCoin = coin.try_into().unwrap();
|
||||
let coin = Coin::major(amount);
|
||||
let cosmos_coin: CosmosCoin = coin.into_cosmos_coin(&network_denom).unwrap();
|
||||
assert_eq!(
|
||||
cosmos_coin,
|
||||
CosmosCoin {
|
||||
amount: Decimal::from_str(amount).unwrap(),
|
||||
denom: CosmosDenom::from_str(&Denom::Major.to_string()).unwrap()
|
||||
denom: CosmosDenom::from_str("nym").unwrap()
|
||||
}
|
||||
);
|
||||
assert_eq!(Coin::try_from(cosmos_coin).unwrap(), Coin::major(amount));
|
||||
@@ -355,4 +428,23 @@ mod test {
|
||||
assert_eq!(Coin::minor("1") - Coin::major("1"), Coin::minor("-999999"));
|
||||
assert_eq!(Coin::major("1") - Coin::minor("1"), Coin::major("0.999999"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn coin_from_gas_price() {
|
||||
assert_eq!(
|
||||
Coin::try_from(GasPrice::from_str("42unym").unwrap()).unwrap(),
|
||||
Coin {
|
||||
amount: "42".to_string(),
|
||||
denom: Denom::Minor,
|
||||
}
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
Coin::try_from(GasPrice::from_str("42nym").unwrap()).unwrap(),
|
||||
Coin {
|
||||
amount: "42".to_string(),
|
||||
denom: Denom::Major,
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,6 +61,8 @@ pub enum BackendError {
|
||||
NoBalance(String),
|
||||
#[error("{0} is not a valid denomination string")]
|
||||
InvalidDenom(String),
|
||||
#[error("{0} is not a valid network denomination string")]
|
||||
InvalidNetworkDenom(String),
|
||||
#[error("The provided network is not supported (yet)")]
|
||||
NetworkNotSupported(config::defaults::all::Network),
|
||||
#[error("Could not access the local data storage directory")]
|
||||
|
||||
@@ -4,7 +4,6 @@ use crate::nymd_client;
|
||||
use crate::state::State;
|
||||
use crate::{Gateway, MixNode};
|
||||
use mixnet_contract_common::{GatewayBond, MixNodeBond};
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
@@ -15,8 +14,10 @@ pub async fn bond_gateway(
|
||||
owner_signature: String,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
let denom = state.read().await.current_network().denom();
|
||||
let pledge = pledge.into_cosmwasm_coin(&denom)?;
|
||||
nymd_client!(state)
|
||||
.bond_gateway(gateway, owner_signature, pledge.try_into()?)
|
||||
.bond_gateway(gateway, owner_signature, pledge)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -44,8 +45,10 @@ pub async fn bond_mixnode(
|
||||
pledge: Coin,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
let denom = state.read().await.current_network().denom();
|
||||
let pledge = pledge.into_cosmwasm_coin(&denom)?;
|
||||
nymd_client!(state)
|
||||
.bond_mixnode(mixnode, owner_signature, pledge.try_into()?)
|
||||
.bond_mixnode(mixnode, owner_signature, pledge)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ use crate::state::State;
|
||||
use crate::utils::DelegationResult;
|
||||
use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
use mixnet_contract_common::PagedDelegatorDelegationsResponse;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
|
||||
@@ -15,7 +14,8 @@ pub async fn delegate_to_mixnode(
|
||||
amount: Coin,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<DelegationResult, BackendError> {
|
||||
let delegation: CosmWasmCoin = amount.try_into()?;
|
||||
let denom = state.read().await.current_network().denom();
|
||||
let delegation: CosmWasmCoin = amount.into_cosmwasm_coin(&denom)?;
|
||||
nymd_client!(state)
|
||||
.delegate_to_mixnode(identity, &delegation)
|
||||
.await?;
|
||||
|
||||
@@ -3,7 +3,6 @@ use crate::error::BackendError;
|
||||
use crate::nymd_client;
|
||||
use crate::state::State;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::convert::TryInto;
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
use tendermint_rpc::endpoint::broadcast::tx_commit::Response;
|
||||
@@ -50,7 +49,8 @@ pub async fn send(
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<TauriTxResult, BackendError> {
|
||||
let address = AccountId::from_str(address)?;
|
||||
let cosmos_amount: CosmosCoin = amount.clone().try_into()?;
|
||||
let network_denom = state.read().await.current_network().denom();
|
||||
let cosmos_amount: CosmosCoin = amount.clone().into_cosmos_coin(&network_denom)?;
|
||||
let result = nymd_client!(state)
|
||||
.send(&address, vec![cosmos_amount], memo)
|
||||
.await?;
|
||||
|
||||
@@ -4,7 +4,6 @@ use crate::nymd_client;
|
||||
use crate::state::State;
|
||||
use crate::{Gateway, MixNode};
|
||||
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use validator_client::nymd::VestingSigningClient;
|
||||
@@ -16,8 +15,10 @@ pub async fn vesting_bond_gateway(
|
||||
owner_signature: String,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
let denom = state.read().await.current_network().denom();
|
||||
let pledge = pledge.into_cosmwasm_coin(&denom)?;
|
||||
nymd_client!(state)
|
||||
.vesting_bond_gateway(gateway, &owner_signature, pledge.try_into()?)
|
||||
.vesting_bond_gateway(gateway, &owner_signature, pledge)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -45,8 +46,10 @@ pub async fn vesting_bond_mixnode(
|
||||
pledge: Coin,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
let denom = state.read().await.current_network().denom();
|
||||
let pledge = pledge.into_cosmwasm_coin(&denom)?;
|
||||
nymd_client!(state)
|
||||
.vesting_bond_mixnode(mixnode, &owner_signature, pledge.try_into()?)
|
||||
.vesting_bond_mixnode(mixnode, &owner_signature, pledge)
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -56,8 +59,8 @@ pub async fn withdraw_vested_coins(
|
||||
amount: Coin,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<(), BackendError> {
|
||||
nymd_client!(state)
|
||||
.withdraw_vested_coins(amount.try_into()?)
|
||||
.await?;
|
||||
let denom = state.read().await.current_network().denom();
|
||||
let amount = amount.into_cosmwasm_coin(&denom)?;
|
||||
nymd_client!(state).withdraw_vested_coins(amount).await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -3,8 +3,6 @@ use crate::error::BackendError;
|
||||
use crate::nymd_client;
|
||||
use crate::state::State;
|
||||
use crate::utils::DelegationResult;
|
||||
use cosmwasm_std::Coin as CosmWasmCoin;
|
||||
use std::convert::TryInto;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::RwLock;
|
||||
use validator_client::nymd::VestingSigningClient;
|
||||
@@ -15,7 +13,8 @@ pub async fn vesting_delegate_to_mixnode(
|
||||
amount: Coin,
|
||||
state: tauri::State<'_, Arc<RwLock<State>>>,
|
||||
) -> Result<DelegationResult, BackendError> {
|
||||
let delegation: CosmWasmCoin = amount.try_into()?;
|
||||
let denom = state.read().await.current_network().denom();
|
||||
let delegation = amount.into_cosmwasm_coin(&denom)?;
|
||||
nymd_client!(state)
|
||||
.vesting_delegate_to_mixnode(identity, &delegation)
|
||||
.await?;
|
||||
|
||||
Reference in New Issue
Block a user