feat: support interacting with smart contracts on EVM-based blockchains
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
Some checks are pending
Rhai Tests / Run Rhai Tests (push) Waiting to run
This commit is contained in:
parent
2695b5f5f7
commit
619ce57776
101
examples/hero_vault/contract_example.rhai
Normal file
101
examples/hero_vault/contract_example.rhai
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
// Example Rhai script for interacting with smart contracts using Hero Vault
|
||||||
|
// This script demonstrates loading a contract ABI and interacting with a contract
|
||||||
|
|
||||||
|
// Step 1: Set up wallet and network
|
||||||
|
let space_name = "contract_demo_space";
|
||||||
|
let password = "secure_password123";
|
||||||
|
|
||||||
|
print("Creating key space: " + space_name);
|
||||||
|
if create_key_space(space_name, password) {
|
||||||
|
print("✓ Key space created successfully");
|
||||||
|
|
||||||
|
// Create a keypair
|
||||||
|
print("\nCreating keypair...");
|
||||||
|
if create_keypair("contract_key", password) {
|
||||||
|
print("✓ Created contract keypair");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 2: Create an Ethereum wallet for Gnosis Chain
|
||||||
|
print("\nCreating Ethereum wallet...");
|
||||||
|
if create_ethereum_wallet() {
|
||||||
|
print("✓ Ethereum wallet created");
|
||||||
|
|
||||||
|
let address = get_ethereum_address();
|
||||||
|
print("Ethereum address: " + address);
|
||||||
|
|
||||||
|
// Step 3: Define a simple ERC-20 ABI (partial)
|
||||||
|
let erc20_abi = `[
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "name",
|
||||||
|
"outputs": [{"name": "", "type": "string"}],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "symbol",
|
||||||
|
"outputs": [{"name": "", "type": "string"}],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [],
|
||||||
|
"name": "decimals",
|
||||||
|
"outputs": [{"name": "", "type": "uint8"}],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"constant": true,
|
||||||
|
"inputs": [{"name": "owner", "type": "address"}],
|
||||||
|
"name": "balanceOf",
|
||||||
|
"outputs": [{"name": "", "type": "uint256"}],
|
||||||
|
"payable": false,
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
}
|
||||||
|
]`;
|
||||||
|
|
||||||
|
// Step 4: Load the contract ABI
|
||||||
|
print("\nLoading contract ABI...");
|
||||||
|
let contract = load_contract_abi("Gnosis", "0x4ECaBa5870353805a9F068101A40E0f32ed605C6", erc20_abi);
|
||||||
|
if contract != "" {
|
||||||
|
print("✓ Contract loaded successfully");
|
||||||
|
|
||||||
|
// Step 5: Call read-only functions
|
||||||
|
print("\nCalling read-only functions...");
|
||||||
|
|
||||||
|
// Get token name
|
||||||
|
let token_name = call_contract_read(contract, "name");
|
||||||
|
print("Token name: " + token_name);
|
||||||
|
|
||||||
|
// Get token symbol
|
||||||
|
let token_symbol = call_contract_read(contract, "symbol");
|
||||||
|
print("Token symbol: " + token_symbol);
|
||||||
|
|
||||||
|
// Get token decimals
|
||||||
|
let token_decimals = call_contract_read(contract, "decimals");
|
||||||
|
print("Token decimals: " + token_decimals);
|
||||||
|
|
||||||
|
// Note: In a full implementation, we would handle function arguments
|
||||||
|
// For now, we're just demonstrating the basic structure
|
||||||
|
print("Note: balanceOf function requires an address argument, which is not implemented in this example");
|
||||||
|
print("In a full implementation, we would pass the address and get the balance");
|
||||||
|
} else {
|
||||||
|
print("✗ Failed to load contract");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print("✗ Failed to create Ethereum wallet");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
print("✗ Failed to create key space");
|
||||||
|
}
|
||||||
|
|
||||||
|
print("\nContract example completed");
|
@ -8,38 +8,46 @@ pub enum CryptoError {
|
|||||||
/// Invalid key length
|
/// Invalid key length
|
||||||
#[error("Invalid key length")]
|
#[error("Invalid key length")]
|
||||||
InvalidKeyLength,
|
InvalidKeyLength,
|
||||||
|
|
||||||
/// Encryption failed
|
/// Encryption failed
|
||||||
#[error("Encryption failed: {0}")]
|
#[error("Encryption failed: {0}")]
|
||||||
EncryptionFailed(String),
|
EncryptionFailed(String),
|
||||||
|
|
||||||
/// Decryption failed
|
/// Decryption failed
|
||||||
#[error("Decryption failed: {0}")]
|
#[error("Decryption failed: {0}")]
|
||||||
DecryptionFailed(String),
|
DecryptionFailed(String),
|
||||||
|
|
||||||
/// Signature format error
|
/// Signature format error
|
||||||
#[error("Signature format error: {0}")]
|
#[error("Signature format error: {0}")]
|
||||||
SignatureFormatError(String),
|
SignatureFormatError(String),
|
||||||
|
|
||||||
/// Keypair already exists
|
/// Keypair already exists
|
||||||
#[error("Keypair already exists: {0}")]
|
#[error("Keypair already exists: {0}")]
|
||||||
KeypairAlreadyExists(String),
|
KeypairAlreadyExists(String),
|
||||||
|
|
||||||
/// Keypair not found
|
/// Keypair not found
|
||||||
#[error("Keypair not found: {0}")]
|
#[error("Keypair not found: {0}")]
|
||||||
KeypairNotFound(String),
|
KeypairNotFound(String),
|
||||||
|
|
||||||
/// No active key space
|
/// No active key space
|
||||||
#[error("No active key space")]
|
#[error("No active key space")]
|
||||||
NoActiveSpace,
|
NoActiveSpace,
|
||||||
|
|
||||||
/// No keypair selected
|
/// No keypair selected
|
||||||
#[error("No keypair selected")]
|
#[error("No keypair selected")]
|
||||||
NoKeypairSelected,
|
NoKeypairSelected,
|
||||||
|
|
||||||
/// Serialization error
|
/// Serialization error
|
||||||
#[error("Serialization error: {0}")]
|
#[error("Serialization error: {0}")]
|
||||||
SerializationError(String),
|
SerializationError(String),
|
||||||
|
|
||||||
|
/// Invalid address format
|
||||||
|
#[error("Invalid address format: {0}")]
|
||||||
|
InvalidAddress(String),
|
||||||
|
|
||||||
|
/// Smart contract error
|
||||||
|
#[error("Smart contract error: {0}")]
|
||||||
|
ContractError(String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert CryptoError to SAL's Error type
|
/// Convert CryptoError to SAL's Error type
|
||||||
|
159
src/hero_vault/ethereum/contract.rs
Normal file
159
src/hero_vault/ethereum/contract.rs
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
//! Smart contract interaction functionality.
|
||||||
|
//!
|
||||||
|
//! This module provides functionality for interacting with smart contracts on EVM-based blockchains.
|
||||||
|
|
||||||
|
use ethers::prelude::*;
|
||||||
|
use ethers::abi::{Abi, Token};
|
||||||
|
use std::sync::Arc;
|
||||||
|
use std::str::FromStr;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use crate::hero_vault::error::CryptoError;
|
||||||
|
use super::wallet::EthereumWallet;
|
||||||
|
use super::networks::NetworkConfig;
|
||||||
|
|
||||||
|
/// A smart contract instance.
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Contract {
|
||||||
|
/// The contract address
|
||||||
|
pub address: Address,
|
||||||
|
/// The contract ABI
|
||||||
|
pub abi: Abi,
|
||||||
|
/// The network the contract is deployed on
|
||||||
|
pub network: NetworkConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Contract {
|
||||||
|
/// Creates a new contract instance.
|
||||||
|
pub fn new(address: Address, abi: Abi, network: NetworkConfig) -> Self {
|
||||||
|
Contract {
|
||||||
|
address,
|
||||||
|
abi,
|
||||||
|
network,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new contract instance from an address string and ABI.
|
||||||
|
pub fn from_address_string(address_str: &str, abi: Abi, network: NetworkConfig) -> Result<Self, CryptoError> {
|
||||||
|
let address = Address::from_str(address_str)
|
||||||
|
.map_err(|e| CryptoError::InvalidAddress(format!("Invalid address format: {}", e)))?;
|
||||||
|
|
||||||
|
Ok(Contract::new(address, abi, network))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates an ethers Contract instance for interaction.
|
||||||
|
pub fn create_ethers_contract(&self, provider: Provider<Http>, _wallet: Option<&EthereumWallet>) -> Result<ethers::contract::Contract<ethers::providers::Provider<Http>>, CryptoError> {
|
||||||
|
let contract = ethers::contract::Contract::new(
|
||||||
|
self.address,
|
||||||
|
self.abi.clone(),
|
||||||
|
Arc::new(provider),
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(contract)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Loads a contract ABI from a JSON string.
|
||||||
|
pub fn load_abi_from_json(json_str: &str) -> Result<Abi, CryptoError> {
|
||||||
|
serde_json::from_str(json_str)
|
||||||
|
.map_err(|e| CryptoError::SerializationError(format!("Failed to parse ABI JSON: {}", e)))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Calls a read-only function on a contract.
|
||||||
|
pub async fn call_read_function(
|
||||||
|
contract: &Contract,
|
||||||
|
provider: &Provider<Http>,
|
||||||
|
function_name: &str,
|
||||||
|
args: Vec<Token>,
|
||||||
|
) -> Result<Vec<Token>, CryptoError> {
|
||||||
|
// Create the ethers contract (not used directly but kept for future extensions)
|
||||||
|
let _ethers_contract = contract.create_ethers_contract(provider.clone(), None)?;
|
||||||
|
|
||||||
|
// Get the function from the ABI
|
||||||
|
let function = contract.abi.function(function_name)
|
||||||
|
.map_err(|e| CryptoError::ContractError(format!("Function not found in ABI: {}", e)))?;
|
||||||
|
|
||||||
|
// Encode the function call
|
||||||
|
let call_data = function.encode_input(&args)
|
||||||
|
.map_err(|e| CryptoError::ContractError(format!("Failed to encode function call: {}", e)))?;
|
||||||
|
|
||||||
|
// Make the call
|
||||||
|
let tx = TransactionRequest::new()
|
||||||
|
.to(contract.address)
|
||||||
|
.data(call_data);
|
||||||
|
|
||||||
|
let result = provider.call(&tx.into(), None).await
|
||||||
|
.map_err(|e| CryptoError::ContractError(format!("Contract call failed: {}", e)))?;
|
||||||
|
|
||||||
|
// Decode the result
|
||||||
|
let decoded = function.decode_output(&result)
|
||||||
|
.map_err(|e| CryptoError::ContractError(format!("Failed to decode function output: {}", e)))?;
|
||||||
|
|
||||||
|
Ok(decoded)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Executes a state-changing function on a contract.
|
||||||
|
pub async fn call_write_function(
|
||||||
|
contract: &Contract,
|
||||||
|
wallet: &EthereumWallet,
|
||||||
|
provider: &Provider<Http>,
|
||||||
|
function_name: &str,
|
||||||
|
args: Vec<Token>,
|
||||||
|
) -> Result<H256, CryptoError> {
|
||||||
|
// Create a client with the wallet
|
||||||
|
let client = SignerMiddleware::new(
|
||||||
|
provider.clone(),
|
||||||
|
wallet.wallet.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Get the function from the ABI
|
||||||
|
let function = contract.abi.function(function_name)
|
||||||
|
.map_err(|e| CryptoError::ContractError(format!("Function not found in ABI: {}", e)))?;
|
||||||
|
|
||||||
|
// Encode the function call
|
||||||
|
let call_data = function.encode_input(&args)
|
||||||
|
.map_err(|e| CryptoError::ContractError(format!("Failed to encode function call: {}", e)))?;
|
||||||
|
|
||||||
|
// Create the transaction request
|
||||||
|
let tx = TransactionRequest::new()
|
||||||
|
.to(contract.address)
|
||||||
|
.data(call_data);
|
||||||
|
|
||||||
|
// Send the transaction using the client directly
|
||||||
|
let pending_tx = client.send_transaction(tx, None)
|
||||||
|
.await
|
||||||
|
.map_err(|e| CryptoError::ContractError(format!("Failed to send transaction: {}", e)))?;
|
||||||
|
|
||||||
|
// Return the transaction hash
|
||||||
|
Ok(pending_tx.tx_hash())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Estimates gas for a contract function call.
|
||||||
|
pub async fn estimate_gas(
|
||||||
|
contract: &Contract,
|
||||||
|
wallet: &EthereumWallet,
|
||||||
|
provider: &Provider<Http>,
|
||||||
|
function_name: &str,
|
||||||
|
args: Vec<Token>,
|
||||||
|
) -> Result<U256, CryptoError> {
|
||||||
|
// Get the function from the ABI
|
||||||
|
let function = contract.abi.function(function_name)
|
||||||
|
.map_err(|e| CryptoError::ContractError(format!("Function not found in ABI: {}", e)))?;
|
||||||
|
|
||||||
|
// Encode the function call
|
||||||
|
let call_data = function.encode_input(&args)
|
||||||
|
.map_err(|e| CryptoError::ContractError(format!("Failed to encode function call: {}", e)))?;
|
||||||
|
|
||||||
|
// Create the transaction request
|
||||||
|
let tx = TransactionRequest::new()
|
||||||
|
.from(wallet.address)
|
||||||
|
.to(contract.address)
|
||||||
|
.data(call_data);
|
||||||
|
|
||||||
|
// Estimate gas
|
||||||
|
let gas = provider.estimate_gas(&tx.into(), None)
|
||||||
|
.await
|
||||||
|
.map_err(|e| CryptoError::ContractError(format!("Failed to estimate gas: {}", e)))?;
|
||||||
|
|
||||||
|
Ok(gas)
|
||||||
|
}
|
@ -1,18 +1,21 @@
|
|||||||
//! Ethereum wallet functionality
|
//! Ethereum wallet functionality
|
||||||
//!
|
//!
|
||||||
//! This module provides functionality for creating and managing Ethereum wallets.
|
//! This module provides functionality for creating and managing Ethereum wallets
|
||||||
//!
|
//! and interacting with smart contracts on EVM-based blockchains.
|
||||||
|
//!
|
||||||
//! The module is organized into several components:
|
//! The module is organized into several components:
|
||||||
//! - `wallet.rs`: Core Ethereum wallet implementation
|
//! - `wallet.rs`: Core Ethereum wallet implementation
|
||||||
//! - `networks.rs`: Network registry and configuration
|
//! - `networks.rs`: Network registry and configuration
|
||||||
//! - `provider.rs`: Provider creation and management
|
//! - `provider.rs`: Provider creation and management
|
||||||
//! - `transaction.rs`: Transaction-related functionality
|
//! - `transaction.rs`: Transaction-related functionality
|
||||||
//! - `storage.rs`: Wallet storage functionality
|
//! - `storage.rs`: Wallet storage functionality
|
||||||
|
//! - `contract.rs`: Smart contract interaction functionality
|
||||||
|
|
||||||
mod wallet;
|
mod wallet;
|
||||||
mod provider;
|
mod provider;
|
||||||
mod transaction;
|
mod transaction;
|
||||||
mod storage;
|
mod storage;
|
||||||
|
mod contract;
|
||||||
pub mod networks;
|
pub mod networks;
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub mod tests;
|
pub mod tests;
|
||||||
@ -64,3 +67,12 @@ pub use networks::{
|
|||||||
get_all_networks,
|
get_all_networks,
|
||||||
names,
|
names,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Re-export contract functions
|
||||||
|
pub use contract::{
|
||||||
|
Contract,
|
||||||
|
load_abi_from_json,
|
||||||
|
call_read_function,
|
||||||
|
call_write_function,
|
||||||
|
estimate_gas,
|
||||||
|
};
|
||||||
|
@ -1,13 +1,14 @@
|
|||||||
//! Ethereum network registry
|
//! Ethereum network registry
|
||||||
//!
|
//!
|
||||||
//! This module provides a centralized registry of Ethereum networks and utilities
|
//! This module provides a centralized registry of Ethereum networks and utilities
|
||||||
//! to work with them.
|
//! to work with them.
|
||||||
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
/// Configuration for an EVM-compatible network
|
/// Configuration for an EVM-compatible network
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct NetworkConfig {
|
pub struct NetworkConfig {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub chain_id: u64,
|
pub chain_id: u64,
|
||||||
@ -90,7 +91,7 @@ pub fn list_network_names() -> Vec<&'static str> {
|
|||||||
/// Get a map of all networks
|
/// Get a map of all networks
|
||||||
pub fn get_all_networks() -> &'static HashMap<&'static str, NetworkConfig> {
|
pub fn get_all_networks() -> &'static HashMap<&'static str, NetworkConfig> {
|
||||||
static NETWORKS: OnceLock<HashMap<&'static str, NetworkConfig>> = OnceLock::new();
|
static NETWORKS: OnceLock<HashMap<&'static str, NetworkConfig>> = OnceLock::new();
|
||||||
|
|
||||||
NETWORKS.get_or_init(|| {
|
NETWORKS.get_or_init(|| {
|
||||||
let mut map = HashMap::new();
|
let mut map = HashMap::new();
|
||||||
map.insert(names::GNOSIS, gnosis());
|
map.insert(names::GNOSIS, gnosis());
|
||||||
|
83
src/hero_vault/ethereum/tests/contract_tests.rs
Normal file
83
src/hero_vault/ethereum/tests/contract_tests.rs
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
//! Tests for smart contract functionality.
|
||||||
|
|
||||||
|
use ethers::types::Address;
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use crate::hero_vault::ethereum::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_contract_creation() {
|
||||||
|
// Create a simple ABI
|
||||||
|
let abi_json = r#"[
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "getValue",
|
||||||
|
"outputs": [{"type": "uint256", "name": ""}],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"inputs": [{"type": "uint256", "name": "newValue"}],
|
||||||
|
"name": "setValue",
|
||||||
|
"outputs": [],
|
||||||
|
"stateMutability": "nonpayable",
|
||||||
|
"type": "function"
|
||||||
|
}
|
||||||
|
]"#;
|
||||||
|
|
||||||
|
// Parse the ABI
|
||||||
|
let abi = load_abi_from_json(abi_json).unwrap();
|
||||||
|
|
||||||
|
// Create a contract address
|
||||||
|
let address = Address::from_str("0x1234567890123456789012345678901234567890").unwrap();
|
||||||
|
|
||||||
|
// Create a network config
|
||||||
|
let network = networks::gnosis();
|
||||||
|
|
||||||
|
// Create a contract
|
||||||
|
let contract = Contract::new(address, abi, network);
|
||||||
|
|
||||||
|
// Verify the contract was created correctly
|
||||||
|
assert_eq!(contract.address, address);
|
||||||
|
assert_eq!(contract.network.name, "Gnosis");
|
||||||
|
|
||||||
|
// Verify the ABI contains the expected functions
|
||||||
|
assert!(contract.abi.function("getValue").is_ok());
|
||||||
|
assert!(contract.abi.function("setValue").is_ok());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_contract_from_address_string() {
|
||||||
|
// Create a simple ABI
|
||||||
|
let abi_json = r#"[
|
||||||
|
{
|
||||||
|
"inputs": [],
|
||||||
|
"name": "getValue",
|
||||||
|
"outputs": [{"type": "uint256", "name": ""}],
|
||||||
|
"stateMutability": "view",
|
||||||
|
"type": "function"
|
||||||
|
}
|
||||||
|
]"#;
|
||||||
|
|
||||||
|
// Parse the ABI
|
||||||
|
let abi = load_abi_from_json(abi_json).unwrap();
|
||||||
|
|
||||||
|
// Create a network config
|
||||||
|
let network = networks::gnosis();
|
||||||
|
|
||||||
|
// Create a contract from an address string
|
||||||
|
let address_str = "0x1234567890123456789012345678901234567890";
|
||||||
|
let contract = Contract::from_address_string(address_str, abi, network).unwrap();
|
||||||
|
|
||||||
|
// Verify the contract was created correctly
|
||||||
|
assert_eq!(contract.address, Address::from_str(address_str).unwrap());
|
||||||
|
|
||||||
|
// Test with an invalid address
|
||||||
|
let invalid_address = "0xinvalid";
|
||||||
|
let result = Contract::from_address_string(invalid_address, contract.abi.clone(), contract.network.clone());
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: We can't easily test the actual contract calls in unit tests without mocking
|
||||||
|
// the provider, which would be complex. These would be better tested in integration tests
|
||||||
|
// with a local blockchain or testnet.
|
@ -3,3 +3,4 @@
|
|||||||
mod wallet_tests;
|
mod wallet_tests;
|
||||||
mod network_tests;
|
mod network_tests;
|
||||||
mod transaction_tests;
|
mod transaction_tests;
|
||||||
|
mod contract_tests;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
//! Rhai bindings for SAL crypto functionality
|
//! Rhai bindings for SAL crypto functionality
|
||||||
|
|
||||||
use rhai::{Engine, Dynamic, FnPtr, 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::path::PathBuf;
|
||||||
@ -9,11 +9,10 @@ use std::sync::Mutex;
|
|||||||
use once_cell::sync::Lazy;
|
use once_cell::sync::Lazy;
|
||||||
use tokio::runtime::Runtime;
|
use tokio::runtime::Runtime;
|
||||||
use ethers::types::{Address, U256};
|
use ethers::types::{Address, U256};
|
||||||
|
use ethers::abi::Token;
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
|
|
||||||
use crate::hero_vault::{keypair, symmetric, ethereum};
|
use crate::hero_vault::{keypair, symmetric, ethereum};
|
||||||
use crate::hero_vault::error::CryptoError;
|
|
||||||
use crate::hero_vault::kvs;
|
|
||||||
|
|
||||||
// Global Tokio runtime for blocking async operations
|
// Global Tokio runtime for blocking async operations
|
||||||
static RUNTIME: Lazy<Mutex<Runtime>> = Lazy::new(|| {
|
static RUNTIME: Lazy<Mutex<Runtime>> = Lazy::new(|| {
|
||||||
@ -30,22 +29,22 @@ fn load_key_space(name: &str, password: &str) -> bool {
|
|||||||
// Get the key spaces directory from config
|
// Get the key spaces directory from config
|
||||||
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||||
let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces");
|
let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces");
|
||||||
|
|
||||||
// Check if directory exists
|
// Check if directory exists
|
||||||
if !key_spaces_dir.exists() {
|
if !key_spaces_dir.exists() {
|
||||||
log::error!("Key spaces directory does not exist");
|
log::error!("Key spaces directory does not exist");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the key space file path
|
// Get the key space file path
|
||||||
let space_path = key_spaces_dir.join(format!("{}.json", name));
|
let space_path = key_spaces_dir.join(format!("{}.json", name));
|
||||||
|
|
||||||
// Check if file exists
|
// Check if file exists
|
||||||
if !space_path.exists() {
|
if !space_path.exists() {
|
||||||
log::error!("Key space file not found: {}", space_path.display());
|
log::error!("Key space file not found: {}", space_path.display());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read the file
|
// Read the file
|
||||||
let serialized = match fs::read_to_string(&space_path) {
|
let serialized = match fs::read_to_string(&space_path) {
|
||||||
Ok(data) => data,
|
Ok(data) => data,
|
||||||
@ -54,7 +53,7 @@ fn load_key_space(name: &str, password: &str) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Deserialize the encrypted space
|
// Deserialize the encrypted space
|
||||||
let encrypted_space = match symmetric::deserialize_encrypted_space(&serialized) {
|
let encrypted_space = match symmetric::deserialize_encrypted_space(&serialized) {
|
||||||
Ok(space) => space,
|
Ok(space) => space,
|
||||||
@ -63,7 +62,7 @@ fn load_key_space(name: &str, password: &str) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Decrypt the space
|
// Decrypt the space
|
||||||
let space = match symmetric::decrypt_key_space(&encrypted_space, password) {
|
let space = match symmetric::decrypt_key_space(&encrypted_space, password) {
|
||||||
Ok(space) => space,
|
Ok(space) => space,
|
||||||
@ -72,7 +71,7 @@ fn load_key_space(name: &str, password: &str) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set as current space
|
// Set as current space
|
||||||
match keypair::set_current_space(space) {
|
match keypair::set_current_space(space) {
|
||||||
Ok(_) => true,
|
Ok(_) => true,
|
||||||
@ -97,7 +96,7 @@ fn create_key_space(name: &str, password: &str) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Serialize the encrypted space
|
// Serialize the encrypted space
|
||||||
let serialized = match symmetric::serialize_encrypted_space(&encrypted_space) {
|
let serialized = match symmetric::serialize_encrypted_space(&encrypted_space) {
|
||||||
Ok(json) => json,
|
Ok(json) => json,
|
||||||
@ -106,11 +105,11 @@ fn create_key_space(name: &str, password: &str) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the key spaces directory
|
// Get the key spaces directory
|
||||||
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||||
let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces");
|
let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces");
|
||||||
|
|
||||||
// Create directory if it doesn't exist
|
// Create directory if it doesn't exist
|
||||||
if !key_spaces_dir.exists() {
|
if !key_spaces_dir.exists() {
|
||||||
match fs::create_dir_all(&key_spaces_dir) {
|
match fs::create_dir_all(&key_spaces_dir) {
|
||||||
@ -121,7 +120,7 @@ fn create_key_space(name: &str, password: &str) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write to file
|
// Write to file
|
||||||
let space_path = key_spaces_dir.join(format!("{}.json", name));
|
let space_path = key_spaces_dir.join(format!("{}.json", name));
|
||||||
match fs::write(&space_path, serialized) {
|
match fs::write(&space_path, serialized) {
|
||||||
@ -160,7 +159,7 @@ fn auto_save_key_space(password: &str) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Serialize the encrypted space
|
// Serialize the encrypted space
|
||||||
let serialized = match symmetric::serialize_encrypted_space(&encrypted_space) {
|
let serialized = match symmetric::serialize_encrypted_space(&encrypted_space) {
|
||||||
Ok(json) => json,
|
Ok(json) => json,
|
||||||
@ -169,11 +168,11 @@ fn auto_save_key_space(password: &str) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the key spaces directory
|
// Get the key spaces directory
|
||||||
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
let home_dir = dirs::home_dir().unwrap_or_else(|| PathBuf::from("."));
|
||||||
let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces");
|
let key_spaces_dir = home_dir.join(".hero-vault").join("key-spaces");
|
||||||
|
|
||||||
// Create directory if it doesn't exist
|
// Create directory if it doesn't exist
|
||||||
if !key_spaces_dir.exists() {
|
if !key_spaces_dir.exists() {
|
||||||
match fs::create_dir_all(&key_spaces_dir) {
|
match fs::create_dir_all(&key_spaces_dir) {
|
||||||
@ -184,7 +183,7 @@ fn auto_save_key_space(password: &str) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write to file
|
// Write to file
|
||||||
let space_path = key_spaces_dir.join(format!("{}.json", space.name));
|
let space_path = key_spaces_dir.join(format!("{}.json", space.name));
|
||||||
match fs::write(&space_path, serialized) {
|
match fs::write(&space_path, serialized) {
|
||||||
@ -455,7 +454,7 @@ fn create_wallet_for_network(network_name: &str) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match ethereum::create_ethereum_wallet_for_network(network) {
|
match ethereum::create_ethereum_wallet_for_network(network) {
|
||||||
Ok(_) => true,
|
Ok(_) => true,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -474,7 +473,7 @@ fn get_wallet_address_for_network(network_name: &str) -> String {
|
|||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match ethereum::get_current_ethereum_wallet_for_network(network_name_proper) {
|
match ethereum::get_current_ethereum_wallet_for_network(network_name_proper) {
|
||||||
Ok(wallet) => wallet.address_string(),
|
Ok(wallet) => wallet.address_string(),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -493,7 +492,7 @@ fn clear_wallets_for_network(network_name: &str) -> bool {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
ethereum::clear_ethereum_wallets_for_network(network_name_proper);
|
ethereum::clear_ethereum_wallets_for_network(network_name_proper);
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@ -538,7 +537,7 @@ fn create_wallet_from_private_key_for_network(private_key: &str, network_name: &
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
match ethereum::create_ethereum_wallet_from_private_key_for_network(private_key, network) {
|
match ethereum::create_ethereum_wallet_from_private_key_for_network(private_key, network) {
|
||||||
Ok(_) => true,
|
Ok(_) => true,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
@ -554,13 +553,13 @@ fn create_agung_provider() -> String {
|
|||||||
Ok(provider) => {
|
Ok(provider) => {
|
||||||
// Generate a unique ID for the provider
|
// Generate a unique ID for the provider
|
||||||
let id = format!("provider_{}", uuid::Uuid::new_v4());
|
let id = format!("provider_{}", uuid::Uuid::new_v4());
|
||||||
|
|
||||||
// Store the provider in the registry
|
// Store the provider in the registry
|
||||||
if let Ok(mut providers) = PROVIDERS.lock() {
|
if let Ok(mut providers) = PROVIDERS.lock() {
|
||||||
providers.insert(id.clone(), provider);
|
providers.insert(id.clone(), provider);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
log::error!("Failed to acquire provider registry lock");
|
log::error!("Failed to acquire provider registry lock");
|
||||||
String::new()
|
String::new()
|
||||||
},
|
},
|
||||||
@ -581,7 +580,7 @@ fn get_balance(network_name: &str, address: &str) -> String {
|
|||||||
return String::new();
|
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,
|
||||||
@ -590,7 +589,7 @@ fn get_balance(network_name: &str, address: &str) -> String {
|
|||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the proper network name
|
// Get the proper network name
|
||||||
let network_name_proper = match ethereum::networks::get_proper_network_name(network_name) {
|
let network_name_proper = match ethereum::networks::get_proper_network_name(network_name) {
|
||||||
Some(name) => name,
|
Some(name) => name,
|
||||||
@ -599,7 +598,7 @@ fn get_balance(network_name: &str, address: &str) -> String {
|
|||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the network config
|
// Get the network config
|
||||||
let network = match ethereum::networks::get_network_by_name(network_name_proper) {
|
let network = match ethereum::networks::get_network_by_name(network_name_proper) {
|
||||||
Some(n) => n,
|
Some(n) => n,
|
||||||
@ -608,7 +607,7 @@ fn get_balance(network_name: &str, address: &str) -> String {
|
|||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a provider
|
// Create a provider
|
||||||
let provider = match ethereum::create_provider(&network) {
|
let provider = match ethereum::create_provider(&network) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
@ -617,7 +616,7 @@ fn get_balance(network_name: &str, address: &str) -> String {
|
|||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Execute the balance query in a blocking manner
|
// Execute the balance query in a blocking manner
|
||||||
match rt.block_on(async {
|
match rt.block_on(async {
|
||||||
ethereum::get_balance(&provider, addr).await
|
ethereum::get_balance(&provider, addr).await
|
||||||
@ -640,7 +639,7 @@ fn send_eth(wallet_network: &str, to_address: &str, amount_str: &str) -> String
|
|||||||
return String::new();
|
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,
|
||||||
@ -649,7 +648,7 @@ fn send_eth(wallet_network: &str, to_address: &str, amount_str: &str) -> String
|
|||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Parse the amount (using string to handle large numbers)
|
// Parse the amount (using string to handle large numbers)
|
||||||
let amount = match U256::from_dec_str(amount_str) {
|
let amount = match U256::from_dec_str(amount_str) {
|
||||||
Ok(amt) => amt,
|
Ok(amt) => amt,
|
||||||
@ -658,7 +657,7 @@ fn send_eth(wallet_network: &str, to_address: &str, amount_str: &str) -> String
|
|||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the proper network name
|
// Get the proper network name
|
||||||
let network_name_proper = match ethereum::networks::get_proper_network_name(wallet_network) {
|
let network_name_proper = match ethereum::networks::get_proper_network_name(wallet_network) {
|
||||||
Some(name) => name,
|
Some(name) => name,
|
||||||
@ -667,7 +666,7 @@ fn send_eth(wallet_network: &str, to_address: &str, amount_str: &str) -> String
|
|||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Get the wallet
|
// Get the wallet
|
||||||
let wallet = match ethereum::get_current_ethereum_wallet_for_network(network_name_proper) {
|
let wallet = match ethereum::get_current_ethereum_wallet_for_network(network_name_proper) {
|
||||||
Ok(w) => w,
|
Ok(w) => w,
|
||||||
@ -676,7 +675,7 @@ fn send_eth(wallet_network: &str, to_address: &str, amount_str: &str) -> String
|
|||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Create a provider
|
// Create a provider
|
||||||
let provider = match ethereum::create_provider(&wallet.network) {
|
let provider = match ethereum::create_provider(&wallet.network) {
|
||||||
Ok(p) => p,
|
Ok(p) => p,
|
||||||
@ -685,7 +684,7 @@ fn send_eth(wallet_network: &str, to_address: &str, amount_str: &str) -> String
|
|||||||
return String::new();
|
return String::new();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Execute the transaction in a blocking manner
|
// Execute the transaction in a blocking manner
|
||||||
match rt.block_on(async {
|
match rt.block_on(async {
|
||||||
ethereum::send_eth(&wallet, &provider, to_addr, amount).await
|
ethereum::send_eth(&wallet, &provider, to_addr, amount).await
|
||||||
@ -698,6 +697,175 @@ fn send_eth(wallet_network: &str, to_address: &str, amount_str: &str) -> String
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call a read-only function on a contract
|
||||||
|
fn call_contract_read(contract_json: &str, function_name: &str) -> 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// For simplicity, we're not handling arguments in this implementation
|
||||||
|
let tokens: Vec<Token> = Vec::new();
|
||||||
|
|
||||||
|
// 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 the result to a Rhai value
|
||||||
|
if result.is_empty() {
|
||||||
|
Dynamic::UNIT
|
||||||
|
} else {
|
||||||
|
// For simplicity, we'll just return the first value as a string
|
||||||
|
match &result[0] {
|
||||||
|
Token::String(s) => Dynamic::from(s.clone()),
|
||||||
|
Token::Uint(u) => Dynamic::from(u.to_string()),
|
||||||
|
Token::Int(i) => Dynamic::from(i.to_string()),
|
||||||
|
Token::Bool(b) => Dynamic::from(*b),
|
||||||
|
Token::Address(a) => Dynamic::from(format!("{:?}", a)),
|
||||||
|
_ => {
|
||||||
|
log::error!("Unsupported return type");
|
||||||
|
Dynamic::UNIT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
Err(e) => {
|
||||||
|
log::error!("Failed to call contract function: {}", e);
|
||||||
|
Dynamic::UNIT
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Call a state-changing function on a contract
|
||||||
|
fn call_contract_write(contract_json: &str, function_name: &str) -> 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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 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();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// For simplicity, we're not handling arguments in this implementation
|
||||||
|
let tokens: Vec<Token> = Vec::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::error!("Transaction failed: {}", e);
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Register crypto functions with the Rhai engine
|
/// Register crypto functions with the Rhai engine
|
||||||
pub fn register_crypto_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
|
pub fn register_crypto_module(engine: &mut Engine) -> Result<(), Box<EvalAltResult>> {
|
||||||
// Register key space functions
|
// Register key space functions
|
||||||
@ -705,33 +873,33 @@ pub fn register_crypto_module(engine: &mut Engine) -> Result<(), Box<EvalAltResu
|
|||||||
engine.register_fn("create_key_space", create_key_space);
|
engine.register_fn("create_key_space", create_key_space);
|
||||||
engine.register_fn("encrypt_key_space", encrypt_key_space);
|
engine.register_fn("encrypt_key_space", encrypt_key_space);
|
||||||
engine.register_fn("decrypt_key_space", decrypt_key_space);
|
engine.register_fn("decrypt_key_space", decrypt_key_space);
|
||||||
|
|
||||||
// Register keypair functions
|
// Register keypair functions
|
||||||
engine.register_fn("create_keypair", create_keypair);
|
engine.register_fn("create_keypair", create_keypair);
|
||||||
engine.register_fn("select_keypair", select_keypair);
|
engine.register_fn("select_keypair", select_keypair);
|
||||||
engine.register_fn("list_keypairs", list_keypairs);
|
engine.register_fn("list_keypairs", list_keypairs);
|
||||||
|
|
||||||
// Register signing/verification functions
|
// Register signing/verification functions
|
||||||
engine.register_fn("sign", sign);
|
engine.register_fn("sign", sign);
|
||||||
engine.register_fn("verify", verify);
|
engine.register_fn("verify", verify);
|
||||||
|
|
||||||
// Register symmetric encryption functions
|
// Register symmetric encryption functions
|
||||||
engine.register_fn("generate_key", generate_key);
|
engine.register_fn("generate_key", generate_key);
|
||||||
engine.register_fn("encrypt", encrypt);
|
engine.register_fn("encrypt", encrypt);
|
||||||
engine.register_fn("decrypt", decrypt);
|
engine.register_fn("decrypt", decrypt);
|
||||||
|
|
||||||
// Register Ethereum functions (Gnosis Chain)
|
// Register Ethereum functions (Gnosis Chain)
|
||||||
engine.register_fn("create_ethereum_wallet", create_ethereum_wallet);
|
engine.register_fn("create_ethereum_wallet", create_ethereum_wallet);
|
||||||
engine.register_fn("get_ethereum_address", get_ethereum_address);
|
engine.register_fn("get_ethereum_address", get_ethereum_address);
|
||||||
|
|
||||||
// Register Peaq network functions
|
// Register Peaq network functions
|
||||||
engine.register_fn("create_peaq_wallet", create_peaq_wallet);
|
engine.register_fn("create_peaq_wallet", create_peaq_wallet);
|
||||||
engine.register_fn("get_peaq_address", get_peaq_address);
|
engine.register_fn("get_peaq_address", get_peaq_address);
|
||||||
|
|
||||||
// Register Agung testnet functions
|
// Register Agung testnet functions
|
||||||
engine.register_fn("create_agung_wallet", create_agung_wallet);
|
engine.register_fn("create_agung_wallet", create_agung_wallet);
|
||||||
engine.register_fn("get_agung_address", get_agung_address);
|
engine.register_fn("get_agung_address", get_agung_address);
|
||||||
|
|
||||||
// Register generic network functions
|
// Register generic network functions
|
||||||
engine.register_fn("create_wallet_for_network", create_wallet_for_network);
|
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("get_wallet_address_for_network", get_wallet_address_for_network);
|
||||||
@ -739,12 +907,18 @@ pub fn register_crypto_module(engine: &mut Engine) -> Result<(), Box<EvalAltResu
|
|||||||
engine.register_fn("list_supported_networks", list_supported_networks);
|
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_token_symbol", get_network_token_symbol);
|
||||||
engine.register_fn("get_network_explorer_url", get_network_explorer_url);
|
engine.register_fn("get_network_explorer_url", get_network_explorer_url);
|
||||||
|
|
||||||
// Register new Ethereum functions for wallet creation from private key and transactions
|
// 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_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("create_agung_provider", create_agung_provider);
|
||||||
engine.register_fn("send_eth", send_eth);
|
engine.register_fn("send_eth", send_eth);
|
||||||
engine.register_fn("get_balance", get_balance);
|
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);
|
||||||
|
engine.register_fn("call_contract_read", call_contract_read);
|
||||||
|
engine.register_fn("call_contract_write", call_contract_write);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user