Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
52 commits
Select commit Hold shift + click to select a range
82ae188
Add foundry flake
shazow May 1, 2023
d819cf1
forge init
shazow May 1, 2023
fce2b28
Makefile
shazow May 1, 2023
0d13f0f
wip: Replace sample code with some skeleton contracts
shazow May 1, 2023
4940796
forge install: solmate
shazow May 1, 2023
539fc64
wip: ManagedENSResolver
shazow May 2, 2023
601ed7d
WIP: ManagedRegistrar
shazow May 2, 2023
8ce2b2b
string subdomains -> bytes32
shazow May 2, 2023
9e192db
WIP: ManagedResolver, ManagedENSResolver
shazow May 2, 2023
e2c947b
Resolver: Comply with EIP-137 spec
shazow May 2, 2023
cebefe6
Registrar: Remove Archive, lets do it offchain instead
shazow May 2, 2023
bd6922e
builds!
shazow May 2, 2023
5ec2b1e
forge install: ens-contracts
shazow May 3, 2023
f4a611b
tests: Add basic ManagedRegistrar tests
shazow May 3, 2023
fcff63e
tests: testOwner, testSet for invalid addr
shazow May 3, 2023
97b69ea
WIP: tests: ManagedENSResolver sketch but BytesUtils is not working yet
shazow May 3, 2023
4b8b982
tests: Use our own namehash implementation 🙃
shazow May 3, 2023
5e149f1
tests: Reformat, add Multiset tests
shazow May 4, 2023
066e05a
tests: Added two tier helper
shazow May 3, 2023
69eef63
tests: Add a subdomain namehash function, may need it later
shazow May 3, 2023
0212621
test: Pull out helpers
shazow May 8, 2023
61718a0
ManagedENSResolver: Remove Owned
shazow May 8, 2023
a2f5f8c
script: Add basic deploy script
shazow May 8, 2023
d0052a6
deploy, deploy-forked
shazow May 8, 2023
7b81f7a
test: Add generative tests for benchmarking
shazow May 8, 2023
93ab89f
WIP: Forked chain override for resolver, impersonate not working
shazow May 8, 2023
e19febf
script: Add override helper
shazow May 9, 2023
656a72c
script/override.sh: Add example call for setSubnodeRecord
shazow May 9, 2023
52034dd
src/ManagedENSResolver.sol: Added resolver and resolve interfaces, wo…
shazow May 9, 2023
5128d74
WIP: Started rewriting ManagedRegistrar to use ens.setSubnodeOwner(...)
shazow May 9, 2023
f58b7d3
script/override.sh: Add alternative register method
shazow May 9, 2023
5c90ac4
WIP: Changing Resolver to wrap Registrar.set with subnode setting
shazow May 10, 2023
dd72554
WIP: E2E test using a forked ENS instance, almost passing
shazow May 10, 2023
ea7c9c9
test: ENSFork passes
shazow May 10, 2023
b4f25a0
ManagedRegistrar: Allow both owner and adminSetter, add tests
shazow May 10, 2023
4f48b1c
script/override.sh: Take registrar for overriding
shazow May 15, 2023
3ff16cb
src: Split a base Resolver from ManagedENSResolver
shazow May 15, 2023
f367289
src: Add ChildResolver which proxies a parent IFullResolver for addit…
shazow May 15, 2023
57be9df
src: Add ManagedRegistrarWithReverse impl, INameResolver impl for res…
shazow May 15, 2023
58de192
forge install: openzeppelin-contracts
shazow May 16, 2023
3bb2516
Switch solmate to openzeppelin, mainly owned to ownable
shazow May 16, 2023
c1cda84
reorg layout, add interfacs and extended dirs
shazow May 16, 2023
01a63e7
src: Registrar: Pull out internal helpers, permission checking
shazow May 16, 2023
fb9dc95
WIP: src/extended/PermitRegistrarWithReverse.sol
shazow May 16, 2023
704eac4
WIP: src/extended/PermitRegistrarWithReverse.sol
shazow May 16, 2023
eabf852
src/extended/PermitRegistrarWithReverse.sol: Rename const
shazow May 18, 2023
2d9a3dd
src, test: PermitRegistrarWithReverse is working
shazow May 23, 2023
cbdbe4c
test: PermitRegistrar permutations
shazow May 23, 2023
ebc4d1c
Add documentation
shazow May 23, 2023
75deb4e
test/Helpers.sol: Remove unused ReverseHelpers
shazow May 23, 2023
5d448a1
README: Clarify namehash
shazow May 23, 2023
6c5ad75
skyteller -> example
shazow Jun 27, 2023
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
34 changes: 34 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: test

