Feature/nymd client fee handling (#730)
* Calculating gas fees * Ability to set custom fees * Added extra test * Removed commented code * Removed needless borrow
This commit is contained in:
committed by
GitHub
parent
4ac95ad8cf
commit
fdd34863ba
@@ -137,6 +137,10 @@ pub enum ValidatorClientError {
|
||||
code: u32,
|
||||
raw_log: String,
|
||||
},
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
#[error("The provided gas price is malformed")]
|
||||
MalformedGasPrice,
|
||||
}
|
||||
|
||||
#[cfg(feature = "nymd-client")]
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::nymd::GasPrice;
|
||||
use cosmos_sdk::tx::{Fee, Gas};
|
||||
use cosmos_sdk::Coin;
|
||||
use cosmwasm_std::Uint128;
|
||||
|
||||
#[derive(Debug, Copy, Clone, Ord, PartialOrd, Eq, PartialEq, Hash)]
|
||||
pub enum Operation {
|
||||
Upload,
|
||||
Init,
|
||||
Migrate,
|
||||
ChangeAdmin,
|
||||
Send,
|
||||
|
||||
BondMixnode,
|
||||
}
|
||||
|
||||
pub(crate) fn calculate_fee(gas_price: &GasPrice, gas_limit: Gas) -> Coin {
|
||||
let limit_uint128 = Uint128::from(gas_limit.value());
|
||||
let amount = gas_price.amount * limit_uint128;
|
||||
assert!(amount.u128() <= u64::MAX as u128);
|
||||
Coin {
|
||||
denom: gas_price.denom.clone(),
|
||||
amount: (amount.u128() as u64).into(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Operation {
|
||||
pub(crate) fn default_gas_limit(&self) -> Gas {
|
||||
match self {
|
||||
Operation::Upload => 2_500_000u64.into(),
|
||||
Operation::Init => 500_000u64.into(),
|
||||
Operation::Migrate => 200_000u64.into(),
|
||||
Operation::ChangeAdmin => 80_000u64.into(),
|
||||
Operation::Send => 80_000u64.into(),
|
||||
|
||||
Operation::BondMixnode => 175_000u64.into(),
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn determine_fee(&self, gas_price: &GasPrice, gas_limit: Option<Gas>) -> Fee {
|
||||
// we need to know 2 of the following 3 parameters (the third one is being implicit) in order to construct Fee:
|
||||
// (source: https://docs.cosmos.network/v0.42/basics/gas-fees.html)
|
||||
// - gas price
|
||||
// - gas limit
|
||||
// - fees
|
||||
let gas_limit = gas_limit.unwrap_or_else(|| self.default_gas_limit());
|
||||
let fee = calculate_fee(gas_price, gas_limit);
|
||||
Fee::from_amount_and_gas(fee, gas_limit)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn calculating_fee() {
|
||||
let expected = Coin {
|
||||
denom: "upunk".parse().unwrap(),
|
||||
amount: 1000u64.into(),
|
||||
};
|
||||
let gas_price = "1upunk".parse().unwrap();
|
||||
let gas_limit = 1000u64.into();
|
||||
|
||||
assert_eq!(expected, calculate_fee(&gas_price, gas_limit));
|
||||
|
||||
let expected = Coin {
|
||||
denom: "upunk".parse().unwrap(),
|
||||
amount: 50u64.into(),
|
||||
};
|
||||
let gas_price = "0.05upunk".parse().unwrap();
|
||||
let gas_limit = 1000u64.into();
|
||||
|
||||
assert_eq!(expected, calculate_fee(&gas_price, gas_limit));
|
||||
|
||||
let expected = Coin {
|
||||
denom: "upunk".parse().unwrap(),
|
||||
amount: 100000u64.into(),
|
||||
};
|
||||
let gas_price = "100upunk".parse().unwrap();
|
||||
let gas_limit = 1000u64.into();
|
||||
|
||||
assert_eq!(expected, calculate_fee(&gas_price, gas_limit))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
// Copyright 2021 - Nym Technologies SA <contact@nymtech.net>
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
use crate::ValidatorClientError;
|
||||
use config::defaults;
|
||||
use cosmos_sdk::Denom;
|
||||
use cosmwasm_std::Decimal;
|
||||
use std::str::FromStr;
|
||||
|
||||
/// A gas price, i.e. the price of a single unit of gas. This is typically a fraction of
|
||||
/// the smallest fee token unit, such as 0.012utoken.
|
||||
#[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct GasPrice {
|
||||
// I really hate the combination of cosmwasm Decimal with cosmos-sdk Denom,
|
||||
// but cosmos-sdk's Decimal is too basic for our needs
|
||||
pub amount: Decimal,
|
||||
|
||||
pub denom: Denom,
|
||||
}
|
||||
|
||||
impl FromStr for GasPrice {
|
||||
type Err = ValidatorClientError;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let possible_amount = s
|
||||
.chars()
|
||||
.take_while(|c| c.is_ascii_digit() || c == &'.')
|
||||
.collect::<String>();
|
||||
let amount_len = possible_amount.len();
|
||||
let amount = possible_amount
|
||||
.parse()
|
||||
.map_err(|_| ValidatorClientError::MalformedGasPrice)?;
|
||||
let possible_denom = s.chars().skip(amount_len).collect::<String>();
|
||||
let denom = possible_denom
|
||||
.parse()
|
||||
.map_err(|_| ValidatorClientError::MalformedGasPrice)?;
|
||||
|
||||
Ok(GasPrice { amount, denom })
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for GasPrice {
|
||||
fn default() -> Self {
|
||||
format!("{}{}", defaults::GAS_PRICE_AMOUNT, defaults::DENOM)
|
||||
.parse()
|
||||
.unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn gas_price_parsing() {
|
||||
assert_eq!(
|
||||
GasPrice {
|
||||
amount: "0.025".parse().unwrap(),
|
||||
denom: "upunk".parse().unwrap()
|
||||
},
|
||||
"0.025upunk".parse().unwrap()
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
GasPrice {
|
||||
amount: "123".parse().unwrap(),
|
||||
denom: "upunk".parse().unwrap()
|
||||
},
|
||||
"123upunk".parse().unwrap()
|
||||
);
|
||||
|
||||
assert!(".25upunk".parse::<GasPrice>().is_err());
|
||||
assert!("0.025 upunk".parse::<GasPrice>().is_err());
|
||||
assert!("0.025UPUNK".parse::<GasPrice>().is_err());
|
||||
}
|
||||
}
|
||||
@@ -6,23 +6,30 @@ use crate::nymd::cosmwasm_client::client::CosmWasmClient;
|
||||
use crate::nymd::cosmwasm_client::signing_client;
|
||||
use crate::nymd::cosmwasm_client::signing_client::SigningCosmWasmClient;
|
||||
use crate::nymd::cosmwasm_client::types::ExecuteResult;
|
||||
use crate::nymd::fee_helpers::Operation;
|
||||
pub use crate::nymd::gas_price::GasPrice;
|
||||
use crate::nymd::wallet::DirectSecp256k1HdWallet;
|
||||
use crate::ValidatorClientError;
|
||||
use cosmos_sdk::rpc::{Error as TendermintRpcError, HttpClient, HttpClientUrl};
|
||||
use cosmos_sdk::tx::Fee;
|
||||
use cosmos_sdk::tx::Gas;
|
||||
use cosmos_sdk::Coin as CosmosCoin;
|
||||
use cosmos_sdk::{AccountId, Denom};
|
||||
use cosmwasm_std::Coin;
|
||||
use mixnet_contract::LayerDistribution;
|
||||
use std::collections::HashMap;
|
||||
use std::convert::TryInto;
|
||||
|
||||
pub mod cosmwasm_client;
|
||||
pub(crate) mod fee_helpers;
|
||||
pub mod gas_price;
|
||||
pub mod wallet;
|
||||
|
||||
pub struct NymdClient<C> {
|
||||
client: C,
|
||||
contract_address: AccountId,
|
||||
client_address: Option<Vec<AccountId>>,
|
||||
gas_price: GasPrice,
|
||||
custom_gas_limits: HashMap<Operation, Gas>,
|
||||
}
|
||||
|
||||
impl NymdClient<HttpClient> {
|
||||
@@ -37,6 +44,8 @@ impl NymdClient<HttpClient> {
|
||||
client: HttpClient::new(endpoint)?,
|
||||
contract_address,
|
||||
client_address: None,
|
||||
gas_price: Default::default(),
|
||||
custom_gas_limits: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -61,6 +70,8 @@ impl NymdClient<signing_client::Client> {
|
||||
client: signing_client::Client::connect_with_signer(endpoint, signer)?,
|
||||
contract_address,
|
||||
client_address: Some(client_address),
|
||||
gas_price: Default::default(),
|
||||
custom_gas_limits: Default::default(),
|
||||
})
|
||||
}
|
||||
|
||||
@@ -83,11 +94,21 @@ impl NymdClient<signing_client::Client> {
|
||||
client: signing_client::Client::connect_with_signer(endpoint, wallet)?,
|
||||
contract_address,
|
||||
client_address: Some(client_address),
|
||||
gas_price: Default::default(),
|
||||
custom_gas_limits: Default::default(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<C> NymdClient<C> {
|
||||
pub fn set_gas_price(&mut self, gas_price: GasPrice) {
|
||||
self.gas_price = gas_price
|
||||
}
|
||||
|
||||
pub fn set_custom_gas_limit(&mut self, operation: Operation, limit: Gas) {
|
||||
self.custom_gas_limits.insert(operation, limit);
|
||||
}
|
||||
|
||||
pub fn address(&self) -> &AccountId
|
||||
where
|
||||
C: SigningCosmWasmClient,
|
||||
@@ -97,7 +118,7 @@ impl<C> NymdClient<C> {
|
||||
}
|
||||
|
||||
// now the question is as follows: will denom always be in the format of `u{prefix}`?
|
||||
fn denom(&self) -> Denom {
|
||||
pub fn denom(&self) -> Denom {
|
||||
format!("u{}", self.contract_address.prefix())
|
||||
.parse()
|
||||
.unwrap()
|
||||
@@ -114,16 +135,12 @@ impl<C> NymdClient<C> {
|
||||
where
|
||||
C: SigningCosmWasmClient + Sync,
|
||||
{
|
||||
// TODO: will need to create some nice fee table, for now, for test sake, just use hardcoded value
|
||||
let fee_amount = CosmosCoin {
|
||||
amount: 100_0000u64.into(),
|
||||
denom: self.denom(),
|
||||
};
|
||||
let gas = 100_0000;
|
||||
let fee = Fee::from_amount_and_gas(fee_amount, gas);
|
||||
let fee = Operation::BondMixnode.determine_fee(
|
||||
&self.gas_price,
|
||||
self.custom_gas_limits.get(&Operation::BondMixnode).cloned(),
|
||||
);
|
||||
|
||||
let req = ExecuteMsg::BondMixnode { mix_node: mixnode };
|
||||
|
||||
self.client
|
||||
.execute(
|
||||
self.address(),
|
||||
|
||||
@@ -11,6 +11,9 @@ pub const NETWORK_MONITOR_ADDRESS: &str = "punk1v9qauwdq5terag6uvfsdytcs2d0sdmfd
|
||||
pub const COSMOS_DERIVATION_PATH: &str = "m/44'/118'/0'/0/0";
|
||||
pub const BECH32_PREFIX: &str = "punk";
|
||||
pub const DENOM: &str = "upunk";
|
||||
// as set by validators in their configs
|
||||
// (note that the 'amount' postfix is relevant here as the full gas price also includes denom)
|
||||
pub const GAS_PRICE_AMOUNT: f64 = 0.025;
|
||||
|
||||
pub const DEFAULT_MIX_LISTENING_PORT: u16 = 1789;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user