Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

312 use stellar fee statistics to derive transaction fee #474

Merged
merged 4 commits into from
Jan 11, 2024
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
47 changes: 47 additions & 0 deletions Cargo.lock

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

6 changes: 0 additions & 6 deletions clients/vault/src/requests/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,6 @@ use stellar_relay_lib::sdk::{Asset, TransactionEnvelope, XdrCodec};
use tokio::sync::RwLock;
use wallet::{StellarWallet, TransactionResponse};

/// Determines how much the vault is going to pay for the Stellar transaction fees.
/// We use a fixed fee of 300 stroops for now but might want to make this dynamic in the future.
const DEFAULT_STROOP_FEE_PER_OPERATION: u32 = 300;

#[derive(Debug, Clone, PartialEq)]
struct Deadline {
parachain: u32,
Expand Down Expand Up @@ -291,7 +287,6 @@ impl Request {
self.asset.clone(),
stroop_amount,
request_id,
DEFAULT_STROOP_FEE_PER_OPERATION,
true,
)
.await,
Expand All @@ -302,7 +297,6 @@ impl Request {
self.asset.clone(),
stroop_amount,
request_id,
DEFAULT_STROOP_FEE_PER_OPERATION,
false,
)
.await,
Expand Down
3 changes: 0 additions & 3 deletions clients/vault/tests/helper/helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ pub async fn send_payment_to_address(
asset: StellarAsset,
stroop_amount: StellarStroops,
request_id: [u8; 32],
stroop_fee_per_operation: u32,
is_payment_for_redeem_request: bool,
) -> Result<TransactionResponse, Error> {
let response;
Expand All @@ -130,7 +129,6 @@ pub async fn send_payment_to_address(
asset.clone(),
stroop_amount,
request_id,
stroop_fee_per_operation,
is_payment_for_redeem_request,
)
.await;
Expand Down Expand Up @@ -175,7 +173,6 @@ pub async fn assert_issue(
asset,
stroop_amount,
issue.issue_id.0,
300,
false,
)
.await
Expand Down
4 changes: 0 additions & 4 deletions clients/vault/tests/vault_integration_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -597,7 +597,6 @@ async fn test_issue_overpayment_succeeds() {
stellar_asset,
stroop_amount.try_into().unwrap(),
issue.issue_id.0,
300,
false,
)
.await
Expand Down Expand Up @@ -685,7 +684,6 @@ async fn test_automatic_issue_execution_succeeds() {
stellar_asset,
stroop_amount,
issue.issue_id.0,
300,
false,
)
.await
Expand Down Expand Up @@ -822,7 +820,6 @@ async fn test_automatic_issue_execution_succeeds_for_other_vault() {
stellar_asset,
stroop_amount,
issue.issue_id.0,
300,
false,
)
.await;
Expand Down Expand Up @@ -974,7 +971,6 @@ async fn test_execute_open_requests_succeeds() {
asset,
stroop_amount,
redeem_ids[0].0,
300,
false
)
.await
Expand Down
1 change: 1 addition & 0 deletions clients/wallet/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ testing-utils = []
[dependencies]
async-trait = "0.1.40"
futures = "0.3.5"
cached = { version = "0.47.0", features = ["async"]}
parity-scale-codec = "3.0.0"
rand = "0.8.5"
reqwest = { version = "0.11", features = ["json"] }
Expand Down
3 changes: 3 additions & 0 deletions clients/wallet/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,9 @@ pub enum Error {

#[error("Cannot send payment to self")]
SelfPaymentError,

#[error("Failed to get fee: {0}")]
FailedToGetFee(String),
}

impl Error {
Expand Down
13 changes: 10 additions & 3 deletions clients/wallet/src/horizon/horizon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use crate::{
error::Error,
horizon::{
responses::{
interpret_response, HorizonAccountResponse, HorizonClaimableBalanceResponse,
interpret_response, FeeStats, HorizonAccountResponse, HorizonClaimableBalanceResponse,
HorizonTransactionsResponse, TransactionResponse, TransactionsResponseIter,
},
traits::HorizonClient,
Expand Down Expand Up @@ -95,7 +95,7 @@ impl HorizonClient for reqwest::Client {
) -> Result<HorizonAccountResponse, Error> {
let account_id_encoded = account_id.as_encoded_string()?;
let base_url = horizon_url(is_public_network, false);
let url = format!("{}/accounts/{}", base_url, account_id_encoded);
let url = format!("{base_url}/accounts/{account_id_encoded}");

self.get_from_url(&url).await
}
Expand All @@ -107,7 +107,14 @@ impl HorizonClient for reqwest::Client {
) -> Result<HorizonClaimableBalanceResponse, Error> {
let id_encoded = claimable_balance_id.as_encoded_string()?;
let base_url = horizon_url(is_public_network, false);
let url = format!("{}/claimable_balances/{}", base_url, id_encoded);
let url = format!("{base_url}/claimable_balances/{id_encoded}");

self.get_from_url(&url).await
}

async fn get_fee_stats(&self, is_public_network: bool) -> Result<FeeStats, Error> {
let base_url = horizon_url(is_public_network, false);
let url = format!("{base_url}/fee_stats");

self.get_from_url(&url).await
}
Expand Down
67 changes: 66 additions & 1 deletion clients/wallet/src/horizon/responses.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::{
error::Error,
horizon::{serde::*, traits::HorizonClient, Ledger},
types::{PagingToken, StatusCode},
types::{FeeAttribute, PagingToken, StatusCode},
};
use parity_scale_codec::{Decode, Encode};
use primitives::{
Expand Down Expand Up @@ -356,6 +356,71 @@ pub struct HorizonClaimableBalanceResponse {
pub claimable_balance: ClaimableBalance,
}

#[derive(Deserialize, Debug)]
pub struct FeeDistribution {
#[serde(deserialize_with = "de_string_to_u32")]
pub max: u32,
#[serde(deserialize_with = "de_string_to_u32")]
pub min: u32,
#[serde(deserialize_with = "de_string_to_u32")]
pub mode: u32,
#[serde(deserialize_with = "de_string_to_u32")]
pub p10: u32,
#[serde(deserialize_with = "de_string_to_u32")]
pub p20: u32,
#[serde(deserialize_with = "de_string_to_u32")]
pub p30: u32,
#[serde(deserialize_with = "de_string_to_u32")]
pub p40: u32,
#[serde(deserialize_with = "de_string_to_u32")]
pub p50: u32,
#[serde(deserialize_with = "de_string_to_u32")]
pub p60: u32,
#[serde(deserialize_with = "de_string_to_u32")]
pub p70: u32,
#[serde(deserialize_with = "de_string_to_u32")]
pub p80: u32,
#[serde(deserialize_with = "de_string_to_u32")]
pub p90: u32,
#[serde(deserialize_with = "de_string_to_u32")]
pub p95: u32,
#[serde(deserialize_with = "de_string_to_u32")]
pub p99: u32,
}

#[derive(Deserialize, Debug)]
pub struct FeeStats {
#[serde(deserialize_with = "de_string_to_u32")]
pub last_ledger: Ledger,
#[serde(deserialize_with = "de_string_to_u32")]
pub last_ledger_base_fee: u32,
#[serde(deserialize_with = "de_string_to_f64")]
pub ledger_capacity_usage: f64,
pub fee_charged: FeeDistribution,
pub max_fee: FeeDistribution,
}

impl FeeStats {
pub fn fee_charged_by(&self, fee_attr: FeeAttribute) -> u32 {
match fee_attr {
FeeAttribute::max => self.fee_charged.max,
FeeAttribute::min => self.fee_charged.min,
FeeAttribute::mode => self.fee_charged.mode,
FeeAttribute::p10 => self.fee_charged.p10,
FeeAttribute::p20 => self.fee_charged.p20,
FeeAttribute::p30 => self.fee_charged.p30,
FeeAttribute::p40 => self.fee_charged.p40,
FeeAttribute::p50 => self.fee_charged.p50,
FeeAttribute::p60 => self.fee_charged.p60,
FeeAttribute::p70 => self.fee_charged.p70,
FeeAttribute::p80 => self.fee_charged.p80,
FeeAttribute::p90 => self.fee_charged.p90,
FeeAttribute::p95 => self.fee_charged.p95,
FeeAttribute::p99 => self.fee_charged.p99,
}
}
}

// This represents each record for a claimable balance in the Horizon API response
#[derive(Deserialize, Encode, Decode, Default, Debug)]
pub struct ClaimableBalance {
Expand Down
8 changes: 8 additions & 0 deletions clients/wallet/src/horizon/serde.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,14 @@ where
f64::from_str(s).map_err(serde::de::Error::custom)
}

pub fn de_string_to_u32<'de, D>(de: D) -> Result<u32, D::Error>
where
D: Deserializer<'de>,
{
let s: &str = Deserialize::deserialize(de)?;
u32::from_str(s).map_err(serde::de::Error::custom)
}

pub fn de_string_to_optional_bytes<'de, D>(de: D) -> Result<Option<Vec<u8>>, D::Error>
where
D: Deserializer<'de>,
Expand Down
7 changes: 7 additions & 0 deletions clients/wallet/src/horizon/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -213,3 +213,10 @@ async fn fetch_horizon_and_process_new_transactions_success() {

assert!(!slot_env_map.read().await.is_empty());
}

#[tokio::test(flavor = "multi_thread")]
async fn horizon_get_fee() {
let horizon_client = reqwest::Client::new();
assert!(horizon_client.get_fee_stats(false).await.is_ok());
assert!(horizon_client.get_fee_stats(true).await.is_ok());
}
6 changes: 4 additions & 2 deletions clients/wallet/src/horizon/traits.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
use crate::{
error::Error,
horizon::responses::{
HorizonAccountResponse, HorizonClaimableBalanceResponse, HorizonTransactionsResponse,
TransactionResponse,
FeeStats, HorizonAccountResponse, HorizonClaimableBalanceResponse,
HorizonTransactionsResponse, TransactionResponse,
},
types::PagingToken,
};
Expand Down Expand Up @@ -37,6 +37,8 @@ pub trait HorizonClient {
is_public_network: bool,
) -> Result<HorizonClaimableBalanceResponse, Error>;

async fn get_fee_stats(&self, is_public_network: bool) -> Result<FeeStats, Error>;

async fn submit_transaction(
&self,
transaction: TransactionEnvelope,
Expand Down
3 changes: 1 addition & 2 deletions clients/wallet/src/mock.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,7 @@ impl StellarWallet {
let account_merge_op =
create_account_merge_operation(destination_address, self.public_key())?;

self.send_to_address([9u8; 32], DEFAULT_STROOP_FEE_PER_OPERATION, vec![account_merge_op])
.await
self.send_to_address([9u8; 32], vec![account_merge_op]).await
}

pub fn create_payment_envelope(
Expand Down
1 change: 0 additions & 1 deletion clients/wallet/src/resubmissions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,6 @@ mod test {
asset.clone(),
amount,
rand::random(),
DEFAULT_STROOP_FEE_PER_OPERATION,
false,
)
.await
Expand Down
Loading