feat: Upgrade dependencies and refactor client
- Upgrade several dependencies to their latest versions. - Refactor the EVM client for improved modularity and clarity. - Simplify transaction signing and sending logic.
This commit is contained in:
@@ -1,75 +1,40 @@
|
||||
use evm_client::{EvmClient, EvmProvider, Signer};
|
||||
use evm_client::provider::Transaction;
|
||||
use evm_client::provider::{parse_signature_rs_v, get_balance};
|
||||
// All native (non-WASM) balance and signature tests are in this file.
|
||||
use ethers_core::types::{U256, Address, Bytes};
|
||||
|
||||
// Dummy signer that returns a known Ethereum address (Vitalik's address)
|
||||
struct DummySigner;
|
||||
#[test]
|
||||
fn test_rlp_encode_unsigned() {
|
||||
let tx = Transaction {
|
||||
nonce: U256::from(1),
|
||||
to: Address::zero(),
|
||||
value: U256::from(100),
|
||||
gas: U256::from(21000),
|
||||
gas_price: U256::from(1),
|
||||
data: Bytes::new(),
|
||||
chain_id: 1u64,
|
||||
};
|
||||
let rlp = tx.rlp_encode_unsigned();
|
||||
assert!(!rlp.is_empty());
|
||||
}
|
||||
|
||||
// --- IMPORTANT ---
|
||||
// The Signer trait's async methods require different trait bounds on native vs WASM:
|
||||
// - Native Rust: futures must be Send, so use #[async_trait::async_trait]
|
||||
// - WASM (wasm32): futures do NOT require Send, so use #[async_trait::async_trait(?Send)]
|
||||
// This split is required for cross-platform test compatibility. DO NOT merge these impls!
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[async_trait::async_trait]
|
||||
impl Signer for DummySigner {
|
||||
async fn sign(&self, _message: &[u8]) -> Result<Vec<u8>, evm_client::EvmError> {
|
||||
Err(evm_client::EvmError::Vault("sign not implemented".to_string()))
|
||||
}
|
||||
fn address(&self) -> String {
|
||||
// Vitalik's main address (has funds on mainnet)
|
||||
"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".to_string()
|
||||
}
|
||||
#[test]
|
||||
fn test_parse_signature_rs_v() {
|
||||
let mut sig = [0u8; 65];
|
||||
sig[31] = 1; sig[63] = 2; sig[64] = 27;
|
||||
let (r, s, v) = parse_signature_rs_v(&sig, 1).unwrap();
|
||||
assert_eq!(r, U256::from(1));
|
||||
assert_eq!(s, U256::from(2));
|
||||
assert_eq!(v, 27 + 1 * 2 + 8);
|
||||
}
|
||||
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[tokio::test]
|
||||
async fn test_get_balance_vitalik() {
|
||||
// Use a public Ethereum mainnet RPC
|
||||
let provider = EvmProvider::Http {
|
||||
name: "mainnet".to_string(),
|
||||
url: "https://eth.drpc.org".to_string(),
|
||||
chain_id: 1,
|
||||
};
|
||||
let signer = DummySigner;
|
||||
let mut client = EvmClient::new(signer);
|
||||
client.add_provider("mainnet".to_string(), provider);
|
||||
client.set_current("mainnet").unwrap();
|
||||
let balance = client.get_balance("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045").await.unwrap();
|
||||
assert!(balance > 0, "Vitalik's balance should be greater than zero");
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen_test::*;
|
||||
|
||||
// See explanation above for why this impl uses ?Send
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl Signer for DummySigner {
|
||||
async fn sign(&self, _message: &[u8]) -> Result<Vec<u8>, evm_client::EvmError> {
|
||||
Err(evm_client::EvmError::Vault("sign not implemented".to_string()))
|
||||
}
|
||||
fn address(&self) -> String {
|
||||
// Vitalik's main address (has funds on mainnet)
|
||||
"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[wasm_bindgen_test(async)]
|
||||
async fn test_get_balance_vitalik_browser() {
|
||||
let provider = EvmProvider::Http {
|
||||
name: "mainnet".to_string(),
|
||||
url: "https://eth.drpc.org".to_string(),
|
||||
chain_id: 1,
|
||||
};
|
||||
let signer = DummySigner;
|
||||
let mut client = EvmClient::new(signer);
|
||||
client.add_provider("mainnet".to_string(), provider);
|
||||
client.set_current("mainnet").unwrap();
|
||||
let balance = client.get_balance("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045").await;
|
||||
assert!(balance.is_ok(), "Balance query should succeed in browser");
|
||||
let balance = balance.unwrap();
|
||||
assert!(balance > 0u128, "Vitalik's balance should be greater than zero");
|
||||
async fn test_get_balance_real_address() {
|
||||
// Vitalik's address
|
||||
let address = "d8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
|
||||
let address = ethers_core::types::Address::from_slice(&hex::decode(address).unwrap());
|
||||
let url = "https://ethereum.blockpi.network/v1/rpc/public";
|
||||
let balance = get_balance(url, address).await.expect("Failed to get balance");
|
||||
assert!(balance > ethers_core::types::U256::zero(), "Vitalik's balance should be greater than zero");
|
||||
}
|
||||
|
@@ -1,65 +1,11 @@
|
||||
#![cfg(not(target_arch = "wasm32"))]
|
||||
// tests/evm_client.rs
|
||||
use evm_client::{EvmClient, EvmProvider, Signer, EvmError};
|
||||
|
||||
struct DummySigner;
|
||||
|
||||
// --- IMPORTANT ---
|
||||
// The Signer trait's async methods require different trait bounds on native vs WASM:
|
||||
// - Native Rust: futures must be Send, so use #[async_trait::async_trait]
|
||||
// - WASM (wasm32): futures do NOT require Send, so use #[async_trait::async_trait(?Send)]
|
||||
// This split is required for cross-platform test compatibility. DO NOT merge these impls!
|
||||
#[cfg(target_arch = "wasm32")]
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl Signer for DummySigner {
|
||||
async fn sign(&self, _message: &[u8]) -> Result<Vec<u8>, EvmError> {
|
||||
Ok(vec![0u8; 65]) // dummy signature
|
||||
}
|
||||
fn address(&self) -> String {
|
||||
"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
// See explanation above for why this impl uses #[async_trait::async_trait]
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
#[async_trait::async_trait]
|
||||
impl Signer for DummySigner {
|
||||
async fn sign(&self, _message: &[u8]) -> Result<Vec<u8>, EvmError> {
|
||||
Ok(vec![0u8; 65]) // dummy signature
|
||||
}
|
||||
fn address(&self) -> String {
|
||||
"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".to_string()
|
||||
}
|
||||
}
|
||||
use evm_client::send_rpc;
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_transfer_rlp_encoding() {
|
||||
let provider = EvmProvider::Http {
|
||||
name: "mainnet".to_string(),
|
||||
url: "https://rpc.ankr.com/eth".to_string(),
|
||||
chain_id: 1,
|
||||
};
|
||||
let signer = DummySigner;
|
||||
let mut client = EvmClient::new(signer);
|
||||
client.add_provider("mainnet".to_string(), provider);
|
||||
client.set_current("mainnet").unwrap();
|
||||
// Use a dummy transfer (will fail to send, but will test RLP logic)
|
||||
let result = client.transfer("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", 1u128).await;
|
||||
// Should fail due to dummy signature, but should not panic or error at RLP encoding
|
||||
assert!(matches!(result, Err(EvmError::Rpc(_)) | Err(EvmError::Vault(_))));
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_transfer_invalid_address() {
|
||||
let provider = EvmProvider::Http {
|
||||
name: "mainnet".to_string(),
|
||||
url: "https://rpc.ankr.com/eth".to_string(),
|
||||
chain_id: 1,
|
||||
};
|
||||
let signer = DummySigner;
|
||||
let mut client = EvmClient::new(signer);
|
||||
client.add_provider("mainnet".to_string(), provider);
|
||||
client.set_current("mainnet").unwrap();
|
||||
let result = client.transfer("invalid_address", 1u128).await;
|
||||
assert!(matches!(result, Err(EvmError::Rpc(_))));
|
||||
async fn test_send_rpc_smoke() {
|
||||
// This test just checks the function compiles and can be called.
|
||||
let url = "http://localhost:8545";
|
||||
let body = r#"{"jsonrpc":"2.0","method":"web3_clientVersion","params":[],"id":1}"#;
|
||||
let _ = send_rpc(url, body).await;
|
||||
}
|
||||
|
@@ -1,52 +1,45 @@
|
||||
// This test file is only compiled for wasm32. The DummySigner uses #[async_trait::async_trait(?Send)]
|
||||
// because WASM async traits do not require Send. See balance.rs or evm_client.rs for the trait bound split rationale.
|
||||
#![cfg(target_arch = "wasm32")]
|
||||
use wasm_bindgen_test::*;
|
||||
wasm_bindgen_test::wasm_bindgen_test_configure!(run_in_browser);
|
||||
|
||||
use evm_client::{EvmClient, EvmProvider, Signer, EvmError};
|
||||
use evm_client::provider::{Transaction, parse_signature_rs_v, get_balance};
|
||||
use ethers_core::types::{U256, Address, Bytes};
|
||||
use hex;
|
||||
|
||||
struct DummySigner;
|
||||
|
||||
#[async_trait::async_trait(?Send)]
|
||||
impl Signer for DummySigner {
|
||||
async fn sign(&self, _message: &[u8]) -> Result<Vec<u8>, EvmError> {
|
||||
Ok(vec![0u8; 65]) // dummy signature
|
||||
}
|
||||
fn address(&self) -> String {
|
||||
"0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045".to_string()
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test(async)]
|
||||
async fn test_get_balance_vitalik_browser() {
|
||||
let provider = EvmProvider::Http {
|
||||
name: "mainnet".to_string(),
|
||||
url: "https://eth.drpc.org".to_string(),
|
||||
#[wasm_bindgen_test]
|
||||
fn test_rlp_encode_unsigned() {
|
||||
let tx = Transaction {
|
||||
nonce: U256::from(1),
|
||||
to: Address::zero(),
|
||||
value: U256::from(100),
|
||||
gas: U256::from(21000),
|
||||
gas_price: U256::from(1),
|
||||
data: Bytes::new(),
|
||||
chain_id: 1,
|
||||
};
|
||||
let signer = DummySigner;
|
||||
let mut client = EvmClient::new(signer);
|
||||
client.add_provider("mainnet".to_string(), provider);
|
||||
client.set_current("mainnet").unwrap();
|
||||
let balance = client.get_balance("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045").await;
|
||||
assert!(balance.is_ok(), "Balance query should succeed in browser");
|
||||
let balance = balance.unwrap();
|
||||
assert!(balance > 0u128, "Vitalik's balance should be greater than zero");
|
||||
let rlp = tx.rlp_encode_unsigned();
|
||||
assert!(!rlp.is_empty());
|
||||
}
|
||||
|
||||
|
||||
#[wasm_bindgen_test(async)]
|
||||
async fn test_transfer_rlp_encoding_browser() {
|
||||
let provider = EvmProvider::Http {
|
||||
name: "mainnet".to_string(),
|
||||
url: "https://eth.drpc.org".to_string(),
|
||||
chain_id: 1,
|
||||
};
|
||||
let signer = DummySigner;
|
||||
let mut client = EvmClient::new(signer);
|
||||
client.add_provider("mainnet".to_string(), provider);
|
||||
client.set_current("mainnet").unwrap();
|
||||
let result = client.transfer("0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045", 1u128).await;
|
||||
assert!(matches!(result, Err(EvmError::Rpc(_)) | Err(EvmError::Vault(_))));
|
||||
pub async fn test_get_balance_real_address_wasm_unique() {
|
||||
web_sys::console::log_1(&"WASM balance test running!".into());
|
||||
// Vitalik's address
|
||||
let address = "d8dA6BF26964aF9D7eEd9e03E53415D37aA96045";
|
||||
let address = Address::from_slice(&hex::decode(address).unwrap());
|
||||
let url = "https://ethereum.blockpi.network/v1/rpc/public";
|
||||
let balance = get_balance(url, address).await.expect("Failed to get balance");
|
||||
web_sys::console::log_1(&format!("Balance: {balance:?}").into());
|
||||
assert!(balance > U256::zero(), "Vitalik's balance should be greater than zero");
|
||||
}
|
||||
|
||||
#[wasm_bindgen_test]
|
||||
fn test_parse_signature_rs_v() {
|
||||
let mut sig = [0u8; 65];
|
||||
sig[31] = 1; sig[63] = 2; sig[64] = 27;
|
||||
let (r, s, v) = parse_signature_rs_v(&sig, 1).unwrap();
|
||||
assert_eq!(r, U256::from(1));
|
||||
assert_eq!(s, U256::from(2));
|
||||
assert_eq!(v, 27 + 1 * 2 + 8);
|
||||
}
|
||||
|
Reference in New Issue
Block a user