1import { namehash, Contract, keccak256, concat, toUtf8Bytes, randomBytes, ZeroAddress } from 'ethers' 7const ethRegistrarControllerAddr = '0x59E16fcCd424Cc24e280Be16E11Bcd56fb0CE547' 8const baseRegistrarAddr = '0x57f1887a8BF19b14fC0dF6Fd9B2acc9Af147eA85' 9const nameWrapperAddr = '0xD4416b13d2b3a9aBae7AcD5D6C2BbDBE25686401' 10const ensRegistryAddr = '0x00000000000C2E074eC69A0dFb2997BA6C7d2e1e' 11const publicResolverAddr = '0xF29100983E058B709F3D539b0c765937B804AC15' 13const controllerAbi = [ 14 'function available(string memory name) public view returns (bool)', 15 'function rentPrice(string memory name, uint256 duration) public view returns (uint256)', 16 'function makeCommitment(string memory name, address owner, uint256 duration, bytes32 secret, address resolver, bytes[] calldata data, bool reverseRecord, uint16 ownerControlledFuses) public pure returns (bytes32)', 17 'function commit(bytes32 commitment) public', 18 'function register(string calldata name, address owner, uint256 duration, bytes32 secret, address resolver, bytes[] calldata data, bool reverseRecord, uint16 ownerControlledFuses) external payable', 19 'function renew(string calldata name, uint256 duration) external payable', 22const baseRegistrarAbi = [ 23 'function ownerOf(uint256 tokenId) public view returns (address)', 24 'function controllers(address) public view returns (bool)', 27const nameWrapperAbi = [ 28 'function ownerOf(uint256 id) public view returns (address)', 29 'function setResolver(bytes32 node, address resolver) external', 32const ensRegistryAbi = [ 33 'function resolver(bytes32 node) external view returns (address)', 34 'function setResolver(bytes32 node, address resolver) external', 37const publicResolverAbi = [ 38 'function addr(bytes32 node) external view returns (address)', 39 'function setAddr(bytes32 node, address a) external', 42const stripEthSuffix = (ensName: string) => ensName.replace(/\.eth$/, '') 44const labelToTokenId = (label: string) => BigInt(keccak256(toUtf8Bytes(label))) 46const assertControllerApproved = async () => { 48 const baseRegistrar = new Contract(baseRegistrarAddr, baseRegistrarAbi, provider) 49 const isApproved = await baseRegistrar.controllers(ethRegistrarControllerAddr) 53const isNameWrapped = async ({ensName}: {ensName: string}) => { 55 const baseRegistrar = new Contract(baseRegistrarAddr, baseRegistrarAbi, provider) 56 const label = stripEthSuffix(ensName) 57 const tokenId = labelToTokenId(label) 59 const baseOwner = await baseRegistrar.ownerOf(tokenId) 60 return baseOwner.toLowerCase() === nameWrapperAddr.toLowerCase() 66export const ensEnsureResolver = async ({ensName, wallet}: {ensName: string, wallet: any}) => { 68 const connectedWallet = wallet.connect(provider) 69 const node = namehash(ensName) 70 const registry = new Contract(ensRegistryAddr, ensRegistryAbi, provider) 71 const currentResolver = await registry.resolver(node) 73 if (currentResolver.toLowerCase() === publicResolverAddr.toLowerCase()) { 74 return {updated: false, resolver: currentResolver} 77 console.log(`Updating resolver for ${ensName} from ${currentResolver} to ${publicResolverAddr}...`) 78 const isWrapped = await isNameWrapped({ensName}) 81 const nameWrapper = new Contract(nameWrapperAddr, nameWrapperAbi, connectedWallet) 82 const tx = await nameWrapper.setResolver(node, publicResolverAddr) 83 console.log(`setResolver tx: ${tx.hash}`) 86 const registryWritable = new Contract(ensRegistryAddr, ensRegistryAbi, connectedWallet) 87 const tx = await registryWritable.setResolver(node, publicResolverAddr) 88 console.log(`setResolver tx: ${tx.hash}`) 92 console.log(`Resolver updated for ${ensName}`) 93 return {updated: true, oldResolver: currentResolver, newResolver: publicResolverAddr} 96export const ensEnsureAddrRecord = async ({ensName, wallet}: {ensName: string, wallet: any}) => { 98 const connectedWallet = wallet.connect(provider) 99 const node = namehash(ensName) 100 const resolver = new Contract(publicResolverAddr, publicResolverAbi, provider) 102 const currentAddr = await resolver.addr(node) 103 if (currentAddr.toLowerCase() === wallet.address.toLowerCase()) { 104 return {updated: false, addr: currentAddr} 107 console.log(`Setting addr record for ${ensName} from ${currentAddr} to ${wallet.address}...`) 108 const resolverWritable = new Contract(publicResolverAddr, publicResolverAbi, connectedWallet) 109 const tx = await resolverWritable.setAddr(node, wallet.address) 110 console.log(`setAddr tx: ${tx.hash}`) 113 console.log(`Addr record updated for ${ensName}`) 114 return {updated: true, oldAddr: currentAddr, newAddr: wallet.address} 117export const ensGetOwner = async ({ensName}: {ensName: string}) => { 119 const baseRegistrar = new Contract(baseRegistrarAddr, baseRegistrarAbi, provider) 120 const nameWrapper = new Contract(nameWrapperAddr, nameWrapperAbi, provider) 121 const label = stripEthSuffix(ensName) 122 const tokenId = labelToTokenId(label) 124 const baseOwner = await baseRegistrar.ownerOf(tokenId) 125 if (baseOwner.toLowerCase() === nameWrapperAddr.toLowerCase()) { 126 const wrappedOwner = await nameWrapper.ownerOf(namehash(ensName)) 127 return wrappedOwner as string 129 return baseOwner as string 135export const ensCommitAndRegister = async ({ensName, wallet, durationYears = 1}: {ensName: string, wallet: any, durationYears?: number}) => { 136 await assertControllerApproved() 138 const connectedWallet = wallet.connect(provider) 139 const controller = new Contract(ethRegistrarControllerAddr, controllerAbi, connectedWallet) 140 const label = stripEthSuffix(ensName) 141 const durationSec = durationYears * 365 * 24 * 60 * 60 143 const currentOwner = await ensGetOwner({ensName}) 144 if (currentOwner?.toLowerCase() === wallet.address.toLowerCase()) { 145 console.log(`Already own ${ensName}, ensuring resolver and records...`) 146 const resolverResult = await ensEnsureResolver({ensName, wallet}) 147 const addrResult = await ensEnsureAddrRecord({ensName, wallet}) 148 return {skipped: true, owner: currentOwner, resolver: resolverResult, addr: addrResult} 151 const isAvailable = await controller.available(label) 153 const owner = currentOwner || 'unknown' 154 throw new Error(`${ensName} is not available (owned by ${owner})`) 157 const secret = randomBytes(32) 158 const secretHex = '0x' + Buffer.from(secret).toString('hex') 160 betLog({label, durationYears, durationSec, walletAddress: wallet.address}) 162 const commitment = await controller.makeCommitment(label, wallet.address, durationSec, secretHex, publicResolverAddr, [], false, 0) 165 console.log(`Committing ${ensName}...`) 166 const commitTx = await controller.commit(commitment) 167 console.log(`Commit tx: ${commitTx.hash}`) 168 await commitTx.wait() 169 console.log('Commit confirmed, waiting 60s for minCommitmentAge...') 173 const rentPrice = await controller.rentPrice(label, durationSec) 174 const rentPriceWithBuffer = (rentPrice * 110n) / 100n 175 betLog({rentPriceWei: rentPrice.toString(), rentPriceWithBuffer: rentPriceWithBuffer.toString()}) 177 console.log(`Registering ${ensName}...`) 178 const registerTx = await controller.register(label, wallet.address, durationSec, secretHex, publicResolverAddr, [], false, 0, {value: rentPriceWithBuffer}) 179 console.log(`Register tx: ${registerTx.hash}`) 180 const receipt = await registerTx.wait() 181 console.log(`Registered ${ensName} in block ${receipt.blockNumber}`) 183 return {txHash: registerTx.hash, blockNumber: receipt.blockNumber}