on: workflow_dispatch

env:
FOUNDRY_PROFILE: ci

jobs:
check:
strategy:
fail-fast: true

name: Foundry project
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
submodules: recursive

- name: Install Foundry
uses: foundry-rs/foundry-toolchain@v1
with:
version: nightly

- name: Run Forge build
run: |
forge --version
forge build --sizes
id: build

- name: Run Forge tests
run: |
forge test -vvv
id: test
16 changes: 16 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# Compiler files
cache/
out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/

# Docs
docs/

# Dotenv file
.env

/.*
9 changes: 9 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[submodule "lib/forge-std"]
path = lib/forge-std
url = https://github.com/foundry-rs/forge-std
[submodule "lib/ens-contracts"]
path = lib/ens-contracts
url = https://github.com/ensdomains/ens-contracts
[submodule "lib/openzeppelin-contracts"]
path = lib/openzeppelin-contracts
url = https://github.com/OpenZeppelin/openzeppelin-contracts
23 changes: 23 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
build:
forge build

test:
forge test --offline -vv

gas-report:
NUM=100 forge test --offline --gas-report --match-test "test_Set|test_Multiset|test_RegisterWithPermit"

fork:
anvil --fork-url "$(ETH_RPC_URL)"

deploy:
# ENV should include ETH_RPC_URL, ETH_PRIVATE_KEY, ETH_OWNER_ADDRESS
forge script script/ManagedENSResolver.s.sol:Deploy --rpc-url "$(ETH_RPC_URL)" --broadcast --verify -vvvv

# Call `make fork` and grab one of the forked private keys
# export ETH_PRIVATE_KEY="0x..."
# Note that this will produce a ./broadcast output
deploy-forked:
forge script script/ManagedENSResolver.s.sol:Deploy --rpc-url "http://127.0.0.1:8545" --broadcast -vvvv

.PHONY: test build
62 changes: 61 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,61 @@
# skyteller-ens-registry
# ENS Managed Registry

## Getting Started

```
$ nix develop # or install foundry some other way
$ make test # or `forge test`
```

## Overview

### `src/ManagedRegistrar.sol`

A simple managed registrar, allowing an admin to set namehash node to address
mappings.

