feat: Improve error handling and simplify async operations

This commit is contained in:
Sameh Abouelsaad 2025-05-11 22:37:02 +03:00
parent ae687f17f5
commit 27e5a6df3e
2 changed files with 76 additions and 156 deletions

View File

@ -73,7 +73,7 @@ pub async fn send_eth(
let tx = TransactionRequest::new() let tx = TransactionRequest::new()
.to(to) .to(to)
.value(amount) .value(amount)
.gas(21000); .gas(gas);
// Send the transaction // Send the transaction
let pending_tx = client.send_transaction(tx, None) let pending_tx = client.send_transaction(tx, None)
@ -115,7 +115,7 @@ pub async fn send_eth_with_provider(
let tx = TransactionRequest::new() let tx = TransactionRequest::new()
.to(to) .to(to)
.value(amount) .value(amount)
.gas(21000); .gas(gas);
// Send the transaction // Send the transaction
let pending_tx = client.send_transaction(tx, None) let pending_tx = client.send_transaction(tx, None)

View File

@ -3,8 +3,6 @@
use rhai::{Engine, Dynamic, EvalAltResult}; use rhai::{Engine, Dynamic, EvalAltResult};
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64}; use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
use std::fs; use std::fs;
use std::path::PathBuf;
use std::collections::HashMap;
use std::sync::Mutex; use std::sync::Mutex;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use tokio::runtime::Runtime; use tokio::runtime::Runtime;
@ -12,7 +10,7 @@ use ethers::types::{Address, U256};
use std::str::FromStr; use std::str::FromStr;
use crate::hero_vault::{keypair, symmetric, ethereum, kvs}; use crate::hero_vault::{keypair, symmetric, ethereum, kvs};
use crate::hero_vault::kvs::{KVStore, DefaultStore}; use crate::hero_vault::kvs::DefaultStore;
use crate::hero_vault::ethereum::prepare_function_arguments; use crate::hero_vault::ethereum::prepare_function_arguments;
// Global Tokio runtime for blocking async operations // Global Tokio runtime for blocking async operations
@ -20,6 +18,16 @@ static RUNTIME: Lazy<Mutex<Runtime>> = Lazy::new(|| {
Mutex::new(Runtime::new().expect("Failed to create Tokio runtime")) Mutex::new(Runtime::new().expect("Failed to create Tokio runtime"))
}); });
// Helper function to run async operations and handle errors consistently
fn run_async<F, T, E>(future: F) -> Result<T, String>
where
F: std::future::Future<Output = Result<T, E>>,
E: std::fmt::Display,
{
let rt = RUNTIME.lock().map_err(|e| format!("Failed to acquire runtime lock: {}", e))?;
rt.block_on(async { future.await.map_err(|e| e.to_string()) })
}
// Get a platform-specific DefaultStore implementation for Rhai bindings // Get a platform-specific DefaultStore implementation for Rhai bindings
#[cfg(not(target_arch = "wasm32"))] #[cfg(not(target_arch = "wasm32"))]
fn get_key_store() -> DefaultStore { fn get_key_store() -> DefaultStore {
@ -42,16 +50,16 @@ fn get_key_store() -> DefaultStore {
fn get_key_store() -> DefaultStore { fn get_key_store() -> DefaultStore {
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
static STORE: Lazy<DefaultStore> = Lazy::new(|| { static STORE: Lazy<DefaultStore> = Lazy::new(|| {
// In WASM we have to run this on the main thread the first time match run_async(async {
// This works because the cache in IndexedDbStore handles subsequent synchronous operations kvs::open_default_store("rhai-vault", None).await
let rt = RUNTIME.lock().unwrap(); .or_else(|_| kvs::create_default_store("rhai-vault", false, None).await)
rt.block_on(async { }) {
match kvs::open_default_store("rhai-vault", None).await { Ok(store) => store,
Ok(store) => store, Err(e) => {
Err(_) => kvs::create_default_store("rhai-vault", false, None).await log::error!("Failed to create key store: {}", e);
.expect("Failed to create store") panic!("Could not initialize key store: {}", e);
} }
}) }
}); });
STORE.clone() STORE.clone()
@ -76,26 +84,13 @@ fn auto_save_key_space(password: &str) -> bool {
// Export the current key space to a JSON string // Export the current key space to a JSON string
fn encrypt_key_space(password: &str) -> String { fn encrypt_key_space(password: &str) -> String {
match keypair::get_current_space() { match keypair::get_current_space()
Ok(space) => { .and_then(|space| symmetric::encrypt_key_space(&space, password))
match symmetric::encrypt_key_space(&space, password) { .and_then(|encrypted_space| symmetric::serialize_encrypted_space(&encrypted_space))
Ok(encrypted_space) => { {
match symmetric::serialize_encrypted_space(&encrypted_space) { Ok(json) => json,
Ok(json) => json,
Err(e) => {
log::error!("Error serializing encrypted space: {}", e);
String::new()
}
}
},
Err(e) => {
log::error!("Error encrypting key space: {}", e);
String::new()
}
}
},
Err(e) => { Err(e) => {
log::error!("Error getting current space: {}", e); log::error!("Error encrypting key space: {}", e);
String::new() String::new()
} }
} }
@ -103,26 +98,13 @@ fn encrypt_key_space(password: &str) -> String {
// Import a key space from a JSON string // Import a key space from a JSON string
fn decrypt_key_space(encrypted: &str, password: &str) -> bool { fn decrypt_key_space(encrypted: &str, password: &str) -> bool {
match symmetric::deserialize_encrypted_space(encrypted) { match symmetric::deserialize_encrypted_space(encrypted)
Ok(encrypted_space) => { .and_then(|encrypted_space| symmetric::decrypt_key_space(&encrypted_space, password))
match symmetric::decrypt_key_space(&encrypted_space, password) { .and_then(|space| keypair::set_current_space(space))
Ok(space) => { {
match keypair::set_current_space(space) { Ok(_) => true,
Ok(_) => true,
Err(e) => {
log::error!("Error setting current space: {}", e);
false
}
}
},
Err(e) => {
log::error!("Error decrypting key space: {}", e);
false
}
}
},
Err(e) => { Err(e) => {
log::error!("Error parsing encrypted space: {}", e); log::error!("Error decrypting key space: {}", e);
false false
} }
} }
@ -176,18 +158,14 @@ fn sign(message: &str) -> String {
fn verify(message: &str, signature: &str) -> bool { fn verify(message: &str, signature: &str) -> bool {
let message_bytes = message.as_bytes(); let message_bytes = message.as_bytes();
match BASE64.decode(signature) { match BASE64.decode(signature)
Ok(signature_bytes) => { .map_err(|e| e.to_string())
match keypair::keypair_verify(message_bytes, &signature_bytes) { .and_then(|sig_bytes| keypair::keypair_verify(message_bytes, &sig_bytes)
Ok(is_valid) => is_valid, .map_err(|e| e.to_string()))
Err(e) => { {
log::error!("Error verifying signature: {}", e); Ok(is_valid) => is_valid,
false
}
}
},
Err(e) => { Err(e) => {
log::error!("Error decoding signature: {}", e); log::error!("Error verifying signature: {}", e);
false false
} }
} }
@ -195,58 +173,44 @@ fn verify(message: &str, signature: &str) -> bool {
// Symmetric encryption // Symmetric encryption
fn generate_key() -> String { fn generate_key() -> String {
let key = symmetric::generate_symmetric_key(); BASE64.encode(symmetric::generate_symmetric_key())
BASE64.encode(key)
} }
fn encrypt(key: &str, message: &str) -> String { fn encrypt(key: &str, message: &str) -> String {
match BASE64.decode(key) { match BASE64.decode(key)
Ok(key_bytes) => { .map_err(|e| format!("Error decoding key: {}", e))
let message_bytes = message.as_bytes(); .and_then(|key_bytes| {
match symmetric::encrypt_symmetric(&key_bytes, message_bytes) { symmetric::encrypt_symmetric(&key_bytes, message.as_bytes())
Ok(ciphertext) => BASE64.encode(ciphertext), .map_err(|e| format!("Error encrypting message: {}", e))
Err(e) => { })
log::error!("Error encrypting message: {}", e); {
String::new() Ok(ciphertext) => BASE64.encode(ciphertext),
}
}
},
Err(e) => { Err(e) => {
log::error!("Error decoding key: {}", e); log::error!("{}", e);
String::new() String::new()
} }
} }
} }
fn decrypt(key: &str, ciphertext: &str) -> String { fn decrypt(key: &str, ciphertext: &str) -> String {
match BASE64.decode(key) { match BASE64.decode(key)
Ok(key_bytes) => { .map_err(|e| format!("Error decoding key: {}", e))
match BASE64.decode(ciphertext) { .and_then(|key_bytes| {
Ok(ciphertext_bytes) => { BASE64.decode(ciphertext)
match symmetric::decrypt_symmetric(&key_bytes, &ciphertext_bytes) { .map_err(|e| format!("Error decoding ciphertext: {}", e))
Ok(plaintext) => { .and_then(|cipher_bytes| {
match String::from_utf8(plaintext) { symmetric::decrypt_symmetric(&key_bytes, &cipher_bytes)
Ok(text) => text, .map_err(|e| format!("Error decrypting ciphertext: {}", e))
Err(e) => { })
log::error!("Error converting plaintext to string: {}", e); })
String::new() .and_then(|plaintext| {
} String::from_utf8(plaintext)
} .map_err(|e| format!("Error converting plaintext to string: {}", e))
}, })
Err(e) => { {
log::error!("Error decrypting ciphertext: {}", e); Ok(text) => text,
String::new()
}
}
},
Err(e) => {
log::error!("Error decoding ciphertext: {}", e);
String::new()
}
}
},
Err(e) => { Err(e) => {
log::error!("Error decoding key: {}", e); log::error!("{}", e);
String::new() String::new()
} }
} }
@ -374,15 +338,6 @@ fn get_network_explorer_url(network_name: &str) -> String {
// Get the balance of an address on a specific network // Get the balance of an address on a specific network
fn get_balance(network_name: &str, address: &str) -> String { fn get_balance(network_name: &str, address: &str) -> String {
// Get the runtime
let rt = match RUNTIME.lock() {
Ok(rt) => rt,
Err(e) => {
log::error!("Failed to acquire runtime lock: {}", e);
return String::new();
}
};
// Parse the address // Parse the address
let addr = match Address::from_str(address) { let addr = match Address::from_str(address) {
Ok(addr) => addr, Ok(addr) => addr,
@ -393,9 +348,7 @@ fn get_balance(network_name: &str, address: &str) -> String {
}; };
// Execute the balance query in a blocking manner // Execute the balance query in a blocking manner
match rt.block_on(async { match run_async(ethereum::get_balance(network_name, addr)) {
ethereum::get_balance(network_name, addr).await
}) {
Ok(balance) => { Ok(balance) => {
// Format the balance with the network's token symbol // Format the balance with the network's token symbol
match ethereum::get_network_by_name(network_name) { match ethereum::get_network_by_name(network_name) {
@ -412,15 +365,6 @@ fn get_balance(network_name: &str, address: &str) -> String {
// Send ETH from one address to another // Send ETH from one address to another
fn send_eth(network_name: &str, to_address: &str, amount_str: &str) -> String { fn send_eth(network_name: &str, to_address: &str, amount_str: &str) -> String {
// Get the runtime
let rt = match RUNTIME.lock() {
Ok(rt) => rt,
Err(e) => {
log::error!("Failed to acquire runtime lock: {}", e);
return String::new();
}
};
// Parse the address // Parse the address
let to_addr = match Address::from_str(to_address) { let to_addr = match Address::from_str(to_address) {
Ok(addr) => addr, Ok(addr) => addr,
@ -449,9 +393,7 @@ fn send_eth(network_name: &str, to_address: &str, amount_str: &str) -> String {
}; };
// Execute the transaction in a blocking manner // Execute the transaction in a blocking manner
match rt.block_on(async { match run_async(ethereum::send_eth(&wallet, network_name, to_addr, amount)) {
ethereum::send_eth(&wallet, network_name, to_addr, amount).await
}) {
Ok(tx_hash) => format!("{:?}", tx_hash), Ok(tx_hash) => format!("{:?}", tx_hash),
Err(e) => { Err(e) => {
log::error!("Transaction failed: {}", e); log::error!("Transaction failed: {}", e);
@ -538,15 +480,6 @@ fn call_contract_read(contract_json: &str, function_name: &str, args: rhai::Arra
} }
}; };
// Get the runtime
let rt = match RUNTIME.lock() {
Ok(rt) => rt,
Err(e) => {
log::error!("Failed to acquire runtime lock: {}", e);
return Dynamic::UNIT;
}
};
// Create a provider // Create a provider
let provider = match ethereum::create_provider(&contract.network.name) { let provider = match ethereum::create_provider(&contract.network.name) {
Ok(p) => p, Ok(p) => p,
@ -557,9 +490,7 @@ fn call_contract_read(contract_json: &str, function_name: &str, args: rhai::Arra
}; };
// Execute the call in a blocking manner // Execute the call in a blocking manner
match rt.block_on(async { match run_async(ethereum::call_read_function(&contract, &provider, function_name, tokens)) {
ethereum::call_read_function(&contract, &provider, function_name, tokens).await
}) {
Ok(result) => ethereum::token_to_dynamic(&result), Ok(result) => ethereum::token_to_dynamic(&result),
Err(e) => { Err(e) => {
log::error!("Failed to call contract function: {}", e); log::error!("Failed to call contract function: {}", e);
@ -593,15 +524,6 @@ fn call_contract_write(contract_json: &str, function_name: &str, args: rhai::Arr
} }
}; };
// Get the runtime
let rt = match RUNTIME.lock() {
Ok(rt) => rt,
Err(e) => {
log::error!("Failed to acquire runtime lock: {}", e);
return String::new();
}
};
// Get the wallet // Get the wallet
let wallet = match ethereum::get_current_ethereum_wallet() { let wallet = match ethereum::get_current_ethereum_wallet() {
Ok(w) => w, Ok(w) => w,
@ -621,9 +543,7 @@ fn call_contract_write(contract_json: &str, function_name: &str, args: rhai::Arr
}; };
// Execute the transaction in a blocking manner // Execute the transaction in a blocking manner
match rt.block_on(async { match run_async(ethereum::call_write_function(&contract, &wallet, &provider, function_name, tokens)) {
ethereum::call_write_function(&contract, &wallet, &provider, function_name, tokens).await
}) {
Ok(tx_hash) => format!("{:?}", tx_hash), Ok(tx_hash) => format!("{:?}", tx_hash),
Err(e) => { Err(e) => {
// Log the error details for debugging // Log the error details for debugging
@ -657,20 +577,20 @@ fn get_agung_address() -> String {
get_ethereum_address() get_ethereum_address()
} }
fn create_wallet_for_network(network_name: &str) -> bool { fn create_wallet_for_network(_network_name: &str) -> bool {
create_ethereum_wallet() create_ethereum_wallet()
} }
fn get_wallet_address_for_network(network_name: &str) -> String { fn get_wallet_address_for_network(_network_name: &str) -> String {
get_ethereum_address() get_ethereum_address()
} }
fn clear_wallets_for_network(network_name: &str) -> bool { fn clear_wallets_for_network(_network_name: &str) -> bool {
ethereum::clear_ethereum_wallets(); ethereum::clear_ethereum_wallets();
true true
} }
fn create_wallet_from_private_key_for_network(private_key: &str, network_name: &str) -> bool { fn create_wallet_from_private_key_for_network(private_key: &str, _network_name: &str) -> bool {
create_ethereum_wallet_from_private_key(private_key) create_ethereum_wallet_from_private_key(private_key)
} }