Skip to content

Commit

Permalink
feat: tiny ENS (#1)
Browse files Browse the repository at this point in the history
* feat: implement `TinyENS`

* ci: update `test` job

* chore: update `test` workflow

* test: document test names

* doc: update contract doc

* doc: mention `TinyENS` in `README.md`
  • Loading branch information
leovct authored Oct 27, 2023
1 parent f1c5192 commit e77933b
Show file tree
Hide file tree
Showing 5 changed files with 158 additions and 3 deletions.
5 changes: 2 additions & 3 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: test

on: workflow_dispatch
on: push

env:
FOUNDRY_PROFILE: ci
Expand Down Expand Up @@ -29,6 +29,5 @@ jobs:
id: build

- name: Run Forge tests
run: |
forge test -vvv
run: forge test -vvvv --summary --detailed --gas-report
id: test
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,11 @@ Tiny web3 protocols and applications.
The aim is to keep it simple, quick to implement and interesting to learn Solidity.

Inspired by [lil-web3](https://github.com/m1guelpf/lil-web3) by [Miguel Piedrafita](https://twitter.com/m1guelpf).

## Table of contents

- [Tiny ENS](#tiny-ens)

## Tiny ENS

Map human-readable names like 'vitalik.eth' to machine-readable identifiers such as Ethereum addresses like '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' and support the reverse resolution.
3 changes: 3 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,8 @@
src = "src"
out = "out"
libs = ["lib"]
remappings = [
'@forge-std/=lib/forge-std/src/',
]

# See more config options https://github.com/foundry-rs/foundry/blob/master/crates/config/README.md#all-options
65 changes: 65 additions & 0 deletions src/TinyENS.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

/// @notice Interface for TinyENS.
interface ITinyENS {
/// @notice Register a new ENS name and link it to an address.
/// @param name The ENS name to register.
function register(string memory name) external;

/// @notice Update the ENS name linked to an address.
/// @param newName The new ENS name.
function update(string memory newName) external;

/// @notice Resolve the address associated with a given ENS name.
/// @param name The ENS name to resolve.
/// @return The address associated with the ENS name.
function resolve(string memory name) external view returns (address);

/// @notice Reverse resolve an address to its associated ENS name.
/// @param targetAddress The target address to reverse resolve.
/// @return The ENS name associated with the address.
function reverse(address targetAddress) external view returns (string memory);
}

/// @title Tiny Ethereum Name Service
/// @author leovct
/// @notice Map human-readable names like 'vitalik.eth' to machine-readable identifiers such as
/// Ethereum addresses like '0xd8dA6BF26964aF9D7eEd9e03E53415D37aA96045' and support the reverse
/// resolution.
contract TinyENS is ITinyENS {
/// @notice Thrown when trying to register a name already registered.
error AlreadyRegistered();

/// @notice Map ENS names to addresses.
mapping(string => address) private registry;

/// @notice Map addresses to ENS names.
mapping(address => string) private reverseRegistry;

modifier notRegistered(string memory name) {
if (registry[name] != address(0)) revert AlreadyRegistered();
_;
}

function register(string memory name) public notRegistered(name) {
registry[name] = msg.sender;
reverseRegistry[msg.sender] = name;
}

function update(string memory newName) external notRegistered(newName) {
// Unlink the old name and the address.
string memory oldName = reverseRegistry[msg.sender];
registry[oldName] = address(0);
// Register the new name.
register(newName);
}

function resolve(string memory name) external view returns (address) {
return registry[name];
}

function reverse(address targetAddress) external view returns (string memory) {
return reverseRegistry[targetAddress];
}
}
80 changes: 80 additions & 0 deletions test/TinyENS.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.13;

import '../src/TinyENS.sol';
import '@forge-std/Test.sol';

contract TinyENSTest is Test {
TinyENS private tinyENS;

address private owner = makeAddr('owner');
address private alice = makeAddr('alice');
address private bob = makeAddr('bob');

function setUp() public {
vm.startPrank(owner);
tinyENS = new TinyENS();
console2.log('TinyENS deployed');
vm.stopPrank();
}

function testRegisterNewName() public {
vm.startPrank(alice);
tinyENS.register('alice.eth');
vm.stopPrank();

assertEq(tinyENS.resolve('alice.eth'), alice);
assertEq(tinyENS.reverse(alice), 'alice.eth');
console2.log('Alice registed alice.eth');
}

function testRegisterAlreadyRegisteredName() public {
vm.startPrank(alice);
tinyENS.register('alice.eth');
assertEq(tinyENS.resolve('alice.eth'), alice);
assertEq(tinyENS.reverse(alice), 'alice.eth');
console2.log('Alice registed alice.eth');

console2.log('Bob tries to register alice.eth');
vm.startPrank(bob);
vm.expectRevert(TinyENS.AlreadyRegistered.selector);
tinyENS.register('alice.eth');
assertEq(tinyENS.resolve('alice.eth'), alice);
assertEq(tinyENS.reverse(alice), 'alice.eth');
assertEq(tinyENS.reverse(bob), '');
vm.stopPrank();
}

function testUpdateWithNewName() public {
vm.startPrank(alice);
tinyENS.register('alice.eth');
assertEq(tinyENS.resolve('alice.eth'), alice);
assertEq(tinyENS.reverse(alice), 'alice.eth');
console2.log('Alice registed alice.eth');

tinyENS.register('alice22.eth');
assertEq(tinyENS.resolve('alice22.eth'), alice);
assertEq(tinyENS.reverse(alice), 'alice22.eth');
console2.log('Alice updated its name to alice22.eth');
vm.stopPrank();
}

function testUpdateWithAlreadyRegisteredName() public {
vm.startPrank(alice);
tinyENS.register('alice.eth');
console2.log('Alice registed alice.eth');

vm.startPrank(bob);
tinyENS.register('bob.eth');
console2.log('Bob registed bob.eth');

console2.log('Bob tries to update its name to alice.eth');
vm.expectRevert(TinyENS.AlreadyRegistered.selector);
tinyENS.update('alice.eth');
assertEq(tinyENS.resolve('alice.eth'), alice);
assertEq(tinyENS.reverse(alice), 'alice.eth');
assertEq(tinyENS.resolve('bob.eth'), bob);
assertEq(tinyENS.reverse(bob), 'bob.eth');
vm.stopPrank();
}
}

0 comments on commit e77933b

Please sign in to comment.