...
This commit is contained in:
160
packages/crypt/vault/_archive/src/ethereum/README.md
Normal file
160
packages/crypt/vault/_archive/src/ethereum/README.md
Normal file
@@ -0,0 +1,160 @@
|
||||
# Hero Vault Ethereum Module
|
||||
|
||||
The Ethereum module provides functionality for creating and managing Ethereum wallets and interacting with smart contracts on EVM-based blockchains.
|
||||
|
||||
## Module Structure
|
||||
|
||||
The Ethereum module is organized into several components:
|
||||
|
||||
- `wallet.rs` - Core Ethereum wallet implementation
|
||||
- `networks.rs` - Network registry and configuration
|
||||
- `provider.rs` - Provider creation and management
|
||||
- `transaction.rs` - Transaction-related functionality
|
||||
- `storage.rs` - Wallet storage functionality
|
||||
- `contract.rs` - Smart contract interaction functionality
|
||||
- `contract_utils.rs` - Utilities for contract interactions
|
||||
|
||||
## Key Features
|
||||
|
||||
### Wallet Management
|
||||
|
||||
The module provides functionality for creating and managing Ethereum wallets:
|
||||
|
||||
```rust
|
||||
// Create a new Ethereum wallet for a specific network
|
||||
let wallet = create_ethereum_wallet_for_network("Ethereum")?;
|
||||
|
||||
// Create a wallet for specific networks
|
||||
let peaq_wallet = create_peaq_wallet()?;
|
||||
let agung_wallet = create_agung_wallet()?;
|
||||
|
||||
// Create a wallet with a specific name
|
||||
let named_wallet = create_ethereum_wallet_from_name_for_network("my_wallet", "Gnosis")?;
|
||||
|
||||
// Create a wallet from a private key
|
||||
let imported_wallet = create_ethereum_wallet_from_private_key("0x...")?;
|
||||
|
||||
// Get the current wallet for a network
|
||||
let current_wallet = get_current_ethereum_wallet_for_network("Ethereum")?;
|
||||
|
||||
// Clear wallets
|
||||
clear_ethereum_wallets()?;
|
||||
clear_ethereum_wallets_for_network("Gnosis")?;
|
||||
```
|
||||
|
||||
### Network Management
|
||||
|
||||
The module supports multiple Ethereum networks and provides functionality for managing network configurations:
|
||||
|
||||
```rust
|
||||
// Get a network configuration by name
|
||||
let network = get_network_by_name("Ethereum")?;
|
||||
|
||||
// Get the proper network name (normalized)
|
||||
let name = get_proper_network_name("eth")?; // Returns "Ethereum"
|
||||
|
||||
// List all available network names
|
||||
let networks = list_network_names()?;
|
||||
|
||||
// Get all network configurations
|
||||
let all_networks = get_all_networks()?;
|
||||
```
|
||||
|
||||
### Provider Management
|
||||
|
||||
The module provides functionality for creating and managing Ethereum providers:
|
||||
|
||||
```rust
|
||||
// Create a provider for a specific network
|
||||
let provider = create_provider("Ethereum")?;
|
||||
|
||||
// Create providers for specific networks
|
||||
let gnosis_provider = create_gnosis_provider()?;
|
||||
let peaq_provider = create_peaq_provider()?;
|
||||
let agung_provider = create_agung_provider()?;
|
||||
```
|
||||
|
||||
### Transaction Management
|
||||
|
||||
The module provides functionality for managing Ethereum transactions:
|
||||
|
||||
```rust
|
||||
// Get the balance of an address
|
||||
let balance = get_balance("Ethereum", "0x...")?;
|
||||
|
||||
// Send ETH to an address
|
||||
let tx_hash = send_eth("Ethereum", "0x...", "1000000000000000")?;
|
||||
|
||||
// Format a balance for display
|
||||
let formatted = format_balance(balance, 18)?; // Convert wei to ETH
|
||||
```
|
||||
|
||||
### Smart Contract Interactions
|
||||
|
||||
The module provides functionality for interacting with smart contracts:
|
||||
|
||||
```rust
|
||||
// Load a contract ABI from JSON
|
||||
let abi = load_abi_from_json(json_string)?;
|
||||
|
||||
// Create a contract instance
|
||||
let contract = Contract::new(provider, "0x...", abi)?;
|
||||
|
||||
// Call a read-only function
|
||||
let result = call_read_function(contract, "balanceOf", vec!["0x..."])?;
|
||||
|
||||
// Call a write function
|
||||
let tx_hash = call_write_function(contract, "transfer", vec!["0x...", "1000"])?;
|
||||
|
||||
// Estimate gas for a function call
|
||||
let gas = estimate_gas(contract, "transfer", vec!["0x...", "1000"])?;
|
||||
```
|
||||
|
||||
### Contract Utilities
|
||||
|
||||
The module provides utilities for working with contract function arguments and return values:
|
||||
|
||||
```rust
|
||||
// Convert Rhai values to Ethereum tokens
|
||||
let token = convert_rhai_to_token(value)?;
|
||||
|
||||
// Prepare function arguments
|
||||
let args = prepare_function_arguments(function, vec![arg1, arg2])?;
|
||||
|
||||
// Convert Ethereum tokens to Rhai values
|
||||
let rhai_value = convert_token_to_rhai(token)?;
|
||||
|
||||
// Convert a token to a dynamic value
|
||||
let dynamic = token_to_dynamic(token)?;
|
||||
```
|
||||
|
||||
## Supported Networks
|
||||
|
||||
The module supports multiple Ethereum networks, including:
|
||||
|
||||
- Gnosis Chain
|
||||
- Peaq Network
|
||||
- Agung Network
|
||||
|
||||
Each network has its own configuration, including:
|
||||
|
||||
- RPC URL
|
||||
- Chain ID
|
||||
- Explorer URL
|
||||
- Native currency symbol and decimals
|
||||
|
||||
## Error Handling
|
||||
|
||||
The module uses the `CryptoError` type for handling errors that can occur during Ethereum operations:
|
||||
|
||||
- `InvalidAddress` - Invalid Ethereum address format
|
||||
- `ContractError` - Smart contract interaction error
|
||||
|
||||
## Examples
|
||||
|
||||
For examples of how to use the Ethereum module, see the `examples/hero_vault` directory, particularly:
|
||||
|
||||
- `contract_example.rhai` - Demonstrates loading a contract ABI and interacting with smart contracts
|
||||
- `agung_simple_transfer.rhai` - Shows how to perform a simple ETH transfer on the Agung network
|
||||
- `agung_send_transaction.rhai` - Demonstrates sending transactions on the Agung network
|
||||
- `agung_contract_with_args.rhai` - Shows how to interact with contracts with arguments on Agung
|
197
packages/crypt/vault/_archive/src/ethereum/contract.rs
Normal file
197
packages/crypt/vault/_archive/src/ethereum/contract.rs
Normal file
@@ -0,0 +1,197 @@
|
||||
//! Smart contract interaction functionality.
|
||||
//!
|
||||
//! This module provides functionality for interacting with smart contracts on EVM-based blockchains.
|
||||
|
||||
use ethers::abi::{Abi, Token};
|
||||
use ethers::prelude::*;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::str::FromStr;
|
||||
use std::sync::Arc;
|
||||
|
||||
use super::networks::NetworkConfig;
|
||||
use super::wallet::EthereumWallet;
|
||||
use crate::error::CryptoError;
|
||||
|
||||
/// 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 with gas limit
|
||||
let tx = TransactionRequest::new()
|
||||
.to(contract.address)
|
||||
.data(call_data)
|
||||
.gas(U256::from(300000)); // Set a reasonable gas limit
|
||||
|
||||
// Send the transaction using the client directly
|
||||
log::info!("Sending transaction to contract at {}", contract.address);
|
||||
log::info!("Function: {}, Args: {:?}", function_name, args);
|
||||
|
||||
// Log detailed information about the transaction
|
||||
log::debug!("Sending transaction to contract at {}", contract.address);
|
||||
log::debug!("Function: {}, Args: {:?}", function_name, args);
|
||||
log::debug!("From address: {}", wallet.address);
|
||||
log::debug!("Gas limit: {:?}", tx.gas);
|
||||
|
||||
let pending_tx = match client.send_transaction(tx, None).await {
|
||||
Ok(pending_tx) => {
|
||||
log::debug!("Transaction sent successfully: {:?}", pending_tx.tx_hash());
|
||||
log::info!("Transaction sent successfully: {:?}", pending_tx.tx_hash());
|
||||
pending_tx
|
||||
}
|
||||
Err(e) => {
|
||||
// Log the error for debugging
|
||||
log::error!("Failed to send transaction: {}", e);
|
||||
log::error!("ERROR DETAILS: {:?}", e);
|
||||
return Err(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)
|
||||
}
|
187
packages/crypt/vault/_archive/src/ethereum/contract_utils.rs
Normal file
187
packages/crypt/vault/_archive/src/ethereum/contract_utils.rs
Normal file
@@ -0,0 +1,187 @@
|
||||
//! Utility functions for smart contract interactions.
|
||||
|
||||
use ethers::abi::{Abi, ParamType, Token};
|
||||
use ethers::types::{Address, U256};
|
||||
use rhai::{Array, Dynamic};
|
||||
use std::str::FromStr;
|
||||
|
||||
/// Convert Rhai Dynamic values to ethers Token types
|
||||
pub fn convert_rhai_to_token(
|
||||
value: &Dynamic,
|
||||
expected_type: Option<&ParamType>,
|
||||
) -> Result<Token, String> {
|
||||
match value {
|
||||
// Handle integers
|
||||
v if v.is_int() => {
|
||||
let i = v.as_int().unwrap();
|
||||
if let Some(param_type) = expected_type {
|
||||
match param_type {
|
||||
ParamType::Uint(_) => Ok(Token::Uint(U256::from(i as u64))),
|
||||
ParamType::Int(_) => {
|
||||
// Convert to I256 - in a real implementation, we would handle this properly
|
||||
// For now, we'll just use U256 for both types
|
||||
Ok(Token::Uint(U256::from(i as u64)))
|
||||
}
|
||||
_ => Err(format!("Expected {}, got integer", param_type)),
|
||||
}
|
||||
} else {
|
||||
// Default to Uint256 if no type info
|
||||
Ok(Token::Uint(U256::from(i as u64)))
|
||||
}
|
||||
}
|
||||
|
||||
// Handle strings and addresses
|
||||
v if v.is_string() => {
|
||||
let s = v.to_string();
|
||||
if let Some(param_type) = expected_type {
|
||||
match param_type {
|
||||
ParamType::Address => match Address::from_str(&s) {
|
||||
Ok(addr) => Ok(Token::Address(addr)),
|
||||
Err(e) => Err(format!("Invalid address format: {}", e)),
|
||||
},
|
||||
ParamType::String => Ok(Token::String(s)),
|
||||
ParamType::Bytes => {
|
||||
// Handle hex string conversion to bytes
|
||||
if s.starts_with("0x") {
|
||||
match ethers::utils::hex::decode(&s[2..]) {
|
||||
Ok(bytes) => Ok(Token::Bytes(bytes)),
|
||||
Err(e) => Err(format!("Invalid hex string: {}", e)),
|
||||
}
|
||||
} else {
|
||||
Ok(Token::Bytes(s.as_bytes().to_vec()))
|
||||
}
|
||||
}
|
||||
_ => Err(format!("Expected {}, got string", param_type)),
|
||||
}
|
||||
} else {
|
||||
// Try to detect type from string format
|
||||
if s.starts_with("0x") && s.len() == 42 {
|
||||
// Likely an address
|
||||
match Address::from_str(&s) {
|
||||
Ok(addr) => Ok(Token::Address(addr)),
|
||||
Err(_) => Ok(Token::String(s)),
|
||||
}
|
||||
} else {
|
||||
Ok(Token::String(s))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle booleans
|
||||
v if v.is_bool() => {
|
||||
let b = v.as_bool().unwrap();
|
||||
if let Some(param_type) = expected_type {
|
||||
if matches!(param_type, ParamType::Bool) {
|
||||
Ok(Token::Bool(b))
|
||||
} else {
|
||||
Err(format!("Expected {}, got boolean", param_type))
|
||||
}
|
||||
} else {
|
||||
Ok(Token::Bool(b))
|
||||
}
|
||||
}
|
||||
|
||||
// Handle arrays
|
||||
v if v.is_array() => {
|
||||
let arr = v.clone().into_array().unwrap();
|
||||
if let Some(ParamType::Array(inner_type)) = expected_type {
|
||||
let mut tokens = Vec::new();
|
||||
for item in arr.iter() {
|
||||
match convert_rhai_to_token(item, Some(inner_type)) {
|
||||
Ok(token) => tokens.push(token),
|
||||
Err(e) => return Err(e),
|
||||
}
|
||||
}
|
||||
Ok(Token::Array(tokens))
|
||||
} else {
|
||||
Err("Array type mismatch or no type information available".to_string())
|
||||
}
|
||||
}
|
||||
|
||||
// Handle other types or return error
|
||||
_ => Err(format!("Unsupported Rhai type: {:?}", value)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Validate and convert arguments based on function ABI
|
||||
pub fn prepare_function_arguments(
|
||||
abi: &Abi,
|
||||
function_name: &str,
|
||||
args: &Array,
|
||||
) -> Result<Vec<Token>, String> {
|
||||
// Get the function from the ABI
|
||||
let function = abi
|
||||
.function(function_name)
|
||||
.map_err(|e| format!("Function not found in ABI: {}", e))?;
|
||||
|
||||
// Check if number of arguments matches
|
||||
if function.inputs.len() != args.len() {
|
||||
return Err(format!(
|
||||
"Wrong number of arguments for function '{}': expected {}, got {}",
|
||||
function_name,
|
||||
function.inputs.len(),
|
||||
args.len()
|
||||
));
|
||||
}
|
||||
|
||||
// Convert each argument according to the expected type
|
||||
let mut tokens = Vec::new();
|
||||
for (i, (param, arg)) in function.inputs.iter().zip(args.iter()).enumerate() {
|
||||
match convert_rhai_to_token(arg, Some(¶m.kind)) {
|
||||
Ok(token) => tokens.push(token),
|
||||
Err(e) => return Err(format!("Error converting argument {}: {}", i, e)),
|
||||
}
|
||||
}
|
||||
|
||||
Ok(tokens)
|
||||
}
|
||||
|
||||
/// Convert ethers Token to Rhai Dynamic value
|
||||
pub fn convert_token_to_rhai(tokens: &[Token]) -> Dynamic {
|
||||
if tokens.is_empty() {
|
||||
return Dynamic::UNIT;
|
||||
}
|
||||
|
||||
// If there's only one return value, return it directly
|
||||
if tokens.len() == 1 {
|
||||
return token_to_dynamic(&tokens[0]);
|
||||
}
|
||||
|
||||
// If there are multiple return values, return them as an array
|
||||
let mut array = Array::new();
|
||||
for token in tokens {
|
||||
array.push(token_to_dynamic(token));
|
||||
}
|
||||
Dynamic::from(array)
|
||||
}
|
||||
|
||||
/// Convert a single token to a Dynamic value
|
||||
pub fn token_to_dynamic(token: &Token) -> Dynamic {
|
||||
match token {
|
||||
Token::Address(addr) => Dynamic::from(format!("{:?}", addr)),
|
||||
Token::Bytes(bytes) => Dynamic::from(ethers::utils::hex::encode(bytes)),
|
||||
Token::Int(i) => Dynamic::from(i.to_string()),
|
||||
Token::Uint(u) => Dynamic::from(u.to_string()),
|
||||
Token::Bool(b) => Dynamic::from(*b),
|
||||
Token::String(s) => Dynamic::from(s.clone()),
|
||||
Token::Array(arr) => {
|
||||
let mut rhai_arr = Array::new();
|
||||
for item in arr {
|
||||
rhai_arr.push(token_to_dynamic(item));
|
||||
}
|
||||
Dynamic::from(rhai_arr)
|
||||
}
|
||||
Token::Tuple(tuple) => {
|
||||
let mut rhai_arr = Array::new();
|
||||
for item in tuple {
|
||||
rhai_arr.push(token_to_dynamic(item));
|
||||
}
|
||||
Dynamic::from(rhai_arr)
|
||||
}
|
||||
// Handle other token types
|
||||
_ => {
|
||||
log::warn!("Unsupported token type: {:?}", token);
|
||||
Dynamic::UNIT
|
||||
}
|
||||
}
|
||||
}
|
59
packages/crypt/vault/_archive/src/ethereum/mod.rs
Normal file
59
packages/crypt/vault/_archive/src/ethereum/mod.rs
Normal file
@@ -0,0 +1,59 @@
|
||||
//! Ethereum wallet functionality
|
||||
//!
|
||||
//! 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:
|
||||
//! - `wallet.rs`: Core Ethereum wallet implementation
|
||||
//! - `networks.rs`: Network registry and configuration
|
||||
//! - `provider.rs`: Provider creation and management
|
||||
//! - `transaction.rs`: Transaction-related functionality
|
||||
//! - `storage.rs`: Wallet storage functionality
|
||||
//! - `contract.rs`: Smart contract interaction functionality
|
||||
|
||||
mod contract;
|
||||
pub mod contract_utils;
|
||||
pub mod networks;
|
||||
mod provider;
|
||||
mod storage;
|
||||
mod transaction;
|
||||
mod wallet;
|
||||
// Re-export public types and functions
|
||||
pub use networks::NetworkConfig;
|
||||
pub use wallet::EthereumWallet;
|
||||
|
||||
// Re-export wallet creation functions
|
||||
pub use storage::{
|
||||
create_agung_wallet, create_ethereum_wallet_for_network, create_ethereum_wallet_from_name,
|
||||
create_ethereum_wallet_from_name_for_network, create_ethereum_wallet_from_private_key,
|
||||
create_ethereum_wallet_from_private_key_for_network, create_peaq_wallet,
|
||||
};
|
||||
|
||||
// Re-export wallet management functions
|
||||
pub use storage::{
|
||||
clear_ethereum_wallets, clear_ethereum_wallets_for_network, get_current_agung_wallet,
|
||||
get_current_ethereum_wallet_for_network, get_current_peaq_wallet,
|
||||
};
|
||||
|
||||
// Re-export provider functions
|
||||
pub use provider::{
|
||||
create_agung_provider, create_gnosis_provider, create_peaq_provider, create_provider,
|
||||
};
|
||||
|
||||
// Re-export transaction functions
|
||||
pub use transaction::{format_balance, get_balance, send_eth};
|
||||
|
||||
// Re-export network registry functions
|
||||
pub use networks::{
|
||||
get_all_networks, get_network_by_name, get_proper_network_name, list_network_names, names,
|
||||
};
|
||||
|
||||
// Re-export contract functions
|
||||
pub use contract::{
|
||||
call_read_function, call_write_function, estimate_gas, load_abi_from_json, Contract,
|
||||
};
|
||||
|
||||
// Re-export contract utility functions
|
||||
pub use contract_utils::{
|
||||
convert_rhai_to_token, convert_token_to_rhai, prepare_function_arguments, token_to_dynamic,
|
||||
};
|
102
packages/crypt/vault/_archive/src/ethereum/networks.rs
Normal file
102
packages/crypt/vault/_archive/src/ethereum/networks.rs
Normal file
@@ -0,0 +1,102 @@
|
||||
//! Ethereum network registry
|
||||
//!
|
||||
//! This module provides a centralized registry of Ethereum networks and utilities
|
||||
//! to work with them.
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::collections::HashMap;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
/// Configuration for an EVM-compatible network
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct NetworkConfig {
|
||||
pub name: String,
|
||||
pub chain_id: u64,
|
||||
pub rpc_url: String,
|
||||
pub explorer_url: String,
|
||||
pub token_symbol: String,
|
||||
pub decimals: u8,
|
||||
}
|
||||
|
||||
/// Network name constants
|
||||
pub mod names {
|
||||
pub const GNOSIS: &str = "Gnosis";
|
||||
pub const PEAQ: &str = "Peaq";
|
||||
pub const AGUNG: &str = "Agung";
|
||||
}
|
||||
|
||||
/// Get the Gnosis Chain network configuration
|
||||
pub fn gnosis() -> NetworkConfig {
|
||||
NetworkConfig {
|
||||
name: names::GNOSIS.to_string(),
|
||||
chain_id: 100,
|
||||
rpc_url: "https://rpc.gnosischain.com".to_string(),
|
||||
explorer_url: "https://gnosisscan.io".to_string(),
|
||||
token_symbol: "xDAI".to_string(),
|
||||
decimals: 18,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Peaq Network configuration
|
||||
pub fn peaq() -> NetworkConfig {
|
||||
NetworkConfig {
|
||||
name: names::PEAQ.to_string(),
|
||||
chain_id: 3338,
|
||||
rpc_url: "https://peaq.api.onfinality.io/public".to_string(),
|
||||
explorer_url: "https://peaq.subscan.io/".to_string(),
|
||||
token_symbol: "PEAQ".to_string(),
|
||||
decimals: 18,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the Agung Testnet configuration
|
||||
pub fn agung() -> NetworkConfig {
|
||||
NetworkConfig {
|
||||
name: names::AGUNG.to_string(),
|
||||
chain_id: 9990,
|
||||
rpc_url: "https://wss-async.agung.peaq.network".to_string(),
|
||||
explorer_url: "https://agung-testnet.subscan.io/".to_string(),
|
||||
token_symbol: "AGNG".to_string(),
|
||||
decimals: 18,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a network by its name (case-insensitive)
|
||||
pub fn get_network_by_name(name: &str) -> Option<NetworkConfig> {
|
||||
let name_lower = name.to_lowercase();
|
||||
match name_lower.as_str() {
|
||||
"gnosis" => Some(gnosis()),
|
||||
"peaq" => Some(peaq()),
|
||||
"agung" => Some(agung()),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the proper capitalization of a network name
|
||||
pub fn get_proper_network_name(name: &str) -> Option<&'static str> {
|
||||
let name_lower = name.to_lowercase();
|
||||
match name_lower.as_str() {
|
||||
"gnosis" => Some(names::GNOSIS),
|
||||
"peaq" => Some(names::PEAQ),
|
||||
"agung" => Some(names::AGUNG),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a list of all supported network names
|
||||
pub fn list_network_names() -> Vec<&'static str> {
|
||||
vec![names::GNOSIS, names::PEAQ, names::AGUNG]
|
||||
}
|
||||
|
||||
/// Get a map of all networks
|
||||
pub fn get_all_networks() -> &'static HashMap<&'static str, NetworkConfig> {
|
||||
static NETWORKS: OnceLock<HashMap<&'static str, NetworkConfig>> = OnceLock::new();
|
||||
|
||||
NETWORKS.get_or_init(|| {
|
||||
let mut map = HashMap::new();
|
||||
map.insert(names::GNOSIS, gnosis());
|
||||
map.insert(names::PEAQ, peaq());
|
||||
map.insert(names::AGUNG, agung());
|
||||
map
|
||||
})
|
||||
}
|
31
packages/crypt/vault/_archive/src/ethereum/provider.rs
Normal file
31
packages/crypt/vault/_archive/src/ethereum/provider.rs
Normal file
@@ -0,0 +1,31 @@
|
||||
//! Ethereum provider functionality.
|
||||
|
||||
use ethers::prelude::*;
|
||||
|
||||
use super::networks::{self, NetworkConfig};
|
||||
use crate::error::CryptoError;
|
||||
|
||||
/// Creates a provider for a specific network.
|
||||
pub fn create_provider(network: &NetworkConfig) -> Result<Provider<Http>, CryptoError> {
|
||||
Provider::<Http>::try_from(network.rpc_url.as_str()).map_err(|e| {
|
||||
CryptoError::SerializationError(format!(
|
||||
"Failed to create provider for {}: {}",
|
||||
network.name, e
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a provider for the Gnosis Chain.
|
||||
pub fn create_gnosis_provider() -> Result<Provider<Http>, CryptoError> {
|
||||
create_provider(&networks::gnosis())
|
||||
}
|
||||
|
||||
/// Creates a provider for the Peaq network.
|
||||
pub fn create_peaq_provider() -> Result<Provider<Http>, CryptoError> {
|
||||
create_provider(&networks::peaq())
|
||||
}
|
||||
|
||||
/// Creates a provider for the Agung testnet.
|
||||
pub fn create_agung_provider() -> Result<Provider<Http>, CryptoError> {
|
||||
create_provider(&networks::agung())
|
||||
}
|
133
packages/crypt/vault/_archive/src/ethereum/storage.rs
Normal file
133
packages/crypt/vault/_archive/src/ethereum/storage.rs
Normal file
@@ -0,0 +1,133 @@
|
||||
//! Ethereum wallet storage functionality.
|
||||
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::HashMap;
|
||||
use std::sync::Mutex;
|
||||
|
||||
use super::networks::{self, NetworkConfig};
|
||||
use super::wallet::EthereumWallet;
|
||||
use crate::error::CryptoError;
|
||||
|
||||
/// Global storage for Ethereum wallets.
|
||||
static ETH_WALLETS: Lazy<Mutex<HashMap<String, Vec<EthereumWallet>>>> =
|
||||
Lazy::new(|| Mutex::new(HashMap::new()));
|
||||
|
||||
/// Creates an Ethereum wallet from the currently selected keypair for a specific network.
|
||||
pub fn create_ethereum_wallet_for_network(
|
||||
network: NetworkConfig,
|
||||
) -> Result<EthereumWallet, CryptoError> {
|
||||
// Get the currently selected keypair
|
||||
let keypair = crate::keyspace::get_selected_keypair()?;
|
||||
|
||||
// Create an Ethereum wallet from the keypair
|
||||
let wallet = EthereumWallet::from_keypair(&keypair, network)?;
|
||||
|
||||
// Store the wallet
|
||||
let mut wallets = ETH_WALLETS.lock().unwrap();
|
||||
let network_wallets = wallets
|
||||
.entry(wallet.network.name.clone())
|
||||
.or_insert_with(Vec::new);
|
||||
network_wallets.push(wallet.clone());
|
||||
|
||||
Ok(wallet)
|
||||
}
|
||||
|
||||
/// Creates an Ethereum wallet from the currently selected keypair for the Peaq network.
|
||||
pub fn create_peaq_wallet() -> Result<EthereumWallet, CryptoError> {
|
||||
create_ethereum_wallet_for_network(networks::peaq())
|
||||
}
|
||||
|
||||
/// Creates an Ethereum wallet from the currently selected keypair for the Agung testnet.
|
||||
pub fn create_agung_wallet() -> Result<EthereumWallet, CryptoError> {
|
||||
create_ethereum_wallet_for_network(networks::agung())
|
||||
}
|
||||
|
||||
/// Gets the current Ethereum wallet for a specific network.
|
||||
pub fn get_current_ethereum_wallet_for_network(
|
||||
network_name: &str,
|
||||
) -> Result<EthereumWallet, CryptoError> {
|
||||
let wallets = ETH_WALLETS.lock().unwrap();
|
||||
|
||||
let network_wallets = wallets
|
||||
.get(network_name)
|
||||
.ok_or(CryptoError::NoKeypairSelected)?;
|
||||
|
||||
if network_wallets.is_empty() {
|
||||
return Err(CryptoError::NoKeypairSelected);
|
||||
}
|
||||
|
||||
Ok(network_wallets.last().unwrap().clone())
|
||||
}
|
||||
|
||||
/// Gets the current Ethereum wallet for the Peaq network.
|
||||
pub fn get_current_peaq_wallet() -> Result<EthereumWallet, CryptoError> {
|
||||
get_current_ethereum_wallet_for_network("Peaq")
|
||||
}
|
||||
|
||||
/// Gets the current Ethereum wallet for the Agung testnet.
|
||||
pub fn get_current_agung_wallet() -> Result<EthereumWallet, CryptoError> {
|
||||
get_current_ethereum_wallet_for_network("Agung")
|
||||
}
|
||||
|
||||
/// Clears all Ethereum wallets.
|
||||
pub fn clear_ethereum_wallets() {
|
||||
let mut wallets = ETH_WALLETS.lock().unwrap();
|
||||
wallets.clear();
|
||||
}
|
||||
|
||||
/// Clears Ethereum wallets for a specific network.
|
||||
pub fn clear_ethereum_wallets_for_network(network_name: &str) {
|
||||
let mut wallets = ETH_WALLETS.lock().unwrap();
|
||||
wallets.remove(network_name);
|
||||
}
|
||||
|
||||
/// Creates an Ethereum wallet from a name and the currently selected keypair for a specific network.
|
||||
pub fn create_ethereum_wallet_from_name_for_network(
|
||||
name: &str,
|
||||
network: NetworkConfig,
|
||||
) -> Result<EthereumWallet, CryptoError> {
|
||||
// Get the currently selected keypair
|
||||
let keypair = crate::keyspace::get_selected_keypair()?;
|
||||
|
||||
// Create an Ethereum wallet from the name and keypair
|
||||
let wallet = EthereumWallet::from_name_and_keypair(name, &keypair, network)?;
|
||||
|
||||
// Store the wallet
|
||||
let mut wallets = ETH_WALLETS.lock().unwrap();
|
||||
let network_wallets = wallets
|
||||
.entry(wallet.network.name.clone())
|
||||
.or_insert_with(Vec::new);
|
||||
network_wallets.push(wallet.clone());
|
||||
|
||||
Ok(wallet)
|
||||
}
|
||||
|
||||
/// Creates an Ethereum wallet from a name and the currently selected keypair for the Gnosis network.
|
||||
pub fn create_ethereum_wallet_from_name(name: &str) -> Result<EthereumWallet, CryptoError> {
|
||||
create_ethereum_wallet_from_name_for_network(name, networks::gnosis())
|
||||
}
|
||||
|
||||
/// Creates an Ethereum wallet from a private key for a specific network.
|
||||
pub fn create_ethereum_wallet_from_private_key_for_network(
|
||||
private_key: &str,
|
||||
network: NetworkConfig,
|
||||
) -> Result<EthereumWallet, CryptoError> {
|
||||
// Create an Ethereum wallet from the private key
|
||||
let wallet = EthereumWallet::from_private_key(private_key, network)?;
|
||||
|
||||
// Store the wallet
|
||||
let mut wallets = ETH_WALLETS.lock().unwrap();
|
||||
let network_wallets = wallets
|
||||
.entry(wallet.network.name.clone())
|
||||
.or_insert_with(Vec::new);
|
||||
network_wallets.push(wallet.clone());
|
||||
|
||||
Ok(wallet)
|
||||
}
|
||||
|
||||
/// Creates an Ethereum wallet from a private key for the Gnosis network.
|
||||
pub fn create_ethereum_wallet_from_private_key(
|
||||
private_key: &str,
|
||||
) -> Result<EthereumWallet, CryptoError> {
|
||||
create_ethereum_wallet_from_private_key_for_network(private_key, networks::gnosis())
|
||||
}
|
@@ -0,0 +1,47 @@
|
||||
//! Tests for smart contract argument handling functionality.
|
||||
|
||||
use ethers::types::Address;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::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());
|
||||
}
|
@@ -0,0 +1,83 @@
|
||||
//! Tests for smart contract functionality.
|
||||
|
||||
use ethers::types::Address;
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::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.
|
7
packages/crypt/vault/_archive/src/ethereum/tests/mod.rs
Normal file
7
packages/crypt/vault/_archive/src/ethereum/tests/mod.rs
Normal file
@@ -0,0 +1,7 @@
|
||||
//! Tests for Ethereum functionality.
|
||||
|
||||
mod wallet_tests;
|
||||
mod network_tests;
|
||||
mod transaction_tests;
|
||||
mod contract_tests;
|
||||
mod contract_args_tests;
|
@@ -0,0 +1,74 @@
|
||||
//! Tests for Ethereum network functionality.
|
||||
|
||||
use crate::vault::ethereum::*;
|
||||
|
||||
#[test]
|
||||
fn test_network_config() {
|
||||
let gnosis = networks::gnosis();
|
||||
assert_eq!(gnosis.name, "Gnosis");
|
||||
assert_eq!(gnosis.chain_id, 100);
|
||||
assert_eq!(gnosis.token_symbol, "xDAI");
|
||||
|
||||
let peaq = networks::peaq();
|
||||
assert_eq!(peaq.name, "Peaq");
|
||||
assert_eq!(peaq.chain_id, 3338);
|
||||
assert_eq!(peaq.token_symbol, "PEAQ");
|
||||
|
||||
let agung = networks::agung();
|
||||
assert_eq!(agung.name, "Agung");
|
||||
assert_eq!(agung.chain_id, 9990);
|
||||
assert_eq!(agung.token_symbol, "AGNG");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_network_registry() {
|
||||
let network_names = networks::list_network_names();
|
||||
assert!(network_names.iter().any(|&name| name == "Gnosis"));
|
||||
assert!(network_names.iter().any(|&name| name == "Peaq"));
|
||||
assert!(network_names.iter().any(|&name| name == "Agung"));
|
||||
|
||||
let gnosis_proper = networks::get_proper_network_name("gnosis");
|
||||
assert_eq!(gnosis_proper, Some("Gnosis"));
|
||||
|
||||
let peaq_proper = networks::get_proper_network_name("peaq");
|
||||
assert_eq!(peaq_proper, Some("Peaq"));
|
||||
|
||||
let agung_proper = networks::get_proper_network_name("agung");
|
||||
assert_eq!(agung_proper, Some("Agung"));
|
||||
|
||||
let unknown = networks::get_proper_network_name("unknown");
|
||||
assert_eq!(unknown, None);
|
||||
|
||||
let gnosis_config = networks::get_network_by_name("Gnosis");
|
||||
assert!(gnosis_config.is_some());
|
||||
assert_eq!(gnosis_config.unwrap().chain_id, 100);
|
||||
|
||||
let unknown_config = networks::get_network_by_name("Unknown");
|
||||
assert!(unknown_config.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_provider() {
|
||||
let gnosis = networks::gnosis();
|
||||
let peaq = networks::peaq();
|
||||
let agung = networks::agung();
|
||||
|
||||
// Create providers
|
||||
let gnosis_provider = create_provider(&gnosis);
|
||||
let peaq_provider = create_provider(&peaq);
|
||||
let agung_provider = create_provider(&agung);
|
||||
|
||||
// They should all succeed
|
||||
assert!(gnosis_provider.is_ok());
|
||||
assert!(peaq_provider.is_ok());
|
||||
assert!(agung_provider.is_ok());
|
||||
|
||||
// The convenience functions should also work
|
||||
let gnosis_provider2 = create_gnosis_provider();
|
||||
let peaq_provider2 = create_peaq_provider();
|
||||
let agung_provider2 = create_agung_provider();
|
||||
|
||||
assert!(gnosis_provider2.is_ok());
|
||||
assert!(peaq_provider2.is_ok());
|
||||
assert!(agung_provider2.is_ok());
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
//! Tests for Ethereum transaction functionality.
|
||||
|
||||
use crate::vault::ethereum::*;
|
||||
use crate::vault::keypair::implementation::KeyPair;
|
||||
use ethers::types::U256;
|
||||
// use std::str::FromStr;
|
||||
|
||||
#[test]
|
||||
fn test_format_balance() {
|
||||
let network = networks::gnosis();
|
||||
|
||||
// Test with 0
|
||||
let balance = U256::from(0);
|
||||
let formatted = format_balance(balance, &network);
|
||||
assert_eq!(formatted, "0.000000 xDAI");
|
||||
|
||||
// Test with 1 wei
|
||||
let balance = U256::from(1);
|
||||
let formatted = format_balance(balance, &network);
|
||||
assert_eq!(formatted, "0.000000 xDAI");
|
||||
|
||||
// Test with 1 gwei (10^9 wei)
|
||||
let balance = U256::from(1_000_000_000u64);
|
||||
let formatted = format_balance(balance, &network);
|
||||
assert_eq!(formatted, "0.000000 xDAI");
|
||||
|
||||
// Test with 1 ETH (10^18 wei)
|
||||
let balance = U256::from_dec_str("1000000000000000000").unwrap();
|
||||
let formatted = format_balance(balance, &network);
|
||||
assert_eq!(formatted, "1.000000 xDAI");
|
||||
|
||||
// Test with a larger amount
|
||||
let balance = U256::from_dec_str("123456789000000000000").unwrap();
|
||||
let formatted = format_balance(balance, &network);
|
||||
assert_eq!(formatted, "123.456789 xDAI");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_balance() {
|
||||
// This is a mock test since we can't actually query the blockchain in a unit test
|
||||
// In a real test, we would use a local blockchain or mock the provider
|
||||
|
||||
// Create a provider
|
||||
let network = networks::gnosis();
|
||||
let provider_result = create_provider(&network);
|
||||
|
||||
// The provider creation should succeed
|
||||
assert!(provider_result.is_ok());
|
||||
|
||||
// We can't actually test get_balance without a blockchain
|
||||
// In a real test, we would mock the provider and test the function
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_send_eth() {
|
||||
// This is a mock test since we can't actually send transactions in a unit test
|
||||
// In a real test, we would use a local blockchain or mock the provider
|
||||
|
||||
// Create a wallet
|
||||
let keypair = KeyPair::new("test_keypair6");
|
||||
let network = networks::gnosis();
|
||||
let wallet = EthereumWallet::from_keypair(&keypair, network.clone()).unwrap();
|
||||
|
||||
// Create a provider
|
||||
let provider_result = create_provider(&network);
|
||||
assert!(provider_result.is_ok());
|
||||
|
||||
// We can't actually test send_eth without a blockchain
|
||||
// In a real test, we would mock the provider and test the function
|
||||
}
|
143
packages/crypt/vault/_archive/src/ethereum/tests/wallet_tests.rs
Normal file
143
packages/crypt/vault/_archive/src/ethereum/tests/wallet_tests.rs
Normal file
@@ -0,0 +1,143 @@
|
||||
//! Tests for Ethereum wallet functionality.
|
||||
|
||||
use crate::vault::ethereum::*;
|
||||
use crate::vault::keypair::implementation::KeyPair;
|
||||
use ethers::utils::hex;
|
||||
|
||||
#[test]
|
||||
fn test_ethereum_wallet_from_keypair() {
|
||||
let keypair = KeyPair::new("test_keypair");
|
||||
let network = networks::gnosis();
|
||||
|
||||
let wallet = EthereumWallet::from_keypair(&keypair, network.clone()).unwrap();
|
||||
|
||||
assert_eq!(wallet.network.name, "Gnosis");
|
||||
assert_eq!(wallet.network.chain_id, 100);
|
||||
|
||||
// The address should be a valid Ethereum address
|
||||
assert!(wallet.address_string().starts_with("0x"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ethereum_wallet_from_name_and_keypair() {
|
||||
let keypair = KeyPair::new("test_keypair2");
|
||||
let network = networks::gnosis();
|
||||
|
||||
let wallet = EthereumWallet::from_name_and_keypair("test", &keypair, network.clone()).unwrap();
|
||||
|
||||
assert_eq!(wallet.network.name, "Gnosis");
|
||||
assert_eq!(wallet.network.chain_id, 100);
|
||||
|
||||
// The address should be a valid Ethereum address
|
||||
assert!(wallet.address_string().starts_with("0x"));
|
||||
|
||||
// Creating another wallet with the same name and keypair should yield the same address
|
||||
let wallet2 = EthereumWallet::from_name_and_keypair("test", &keypair, network.clone()).unwrap();
|
||||
assert_eq!(wallet.address, wallet2.address);
|
||||
|
||||
// Creating a wallet with a different name should yield a different address
|
||||
let wallet3 = EthereumWallet::from_name_and_keypair("test2", &keypair, network.clone()).unwrap();
|
||||
assert_ne!(wallet.address, wallet3.address);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_ethereum_wallet_from_private_key() {
|
||||
let private_key = "0x0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef";
|
||||
let network = networks::gnosis();
|
||||
|
||||
let wallet = EthereumWallet::from_private_key(private_key, network.clone()).unwrap();
|
||||
|
||||
assert_eq!(wallet.network.name, "Gnosis");
|
||||
assert_eq!(wallet.network.chain_id, 100);
|
||||
|
||||
// The address should be a valid Ethereum address
|
||||
assert!(wallet.address_string().starts_with("0x"));
|
||||
|
||||
// The address should be deterministic based on the private key
|
||||
let wallet2 = EthereumWallet::from_private_key(private_key, network.clone()).unwrap();
|
||||
assert_eq!(wallet.address, wallet2.address);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_wallet_management() {
|
||||
// Clear any existing wallets
|
||||
clear_ethereum_wallets();
|
||||
|
||||
// Create a key space and keypair
|
||||
crate::vault::keypair::session_manager::create_space("test_space").unwrap();
|
||||
crate::vault::keypair::create_keypair("test_keypair3").unwrap();
|
||||
|
||||
// Create wallets for different networks
|
||||
let gnosis_wallet = create_ethereum_wallet_for_network(networks::gnosis()).unwrap();
|
||||
let peaq_wallet = create_ethereum_wallet_for_network(networks::peaq()).unwrap();
|
||||
let agung_wallet = create_ethereum_wallet_for_network(networks::agung()).unwrap();
|
||||
|
||||
// Get the current wallets
|
||||
let current_gnosis = get_current_ethereum_wallet_for_network("Gnosis").unwrap();
|
||||
let current_peaq = get_current_ethereum_wallet_for_network("Peaq").unwrap();
|
||||
let current_agung = get_current_ethereum_wallet_for_network("Agung").unwrap();
|
||||
|
||||
// Check that they match
|
||||
assert_eq!(gnosis_wallet.address, current_gnosis.address);
|
||||
assert_eq!(peaq_wallet.address, current_peaq.address);
|
||||
assert_eq!(agung_wallet.address, current_agung.address);
|
||||
|
||||
// Clear wallets for a specific network
|
||||
clear_ethereum_wallets_for_network("Gnosis");
|
||||
|
||||
// Check that the wallet is gone
|
||||
let result = get_current_ethereum_wallet_for_network("Gnosis");
|
||||
assert!(result.is_err());
|
||||
|
||||
// But the others should still be there
|
||||
let current_peaq = get_current_ethereum_wallet_for_network("Peaq").unwrap();
|
||||
let current_agung = get_current_ethereum_wallet_for_network("Agung").unwrap();
|
||||
assert_eq!(peaq_wallet.address, current_peaq.address);
|
||||
assert_eq!(agung_wallet.address, current_agung.address);
|
||||
|
||||
// Clear all wallets
|
||||
clear_ethereum_wallets();
|
||||
|
||||
// Check that all wallets are gone
|
||||
let result1 = get_current_ethereum_wallet_for_network("Gnosis");
|
||||
let result2 = get_current_ethereum_wallet_for_network("Peaq");
|
||||
let result3 = get_current_ethereum_wallet_for_network("Agung");
|
||||
assert!(result1.is_err());
|
||||
assert!(result2.is_err());
|
||||
assert!(result3.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_sign_message() {
|
||||
let keypair = KeyPair::new("test_keypair4");
|
||||
let network = networks::gnosis();
|
||||
|
||||
let wallet = EthereumWallet::from_keypair(&keypair, network.clone()).unwrap();
|
||||
|
||||
// Create a tokio runtime for the async test
|
||||
let rt = tokio::runtime::Runtime::new().unwrap();
|
||||
|
||||
// Sign a message
|
||||
let message = b"Hello, world!";
|
||||
let signature = rt.block_on(wallet.sign_message(message)).unwrap();
|
||||
|
||||
// The signature should be a non-empty string
|
||||
assert!(!signature.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_private_key_hex() {
|
||||
let keypair = KeyPair::new("test_keypair5");
|
||||
let network = networks::gnosis();
|
||||
|
||||
let wallet = EthereumWallet::from_keypair(&keypair, network.clone()).unwrap();
|
||||
|
||||
// Get the private key as hex
|
||||
let private_key_hex = wallet.private_key_hex();
|
||||
|
||||
// The private key should be a 64-character hex string (32 bytes)
|
||||
assert_eq!(private_key_hex.len(), 64);
|
||||
|
||||
// It should be possible to parse it as hex
|
||||
let _bytes = hex::decode(private_key_hex).unwrap();
|
||||
}
|
52
packages/crypt/vault/_archive/src/ethereum/transaction.rs
Normal file
52
packages/crypt/vault/_archive/src/ethereum/transaction.rs
Normal file
@@ -0,0 +1,52 @@
|
||||
//! Ethereum transaction functionality.
|
||||
|
||||
use ethers::prelude::*;
|
||||
|
||||
use super::networks::NetworkConfig;
|
||||
use super::wallet::EthereumWallet;
|
||||
use crate::error::CryptoError;
|
||||
|
||||
/// Formats a token balance for display.
|
||||
pub fn format_balance(balance: U256, network: &NetworkConfig) -> String {
|
||||
let wei = balance.as_u128();
|
||||
let divisor = 10u128.pow(network.decimals as u32) as f64;
|
||||
let token = wei as f64 / divisor;
|
||||
|
||||
// Display with the appropriate number of decimal places
|
||||
let display_decimals = std::cmp::min(6, network.decimals);
|
||||
|
||||
format!(
|
||||
"{:.*} {}",
|
||||
display_decimals as usize, token, network.token_symbol
|
||||
)
|
||||
}
|
||||
|
||||
/// Gets the balance of an Ethereum address.
|
||||
pub async fn get_balance(provider: &Provider<Http>, address: Address) -> Result<U256, CryptoError> {
|
||||
provider
|
||||
.get_balance(address, None)
|
||||
.await
|
||||
.map_err(|e| CryptoError::SerializationError(format!("Failed to get balance: {}", e)))
|
||||
}
|
||||
|
||||
/// Sends Ethereum from one address to another.
|
||||
pub async fn send_eth(
|
||||
wallet: &EthereumWallet,
|
||||
provider: &Provider<Http>,
|
||||
to: Address,
|
||||
amount: U256,
|
||||
) -> Result<H256, CryptoError> {
|
||||
// Create a client with the wallet
|
||||
let client = SignerMiddleware::new(provider.clone(), wallet.wallet.clone());
|
||||
|
||||
// Create the transaction
|
||||
let tx = TransactionRequest::new().to(to).value(amount).gas(21000);
|
||||
|
||||
// Send the transaction
|
||||
let pending_tx = client.send_transaction(tx, None).await.map_err(|e| {
|
||||
CryptoError::SerializationError(format!("Failed to send transaction: {}", e))
|
||||
})?;
|
||||
|
||||
// Return the transaction hash instead of waiting for the receipt
|
||||
Ok(pending_tx.tx_hash())
|
||||
}
|
126
packages/crypt/vault/_archive/src/ethereum/wallet.rs
Normal file
126
packages/crypt/vault/_archive/src/ethereum/wallet.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
//! Ethereum wallet implementation.
|
||||
|
||||
use ethers::prelude::*;
|
||||
use ethers::signers::{LocalWallet, Signer, Wallet};
|
||||
use ethers::utils::hex;
|
||||
use k256::ecdsa::SigningKey;
|
||||
use sha2::{Digest, Sha256};
|
||||
use std::str::FromStr;
|
||||
|
||||
use super::networks::NetworkConfig;
|
||||
use crate::error::CryptoError;
|
||||
use crate::keyspace::KeyPair;
|
||||
|
||||
/// An Ethereum wallet derived from a keypair.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct EthereumWallet {
|
||||
pub address: Address,
|
||||
pub wallet: Wallet<SigningKey>,
|
||||
pub network: NetworkConfig,
|
||||
}
|
||||
|
||||
impl EthereumWallet {
|
||||
/// Creates a new Ethereum wallet from a keypair for a specific network.
|
||||
pub fn from_keypair(
|
||||
keypair: &crate::keyspace::keypair_types::KeyPair,
|
||||
network: NetworkConfig,
|
||||
) -> Result<Self, CryptoError> {
|
||||
// Get the private key bytes from the keypair
|
||||
let private_key_bytes = keypair.signing_key.to_bytes();
|
||||
|
||||
// Convert to a hex string (without 0x prefix)
|
||||
let private_key_hex = hex::encode(private_key_bytes);
|
||||
|
||||
// Create an Ethereum wallet from the private key
|
||||
let wallet = LocalWallet::from_str(&private_key_hex)
|
||||
.map_err(|_e| CryptoError::InvalidKeyLength)?
|
||||
.with_chain_id(network.chain_id);
|
||||
|
||||
// Get the Ethereum address
|
||||
let address = wallet.address();
|
||||
|
||||
Ok(EthereumWallet {
|
||||
address,
|
||||
wallet,
|
||||
network,
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new Ethereum wallet from a name and keypair (deterministic derivation) for a specific network.
|
||||
pub fn from_name_and_keypair(
|
||||
name: &str,
|
||||
keypair: &KeyPair,
|
||||
network: NetworkConfig,
|
||||
) -> Result<Self, CryptoError> {
|
||||
// Get the private key bytes from the keypair
|
||||
let private_key_bytes = keypair.signing_key.to_bytes();
|
||||
|
||||
// Create a deterministic seed by combining name and private key
|
||||
let mut hasher = Sha256::default();
|
||||
hasher.update(name.as_bytes());
|
||||
hasher.update(&private_key_bytes);
|
||||
let seed = hasher.finalize();
|
||||
|
||||
// Use the seed as a private key
|
||||
let private_key_hex = hex::encode(seed);
|
||||
|
||||
// Create an Ethereum wallet from the derived private key
|
||||
let wallet = LocalWallet::from_str(&private_key_hex)
|
||||
.map_err(|_e| CryptoError::InvalidKeyLength)?
|
||||
.with_chain_id(network.chain_id);
|
||||
|
||||
// Get the Ethereum address
|
||||
let address = wallet.address();
|
||||
|
||||
Ok(EthereumWallet {
|
||||
address,
|
||||
wallet,
|
||||
network,
|
||||
})
|
||||
}
|
||||
|
||||
/// Creates a new Ethereum wallet from a private key for a specific network.
|
||||
pub fn from_private_key(
|
||||
private_key: &str,
|
||||
network: NetworkConfig,
|
||||
) -> Result<Self, CryptoError> {
|
||||
// Remove 0x prefix if present
|
||||
let private_key_clean = private_key.trim_start_matches("0x");
|
||||
|
||||
// Create an Ethereum wallet from the private key
|
||||
let wallet = LocalWallet::from_str(private_key_clean)
|
||||
.map_err(|_e| CryptoError::InvalidKeyLength)?
|
||||
.with_chain_id(network.chain_id);
|
||||
|
||||
// Get the Ethereum address
|
||||
let address = wallet.address();
|
||||
|
||||
Ok(EthereumWallet {
|
||||
address,
|
||||
wallet,
|
||||
network,
|
||||
})
|
||||
}
|
||||
|
||||
/// Gets the Ethereum address as a string.
|
||||
pub fn address_string(&self) -> String {
|
||||
format!("{:?}", self.address)
|
||||
}
|
||||
|
||||
/// Signs a message with the Ethereum wallet.
|
||||
pub async fn sign_message(&self, message: &[u8]) -> Result<String, CryptoError> {
|
||||
let signature = self
|
||||
.wallet
|
||||
.sign_message(message)
|
||||
.await
|
||||
.map_err(|e| CryptoError::SignatureFormatError(e.to_string()))?;
|
||||
|
||||
Ok(signature.to_string())
|
||||
}
|
||||
|
||||
/// Gets the private key as a hex string.
|
||||
pub fn private_key_hex(&self) -> String {
|
||||
let bytes = self.wallet.signer().to_bytes();
|
||||
hex::encode(bytes)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user