diff --git a/Cargo.toml b/Cargo.toml index 9435b29f..ff33c4eb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,18 +1,18 @@ [workspace] resolver = "2" members = [ - "crates/solver-types", - "crates/solver-core", - "crates/solver-config", - "crates/solver-storage", "crates/solver-account", + "crates/solver-config", + "crates/solver-core", "crates/solver-delivery", + "crates/solver-demo", "crates/solver-discovery", "crates/solver-order", - "crates/solver-settlement", "crates/solver-pricing", "crates/solver-service", - "crates/solver-demo", + "crates/solver-settlement", + "crates/solver-storage", + "crates/solver-types", ] default-members = ["crates/solver-service", "crates/solver-demo"] @@ -48,6 +48,11 @@ anyhow = "1.0" arc-swap = "1.7" async-stream = "0.3" async-trait = "0.1" + +# AWS dependencies (for KMS signer) +# Minimum versions that fix CVE GHSA-g59m-gf8j-gjf5 (region validation flaw) +aws-config = "1.8" +aws-sdk-kms = "1.93" axum = "0.8.4" backoff = { version = "0.4", features = ["tokio"] } bytes = "1.8" @@ -75,11 +80,6 @@ tracing-subscriber = "0.3" uuid = { version = "1.10", features = ["v4", "serde"] } validator = { version = "0.20", features = ["derive"] } -# AWS dependencies (for KMS signer) -# Minimum versions that fix CVE GHSA-g59m-gf8j-gjf5 (region validation flaw) -aws-config = "1.8" -aws-sdk-kms = "1.93" - [profile.release] opt-level = 3 lto = true diff --git a/crates/solver-account/Cargo.toml b/crates/solver-account/Cargo.toml index 6c4afbe3..590adb28 100644 --- a/crates/solver-account/Cargo.toml +++ b/crates/solver-account/Cargo.toml @@ -4,13 +4,22 @@ version = "0.1.0" edition = "2021" rust-version.workspace = true +[features] +testing = ["mockall"] +kms = ["alloy-signer-aws", "aws-config", "aws-sdk-kms"] + [dependencies] alloy-consensus = { workspace = true } alloy-network = { workspace = true } alloy-primitives = { workspace = true } alloy-signer = { workspace = true } + +# KMS dependencies (optional) +alloy-signer-aws = { workspace = true, optional = true } alloy-signer-local = { workspace = true } async-trait = "0.1" +aws-config = { workspace = true, optional = true } +aws-sdk-kms = { workspace = true, optional = true } hex = "0.4" mockall = { workspace = true, optional = true } serde = { workspace = true } @@ -19,14 +28,5 @@ solver-types = { path = "../solver-types" } thiserror = "2.0.17" tokio = { workspace = true } -# KMS dependencies (optional) -alloy-signer-aws = { workspace = true, optional = true } -aws-config = { workspace = true, optional = true } -aws-sdk-kms = { workspace = true, optional = true } - -[features] -testing = ["mockall"] -kms = ["alloy-signer-aws", "aws-config", "aws-sdk-kms"] - [dev-dependencies] tokio = { workspace = true } diff --git a/crates/solver-core/Cargo.toml b/crates/solver-core/Cargo.toml index c6c5e42c..a0c36016 100644 --- a/crates/solver-core/Cargo.toml +++ b/crates/solver-core/Cargo.toml @@ -30,12 +30,12 @@ tracing = "0.1" [dev-dependencies] alloy-signer-local = { workspace = true } -mockall = { workspace = true } async-trait = { workspace = true } +mockall = { workspace = true } +solver-account = { path = "../solver-account", features = ["testing"] } # Add testing feature only for tests solver-delivery = { path = "../solver-delivery", features = ["testing"] } -solver-storage = { path = "../solver-storage", features = ["testing"] } solver-order = { path = "../solver-order", features = ["testing"] } -solver-account = { path = "../solver-account", features = ["testing"] } +solver-pricing = { path = "../solver-pricing", features = ["testing"] } solver-settlement = { path = "../solver-settlement", features = ["testing"] } -solver-pricing = { path = "../solver-pricing", features = ["testing"] } \ No newline at end of file +solver-storage = { path = "../solver-storage", features = ["testing"] } diff --git a/crates/solver-delivery/Cargo.toml b/crates/solver-delivery/Cargo.toml index f6438c81..80e53957 100644 --- a/crates/solver-delivery/Cargo.toml +++ b/crates/solver-delivery/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2021" rust-version.workspace = true +[features] +testing = ["mockall"] + [dependencies] alloy-consensus = { workspace = true } alloy-network = { workspace = true } @@ -27,6 +30,3 @@ solver-types = { path = "../solver-types" } thiserror = "2.0.17" tokio = { version = "1.0", features = ["rt-multi-thread"] } tracing = "0.1" - -[features] -testing = ["mockall"] \ No newline at end of file diff --git a/crates/solver-demo/Cargo.toml b/crates/solver-demo/Cargo.toml index 0a39f60e..c1bfdc64 100644 --- a/crates/solver-demo/Cargo.toml +++ b/crates/solver-demo/Cargo.toml @@ -52,5 +52,5 @@ uuid = { version = "1", features = ["v4"] } [dev-dependencies] tempfile = "3" -wiremock = "0.5" tokio-test = "0.4" +wiremock = "0.5" diff --git a/crates/solver-order/Cargo.toml b/crates/solver-order/Cargo.toml index bd88f8c2..e3d40e07 100644 --- a/crates/solver-order/Cargo.toml +++ b/crates/solver-order/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2021" rust-version.workspace = true +[features] +testing = ["mockall"] + [dependencies] alloy-dyn-abi = { workspace = true } alloy-primitives = { workspace = true } @@ -19,8 +22,5 @@ thiserror = "2.0" tracing = "0.1" uuid = { version = "1.8", features = ["v4", "serde"] } -[features] -testing = ["mockall"] - [dev-dependencies] tokio = { workspace = true } diff --git a/crates/solver-pricing/Cargo.toml b/crates/solver-pricing/Cargo.toml index bda58bea..8dc75e17 100644 --- a/crates/solver-pricing/Cargo.toml +++ b/crates/solver-pricing/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2021" rust-version.workspace = true +[features] +testing = ["mockall"] + [dependencies] alloy-primitives = { workspace = true } async-trait = "0.1.73" @@ -16,7 +19,3 @@ solver-types = { path = "../solver-types" } thiserror = "2.0" tokio = { version = "1.0", features = ["sync", "time"] } tracing = "0.1" - - -[features] -testing = ["mockall"] \ No newline at end of file diff --git a/crates/solver-service/Cargo.toml b/crates/solver-service/Cargo.toml index a4119c02..84ac40b5 100644 --- a/crates/solver-service/Cargo.toml +++ b/crates/solver-service/Cargo.toml @@ -13,6 +13,10 @@ path = "src/lib.rs" name = "solver" path = "src/main.rs" +[features] +default = [] +kms = ["solver-account/kms"] + [dependencies] alloy-primitives = { workspace = true, features = ["std", "serde"] } alloy-signer = { workspace = true } @@ -54,17 +58,13 @@ tracing = "0.1" tracing-subscriber = { version = "0.3", features = ["env-filter"] } uuid = { version = "1.0", features = ["v4", "serde"] } -[features] -default = [] -kms = ["solver-account/kms"] - [dev-dependencies] alloy-signer-local = { workspace = true } mockall = { workspace = true } +serial_test = "3" +solver-delivery = { path = "../solver-delivery", features = ["testing"] } solver-settlement = { path = "../solver-settlement", features = ["testing"] } solver-storage = { path = "../solver-storage", features = ["testing"] } -solver-delivery = { path = "../solver-delivery", features = ["testing"] } -serial_test = "3" tempfile = { workspace = true } tokio = { workspace = true } wiremock = "0.5" diff --git a/crates/solver-settlement/Cargo.toml b/crates/solver-settlement/Cargo.toml index 380348cc..4f9bbaaa 100644 --- a/crates/solver-settlement/Cargo.toml +++ b/crates/solver-settlement/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2021" rust-version.workspace = true +[features] +testing = ["mockall"] + [dependencies] alloy-primitives = { workspace = true } alloy-provider = { workspace = true } @@ -24,8 +27,5 @@ thiserror = "2.0" tokio = { version = "1.0", features = ["rt-multi-thread", "sync"] } tracing = "0.1" -[features] -testing = ["mockall"] - [dev-dependencies] wiremock = "0.5" diff --git a/crates/solver-storage/Cargo.toml b/crates/solver-storage/Cargo.toml index c1ca9243..1d7648b3 100644 --- a/crates/solver-storage/Cargo.toml +++ b/crates/solver-storage/Cargo.toml @@ -4,6 +4,9 @@ version = "0.1.0" edition = "2021" rust-version.workspace = true +[features] +testing = ["mockall"] + [dependencies] async-trait = "0.1" chrono = { version = "0.4", features = ["serde"] } @@ -24,11 +27,8 @@ tokio = { version = "1.0", features = [ tracing = { workspace = true } uuid = { workspace = true } -[features] -testing = ["mockall"] - [dev-dependencies] -tempfile = { workspace = true } futures = { workspace = true } +rust_decimal = { workspace = true } +tempfile = { workspace = true } uuid = { workspace = true } -rust_decimal = { workspace = true } \ No newline at end of file diff --git a/crates/solver-types/src/utils/conversion.rs b/crates/solver-types/src/utils/conversion.rs index 4d07fa58..402169d1 100644 --- a/crates/solver-types/src/utils/conversion.rs +++ b/crates/solver-types/src/utils/conversion.rs @@ -140,8 +140,16 @@ pub fn parse_bytes32_from_hex(hex_str: &str) -> Result<[u8; 32], Box Result { + if hex_str.is_empty() { + return Err("Address cannot be empty".to_string()); + } + let hex_clean = without_0x_prefix(hex_str); + if hex_clean.is_empty() { + return Err("Address cannot be empty".to_string()); + } + // Handle U256 hex that's missing leading zeros (common with EIP-7683 inputs) // U256 serialization drops leading zeros, so "0x8ad..." (31 bytes) should be "0x08ad..." (32 bytes) let padded_hex = match hex_clean.len() { @@ -152,7 +160,7 @@ pub fn parse_address(hex_str: &str) -> Result { // Other missing zeros cases - pad to 64 chars (32 bytes) format!("{hex_clean:0>64}") }, - _ => hex_clean.to_string(), + _ => format!("{hex_clean:0>40}"), // pad short/odd-length to 20-byte address }; hex::decode(&padded_hex) @@ -706,6 +714,73 @@ mod tests { assert_eq!(ceil_dp(value, 3).to_string(), "1.235"); } + #[test] + fn test_parse_address_empty_input() { + assert_eq!(parse_address("").unwrap_err(), "Address cannot be empty"); + // "0x" with nothing after is also empty after stripping prefix + assert_eq!(parse_address("0x").unwrap_err(), "Address cannot be empty"); + } + + #[test] + fn test_parse_address_standard_20_byte() { + // Standard 40-char hex address with 0x prefix + let result = parse_address("0x5fbdb2315678afecb367f032d93f642f64180aa3").unwrap(); + assert_eq!( + hex::encode(&result.0), + "5fbdb2315678afecb367f032d93f642f64180aa3" + ); + } + + #[test] + fn test_parse_address_32_byte_left_padded() { + // 64-char hex (bytes32, address in last 20 bytes) + let result = + parse_address("0x0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3") + .unwrap(); + assert_eq!( + hex::encode(&result.0), + "5fbdb2315678afecb367f032d93f642f64180aa3" + ); + } + + #[test] + fn test_parse_address_31_byte_missing_leading_zero() { + // 62-char hex (U256 dropped a leading zero byte) — padded to 32 bytes + let result = + parse_address("0x000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa3") + .unwrap(); + assert_eq!( + hex::encode(&result.0), + "5fbdb2315678afecb367f032d93f642f64180aa3" + ); + } + + #[test] + fn test_parse_address_short_odd_length_padded_to_address() { + // Fewer than 40 chars, odd-length — padded left to 40 chars (20 bytes) + let result = parse_address("0x1").unwrap(); + assert_eq!( + hex::encode(&result.0), + "0000000000000000000000000000000000000001" + ); + } + + #[test] + fn test_parse_address_without_0x_prefix() { + let result = parse_address("5fbdb2315678afecb367f032d93f642f64180aa3").unwrap(); + assert_eq!( + hex::encode(&result.0), + "5fbdb2315678afecb367f032d93f642f64180aa3" + ); + } + + #[test] + fn test_parse_address_invalid_hex_characters() { + let result = parse_address("0xGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG"); + assert!(result.is_err()); + assert!(result.unwrap_err().contains("Invalid hex")); + } + #[test] fn test_roundtrip_conversions() { // Test roundtrip: ETH -> wei -> ETH (note: format_ether returns full precision)