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
6 changes: 0 additions & 6 deletions src/chains/arbitrum.zig
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,3 @@ test "arbitrum sepolia is testnet" {
try std.testing.expectEqualStrings("Arbitrum Sepolia", sepolia.name);
try std.testing.expectEqual(true, sepolia.testnet);
}

test "arbitrum chains have no ens_registry" {
try std.testing.expect(one.ens_registry == null);
try std.testing.expect(nova.ens_registry == null);
try std.testing.expect(sepolia.ens_registry == null);
}
5 changes: 0 additions & 5 deletions src/chains/base.zig
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,3 @@ test "base sepolia is testnet" {
try std.testing.expectEqualStrings("Base Sepolia", sepolia.name);
try std.testing.expectEqual(true, sepolia.testnet);
}

test "base chains have no ens_registry" {
try std.testing.expect(mainnet.ens_registry == null);
try std.testing.expect(sepolia.ens_registry == null);
}
20 changes: 0 additions & 20 deletions src/chains/chain.zig
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ pub const Chain = struct {
rpc_urls: []const []const u8,
block_explorers: []const BlockExplorer,
multicall3: ?Contract = null,
ens_registry: ?Contract = null,
testnet: bool = false,
};

Expand Down Expand Up @@ -132,25 +131,6 @@ test "addressFromHex produces correct bytes" {
try std.testing.expectEqual(@as(u8, 0x11), addr[19]);
}

test "ethereum mainnet has ens_registry" {
const eth_mainnet = getChain(1);
try std.testing.expect(eth_mainnet != null);
try std.testing.expect(eth_mainnet.?.ens_registry != null);

const expected_ens = addressFromHex("0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e");
try std.testing.expect(std.mem.eql(u8, &eth_mainnet.?.ens_registry.?.address, &expected_ens));
}

test "non-ethereum chains have no ens_registry" {
const arb = getChain(42161);
try std.testing.expect(arb != null);
try std.testing.expect(arb.?.ens_registry == null);

const op = getChain(10);
try std.testing.expect(op != null);
try std.testing.expect(op.?.ens_registry == null);
}

