-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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
Showing
5 changed files
with
158 additions
and
3 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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]; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); | ||
} | ||
} |