Skip to content

Commit

Permalink
Merge pull request #225 from blockful-io/220-offchain-resolver-namewr…
Browse files Browse the repository at this point in the history
…apper-based-interface

Offchain resolver `namewrapper` based interface
  • Loading branch information
pikonha authored Oct 21, 2024
2 parents 1371c31 + 5bc5991 commit bbd353f
Show file tree
Hide file tree
Showing 11 changed files with 387 additions and 82 deletions.
5 changes: 2 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ script_dir := packages/contracts/script
deploy_dir := $(script_dir)/deploy

PRIVATE_KEY?=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
L2_PRIVATE_KEY?=0xb6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659
ETHERSCAN_API_KEY?=VEJ7GISNRKFUESRPC4W4D3ZEM2P9B4J6C4

.PHONY: deploy-arb
Expand Down Expand Up @@ -34,7 +33,7 @@ deploy-arb-resolver:
--rpc-url arb_sepolia \
--broadcast \
-vvv \
--private-key $(L2_PRIVATE_KEY) \
--private-key $(PRIVATE_KEY) \
--verify;


Expand All @@ -47,7 +46,7 @@ deploy-arb-full:
--rpc-url arb_sepolia \
--broadcast \
-vvv \
--private-key $(L2_PRIVATE_KEY) \
--private-key $(PRIVATE_KEY) \
--verify \
&& \
) true; \
Expand Down
22 changes: 22 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,28 @@ This project not only makes ENS more efficient and cost-effective but also opens
- **Increase Usability**: Make ENS more user-friendly and accessible.
- **Reference implementation**: Create a reference on how to implement off-chain storage and management.

## Deployments

### Mainnet

