Skip to content

Commit e9add1b

Browse files
agents fixed
1 parent 86cb690 commit e9add1b

3 files changed

Lines changed: 273 additions & 0 deletions

File tree

tests/agent_integration_test.rs

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
use ferrofluid::{
2+
providers::RawExchangeProvider,
3+
signers::LocalWallet,
4+
types::responses::ExchangeResponseStatus,
5+
};
6+
7+
#[tokio::test]
8+
async fn test_agent_approval_format() {
9+
// Use a fixed test key for reproducible testing
10+
let private_key = "0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80";
11+
let signer = LocalWallet::from_hex_key(private_key).unwrap();
12+
13+
// Create testnet provider
14+
let exchange = RawExchangeProvider::testnet(signer.clone());
15+
16+
// Test approving a specific agent
17+
let agent_key = "0x59c6995e998f97a5a0044966f0945389dc9e86dae88c7a8412f4603b6b78690d";
18+
let agent_signer = LocalWallet::from_hex_key(agent_key).unwrap();
19+
let agent_address = agent_signer.address();
20+
21+
println!("Testing agent approval for address: {}", agent_address);
22+
23+
// This will attempt to approve the agent
24+
// In a real test, we'd mock the HTTP client, but for now we'll just
25+
// verify the request format is correct by checking the debug output
26+
match exchange.approve_agent(agent_address, None).await {
27+
Ok(_) => println!("Request sent successfully"),
28+
Err(e) => println!("Expected error (no real network): {:?}", e),
29+
}
30+
}
31+
32+
#[test]
33+
fn test_agent_address_serialization() {
34+
use alloy::primitives::Address;
35+
use serde_json;
36+
37+
// Test the ApproveAgent struct directly
38+
use ferrofluid::types::actions::ApproveAgent;
39+
40+
let agent = ApproveAgent {
41+
signature_chain_id: 421614,
42+
hyperliquid_chain: "Testnet".to_string(),
43+
agent_address: "0x70997970c51812dc3a010c7d01b50e0d17dc79c8".parse::<Address>().unwrap(),
44+
agent_name: Some("test".to_string()),
45+
nonce: 12345,
46+
};
47+
48+
let json = serde_json::to_string(&agent).unwrap();
49+
50+
// Should serialize with 0x prefix and lowercase
51+
assert!(json.contains(r#""agentAddress":"0x70997970c51812dc3a010c7d01b50e0d17dc79c8""#));
52+
}
53+
54+
#[test]
55+
fn test_approve_agent_serialization() {
56+
use ferrofluid::types::actions::ApproveAgent;
57+
use alloy::primitives::Address;
58+
59+
let agent = ApproveAgent {
60+
signature_chain_id: 421614,
61+
hyperliquid_chain: "Testnet".to_string(),
62+
agent_address: "0x70997970c51812dc3a010c7d01b50e0d17dc79c8".parse::<Address>().unwrap(),
63+
agent_name: None,
64+
nonce: 1234567890,
65+
};
66+
67+
let json = serde_json::to_value(&agent).unwrap();
68+
69+
// Check the serialized format
70+
assert_eq!(json["signatureChainId"], 421614);
71+
assert_eq!(json["hyperliquidChain"], "Testnet");
72+
assert_eq!(json["agentAddress"], "0x70997970c51812dc3a010c7d01b50e0d17dc79c8");
73+
assert_eq!(json["agentName"], serde_json::Value::Null);
74+
assert_eq!(json["nonce"], 1234567890);
75+
}

tests/approve_agent_test.rs

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
//! Test for ApproveAgent EIP-712 signing
2+
3+
#[cfg(test)]
4+
mod tests {
5+
use ferrofluid::types::actions::ApproveAgent;
6+
use ferrofluid::types::eip712::HyperliquidAction;
7+
use alloy::primitives::{address, keccak256};
8+
9+
#[test]
10+
fn test_approve_agent_type_hash() {
11+
let expected = keccak256(
12+
"HyperliquidTransaction:ApproveAgent(string hyperliquidChain,address agentAddress,string agentName,uint64 nonce)"
13+
);
14+
assert_eq!(ApproveAgent::type_hash(), expected);
15+
}
16+
17+
#[test]
18+
fn test_approve_agent_serialization() {
19+
let action = ApproveAgent {
20+
signature_chain_id: 421614,
21+
hyperliquid_chain: "Testnet".to_string(),
22+
agent_address: address!("1234567890123456789012345678901234567890"),
23+
agent_name: Some("Test Agent".to_string()),
24+
nonce: 1234567890,
25+
};
26+
27+
// Serialize to JSON
28+
let json = serde_json::to_string(&action).unwrap();
29+
let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();
30+
31+
// Check that address is serialized as hex string
32+
assert_eq!(
33+
parsed["agentAddress"].as_str().unwrap(),
34+
"0x1234567890123456789012345678901234567890"
35+
);
36+
assert_eq!(parsed["hyperliquidChain"].as_str().unwrap(), "Testnet");
37+
assert_eq!(parsed["agentName"].as_str().unwrap(), "Test Agent");
38+
assert_eq!(parsed["nonce"].as_u64().unwrap(), 1234567890);
39+
}
40+
41+
#[test]
42+
fn test_approve_agent_struct_hash() {
43+
let action = ApproveAgent {
44+
signature_chain_id: 421614,
45+
hyperliquid_chain: "Testnet".to_string(),
46+
agent_address: address!("0D1d9635D0640821d15e323ac8AdADfA9c111414"),
47+
agent_name: None,
48+
nonce: 1690393044548,
49+
};
50+
51+
// Test that struct hash is computed
52+
let struct_hash = action.struct_hash();
53+
// Just verify it's not zero
54+
assert_ne!(struct_hash, alloy::primitives::B256::ZERO);
55+
}
56+
}

tests/managed_exchange_test.rs

Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
//! Tests for ManagedExchangeProvider
2+
3+
use ferrofluid::{
4+
providers::{ManagedExchangeProvider, OrderHandle},
5+
types::requests::{OrderRequest, OrderType, Limit},
6+
constants::*,
7+
};
8+
use alloy::signers::local::PrivateKeySigner;
9+
use std::time::Duration;
10+
11+
#[tokio::test]
12+
async fn test_managed_provider_creation() {
13+
// Initialize CryptoProvider for rustls
14+
rustls::crypto::CryptoProvider::install_default(
15+
rustls::crypto::ring::default_provider()
16+
).ok();
17+
18+
// Create a test signer
19+
let signer = PrivateKeySigner::random();
20+
21+
// Create managed provider with default config
22+
let exchange = ManagedExchangeProvider::builder(signer)
23+
.with_network(ferrofluid::Network::Testnet)
24+
.build()
25+
.await;
26+
27+
assert!(exchange.is_ok());
28+
}
29+
30+
#[tokio::test]
31+
async fn test_managed_provider_with_batching() {
32+
// Initialize CryptoProvider for rustls
33+
rustls::crypto::CryptoProvider::install_default(
34+
rustls::crypto::ring::default_provider()
35+
).ok();
36+
37+
let signer = PrivateKeySigner::random();
38+
39+
// Create with batching enabled
40+
let exchange = ManagedExchangeProvider::builder(signer)
41+
.with_network(ferrofluid::Network::Testnet)
42+
.with_auto_batching(Duration::from_millis(50))
43+
.without_agent_rotation() // Disable for testing
44+
.build()
45+
.await
46+
.unwrap();
47+
48+
// Create a test order
49+
let order = OrderRequest {
50+
asset: 0,
51+
is_buy: true,
52+
limit_px: "50000".to_string(),
53+
sz: "0.01".to_string(),
54+
reduce_only: false,
55+
order_type: OrderType::Limit(Limit { tif: TIF_GTC.to_string() }),
56+
cloid: None,
57+
};
58+
59+
// Place order should return pending handle
60+
let handle = exchange.place_order(&order).await.unwrap();
61+
62+
match handle {
63+
OrderHandle::Pending { .. } => {
64+
// Expected for batched orders
65+
}
66+
OrderHandle::Immediate(_) => {
67+
panic!("Expected pending handle for batched order");
68+
}
69+
}
70+
}
71+
72+
#[tokio::test]
73+
async fn test_alo_order_detection() {
74+
let order = OrderRequest {
75+
asset: 0,
76+
is_buy: true,
77+
limit_px: "50000".to_string(),
78+
sz: "0.01".to_string(),
79+
reduce_only: false,
80+
order_type: OrderType::Limit(Limit { tif: "Alo".to_string() }),
81+
cloid: None,
82+
};
83+
84+
assert!(order.is_alo());
85+
86+
let regular_order = OrderRequest {
87+
asset: 0,
88+
is_buy: true,
89+
limit_px: "50000".to_string(),
90+
sz: "0.01".to_string(),
91+
reduce_only: false,
92+
order_type: OrderType::Limit(Limit { tif: "Gtc".to_string() }),
93+
cloid: None,
94+
};
95+
96+
assert!(!regular_order.is_alo());
97+
}
98+
99+
#[test]
100+
fn test_nonce_generation() {
101+
use ferrofluid::providers::nonce::NonceManager;
102+
103+
let manager = NonceManager::new(false);
104+
105+
let nonce1 = manager.next_nonce(None);
106+
let nonce2 = manager.next_nonce(None);
107+
108+
assert!(nonce2 > nonce1);
109+
assert!(NonceManager::is_valid_nonce(nonce1));
110+
assert!(NonceManager::is_valid_nonce(nonce2));
111+
}
112+
113+
#[test]
114+
fn test_nonce_isolation() {
115+
use ferrofluid::providers::nonce::NonceManager;
116+
use alloy::primitives::Address;
117+
118+
let manager = NonceManager::new(true);
119+
let addr1 = Address::new([1u8; 20]);
120+
let addr2 = Address::new([2u8; 20]);
121+
122+
// Get initial nonces - these should have different millisecond timestamps
123+
let n1_1 = manager.next_nonce(Some(addr1));
124+
std::thread::sleep(std::time::Duration::from_millis(1));
125+
let n2_1 = manager.next_nonce(Some(addr2));
126+
std::thread::sleep(std::time::Duration::from_millis(1));
127+
let n1_2 = manager.next_nonce(Some(addr1));
128+
std::thread::sleep(std::time::Duration::from_millis(1));
129+
let n2_2 = manager.next_nonce(Some(addr2));
130+
131+
// Each address should have independent, increasing nonces
132+
assert!(n1_2 > n1_1, "addr1 nonces should increase");
133+
assert!(n2_2 > n2_1, "addr2 nonces should increase");
134+
135+
// Verify counter independence using the manager's get_counter method
136+
assert_eq!(manager.get_counter(Some(addr1)), 2); // addr1 has 2 nonces
137+
assert_eq!(manager.get_counter(Some(addr2)), 2); // addr2 has 2 nonces
138+
139+
// The nonces themselves should be unique
140+
assert_ne!(n1_1, n2_1);
141+
assert_ne!(n1_2, n2_2);
142+
}

0 commit comments

Comments
 (0)