Skip to content
Draft
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
2,124 changes: 2,086 additions & 38 deletions bun.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions packages/alloy/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
target/
16 changes: 16 additions & 0 deletions packages/alloy/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
[package]
name = "ens-tests-alloy"
version = "0.1.0"
edition = "2021"

[[test]]
name = "resolution"
harness = false

[dependencies]
alloy = { version = "1.8", features = ["ens", "provider-http"] }
alloy-ens = { version = "1.8", features = ["provider"] }
tokio = { version = "1", features = ["full"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"
chrono = "0.4"
47 changes: 47 additions & 0 deletions packages/alloy/results.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
{
"timestamp": "2026-04-06T11:48:49.143133+00:00",
"results": [
{
"caseId": "universal-resolver",
"passed": false,
"actual": "0x1111111111111111111111111111111111111111",
"error": "Expected Some(\"0x2222222222222222222222222222222222222222\"), got Some(\"0x1111111111111111111111111111111111111111\")",
"durationMs": 787
},
{
"caseId": "forward-eth-offchain",
"passed": false,
"actual": null,
"error": "ENS resolver not found for name \"test.offchaindemo.eth\"",
"durationMs": 143
},
{
"caseId": "forward-text-onchain",
"passed": true,
"actual": "https://raw.githubusercontent.com/ensdomains/resolution-tests/refs/heads/main/assets/avatar.svg",
"error": null,
"durationMs": 261
},
{
"caseId": "forward-text-offchain",
"passed": false,
"actual": null,
"error": "ENS resolver not found for name \"test.offchaindemo.eth\"",
"durationMs": 137
},
{
"caseId": "reverse-eth",
"passed": true,
"actual": "devrel.enslabs.eth",
"error": null,
"durationMs": 261
},
{
"caseId": "forward-dns-offchain",
"passed": false,
"actual": null,
"error": "ENS resolver not found for name \"pokersback.com\"",
"durationMs": 109
}
]
}
1 change: 1 addition & 0 deletions packages/alloy/src/lib.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Placeholder lib for the crate
221 changes: 221 additions & 0 deletions packages/alloy/tests/resolution.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
use alloy::providers::ProviderBuilder;
use alloy_ens::ProviderEnsExt;
use serde::{Deserialize, Serialize};
use std::fs;
use std::time::Instant;

#[derive(Deserialize)]
struct TestCase {
id: String,
category: String,
description: String,
status: String,
input: TestInput,
expected: TestExpected,
method: String,
params: serde_json::Value,
}

#[derive(Deserialize)]
struct TestInput {
name: Option<String>,
address: Option<String>,
#[serde(rename = "chainId")]
chain_id: Option<u64>,
}

#[derive(Deserialize)]
struct TestExpected {
address: Option<String>,
name: Option<String>,
value: Option<String>,
}

#[derive(Serialize)]
struct TestResult {
#[serde(rename = "caseId")]
case_id: String,
passed: bool,
actual: Option<String>,
error: Option<String>,
#[serde(rename = "durationMs")]
duration_ms: u64,
}

#[derive(Serialize)]
struct LibraryResults {
timestamp: String,
results: Vec<TestResult>,
}

fn checksum_address(addr: &alloy::primitives::Address) -> String {
format!("{addr}")
}

#[tokio::main]
async fn main() {
let rpc_url = std::env::var("RPC_URL").expect("RPC_URL environment variable is required");

let provider = ProviderBuilder::new()
.connect_http(rpc_url.parse().expect("Invalid RPC_URL"));

// Load test cases
let test_cases_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("../../test-cases.json");
let test_cases_content = fs::read_to_string(&test_cases_path)
.expect("Failed to read test-cases.json");
let test_cases: Vec<TestCase> = serde_json::from_str(&test_cases_content)
.expect("Failed to parse test-cases.json");

let ready_cases: Vec<&TestCase> = test_cases
.iter()
.filter(|tc| tc.status == "ready")
.collect();

let mut results: Vec<TestResult> = Vec::new();

// Unsupported methods/cases
let unsupported_methods = ["contenthash"];
let unsupported_cases = ["forward-base-onchain", "reverse-l2"];

for test_case in &ready_cases {
if unsupported_methods.contains(&test_case.method.as_str()) {
continue;
}
if unsupported_cases.contains(&test_case.id.as_str()) {
continue;
}

println!("Running: {} - {}", test_case.id, test_case.description);

let start = Instant::now();

let result = match test_case.category.as_str() {
"forward" => run_forward_test(&provider, test_case).await,
"reverse" => run_reverse_test(&provider, test_case).await,
_ => Err(format!("Unknown category: {}", test_case.category)),
};

let duration_ms = start.elapsed().as_millis() as u64;

match result {
Ok(actual) => {
let expected = test_case
.expected
.address
.as_deref()
.or(test_case.expected.value.as_deref())
.or(test_case.expected.name.as_deref());

let passed = actual.as_deref() == expected;

println!(
" {} ({}ms)",
if passed { "PASS" } else { "FAIL" },
duration_ms
);
if !passed {
println!(" Expected: {:?}, Got: {:?}", expected, actual);
}

results.push(TestResult {
case_id: test_case.id.clone(),
passed,
actual: actual.clone(),
error: if passed {
None
} else {
Some(format!(
"Expected {:?}, got {:?}",
expected, actual
))
},
duration_ms,
});
}
Err(err) => {
println!(" FAIL ({}ms): {}", duration_ms, err);
results.push(TestResult {
case_id: test_case.id.clone(),
passed: false,
actual: None,
error: Some(err),
duration_ms,
});
}
}
}

// Write results
let output = LibraryResults {
timestamp: chrono::Utc::now().to_rfc3339(),
results,
};

let output_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR")).join("results.json");
let json = serde_json::to_string_pretty(&output).unwrap();
fs::write(&output_path, &json).unwrap();
println!("\nResults written to {:?}", output_path);
}

async fn run_forward_test(
provider: &impl alloy::providers::Provider<alloy::network::Ethereum>,
test_case: &TestCase,
) -> Result<Option<String>, String> {
let name = test_case
.input
.name
.as_deref()
.ok_or("Missing input name")?;

match test_case.method.as_str() {
"addr" => {
let addr = provider
.resolve_name(name)
.await
.map_err(|e| format!("{e}"))?;
Ok(Some(checksum_address(&addr)))
}
"text" => {
let key = test_case.params.get("key")
.and_then(|v| v.as_str())
.ok_or("Missing text key param")?;
let value = provider
.lookup_txt(name, key)
.await
.map_err(|e| format!("{e}"))?;
if value.is_empty() {
Ok(None)
} else {
Ok(Some(value))
}
}
_ => Err(format!("Unsupported method: {}", test_case.method)),
}
}

async fn run_reverse_test(
provider: &impl alloy::providers::Provider<alloy::network::Ethereum>,
test_case: &TestCase,
) -> Result<Option<String>, String> {
let address_str = test_case
.input
.address
.as_deref()
.ok_or("Missing input address")?;

let address: alloy::primitives::Address = address_str
.parse()
.map_err(|e| format!("Invalid address: {e}"))?;

let name = provider
.lookup_address(&address)
.await
.map_err(|e| format!("{e}"))?;

if name.is_empty() {
Ok(None)
} else {
Ok(Some(name))
}
}
16 changes: 16 additions & 0 deletions packages/ensjs-v4/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
{
"name": "@ens-tests/ensjs-v4",
"version": "1.0.0",
"private": true,
"type": "module",
"scripts": {
"test": "bun test"
},
"dependencies": {
"@ensdomains/ensjs": "^4.2.2",
"viem": "^2.21.0"
},
"devDependencies": {
"@types/bun": "latest"
}
}
61 changes: 61 additions & 0 deletions packages/ensjs-v4/results.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"timestamp": "2026-04-06T10:35:25.793Z",
"results": [
{
"caseId": "universal-resolver",
"passed": true,
"actual": "0x2222222222222222222222222222222222222222",
"error": null,
"durationMs": 356
},
{
"caseId": "forward-base-onchain",
"passed": true,
"actual": "0xeE9eeaAB0Bb7D9B969D701f6f8212609EDeA252E",
"error": null,
"durationMs": 96
},
{
"caseId": "forward-eth-offchain",
"passed": true,
"actual": "0x779981590E7Ccc0CFAe8040Ce7151324747cDb97",
"error": null,
"durationMs": 752
},
{
"caseId": "forward-text-onchain",
"passed": true,
"actual": "https://raw.githubusercontent.com/ensdomains/resolution-tests/refs/heads/main/assets/avatar.svg",
"error": null,
"durationMs": 90
},
{
"caseId": "forward-text-offchain",
"passed": true,
"actual": "asdflkjasdflkjasdf",
"error": null,
"durationMs": 428
},
{
"caseId": "forward-contenthash",
"passed": true,
"actual": "ipfs://bafybeifx7yeb55armcsxwwitkymga5xf53dxiarykms3ygqic223w5sk3m",
"error": null,
"durationMs": 91
},
{
"caseId": "forward-dns-offchain",
"passed": true,
"actual": "0x534631Bcf33BDb069fB20A93d2fdb9e4D4dD42CF",
"error": null,
"durationMs": 902
},
{
"caseId": "reverse-eth",
"passed": true,
"actual": "devrel.enslabs.eth",
"error": null,
"durationMs": 94
}
]
}
Loading