| Contract | Network | Address |
| ---------------- | -------- | --------------------------------------------------------------------------------------------------------------------- |
| DatabaseResolver | Ethereum | [0xBF3F57862717099319285c1E2664Cd583f35E333](https://etherscan.io/address/0xBF3F57862717099319285c1E2664Cd583f35E333) |

### Sepolia

| Contract | Network | Address |
| --------------------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
| ArbitrumVerifier | Ethereum | [0x8fc4a214705e3c40032e99f867d964c012bf8efb](https://sepolia.etherscan.io/address/0x8fc4a214705e3c40032e99f867d964c012bf8efb) |
| L1Resolver | Ethereum | [0xF0c1d78C73B2fCBF17e1c4DbBBD9df30a9556BB8](https://sepolia.etherscan.io/address/0xF0c1d78C73B2fCBF17e1c4DbBBD9df30a9556BB8) |
| ENSRegistry | Arbitrum | [0x8d55e297c37993ebbd2e7a8d7688f7e5b35f1b50](https://sepolia.arbiscan.io/address/0x8d55e297c37993ebbd2e7a8d7688f7e5b35f1b50) |
| ReverseRegistrar | Arbitrum | [0xb3c9ff08671bbadddd0436cc46fbfa005c8da0a7](https://sepolia.arbiscan.io/address/0xb3c9ff08671bbadddd0436cc46fbfa005c8da0a7) |
| BaseRegistrarImplementation | Arbitrum | [0x41eedE073217084A30f6f3Bc2c546BDa1F08b5ca](https://sepolia.arbiscan.io/address/0x41eedE073217084A30f6f3Bc2c546BDa1F08b5ca) |
| NameWrapper | Arbitrum | [0xff4f34ac12a84de527cf9e24856fc8d7c42cc379](https://sepolia.arbiscan.io/address/0xff4f34ac12a84de527cf9e24856fc8d7c42cc379) |
| ETHRegistrarController | Arbitrum | [0x263c644d8f5d4bdb44cfab020491ec6fc4ca5271](https://sepolia.arbiscan.io/address/0x263c644d8f5d4bdb44cfab020491ec6fc4ca5271) |
| SubdomainController | Arbitrum | [0x41eede073217084a30f6f3bc2c546bda1f08b5ca](https://sepolia.arbiscan.io/address/0x41eede073217084a30f6f3bc2c546bda1f08b5ca) |
| PublicResolver | Arbitrum | [0x0a33f065c9c8f0F5c56BB84b1593631725F0f3af](https://sepolia.arbiscan.io/address/0x0a33f065c9c8f0F5c56BB84b1593631725F0f3af) |

## Components

The External Resolver consists of three main components, each of them is a self-contained project with its own set of files and logic, ensuring seamless integration and collaboration between them. This modular architecture allows for flexibility and customization, making the External Resolver a versatile solution for various use cases.
Expand Down
22 changes: 13 additions & 9 deletions ensips/ccip-write/ccip-write.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ The function has the following signature:

```solidity
function registerParams(
bytes memory name,
bytes calldata name,
uint256 duration
)
external
Expand Down Expand Up @@ -89,7 +89,8 @@ Aiming to integrate with the already existing interface of domain registration,

```solidity
function register(
string calldata name,
bytes32 parentNode,
string calldata label,
address owner,
uint256 duration,
bytes32 secret,
Expand All @@ -103,7 +104,8 @@ function register(

Parameters:

- `name`: DNS-encoded name to be registered
- `parentNode`: namehash of the parent domain
- `label`: DNS-encoded name to be registered
- `owner`: subdomain owner's address
- `duration`: the duration in miliseconds of the registration
- `secret`: random seed to be used for commit/reveal
Expand Down Expand Up @@ -278,7 +280,8 @@ interface OffchainRegister {
/**
* Forwards the registering of a domain to the L2 contracts
* @param name The DNS-encoded name to resolve.
* @param parentNode namehash of the parent domain
* @param label The DNS-encoded name to resolve.
* @param owner Owner of the domain
* @param duration duration The duration in seconds of the registration.
* @param resolver The address of the resolver to set for this name.
Expand All @@ -287,7 +290,8 @@ interface OffchainRegister {
* @param extraData any encoded additional data
*/
function register(
string calldata name,
bytes32 parentNode,
string calldata label,
address owner,
uint256 duration,
bytes32 secret,
Expand Down Expand Up @@ -336,10 +340,10 @@ interface OffchainMulticallable {
interface OffchainCommitable {
/**
* @notice produces the commit hash from the register calldata
* @returns the hash of the commit to be used
*/
/**
* @notice produces the commit hash from the register calldata
* @returns the hash of the commit to be used
*/
function makeCommitment(
string calldata name,
address owner,
Expand Down
14 changes: 13 additions & 1 deletion packages/client/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ export async function handleDBStorage({
})
}

export function getChain(chainId: number) {
export function getChain(chainId: number): chains.Chain | undefined {
return [
...Object.values(chains),
defineChain({
Expand All @@ -92,3 +92,15 @@ export function getChain(chainId: number) {
}),
].find((chain) => chain.id === chainId)
}

// gather the first part of the domain (e.g. floripa.blockful.eth -> floripa)
export function extractLabelFromName(name: string): string {
const [, label] = /^(\w+)/.exec(name) || []
return label
}

// gather the last part of the domain (e.g. floripa.blockful.eth -> blockful.eth)
export function extractParentFromName(name: string): string {
const [, parent] = /\w*\.(.*)$/.exec(name) || []
return parent
}
85 changes: 41 additions & 44 deletions packages/client/src/l2.write.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,48 +27,50 @@ config({
})

let {
UNIVERSAL_RESOLVER_ADDRESS: resolver,
L2_RESOLVER_ADDRESS: l2Resolver,
UNIVERSAL_RESOLVER_ADDRESS: universalResolver,
RESOLVER_ADDRESS: resolver,
CHAIN_ID: chainId = '31337',
RPC_URL: provider = 'http://127.0.0.1:8545/',
L2_RPC_URL: providerL2 = 'http://127.0.0.1:8547',
PRIVATE_KEY:
privateKey = '0xb6b15c8cb491557369f3c7d2c287b053eb229daa9c22138887752191c9520659', // local arbitrum PK
PRIVATE_KEY: privateKey,
} = process.env

const chain = getChain(parseInt(chainId))
console.log(`Connecting to ${chain?.name}.`)
if (!chain) {
throw new Error('Chain not found')
}

const client = createPublicClient({
chain,
transport: http(provider),
}).extend(walletActions)
console.log(`Connecting to ${chain?.name}.`)

// eslint-disable-next-line
const _ = (async () => {
if (!l2Resolver) {
throw new Error('L2_RESOLVER_ADDRESS is required')
if (!resolver) {
throw new Error('RESOLVER_ADDRESS is required')
}

const publicAddress = normalize('lucas.arb.eth')
const node = namehash(publicAddress)
const name = normalize('gibi.arb.eth')
const encodedName = toHex(packetToBytes(name))
const node = namehash(name)
const signer = privateKeyToAccount(privateKey as Hex)

if (!resolver) {
resolver = getChainContractAddress({
if (!universalResolver) {
universalResolver = getChainContractAddress({
chain: client.chain,
contract: 'ensUniversalResolver',
})
}

const [resolverAddr] = (await client.readContract({
address: resolver as Hex,
address: universalResolver as Hex,
functionName: 'findResolver',
abi: uAbi,
args: [toHex(packetToBytes(publicAddress))],
args: [encodedName],
})) as Hash[]

const name = extractLabelFromName(publicAddress)
const duration = 31556952000n

// SUBDOMAIN PRICING
Expand Down Expand Up @@ -105,13 +107,13 @@ const _ = (async () => {
functionName: 'register',
abi: l1Abi,
args: [
name,
encodedName, // name
signer.address, // owner
duration,
`0x${'a'.repeat(64)}` as Hex, // secret
l2Resolver, // resolver
data, // calldata
false, // primaryName
resolver,
data, // records calldata
false, // reverseRecord
0, // fuses
`0x${'a'.repeat(64)}` as Hex, // extraData
],
Expand All @@ -124,33 +126,28 @@ const _ = (async () => {
await client.simulateContract(calldata)
} catch (err) {
const data = getRevertErrorData(err)
if (data?.errorName === 'StorageHandledByL2') {
const [chainId, contractAddress] = data.args as [bigint, `0x${string}`]

const l2Client = createPublicClient({
chain: getChain(Number(chainId)),
transport: http(providerL2),
}).extend(walletActions)

try {
const { request } = await l2Client.simulateContract({
...calldata,
address: contractAddress,
})
await l2Client.writeContract(request)
} catch (err) {
console.log('error while trying to make the request: ', { err })
switch (data?.errorName) {
case 'StorageHandledByL2': {
const [chainId, contractAddress] = data.args as [bigint, `0x${string}`]

const l2Client = createPublicClient({
chain: getChain(Number(chainId)),
transport: http(providerL2),
}).extend(walletActions)

try {
const { request } = await l2Client.simulateContract({
...calldata,
address: contractAddress,
})
await l2Client.writeContract(request)
} catch (err) {
console.log('error while trying to make the request: ', { err })
}
return
}
} else if (data) {
console.error('error registering domain: ', data.errorName)
} else {
console.error('error registering domain: ', { err })
default:
console.error('error registering domain: ', { err })
}
}
})()

// gather the first part of the domain (e.g. floripa.blockful.eth -> floripa)
function extractLabelFromName(name: string): string {
const [, label] = /^(\w+)/.exec(name) || []
return label
}
3 changes: 2 additions & 1 deletion packages/contracts/script/deploy/SubdomainController.sol
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,9 @@ contract SubdomainControllerScript is DeployHelper, ENSHelper {
vm.startBroadcast();

uint256 subdomainPrice = 0.001 ether;
uint256 commitTime = 0;
SubdomainController subdomainController = new SubdomainController(
namehash("arb.eth"), address(nameWrapper), subdomainPrice, 0
address(nameWrapper), subdomainPrice, commitTime
);
nameWrapper.setApprovalForAll(address(subdomainController), true);

Expand Down
17 changes: 8 additions & 9 deletions packages/contracts/script/local/L2ArbitrumResolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {StaticMetadataService} from
"@ens-contracts/wrapper/StaticMetadataService.sol";
import {IMetadataService} from "@ens-contracts/wrapper/IMetadataService.sol";
import {PublicResolver} from "@ens-contracts/resolvers/PublicResolver.sol";
import {NameEncoder} from "@ens-contracts/utils/NameEncoder.sol";

import {ENSHelper} from "../ENSHelper.sol";
import {SubdomainController} from "../../src/SubdomainController.sol";
Expand Down Expand Up @@ -81,10 +82,7 @@ contract L2ArbitrumResolver is Script, ENSHelper {
uint256 subdomainPrice = 0.001 ether;
uint256 commitTime = 0;
SubdomainController subdomainController = new SubdomainController(
namehash("arb.eth"),
address(nameWrapper),
subdomainPrice,
commitTime
address(nameWrapper), subdomainPrice, commitTime
);
nameWrapper.setApprovalForAll(address(subdomainController), true);

Expand All @@ -102,15 +100,16 @@ contract L2ArbitrumResolver is Script, ENSHelper {
"arb", msg.sender, 31556952000, address(arbResolver), 1
);

(bytes memory name, bytes32 node) =
NameEncoder.dnsEncodeName("blockful.arb.eth");

bytes[] memory data = new bytes[](1);
data[0] = abi.encodeWithSelector(
TextResolver.setText.selector,
namehash("blockful.arb.eth"),
"com.twitter",
"@blockful"
TextResolver.setText.selector, node, "com.twitter", "@blockful"
);

subdomainController.register{value: subdomainController.price()}(
"blockful",
name,
msg.sender,
31556952000,
keccak256("secret"),
Expand Down
8 changes: 5 additions & 3 deletions packages/contracts/src/L1Resolver.sol
Original file line number Diff line number Diff line change
Expand Up @@ -103,16 +103,18 @@ contract L1Resolver is

/**
* Forwards the registering of a subdomain to the L2 contracts
* @param -name The DNS-encoded name to resolve.
* @param -name The DNS-encoded name to be registered.
* @param -owner Owner of the domain
* @param -duration duration The duration in seconds of the registration.
* @param -secret The secret to be used for the registration based on commit/reveal
* @param -resolver The address of the resolver to set for this name.
* @param -data Multicallable data bytes for setting records in the associated resolver upon reigstration.
* @param -reverseRecord Whether this name is the primary name
* @param -fuses The fuses to set for this name.
* @param -extraData any encoded additional data
*/
function register(
string calldata, /* name */
bytes calldata, /* name */
address, /* owner */
uint256, /* duration */
bytes32, /* secret */
Expand All @@ -137,7 +139,7 @@ contract L1Resolver is
* @return extraData any given structure in an ABI encoded format
*/
function registerParams(
bytes memory, /* name */
bytes calldata, /* name */
uint256 /* duration */
)
external
Expand Down
Loading

0 comments on commit bbd353f

Please sign in to comment.