Note: ENS operates on recursive domain component hashes ([namehash](https://docs.ens.domains/contract-api-reference/name-processing)) rather than DNS-style domain strings. Roughly speaking, `foo.example.eth`'s namehash is:

```
sha3(
sha3(
sha3(
bytes32(0x0) + sha3("eth")
) + sha3("example")
) + sha3("foo")
)
```

When a client is resolving a domain, the namehash is provided.


### `src/Resolver.sol`

A wildcard ENS resolver that uses a registrar (like `ManagedRegistrar` above) as a backend.

Note: [Wildcard resolvers](https://docs.ens.domains/ens-improvement-proposals/ensip-10-wildcard-resolution) are different from the original ENS subnode-based resolver, which relied on registering every subdomain on ENS's central subnode registry. With wildcard resolvers, the client is expected to recursively attempt to find a resolver for each level until it succeeds: First `foo.example.eth`, if no resolver is registered then check `example.eth`, if no resolver then fall back to the default `eth` resolver.

### `src/extended/ManagedENSResolver.sol`

Builds on top of the basic Resolver, except it adds a wrapper `register(...)` helper which also performs subnode registering for each subdomain.

This is mainly to demonstrate what it would take to support even naive resolvers, and to measure the gas difference.

### `src/extended/ChildResolver.sol`

Wrapper around another Resolver which proxies all of the optional fields as available default values. If we want to maintain all of the set field values on today's production resolver without reapplying them to a new resolver's state, we can use this.

### `src/extended/ManagedRegistrarWithReverse.sol`

Builds on top of a basic ManagedRegistrar, but adds reverse name lookup registration. Some DApps use this to convert addresses to ENS names for display purposes.

Setting a name mapping is an additional call, with additional gas costs.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Forgot one more thing here: The target address for reverse lookup needs to register a resolver with the global ENS contract: https://docs.ens.domains/contract-api-reference/reverseregistrar

If the target address is a contact (e.g. the skyteller proxy), then the contract would need to call that. This would require adding a helper function to the skyteller proxy (or doing it on init).

### `src/extended/PermitRegistrarWithReverse.sol`

Builds on top of ManagedRegistrarWithReverse, except the user pays for the gas costs of registering their subdomain. A signature oracle provides a signed digest that a user can use to claim a name. A reverse name mapping is set at the same time.


## License

MIT
109 changes: 109 additions & 0 deletions flake.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

32 changes: 32 additions & 0 deletions flake.nix
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
inputs = {
utils.url = "github:numtide/flake-utils";
foundry.url = "github:shazow/foundry.nix/monthly"; # Use monthly branch for permanent releases
};

outputs = { self, nixpkgs, utils, foundry }:
utils.lib.eachDefaultSystem (system:
let
pkgs = import nixpkgs {
inherit system;
overlays = [ foundry.overlay ];
};
in {

devShell = with pkgs; mkShell {
buildInputs = [
# From the foundry overlay
# Note: Can also be referenced without overlaying as: foundry.defaultPackage.${system}
foundry-bin

# ... any other dependencies we need
#solc
];

# Decorative prompt override so we know when we're in a dev shell
shellHook = ''
export PS1="[dev] $PS1"
'';
};
});
}
6 changes: 6 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]

# See more config options https://github.com/foundry-rs/foundry/tree/master/config
1 change: 1 addition & 0 deletions lib/ens-contracts
Submodule ens-contracts added at f5f2ed
1 change: 1 addition & 0 deletions lib/forge-std
Submodule forge-std added at 73d44e
1 change: 1 addition & 0 deletions lib/openzeppelin-contracts
Submodule openzeppelin-contracts added at 0a25c1
35 changes: 35 additions & 0 deletions script/ManagedENSResolver.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import "forge-std/Script.sol";

import {ManagedENSResolver} from "../src/extended/ManagedENSResolver.sol";
import {ManagedRegistrarWithReverse} from "../src/extended/ManagedRegistrarWithReverse.sol";

import {Helpers} from "../test/Helpers.sol";

contract Deploy is Script {
function run() public {
uint256 deployerPrivateKey = vm.envUint("ETH_PRIVATE_KEY");
address ownerAddress = vm.envAddress("ETH_OWNER_ADDRESS");

vm.startBroadcast(deployerPrivateKey);

ManagedRegistrarWithReverse registrar = new ManagedRegistrarWithReverse();
new ManagedENSResolver(
registrar, // IRegistrar
registrar, // INameResolver
Helpers.namehash("example", "eth") // parentNode
);

bytes32 ownerNode = Helpers.namehash("owner", "example", "eth");
registrar.set(ownerNode, ownerAddress);

if (ownerAddress != address(0)) {
console.log("Changing registrar owner: %s -> %s", registrar.owner(), ownerAddress);
registrar.transferOwnership(ownerAddress);
}

vm.stopBroadcast();
}
}
37 changes: 37 additions & 0 deletions script/OverrideResolver.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.13;

import "forge-std/Script.sol";

interface ENS {
function resolver(bytes32 node) external view returns (address);
function owner(bytes32 node) external view returns (address);

function setResolver(bytes32 node, address resolver) external;
}

// FIXME: This isn't working with impersonate. Use the cast-based script instead for now.

// Override replaces the ENS resolver in a forked environment, for testing.
// We use --unlocked --sender "0x4863A39d26F8b2e40d2AAbFf1eEe55E4B5015C4f"
contract Override is Script {
function run() public {
address resolverAddress = vm.envAddress("RESOLVER_ADDRESS");

ENS ens = ENS(0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e);

bytes32 exampleNode = 0x3d5d2e21162745e4df4f56471fd7f651f441adaaca25deb70e4738c6f63d1224;
address currentOwner = ens.owner(exampleNode);

vm.startBroadcast(currentOwner);

// Replace the resolver
ens.setResolver(exampleNode, resolverAddress);

console.log("New resolver: %s", ens.resolver(exampleNode));

vm.stopBroadcast();

// $ cast call --rpc-url "http://127.0.0.1:8545" 0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e "resolver(bytes32) returns (address)" "0x3d5d2e21162745e4df4f56471fd7f651f441adaaca25deb70e4738c6f63d1224"
}
}
Loading