How to Assign a .vet Name to Your Contract
The following steps outline the process for automatically managing name assignments for a contract project.
You will:
- Assign management permissions to a private key within your deployment project.
- Dynamically create subdomains.
- Assign forward-pointing records for subdomains.
- Assign reverse records for the contract, pointing to a subdomain.
The addresses for the contracts on TestNet and MainNet are available in the Contracts section.
To test the resolution of a name or address, visit vet.domains/lookup (opens in a new tab) or, for TestNet, testnet.vet.domains/lookup (opens in a new tab).
Creating Management Permissions
By assigning management permissions to the address associated with your deployment project, you enable the ability to programmatically manage naming records.
Option 1: Creating a Subdomain
Create a subdomain to isolate all contracts, which can then be managed using your deployment keys.
Option 2: Assigning Name Manager
Alternatively, assign the name manager to your deployment keys.
Name managers can be configured in the Ownership
section. The advantage of appointing a name manager without transferring ownership is the capability to reclaim full ownership if necessary.
Creating a Subdomain for Your Contract
The initial step involves generating a new subdomain specifically for your newly deployed contract. This action is performed on the Registry Contract.
Here is the function definition:
function setSubnodeRecord(
bytes32 node,
bytes32 label,
address owner,
address resolver,
uint64 ttl
) external;
With setSubnodeRecord
you will pass:
- node: the namehash of the parent domain
- label: the hash of your the new subdomain
- owner: the new owner of the subdomain
- resolver: the responsible resolver, should be the PublicResolver
- ttl: time to live setting, indicating clients on how long it is suggested to cache results
Below is an example from a Hardhat script:
import { namehash, id } from "ethers";
// ..
const registry = await hre.ethers.getContractAt(Registry.abi, REGISTRY_ADDRESS)
await registry.setSubnodeRecord(namehash('contracts.demo.vet'), id('mytoken'), owner, PUBLIC_RESOLVER_ADDRESS, 0)
Assigning a Forward Record
You can configure the newly created subdomain with records by utilizing the setAddr()
function at the specified resolver.
Here is the function definition:
function setAddr(
bytes32 node,
address a
) external virtual authorised(node) {
setAddr(node, COIN_TYPE_ETH, addressToBytes(a));
}
When utilizing setAddr
, the following parameters must be supplied:
- node: the namehash of the new subdomain including its parent domain
- a: the address record to which it will point
Below is an example from a Hardhat script:
import { namehash, id } from "ethers";
// ..
const publicResolver = await hre.ethers.getContractAt(PublicResolver.abi, PUBLIC_RESOLVER_ADDRESS)
await publicResolver.setAddr(namehash('token.contracts.demo.vet'), contractAddress);
Assign a Reverse Record
To close the loop, the contract's address must be linked to the newly created subdomain. This is achieved through the Reverse Registrar contract and its setNameForAddr()
function.
Here is the function definition:
function setNameForAddr(
address addr,
address owner,
address resolver,
string memory name
) external returns (bytes32);
When using setNameForAddr
, you need to provide:
- addr: The contract address for which you are setting the primary name.
- owner: The owner of the reverse node in the Registry.
- resolver: The resolver to be used for the reverse node, typically the Public Resolver.
- name: The primary name to be assigned to the contract. This name must already forward-resolve to the contract address for the Primary Name resolution to work correctly.
Below is an example from a Hardhat script:
import { namehash, id } from "ethers";
// ..
const reverseRegistrar = await hre.ethers.getContractAt(ReverseRegistrar.abi, REVERSE_REGISTRAR_ADDRESS)
await reverseRegistrar.setNameForAddr(contractAddress, owner, PUBLIC_RESOLVER_ADDRESS, FULL_CONTRACT_NAME);
Example Hardhat Script
Below is a single script that combines the example snippets for a test deployment.
For simplified interaction, the script utilizes the official ENS module @ensdomains/ens-contracts
, which has been installed (yarn add @ensdomains/ens-contracts
) into the project for their interfaces.
import hre from "hardhat";
// import interfaces for contract interactions
import ReverseRegistrar from "@ensdomains/ens-contracts/artifacts/contracts/reverseRegistrar/ReverseRegistrar.sol/ReverseRegistrar.json"
import PublicResolver from "@ensdomains/ens-contracts/artifacts/contracts/resolvers/PublicResolver.sol/PublicResolver.json"
import Registry from "@ensdomains/ens-contracts/artifacts/contracts/registry/ENSRegistry.sol/ENSRegistry.json"
// import ethers hashing helpers
import { namehash, id } from "ethers";
// contract addresses are available on: https://docs.vet.domains/Developers/Contracts/
const REVERSE_REGISTRAR_ADDRESS = "0x6878f1aD5e3015310CfE5B38d7B7071C5D8818Ca" // TestNet address
const PUBLIC_RESOLVER_ADDRESS = "0xA6eFd130085a127D090ACb0b100294aD1079EA6f" // TestNet address
const REGISTRY_ADDRESS = "0xcBFB30c1F267914816668d53AcBA7bA7c9806D13" // TestNet address
async function main() {
// deploy a sample contract "MyToken"
const nft = await hre.ethers.deployContract("MyToken", [], {});
const tx = await nft.deploymentTransaction();
const receipt = await hre.ethers.provider.getTransactionReceipt(tx!.hash);
const contractAddress = receipt!.contractAddress
console.log(`NFT deployed to ${contractAddress}`);
// define the contracts subdomain name
const CONTRACT_NAME = 'test-deployment'
// and the parent name
const BASE_NAME = 'contracts.demo.vet'
// both joined result in the full domain name
const CONTRACT_SUBDOMAIN = [CONTRACT_NAME, BASE_NAME].join('.')
console.log(`Setting name for ${contractAddress} to ${CONTRACT_SUBDOMAIN}`);
// get the address of the current signer been used
const owner = (await hre.ethers.provider.getSigner()).address
// create subnode
const registry = await hre.ethers.getContractAt(Registry.abi, REGISTRY_ADDRESS)
await registry.setSubnodeRecord(namehash(BASE_NAME), id(CONTRACT_NAME), owner, PUBLIC_RESOLVER_ADDRESS, 0)
// set forward record
const publicResolver = await hre.ethers.getContractAt(PublicResolver.abi, PUBLIC_RESOLVER_ADDRESS)
await publicResolver.setAddr(namehash(CONTRACT_SUBDOMAIN), contractAddress);
// set reverse entry
const reverseRegistrar = await hre.ethers.getContractAt(ReverseRegistrar.abi, REVERSE_REGISTRAR_ADDRESS)
await reverseRegistrar.setNameForAddr(contractAddress, owner, PUBLIC_RESOLVER_ADDRESS, CONTRACT_SUBDOMAIN);
}
main()
.then(() => process.exit(0))
.catch((error) => {
console.error(error);
process.exitCode = 1;
})