Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 11 additions & 11 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -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"]

Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand Down
18 changes: 9 additions & 9 deletions crates/solver-account/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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 }
8 changes: 4 additions & 4 deletions crates/solver-core/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
solver-storage = { path = "../solver-storage", features = ["testing"] }
6 changes: 3 additions & 3 deletions crates/solver-delivery/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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"]
2 changes: 1 addition & 1 deletion crates/solver-demo/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -52,5 +52,5 @@ uuid = { version = "1", features = ["v4"] }

[dev-dependencies]
tempfile = "3"
wiremock = "0.5"
tokio-test = "0.4"
wiremock = "0.5"
6 changes: 3 additions & 3 deletions crates/solver-order/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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 }
7 changes: 3 additions & 4 deletions crates/solver-pricing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"]
12 changes: 6 additions & 6 deletions crates/solver-service/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand Down Expand Up @@ -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"
6 changes: 3 additions & 3 deletions crates/solver-settlement/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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"
10 changes: 5 additions & 5 deletions crates/solver-storage/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"] }
Expand All @@ -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 }
77 changes: 76 additions & 1 deletion crates/solver-types/src/utils/conversion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -140,8 +140,16 @@ pub fn parse_bytes32_from_hex(hex_str: &str) -> Result<[u8; 32], Box<dyn std::er
/// * `Ok(Address)` if the string is a valid address
/// * `Err(String)` with error description if parsing fails
pub fn parse_address(hex_str: &str) -> Result<Address, String> {
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() {
Expand All @@ -152,7 +160,7 @@ pub fn parse_address(hex_str: &str) -> Result<Address, String> {
// 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)
Expand Down Expand Up @@ -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)
Expand Down
Loading