Merge branch 'development_hero_vault'
* development_hero_vault: Add docs Support conatrcts call args in rhai bindings remove obsolete print feat: support interacting with smart contracts on EVM-based blockchains refactor and add peaq support feat: introduce hero_vault
This commit is contained in:
943
src/rhai/hero_vault.rs
Normal file
943
src/rhai/hero_vault.rs
Normal file
@@ -0,0 +1,943 @@
|
||||
//! Rhai bindings for SAL crypto functionality
|
||||
|
||||
use rhai::{Engine, Dynamic, EvalAltResult};
|
||||
use base64::{Engine as _, engine::general_purpose::STANDARD as BASE64};
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
use once_cell::sync::Lazy;
|
||||
use tokio::runtime::Runtime;
|
||||
use ethers::types::{Address, U256};
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::hero_vault::{keypair, symmetric, ethereum};
|
||||
use crate::hero_vault::ethereum::{prepare_function_arguments, convert_token_to_rhai};
|
||||
|
||||
// Global Tokio runtime for blocking async operations
|
||||
static RUNTIME: Lazy<Mutex<Runtime>> = Lazy::new(|| {
|
||||
Mutex::new(Runtime::new().expect("Failed to create Tokio runtime"))
|
||||
});
|
||||
|
||||
// Global provider registry
|
||||
static PROVIDERS: Lazy<Mutex<HashMap<String, ethers::providers::Provider<ethers::providers::Http>>>> = Lazy::new(|| {
|
||||
Mutex::new(HashMap::new())
|
||||
});
|
||||
|
||||
// Key space management functions
|
||||
fn load_key_space(name: &str, password: &str) -> bool {
|
||||
// Get the key spaces directory from config
|
||||
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||
let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces");
|
||||
|
||||
// Check if directory exists
|
||||
if !key_spaces_dir.exists() {
|
||||
log::error!("Key spaces directory does not exist");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Get the key space file path
|
||||
let space_path = key_spaces_dir.join(format!("{}.json", name));
|
||||
|
||||
// Check if file exists
|
||||
if !space_path.exists() {
|
||||
log::error!("Key space file not found: {}", space_path.display());
|
||||
return false;
|
||||
}
|
||||
|
||||
// Read the file
|
||||
let serialized = match fs::read_to_string(&space_path) {
|
||||
Ok(data) => data,
|
||||
Err(e) => {
|
||||
log::error!("Error reading key space file: {}", e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Deserialize the encrypted space
|
||||
let encrypted_space = match symmetric::deserialize_encrypted_space(&serialized) {
|
||||
Ok(space) => space,
|
||||
Err(e) => {
|
||||
log::error!("Error deserializing key space: {}", e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Decrypt the space
|
||||
let space = match symmetric::decrypt_key_space(&encrypted_space, password) {
|
||||
Ok(space) => space,
|
||||
Err(e) => {
|
||||
log::error!("Error decrypting key space: {}", e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Set as current space
|
||||
match keypair::set_current_space(space) {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
log::error!("Error setting current space: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn create_key_space(name: &str, password: &str) -> bool {
|
||||
match keypair::create_space(name) {
|
||||
Ok(_) => {
|
||||
// Get the current space
|
||||
match keypair::get_current_space() {
|
||||
Ok(space) => {
|
||||
// Encrypt the key space
|
||||
let encrypted_space = match symmetric::encrypt_key_space(&space, password) {
|
||||
Ok(encrypted) => encrypted,
|
||||
Err(e) => {
|
||||
log::error!("Error encrypting key space: {}", e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Serialize the encrypted space
|
||||
let serialized = match symmetric::serialize_encrypted_space(&encrypted_space) {
|
||||
Ok(json) => json,
|
||||
Err(e) => {
|
||||
log::error!("Error serializing encrypted space: {}", e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Get the key spaces directory
|
||||
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||
let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces");
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
if !key_spaces_dir.exists() {
|
||||
match fs::create_dir_all(&key_spaces_dir) {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
log::error!("Error creating key spaces directory: {}", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write to file
|
||||
let space_path = key_spaces_dir.join(format!("{}.json", name));
|
||||
match fs::write(&space_path, serialized) {
|
||||
Ok(_) => {
|
||||
log::info!("Key space created and saved to {}", space_path.display());
|
||||
true
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Error writing key space file: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Error getting current space: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Error creating key space: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Auto-save function for internal use
|
||||
fn auto_save_key_space(password: &str) -> bool {
|
||||
match keypair::get_current_space() {
|
||||
Ok(space) => {
|
||||
// Encrypt the key space
|
||||
let encrypted_space = match symmetric::encrypt_key_space(&space, password) {
|
||||
Ok(encrypted) => encrypted,
|
||||
Err(e) => {
|
||||
log::error!("Error encrypting key space: {}", e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Serialize the encrypted space
|
||||
let serialized = match symmetric::serialize_encrypted_space(&encrypted_space) {
|
||||
Ok(json) => json,
|
||||
Err(e) => {
|
||||
log::error!("Error serializing encrypted space: {}", e);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
// Get the key spaces directory
|
||||
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||
let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces");
|
||||
|
||||
// Create directory if it doesn't exist
|
||||
if !key_spaces_dir.exists() {
|
||||
match fs::create_dir_all(&key_spaces_dir) {
|
||||
Ok(_) => {},
|
||||
Err(e) => {
|
||||
log::error!("Error creating key spaces directory: {}", e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write to file
|
||||
let space_path = key_spaces_dir.join(format!("{}.json", space.name));
|
||||
match fs::write(&space_path, serialized) {
|
||||
Ok(_) => {
|
||||
log::info!("Key space saved to {}", space_path.display());
|
||||
true
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Error writing key space file: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Error getting current space: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn encrypt_key_space(password: &str) -> String {
|
||||
match keypair::get_current_space() {
|
||||
Ok(space) => {
|
||||
match symmetric::encrypt_key_space(&space, password) {
|
||||
Ok(encrypted_space) => {
|
||||
match serde_json::to_string(&encrypted_space) {
|
||||
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) => {
|
||||
log::error!("Error getting current space: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decrypt_key_space(encrypted: &str, password: &str) -> bool {
|
||||
match serde_json::from_str(encrypted) {
|
||||
Ok(encrypted_space) => {
|
||||
match symmetric::decrypt_key_space(&encrypted_space, password) {
|
||||
Ok(space) => {
|
||||
match keypair::set_current_space(space) {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
log::error!("Error setting current space: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Error decrypting key space: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Error parsing encrypted space: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Keypair management functions
|
||||
fn create_keypair(name: &str, password: &str) -> bool {
|
||||
match keypair::create_keypair(name) {
|
||||
Ok(_) => {
|
||||
// Auto-save the key space after creating a keypair
|
||||
auto_save_key_space(password)
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Error creating keypair: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn select_keypair(name: &str) -> bool {
|
||||
match keypair::select_keypair(name) {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
log::error!("Error selecting keypair: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn list_keypairs() -> Vec<String> {
|
||||
match keypair::list_keypairs() {
|
||||
Ok(keypairs) => keypairs,
|
||||
Err(e) => {
|
||||
log::error!("Error listing keypairs: {}", e);
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Cryptographic operations
|
||||
fn sign(message: &str) -> String {
|
||||
let message_bytes = message.as_bytes();
|
||||
match keypair::keypair_sign(message_bytes) {
|
||||
Ok(signature) => BASE64.encode(signature),
|
||||
Err(e) => {
|
||||
log::error!("Error signing message: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn verify(message: &str, signature: &str) -> bool {
|
||||
let message_bytes = message.as_bytes();
|
||||
match BASE64.decode(signature) {
|
||||
Ok(signature_bytes) => {
|
||||
match keypair::keypair_verify(message_bytes, &signature_bytes) {
|
||||
Ok(is_valid) => is_valid,
|
||||
Err(e) => {
|
||||
log::error!("Error verifying signature: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Error decoding signature: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Symmetric encryption
|
||||
fn generate_key() -> String {
|
||||
let key = symmetric::generate_symmetric_key();
|
||||
BASE64.encode(key)
|
||||
}
|
||||
|
||||
fn encrypt(key: &str, message: &str) -> String {
|
||||
match BASE64.decode(key) {
|
||||
Ok(key_bytes) => {
|
||||
let message_bytes = message.as_bytes();
|
||||
match symmetric::encrypt_symmetric(&key_bytes, message_bytes) {
|
||||
Ok(ciphertext) => BASE64.encode(ciphertext),
|
||||
Err(e) => {
|
||||
log::error!("Error encrypting message: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Error decoding key: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn decrypt(key: &str, ciphertext: &str) -> String {
|
||||
match BASE64.decode(key) {
|
||||
Ok(key_bytes) => {
|
||||
match BASE64.decode(ciphertext) {
|
||||
Ok(ciphertext_bytes) => {
|
||||
match symmetric::decrypt_symmetric(&key_bytes, &ciphertext_bytes) {
|
||||
Ok(plaintext) => {
|
||||
match String::from_utf8(plaintext) {
|
||||
Ok(text) => text,
|
||||
Err(e) => {
|
||||
log::error!("Error converting plaintext to string: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Error decrypting ciphertext: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Error decoding ciphertext: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Error decoding key: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Ethereum operations
|
||||
|
||||
// Gnosis Chain operations
|
||||
fn create_ethereum_wallet() -> bool {
|
||||
match ethereum::create_ethereum_wallet_for_network(ethereum::networks::gnosis()) {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
log::error!("Error creating Ethereum wallet: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_ethereum_address() -> String {
|
||||
match ethereum::get_current_ethereum_wallet_for_network("Gnosis") {
|
||||
Ok(wallet) => wallet.address_string(),
|
||||
Err(e) => {
|
||||
log::error!("Error getting Ethereum address: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Peaq network operations
|
||||
fn create_peaq_wallet() -> bool {
|
||||
match ethereum::create_peaq_wallet() {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
log::error!("Error creating Peaq wallet: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_peaq_address() -> String {
|
||||
match ethereum::get_current_peaq_wallet() {
|
||||
Ok(wallet) => wallet.address_string(),
|
||||
Err(e) => {
|
||||
log::error!("Error getting Peaq address: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Agung testnet operations
|
||||
fn create_agung_wallet() -> bool {
|
||||
match ethereum::create_agung_wallet() {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
log::error!("Error creating Agung wallet: {}", e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_agung_address() -> String {
|
||||
match ethereum::get_current_agung_wallet() {
|
||||
Ok(wallet) => wallet.address_string(),
|
||||
Err(e) => {
|
||||
log::error!("Error getting Agung address: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generic network operations
|
||||
fn create_wallet_for_network(network_name: &str) -> bool {
|
||||
let network = match ethereum::networks::get_network_by_name(network_name) {
|
||||
Some(network) => network,
|
||||
None => {
|
||||
log::error!("Unknown network: {}", network_name);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
match ethereum::create_ethereum_wallet_for_network(network) {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
log::error!("Error creating wallet for network {}: {}", network_name, e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get wallet address for a specific network
|
||||
fn get_wallet_address_for_network(network_name: &str) -> String {
|
||||
let network_name_proper = match ethereum::networks::get_proper_network_name(network_name) {
|
||||
Some(name) => name,
|
||||
None => {
|
||||
log::error!("Unknown network: {}", network_name);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
match ethereum::get_current_ethereum_wallet_for_network(network_name_proper) {
|
||||
Ok(wallet) => wallet.address_string(),
|
||||
Err(e) => {
|
||||
log::error!("Error getting wallet address for network {}: {}", network_name, e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clear wallets for a specific network
|
||||
fn clear_wallets_for_network(network_name: &str) -> bool {
|
||||
let network_name_proper = match ethereum::networks::get_proper_network_name(network_name) {
|
||||
Some(name) => name,
|
||||
None => {
|
||||
log::error!("Unknown network: {}", network_name);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
ethereum::clear_ethereum_wallets_for_network(network_name_proper);
|
||||
true
|
||||
}
|
||||
|
||||
// List supported networks
|
||||
fn list_supported_networks() -> rhai::Array {
|
||||
let mut arr = rhai::Array::new();
|
||||
for name in ethereum::networks::list_network_names() {
|
||||
arr.push(Dynamic::from(name.to_lowercase()));
|
||||
}
|
||||
arr
|
||||
}
|
||||
|
||||
// Get network token symbol
|
||||
fn get_network_token_symbol(network_name: &str) -> String {
|
||||
match ethereum::networks::get_network_by_name(network_name) {
|
||||
Some(network) => network.token_symbol,
|
||||
None => {
|
||||
log::error!("Unknown network: {}", network_name);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get network explorer URL
|
||||
fn get_network_explorer_url(network_name: &str) -> String {
|
||||
match ethereum::networks::get_network_by_name(network_name) {
|
||||
Some(network) => network.explorer_url,
|
||||
None => {
|
||||
log::error!("Unknown network: {}", network_name);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a wallet from a private key for a specific network
|
||||
fn create_wallet_from_private_key_for_network(private_key: &str, network_name: &str) -> bool {
|
||||
let network = match ethereum::networks::get_network_by_name(network_name) {
|
||||
Some(network) => network,
|
||||
None => {
|
||||
log::error!("Unknown network: {}", network_name);
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
match ethereum::create_ethereum_wallet_from_private_key_for_network(private_key, network) {
|
||||
Ok(_) => true,
|
||||
Err(e) => {
|
||||
log::error!("Error creating wallet from private key for network {}: {}", network_name, e);
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Create a provider for the Agung network
|
||||
fn create_agung_provider() -> String {
|
||||
match ethereum::create_agung_provider() {
|
||||
Ok(provider) => {
|
||||
// Generate a unique ID for the provider
|
||||
let id = format!("provider_{}", uuid::Uuid::new_v4());
|
||||
|
||||
// Store the provider in the registry
|
||||
if let Ok(mut providers) = PROVIDERS.lock() {
|
||||
providers.insert(id.clone(), provider);
|
||||
return id;
|
||||
}
|
||||
|
||||
log::error!("Failed to acquire provider registry lock");
|
||||
String::new()
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Error creating Agung provider: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Get the balance of an address on a specific network
|
||||
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
|
||||
let addr = match Address::from_str(address) {
|
||||
Ok(addr) => addr,
|
||||
Err(e) => {
|
||||
log::error!("Invalid address format: {}", e);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
// Get the proper network name
|
||||
let network_name_proper = match ethereum::networks::get_proper_network_name(network_name) {
|
||||
Some(name) => name,
|
||||
None => {
|
||||
log::error!("Unknown network: {}", network_name);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
// Get the network config
|
||||
let network = match ethereum::networks::get_network_by_name(network_name_proper) {
|
||||
Some(n) => n,
|
||||
None => {
|
||||
log::error!("Failed to get network config for: {}", network_name_proper);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
// Create a provider
|
||||
let provider = match ethereum::create_provider(&network) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
log::error!("Failed to create provider: {}", e);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
// Execute the balance query in a blocking manner
|
||||
match rt.block_on(async {
|
||||
ethereum::get_balance(&provider, addr).await
|
||||
}) {
|
||||
Ok(balance) => balance.to_string(),
|
||||
Err(e) => {
|
||||
log::error!("Failed to get balance: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Send ETH from one address to another using the blocking approach
|
||||
fn send_eth(wallet_network: &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
|
||||
let to_addr = match Address::from_str(to_address) {
|
||||
Ok(addr) => addr,
|
||||
Err(e) => {
|
||||
log::error!("Invalid address format: {}", e);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
// Parse the amount (using string to handle large numbers)
|
||||
let amount = match U256::from_dec_str(amount_str) {
|
||||
Ok(amt) => amt,
|
||||
Err(e) => {
|
||||
log::error!("Invalid amount format: {}", e);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
// Get the proper network name
|
||||
let network_name_proper = match ethereum::networks::get_proper_network_name(wallet_network) {
|
||||
Some(name) => name,
|
||||
None => {
|
||||
log::error!("Unknown network: {}", wallet_network);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
// Get the wallet
|
||||
let wallet = match ethereum::get_current_ethereum_wallet_for_network(network_name_proper) {
|
||||
Ok(w) => w,
|
||||
Err(e) => {
|
||||
log::error!("Failed to get wallet: {}", e);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
// Create a provider
|
||||
let provider = match ethereum::create_provider(&wallet.network) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
log::error!("Failed to create provider: {}", e);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
// Execute the transaction in a blocking manner
|
||||
match rt.block_on(async {
|
||||
ethereum::send_eth(&wallet, &provider, to_addr, amount).await
|
||||
}) {
|
||||
Ok(tx_hash) => format!("{:?}", tx_hash),
|
||||
Err(e) => {
|
||||
log::error!("Transaction failed: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Smart contract operations
|
||||
|
||||
// Load a contract ABI from a JSON string and create a contract instance
|
||||
fn load_contract_abi(network_name: &str, address: &str, abi_json: &str) -> String {
|
||||
// Get the network
|
||||
let network = match ethereum::networks::get_network_by_name(network_name) {
|
||||
Some(network) => network,
|
||||
None => {
|
||||
log::error!("Unknown network: {}", network_name);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
// Parse the ABI
|
||||
let abi = match ethereum::load_abi_from_json(abi_json) {
|
||||
Ok(abi) => abi,
|
||||
Err(e) => {
|
||||
log::error!("Error parsing ABI: {}", e);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
// Create the contract
|
||||
match ethereum::Contract::from_address_string(address, abi, network) {
|
||||
Ok(contract) => {
|
||||
// Serialize the contract to JSON for storage
|
||||
match serde_json::to_string(&contract) {
|
||||
Ok(json) => json,
|
||||
Err(e) => {
|
||||
log::error!("Error serializing contract: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
},
|
||||
Err(e) => {
|
||||
log::error!("Error creating contract: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Load a contract ABI from a file
|
||||
fn load_contract_abi_from_file(network_name: &str, address: &str, file_path: &str) -> String {
|
||||
// Read the ABI file
|
||||
match fs::read_to_string(file_path) {
|
||||
Ok(abi_json) => load_contract_abi(network_name, address, &abi_json),
|
||||
Err(e) => {
|
||||
log::error!("Error reading ABI file: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Use the utility functions from the ethereum module
|
||||
|
||||
// Call a read-only function on a contract (no arguments version)
|
||||
fn call_contract_read_no_args(contract_json: &str, function_name: &str) -> Dynamic {
|
||||
call_contract_read(contract_json, function_name, rhai::Array::new())
|
||||
}
|
||||
|
||||
// Call a read-only function on a contract with arguments
|
||||
fn call_contract_read(contract_json: &str, function_name: &str, args: rhai::Array) -> Dynamic {
|
||||
// Deserialize the contract
|
||||
let contract: ethereum::Contract = match serde_json::from_str(contract_json) {
|
||||
Ok(contract) => contract,
|
||||
Err(e) => {
|
||||
log::error!("Error deserializing contract: {}", e);
|
||||
return Dynamic::UNIT;
|
||||
}
|
||||
};
|
||||
|
||||
// Prepare the arguments
|
||||
let tokens = match prepare_function_arguments(&contract.abi, function_name, &args) {
|
||||
Ok(tokens) => tokens,
|
||||
Err(e) => {
|
||||
log::error!("Error preparing arguments: {}", e);
|
||||
return Dynamic::UNIT;
|
||||
}
|
||||
};
|
||||
|
||||
// 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
|
||||
let provider = match ethereum::create_provider(&contract.network) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
log::error!("Failed to create provider: {}", e);
|
||||
return Dynamic::UNIT;
|
||||
}
|
||||
};
|
||||
|
||||
// Execute the call in a blocking manner
|
||||
match rt.block_on(async {
|
||||
ethereum::call_read_function(&contract, &provider, function_name, tokens).await
|
||||
}) {
|
||||
Ok(result) => convert_token_to_rhai(&result),
|
||||
Err(e) => {
|
||||
log::error!("Failed to call contract function: {}", e);
|
||||
Dynamic::UNIT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Call a state-changing function on a contract (no arguments version)
|
||||
fn call_contract_write_no_args(contract_json: &str, function_name: &str) -> String {
|
||||
call_contract_write(contract_json, function_name, rhai::Array::new())
|
||||
}
|
||||
|
||||
// Call a state-changing function on a contract with arguments
|
||||
fn call_contract_write(contract_json: &str, function_name: &str, args: rhai::Array) -> String {
|
||||
// Deserialize the contract
|
||||
let contract: ethereum::Contract = match serde_json::from_str(contract_json) {
|
||||
Ok(contract) => contract,
|
||||
Err(e) => {
|
||||
log::error!("Error deserializing contract: {}", e);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
// Prepare the arguments
|
||||
let tokens = match prepare_function_arguments(&contract.abi, function_name, &args) {
|
||||
Ok(tokens) => tokens,
|
||||
Err(e) => {
|
||||
log::error!("Error preparing arguments: {}", e);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
// 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
|
||||
let network_name_proper = contract.network.name.as_str();
|
||||
let wallet = match ethereum::get_current_ethereum_wallet_for_network(network_name_proper) {
|
||||
Ok(w) => w,
|
||||
Err(e) => {
|
||||
log::error!("Failed to get wallet: {}", e);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
// Create a provider
|
||||
let provider = match ethereum::create_provider(&contract.network) {
|
||||
Ok(p) => p,
|
||||
Err(e) => {
|
||||
log::error!("Failed to create provider: {}", e);
|
||||
return String::new();
|
||||
}
|
||||
};
|
||||
|
||||
// Execute the transaction in a blocking manner
|
||||
match rt.block_on(async {
|
||||
ethereum::call_write_function(&contract, &wallet, &provider, function_name, tokens).await
|
||||
}) {
|
||||
Ok(tx_hash) => format!("{:?}", tx_hash),
|
||||
Err(e) => {
|
||||
// Log the error details for debugging
|
||||
log::debug!("\nERROR DETAILS: Transaction failed: {}", e);
|
||||
log::debug!("Contract address: {}", contract.address);
|
||||
log::debug!("Function: {}", function_name);
|
||||
log::debug!("Arguments: {:?}", args);
|
||||
log::debug!("Wallet address: {}", wallet.address);
|
||||
log::debug!("Network: {}", contract.network.name);
|
||||
log::error!("Transaction failed: {}", e);
|
||||
String::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Register crypto functions with the Rhai engine
|
||||
pub fn register_crypto_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
|
||||
// Register key space functions
|
||||
engine.register_fn("load_key_space", load_key_space);
|
||||
engine.register_fn("create_key_space", create_key_space);
|
||||
engine.register_fn("encrypt_key_space", encrypt_key_space);
|
||||
engine.register_fn("decrypt_key_space", decrypt_key_space);
|
||||
|
||||
// Register keypair functions
|
||||
engine.register_fn("create_keypair", create_keypair);
|
||||
engine.register_fn("select_keypair", select_keypair);
|
||||
engine.register_fn("list_keypairs", list_keypairs);
|
||||
|
||||
// Register signing/verification functions
|
||||
engine.register_fn("sign", sign);
|
||||
engine.register_fn("verify", verify);
|
||||
|
||||
// Register symmetric encryption functions
|
||||
engine.register_fn("generate_key", generate_key);
|
||||
engine.register_fn("encrypt", encrypt);
|
||||
engine.register_fn("decrypt", decrypt);
|
||||
|
||||
// Register Ethereum functions (Gnosis Chain)
|
||||
engine.register_fn("create_ethereum_wallet", create_ethereum_wallet);
|
||||
engine.register_fn("get_ethereum_address", get_ethereum_address);
|
||||
|
||||
// Register Peaq network functions
|
||||
engine.register_fn("create_peaq_wallet", create_peaq_wallet);
|
||||
engine.register_fn("get_peaq_address", get_peaq_address);
|
||||
|
||||
// Register Agung testnet functions
|
||||
engine.register_fn("create_agung_wallet", create_agung_wallet);
|
||||
engine.register_fn("get_agung_address", get_agung_address);
|
||||
|
||||
// Register generic network functions
|
||||
engine.register_fn("create_wallet_for_network", create_wallet_for_network);
|
||||
engine.register_fn("get_wallet_address_for_network", get_wallet_address_for_network);
|
||||
engine.register_fn("clear_wallets_for_network", clear_wallets_for_network);
|
||||
engine.register_fn("list_supported_networks", list_supported_networks);
|
||||
engine.register_fn("get_network_token_symbol", get_network_token_symbol);
|
||||
engine.register_fn("get_network_explorer_url", get_network_explorer_url);
|
||||
|
||||
// Register new Ethereum functions for wallet creation from private key and transactions
|
||||
engine.register_fn("create_wallet_from_private_key_for_network", create_wallet_from_private_key_for_network);
|
||||
engine.register_fn("create_agung_provider", create_agung_provider);
|
||||
engine.register_fn("send_eth", send_eth);
|
||||
engine.register_fn("get_balance", get_balance);
|
||||
|
||||
// Register smart contract functions
|
||||
engine.register_fn("load_contract_abi", load_contract_abi);
|
||||
engine.register_fn("load_contract_abi_from_file", load_contract_abi_from_file);
|
||||
|
||||
// Register the read function with different arities
|
||||
engine.register_fn("call_contract_read", call_contract_read_no_args);
|
||||
engine.register_fn("call_contract_read", call_contract_read);
|
||||
|
||||
// Register the write function with different arities
|
||||
engine.register_fn("call_contract_write", call_contract_write_no_args);
|
||||
engine.register_fn("call_contract_write", call_contract_write);
|
||||
|
||||
Ok(())
|
||||
}
|
@@ -12,6 +12,7 @@ mod postgresclient;
|
||||
mod process;
|
||||
mod redisclient;
|
||||
mod rfs;
|
||||
mod hero_vault; // This module now uses hero_vault internally
|
||||
mod text;
|
||||
|
||||
#[cfg(test)]
|
||||
@@ -105,6 +106,9 @@ pub use crate::text::{
|
||||
// Re-export TextReplacer functions
|
||||
pub use text::*;
|
||||
|
||||
// Re-export crypto module
|
||||
pub use hero_vault::register_crypto_module;
|
||||
|
||||
// Rename copy functions to avoid conflicts
|
||||
pub use os::copy as os_copy;
|
||||
|
||||
@@ -148,6 +152,10 @@ pub fn register(engine: &mut Engine) -> Result<(), Box<rhai::EvalAltResult>> {
|
||||
|
||||
// Register RFS module functions
|
||||
rfs::register(engine)?;
|
||||
|
||||
// Register Crypto module functions
|
||||
hero_vault::register_crypto_module(engine)?;
|
||||
|
||||
|
||||
// Register Redis client module functions
|
||||
redisclient::register_redisclient_module(engine)?;
|
||||
|
Reference in New Issue
Block a user