Skip to content

Commit

Permalink
feat: Continue send_transaction - add fees and amount conversion
Browse files Browse the repository at this point in the history
  • Loading branch information
MishkaRogachev committed Oct 7, 2024
1 parent 4fb21ca commit 6409958
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 33 deletions.
43 changes: 25 additions & 18 deletions src/core/provider_eth.rs
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
use regex::Regex;
use web3::{
contract::{Contract, Options},
signing::SecretKey,
types::{Address, CallRequest, Transaction, TransactionId, TransactionParameters, TransactionReceipt, H256, U256},
};

use super::{balance::{Balance, Balances}, eth_utils, provider::Provider, token::{Token, TokenList}};
use super::{balance::{Balance, Balances}, eth_utils, provider::Provider, token::{Token, TokenList}, transaction::TransactionFees};

const ETH: &str = "ETH";

Expand Down Expand Up @@ -99,25 +100,31 @@ impl<T: web3::Transport> Provider<T> {
Ok(balances)
}

pub async fn estimate_transaction_fees(&self, transaction: TransactionParameters, from: Address) -> anyhow::Result<Balance> {
let gas_limit: U256 = self.web3.eth()
.estimate_gas(
CallRequest {
from: Some(from),
to: transaction.to,
gas: None,
gas_price: None,
value: Some(transaction.value),
data: Some(transaction.data),
..Default::default()
},
None
)
.await?;
pub async fn estimate_transaction_fees(&self, transaction: TransactionParameters, from: Address) -> anyhow::Result<TransactionFees> {
let gas_limit = match self.web3.eth().estimate_gas(
CallRequest {
from: Some(from),
to: transaction.to,
gas: None,
gas_price: None,
value: Some(transaction.value),
data: Some(transaction.data),
..Default::default()
},
None
)
.await {
Ok(gas) => gas,
Err(e) => {
// TODO: handle specific errors
return Ok(TransactionFees::NotEnoughFunds { currency: ETH.to_string() });
}
};

let gas_price: U256 = self.web3.eth().gas_price().await?;
let wei = gas_limit * gas_price;
let usd_value = eth_utils::wei_to_eth(wei) * self.get_eth_usd_rate().await?;
Ok(Balance::new(ETH, self.chain, eth_utils::wei_to_eth(wei), usd_value))

Ok(TransactionFees::Estimated { currency: ETH.to_string(), amount: eth_utils::wei_to_eth(wei) })
}

pub async fn send_transaction(&self, transaction: TransactionParameters, secret_key: &SecretKey) -> anyhow::Result<H256> {
Expand Down
5 changes: 5 additions & 0 deletions src/core/transaction.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,8 @@ pub struct TransactionResult {
pub chain: EthChain,
pub successed: bool,
}

pub enum TransactionFees {
Estimated { currency: String, amount: f64 },
NotEnoughFunds { currency: String },
}
7 changes: 6 additions & 1 deletion src/service/crypto_balances.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
use crate::core::balance::{Balance, Balances};
use crate::core::{balance::{Balance, Balances}, eth_chain::EthChain};
use super::crypto::Crypto;

const BALANCES_FETCH_PROVIDER_DELAY: std::time::Duration = std::time::Duration::from_millis(100);

impl Crypto {
pub async fn get_eth_usd_rate(&self, chain: EthChain) -> anyhow::Result<f64> {
let provider = self.providers.get(&chain).ok_or_else(|| anyhow::anyhow!("No provider for chain {}", chain))?;
provider.get_eth_usd_rate().await
}

pub async fn get_balances(&self, account: web3::types::Address) -> Option<Balances> {
let balances = self.account_balances.read().await;
if let Some(balance) = balances.get(&account) {
Expand Down
4 changes: 2 additions & 2 deletions src/service/crypto_transactions.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@

use web3::{signing::SecretKey, types::{TransactionParameters, U64}};

use crate::core::{balance::Balance, eth_utils, transaction::{TransactionRequest, TransactionResult}};
use crate::core::{eth_utils, transaction::{TransactionFees, TransactionRequest, TransactionResult}};
use super::crypto::Crypto;

const ERR_NO_TRANSACTION_FOUND: &str = "No transaction found";

impl Crypto {
pub async fn estimate_transaction_fees(&self, request: TransactionRequest) -> anyhow::Result<Balance> {
pub async fn estimate_transaction_fees(&self, request: TransactionRequest) -> anyhow::Result<TransactionFees> {
if request.currency != "ETH" { // TODO: token transactions
return Err(anyhow::anyhow!("Non-ETH transactions are not supported yet"));
}
Expand Down
37 changes: 25 additions & 12 deletions src/tui/popups/send.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ use ratatui::{
Frame
};

use crate::core::{balance::Balance, eth_chain::EthChain, eth_utils, transaction::TransactionRequest};
use crate::core::{eth_chain::EthChain, eth_utils, transaction::{TransactionFees, TransactionRequest}};
use crate::service::crypto::Crypto;
use crate::tui::{widgets::controls, app::AppScreen};

Expand All @@ -23,7 +23,7 @@ pub struct Popup {
eth_usd_rate: Option<f64>,
amount_value: f64,
alt_amount_value: Option<f64>,
fees: Option<Balance>,
fees: Option<TransactionFees>,

chain_button: controls::MenuButton<EthChain>,
to: controls::Input,
Expand Down Expand Up @@ -94,7 +94,7 @@ impl Popup {
})
}

fn invalidate(&mut self) {
fn invalidate_amount_and_fees(&mut self) {
self.amount_value = 0.0;
self.alt_amount_value = None;
self.fees = None;
Expand All @@ -105,19 +105,24 @@ impl Popup {
impl AppScreen for Popup {
async fn handle_event(&mut self, event: Event) -> anyhow::Result<bool> {
if let Some(_) = controls::handle_scoped_event(&mut [&mut self.to, &mut self.amount], &event) {
self.invalidate();
self.invalidate_amount_and_fees();
return Ok(false);
}
if let Some(chain_event) = self.chain_button.handle_event(&event) {
if let controls::MenuEvent::Selected(chain) = chain_event {
self.chain = Some(chain);
self.invalidate();
self.invalidate_amount_and_fees();

// Update USD rate
let crypto = self.crypto.lock().await.clone();
self.eth_usd_rate = crypto.get_eth_usd_rate(chain).await.ok();
}
return Ok(false);
}
if let Some(_) = self.swap_button.handle_event(&event) {
if let Some(alt_amount_value) = self.alt_amount_value {
self.amount_value = alt_amount_value;
self.amount.value = format!("{}", self.amount_value).into();
}
self.alt_amount_value = None;
return Ok(false);
Expand Down Expand Up @@ -172,16 +177,15 @@ impl AppScreen for Popup {
// Calc alt amount
if amount_valid && self.alt_amount_value.is_none() {
if let Some(eth_usd_rate) = self.eth_usd_rate {
// TODO: Wai ot eth, delecgate to service
self.alt_amount_value = Some(if self.swap_button.state {
self.amount_value * eth_usd_rate
self.amount_value * eth_usd_rate
} else {
self.amount_value / eth_usd_rate
});
} else {
self.alt_amount_value = None;
}
} else {
self.alt_amount_value = None;
}

self.send_button.disabled = !is_ready;
Expand Down Expand Up @@ -323,12 +327,21 @@ impl AppScreen for Popup {
frame.render_widget(fees_label, fees_layout[1].inner(label_margin));

let fees_value = if let Some(fees) = &self.fees {
Paragraph::new(fees.to_string())
.style(Style::default().fg(Color::Yellow))
.alignment(Alignment::Left)
match fees {
TransactionFees::Estimated { currency, amount } => {
Paragraph::new(format!("{} {}", amount, currency))
.style(Style::default().fg(Color::Yellow))
.alignment(Alignment::Left)
},
TransactionFees::NotEnoughFunds { currency } => {
Paragraph::new(format!("Not enough funds ({})", currency))
.style(Style::default().fg(Color::Red))
.alignment(Alignment::Left)
}
}
} else {
Paragraph::new("---")
.style(Style::default().fg(Color::Yellow))
.style(Style::default().fg(Color::Gray))
.alignment(Alignment::Left)
};
frame.render_widget(fees_value, fees_layout[2].inner(label_margin));
Expand Down

0 comments on commit 6409958

Please sign in to comment.