Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Decouple register func #247

Merged
merged 11 commits into from
Jan 31, 2025
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ This project not only makes ENS more efficient and cost-effective but also opens
| BaseRegistrarImplementation | Arbitrum | [0x2C6a113C513fa0fd404abcCE3aC8a4BE16ccb651](https://sepolia.arbiscan.io/address/0x2C6a113C513fa0fd404abcCE3aC8a4BE16ccb651) |
| 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) |
| SubdomainController | Arbitrum | [0x2e28f723818ED7B70a3ec10879309aa39CC4b3D6](https://sepolia.arbiscan.io/address/0x2e28f723818ED7B70a3ec10879309aa39CC4b3D6) |
| PublicResolver | Arbitrum | [0x0a33f065c9c8f0F5c56BB84b1593631725F0f3af](https://sepolia.arbiscan.io/address/0x0a33f065c9c8f0F5c56BB84b1593631725F0f3af) |

## Components
Expand Down Expand Up @@ -79,7 +79,7 @@ try {
})
} catch (err) {
const data = getRevertErrorData(err)
if (data?.errorName === 'StorageHandledByOffChainDatabase') {
if (data?.errorName === 'OperationHandledOffchain') {
const [domain, url, message] = data.args as [
DomainData,
string,
Expand All @@ -104,7 +104,7 @@ try {
})
} catch (err) {
const data = getRevertErrorData(err)
if (data?.errorName === 'StorageHandledByL2') {
if (data?.errorName === 'OperationHandledOnchain') {
const [chainId, contractAddress] = data.args as [bigint, `0x${string}`]

await handleL2Storage({
Expand Down Expand Up @@ -290,7 +290,7 @@ Domain Register:
1. Find the resolver associated with the given domain through the Universal Resolver
2. Call the `register` function on the resolver passing the address of the Layer 2 resolver that will be managing the properties of a given domain
3. Client calls `setOwner` on the L1 Resolver
4. Client receive a `StorageHandledByL2` revert with the arguments required to call the gateway
4. Client receive a `OperationHandledOnchain` revert with the arguments required to call the gateway
5. Client calls the L2 Resolver with the returned arguments

![domain register](https://github.com/blockful-io/external-resolver/assets/29408363/1ef65db2-a979-4e2f-bb9f-7dde0769fae4)
Expand Down
30 changes: 1 addition & 29 deletions packages/client/src/read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,9 @@
*/

import { config } from 'dotenv'
import {
Hex,
createPublicClient,
http,
namehash,
decodeFunctionResult,
hexToString,
} from 'viem'
import { Hex, createPublicClient, http } from 'viem'
import { normalize } from 'viem/ens'
import { getChain } from './client'
import { abi as l1Abi } from '@blockful/contracts/out/L1Resolver.sol/L1Resolver.json'

config({
path: process.env.ENV_FILE || '../.env',
Expand Down Expand Up @@ -68,31 +60,11 @@ const _ = (async () => {
gatewayUrls: [gateway],
})

const resolver = await client.getEnsResolver({
name,
universalResolverAddress: universalResolverAddress as Hex,
})
const encodedContentHash = (await client.readContract({
address: resolver,
functionName: 'contenthash',
abi: l1Abi,
args: [namehash(name)],
})) as Hex

const contentHash = hexToString(
decodeFunctionResult({
abi: l1Abi,
functionName: 'contenthash',
data: encodedContentHash,
}) as Hex,
)

console.log({
twitter,
avatar,
address,
addressBtc,
name: domainName,
contentHash,
})
})()
210 changes: 109 additions & 101 deletions packages/client/src/write.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import { privateKeyToAccount } from 'viem/accounts'
import { addEnsContracts } from '@ensdomains/ensjs'

import { abi } from '@blockful/contracts/out/DatabaseResolver.sol/DatabaseResolver.json'
import { abi as universalResolverResolveAbi } from '@blockful/contracts/out/UniversalResolver.sol/UniversalResolver.json'
import { abi as urAbi } from '@blockful/contracts/out/UniversalResolver.sol/UniversalResolver.json'
import { abi as scAbi } from '@blockful/contracts/out/SubdomainController.sol/SubdomainController.json'
import { abi as nwAbi } from '@blockful/contracts/out/NameWrapper.sol/NameWrapper.json'
import { MessageData, DomainData } from '@blockful/gateway/src/types'
import { getRevertErrorData, getChain, handleDBStorage } from './client'

Expand All @@ -36,7 +37,7 @@ let {
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',
L2_RPC_URL: providerL2,
PRIVATE_KEY: privateKey,
} = process.env

Expand Down Expand Up @@ -68,24 +69,43 @@ const _ = (async () => {
const encodedName = toHex(packetToBytes(name))
const node = namehash(name)
const signer = privateKeyToAccount(privateKey as Hex)
const duration = 31556952000n

const data: Hex[] = [
encodeFunctionData({
const multicallData = [
{
functionName: 'register',
abi: scAbi,
args: [
{
name: encodedName,
owner: signer.address,
duration,
secret: zeroHash,
extraData: zeroHash,
},
],
},
{
functionName: 'setResolver',
abi: nwAbi,
args: [node, resolver],
},
{
functionName: 'setText',
abi,
args: [node, 'com.twitter', `@${name}`],
}),
encodeFunctionData({
},
{
functionName: 'setAddr',
abi,
args: [node, '0x3a872f8FED4421E7d5BE5c98Ab5Ea0e0245169A0'],
}),
encodeFunctionData({
},
{
functionName: 'setAddr',
abi,
args: [node, 1n, '0x3a872f8FED4421E7d5BE5c98Ab5Ea0e0245169A0'],
}),
encodeFunctionData({
},
{
functionName: 'setContenthash',
abi,
args: [
Expand All @@ -94,110 +114,98 @@ const _ = (async () => {
'ipns://k51qzi5uqu5dgccx524mfjv7znyfsa6g013o6v4yvis9dxnrjbwojc62pt0450',
),
],
}),
},
]

const duration = 31556952000n
const calldata = {
functionName: 'register',
abi: scAbi,
args: [
{
name: encodedName,
owner: signer.address,
duration,
secret: zeroHash,
resolver,
data,
reverseRecord: false,
fuses: 0,
extraData: zeroHash,
},
],
account: signer,
}

try {
await client.readContract({
address: universalResolver as Hex,
abi: universalResolverResolveAbi,
functionName: 'resolve',
args: [
encodedName,
encodeFunctionData({
functionName: 'getDeferralHandler',
abi,
args: [encodeFunctionData(calldata)],
}),
],
})
} catch (err) {
const data = getRevertErrorData(err)
if (!data || !data.args || data.args?.length === 0) return

const [params] = data.args
const errorResult = decodeErrorResult({
abi,
data: params as Hex,
})
switch (errorResult?.errorName) {
case 'StorageHandledByOffChainDatabase': {
const [domain, url, message] = errorResult.args as [
DomainData,
string,
MessageData,
]
await handleDBStorage({ domain, url, message, signer })
for (const calldata of multicallData) {
try {
await client.readContract({
address: universalResolver as Hex,
abi: urAbi,
functionName: 'resolve',
args: [
encodedName,
encodeFunctionData({
functionName: 'getOperationHandler',
abi,
args: [
encodeFunctionData({
functionName: calldata.functionName,
abi: calldata.abi,
args: calldata.args,
}),
],
}),
],
})
} catch (err) {
const data = getRevertErrorData(err)
if (!data || !data.args || data.args?.length === 0) {
console.log({ err })
return
}
case 'StorageHandledByL2': {
const [chainId, contractAddress] = errorResult.args as [
bigint,
`0x${string}`,
]

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

// SUBDOMAIN PRICING

let value = 0n
if (calldata.functionName === 'register') {
const registerParams = (await l2Client.readContract({
address: contractAddress,
abi: scAbi,
functionName: 'registerParams',
args: [encodedName, duration],
})) as {
price: bigint
commitTime: bigint
extraData: Hex
available: boolean
token: Hex
}
value = registerParams.price

if (!registerParams.available) {
console.log('Domain unavailable')
return
}
const [params] = data.args
const errorResult = decodeErrorResult({
abi,
data: params as Hex,
})
switch (errorResult?.errorName) {
case 'OperationHandledOffchain': {
const [domain, url, message] = errorResult.args as [
DomainData,
string,
MessageData,
]
await handleDBStorage({ domain, url, message, signer })
continue
}
try {
case 'OperationHandledOnchain': {
const [chainId, contractAddress] = errorResult.args as [
bigint,
`0x${string}`,
]

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

let value = 0n
if (calldata.functionName === 'register') {
const registerParams = (await l2Client.readContract({
address: contractAddress,
abi: scAbi,
functionName: 'registerParams',
args: [encodedName, duration],
})) as {
price: bigint
commitTime: bigint
extraData: Hex
available: boolean
token: Hex
}
value = registerParams.price

if (!registerParams.available) {
console.log('Domain unavailable')
return
}
}
const { request } = await l2Client.simulateContract({
...calldata,
functionName: calldata.functionName,
abi: calldata.abi,
args: calldata.args,
address: contractAddress,
value,
account: signer,
})
await l2Client.writeContract(request)
} catch (err) {
console.log('error while trying to make the request: ', { err })
return
}
return
default:
console.error('error registering domain: ', { err })
}
default:
console.error('error registering domain: ', { err })
}
}
})()
Loading
Loading