test "all chains have empty rpc_urls" {
const chain_ids = [_]u64{ 1, 11155111, 17000, 42161, 42170, 421614, 10, 11155420, 8453, 84532, 137, 80002 };

Expand Down
20 changes: 0 additions & 20 deletions src/chains/ethereum.zig
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@ const multicall3_contract = Contract{
.block_created = 14353601,
};

const ens_registry_contract = Contract{
.address = addressFromHex("0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e"),
.block_created = 9380380,
};

pub const mainnet: Chain = .{
.id = 1,
.name = "Ethereum",
Expand All @@ -32,7 +27,6 @@ pub const mainnet: Chain = .{
.{ .name = "Etherscan", .url = "https://etherscan.io" },
},
.multicall3 = multicall3_contract,
.ens_registry = ens_registry_contract,
.testnet = false,
};
Comment on lines 27 to 31

Expand Down Expand Up @@ -86,12 +80,6 @@ test "mainnet multicall3 address" {
try std.testing.expect(std.mem.eql(u8, &mainnet.multicall3.?.address, &expected));
}

test "mainnet ens registry" {
const expected = addressFromHex("0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e");
try std.testing.expect(mainnet.ens_registry != null);
try std.testing.expect(std.mem.eql(u8, &mainnet.ens_registry.?.address, &expected));
}

test "mainnet block explorer" {
try std.testing.expectEqual(@as(usize, 1), mainnet.block_explorers.len);
try std.testing.expectEqualStrings("https://etherscan.io", mainnet.block_explorers[0].url);
Expand All @@ -108,11 +96,3 @@ test "holesky is testnet" {
try std.testing.expectEqualStrings("Holesky", holesky.name);
try std.testing.expectEqual(true, holesky.testnet);
}

test "sepolia has no ens_registry" {
try std.testing.expect(sepolia.ens_registry == null);
}

test "holesky has no ens_registry" {
try std.testing.expect(holesky.ens_registry == null);
}
5 changes: 0 additions & 5 deletions src/chains/optimism.zig
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,3 @@ test "optimism sepolia is testnet" {
try std.testing.expectEqualStrings("OP Sepolia", sepolia.name);
try std.testing.expectEqual(true, sepolia.testnet);
}

test "optimism chains have no ens_registry" {
try std.testing.expect(mainnet.ens_registry == null);
try std.testing.expect(sepolia.ens_registry == null);
}
5 changes: 0 additions & 5 deletions src/chains/polygon.zig
Original file line number Diff line number Diff line change
Expand Up @@ -72,8 +72,3 @@ test "polygon amoy is testnet" {
try std.testing.expectEqualStrings("Polygon Amoy", amoy.name);
try std.testing.expectEqual(true, amoy.testnet);
}

test "polygon chains have no ens_registry" {
try std.testing.expect(mainnet.ens_registry == null);
try std.testing.expect(amoy.ens_registry == null);
}
80 changes: 63 additions & 17 deletions src/ens/namehash.zig
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,31 @@ pub fn namehash(name: []const u8) [32]u8 {
return node;
}

/// Compute the labelhash (keccak256 of a single label).
/// This is used for registering second-level domains.
/// e.g., labelhash("eth") = keccak256("eth")
pub fn labelhash(label: []const u8) [32]u8 {
return keccak.hash(label);
/// DNS-encode an ENS name for Universal Resolver calls (ENSIP-10).
///
/// Each label is prefixed with its length byte, then a trailing 0x00.
/// Empty labels (e.g. from a trailing `.`) are skipped.
/// Returns an error if any label is longer than 63 bytes.
/// Caller owns the returned memory.
pub fn dnsEncode(allocator: std.mem.Allocator, name: []const u8) error{ OutOfMemory, LabelTooLong }![]u8 {
var buf: std.ArrayList(u8) = .empty;
errdefer buf.deinit(allocator);

var start: usize = 0;
var i: usize = 0;
while (i <= name.len) : (i += 1) {
if (i == name.len or name[i] == '.') {
const label = name[start..i];
if (label.len > 0) {
if (label.len > 63) return error.LabelTooLong;
try buf.append(allocator, @intCast(label.len));
try buf.appendSlice(allocator, label);
}
start = i + 1;
}
}
try buf.append(allocator, 0);
return try buf.toOwnedSlice(allocator);
}

// ============================================================================
Expand Down Expand Up @@ -104,18 +124,6 @@ test "namehash deep subdomain" {
try std.testing.expectEqualSlices(u8, &node, &result);
}

test "labelhash eth" {
const result = labelhash("eth");
const expected = keccak.hash("eth");
try std.testing.expectEqualSlices(u8, &expected, &result);
}

test "labelhash is just keccak256 of the label" {
const result = labelhash("vitalik");
const expected = keccak.hash("vitalik");
try std.testing.expectEqualSlices(u8, &expected, &result);
}

test "namehash single label (no dots)" {
// A single label like "eth" should produce: keccak256(0x00..00 ++ keccak256("eth"))
const result = namehash("eth");
Expand Down Expand Up @@ -160,3 +168,41 @@ test "namehash trailing dot handling" {
const without_dot = namehash("foo.eth");
try std.testing.expectEqualSlices(u8, &without_dot, &with_dot);
}

test "dnsEncode ur.integration-tests.eth" {
const allocator = std.testing.allocator;
const encoded = try dnsEncode(allocator, "ur.integration-tests.eth");
defer allocator.free(encoded);

const expected = try hex.hexToBytesFixed(26, "02757211696e746567726174696f6e2d74657374730365746800");
try std.testing.expectEqualSlices(u8, &expected, encoded);
}

test "dnsEncode yashgoyal.eth" {
const allocator = std.testing.allocator;
const encoded = try dnsEncode(allocator, "yashgoyal.eth");
defer allocator.free(encoded);

// 9 "yashgoyal" + 3 "eth" + terminator = 1+9 + 1+3 + 1 = 15
try std.testing.expectEqual(@as(usize, 15), encoded.len);
try std.testing.expectEqual(@as(u8, 9), encoded[0]);
try std.testing.expectEqualSlices(u8, "yashgoyal", encoded[1..10]);
try std.testing.expectEqual(@as(u8, 3), encoded[10]);
try std.testing.expectEqualSlices(u8, "eth", encoded[11..14]);
try std.testing.expectEqual(@as(u8, 0), encoded[14]);
}

test "dnsEncode skips empty labels" {
const allocator = std.testing.allocator;
const with_dot = try dnsEncode(allocator, "foo.eth.");
defer allocator.free(with_dot);
const without_dot = try dnsEncode(allocator, "foo.eth");
defer allocator.free(without_dot);
try std.testing.expectEqualSlices(u8, without_dot, with_dot);
}

test "dnsEncode rejects labels longer than 63" {
const allocator = std.testing.allocator;
const long_label = "a" ** 64 ++ ".eth";
try std.testing.expectError(error.LabelTooLong, dnsEncode(allocator, long_label));
}
Loading