Skip to main content

Overview

The PublicResolver is a general-purpose ENS resolver that implements all standard resolver profiles. It’s the most commonly deployed resolver in the ENS ecosystem and is suitable for most use cases.

Contract Location

import '@ensdomains/ens-contracts/contracts/resolvers/PublicResolver.sol';

Inheritance

PublicResolver inherits from multiple resolver profiles:
contract PublicResolver is
    Multicallable,
    ABIResolver,
    AddrResolver,
    ContentHashResolver,
    DNSResolver,
    InterfaceResolver,
    NameResolver,
    PubkeyResolver,
    TextResolver,
    ReverseClaimer
{
    // Implementation
}

Constructor

constructor(
    ENS _ens,
    INameWrapper wrapperAddress,
    address _trustedETHController,
    address _trustedReverseRegistrar
)

Parameters

  • _ens: The ENS registry contract address
  • wrapperAddress: The NameWrapper contract address for wrapped names
  • _trustedETHController: Address of the ETH Registrar Controller (can bypass authorization)
  • _trustedReverseRegistrar: Address of the Reverse Registrar (can bypass authorization)

Authorization System

The PublicResolver implements a three-tier authorization system to control who can update records.

Operators

Operators can manage all names owned by a specific address.
// Set operator approval
function setApprovalForAll(address operator, bool approved) external

// Check operator approval
function isApprovedForAll(address account, address operator) public view returns (bool)

Events

event ApprovalForAll(
    address indexed owner,
    address indexed operator,
    bool approved
);

Usage Example

// Allow an operator to manage all your names
resolver.setApprovalForAll(operatorAddress, true);

// Revoke operator access
resolver.setApprovalForAll(operatorAddress, false);

Delegates

Delegates can manage a specific name on behalf of the owner.
// Approve a delegate for a specific node
function approve(bytes32 node, address delegate, bool approved) external

// Check delegate approval
function isApprovedFor(
    address owner,
    bytes32 node,
    address delegate
) public view returns (bool)

Events

event Approved(
    address owner,
    bytes32 indexed node,
    address indexed delegate,
    bool indexed approved
);

Usage Example

// Allow a delegate to manage a specific name
bytes32 node = namehash("example.eth");
resolver.approve(node, delegateAddress, true);

// Revoke delegate access
resolver.approve(node, delegateAddress, false);

Authorization Logic

The internal isAuthorised() function determines if an address can modify a node:
function isAuthorised(bytes32 node) internal view override returns (bool) {
    // Trusted controllers always authorized
    if (
        msg.sender == trustedETHController ||
        msg.sender == trustedReverseRegistrar
    ) {
        return true;
    }
    
    // Get the owner (check NameWrapper if needed)
    address owner = ens.owner(node);
    if (owner == address(nameWrapper)) {
        owner = nameWrapper.ownerOf(uint256(node));
    }
    
    // Check if sender is owner, operator, or delegate
    return
        owner == msg.sender ||
        isApprovedForAll(owner, msg.sender) ||
        isApprovedFor(owner, node, msg.sender);
}

Multicall Support

The PublicResolver inherits from Multicallable, allowing multiple operations in a single transaction.
function multicall(bytes[] calldata data) external returns (bytes[] memory results)

function multicallWithNodeCheck(
    bytes32 nodehash,
    bytes[] calldata data
) external returns (bytes[] memory results)

Usage Example

// Prepare multiple function calls
bytes[] memory calls = new bytes[](3);
calls[0] = abi.encodeWithSelector(
    resolver.setAddr.selector,
    node,
    myAddress
);
calls[1] = abi.encodeWithSelector(
    resolver.setText.selector,
    node,
    "email",
    "[email protected]"
);
calls[2] = abi.encodeWithSelector(
    resolver.setText.selector,
    node,
    "url",
    "https://example.com"
);

// Execute all in one transaction
resolver.multicall(calls);

Interface Support

The PublicResolver supports interface detection via EIP-165:
function supportsInterface(bytes4 interfaceID) public view override returns (bool)
This function returns true for all supported resolver profile interfaces.

Supported Interfaces

  • IABIResolver
  • IAddrResolver
  • IAddressResolver
  • IContentHashResolver
  • IDNSRecordResolver
  • IDNSZoneResolver
  • IInterfaceResolver
  • INameResolver
  • IPubkeyResolver
  • ITextResolver

Resolver Profiles

The PublicResolver implements all standard resolver profiles. Each profile provides specific functionality:
Set and retrieve Ethereum and multicoin addresses.See AddrResolver profile for details.
Store arbitrary key-value text data.See TextResolver profile for details.
Store IPFS, Swarm, and other content hashes.See ContentHashResolver profile for details.
Store SECP256k1 public keys for encryption.See PubkeyResolver profile for details.
Store contract ABI definitions.See ABIResolver profile for details.
Store DNS records in ENS.See DNSResolver profile for details.
Associate a name with an address for reverse lookups.See NameResolver profile for details.
Detect which interfaces a contract implements.See InterfaceResolver profile for details.

Complete Usage Example

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

import "@ensdomains/ens-contracts/contracts/registry/ENS.sol";
import "@ensdomains/ens-contracts/contracts/resolvers/PublicResolver.sol";

contract ENSExample {
    ENS public ens;
    PublicResolver public resolver;
    
    constructor(address ensAddress, address resolverAddress) {
        ens = ENS(ensAddress);
        resolver = PublicResolver(resolverAddress);
    }
    
    // Set up a complete ENS name
    function setupName(
        bytes32 node,
        address ethAddress,
        string memory email,
        string memory url,
        bytes memory contentHash
    ) external {
        // Ensure this contract is authorized
        require(resolver.isAuthorised(node), "Not authorized");
        
        // Batch all updates in one transaction
        bytes[] memory calls = new bytes[](4);
        
        calls[0] = abi.encodeWithSelector(
            resolver.setAddr.selector,
            node,
            ethAddress
        );
        
        calls[1] = abi.encodeWithSelector(
            resolver.setText.selector,
            node,
            "email",
            email
        );
        
        calls[2] = abi.encodeWithSelector(
            resolver.setText.selector,
            node,
            "url",
            url
        );
        
        calls[3] = abi.encodeWithSelector(
            resolver.setContenthash.selector,
            node,
            contentHash
        );
        
        resolver.multicall(calls);
    }
    
    // Read all data for a name
    function getName(bytes32 node) external view returns (
        address ethAddress,
        string memory email,
        string memory url,
        bytes memory contentHash
    ) {
        ethAddress = resolver.addr(node);
        email = resolver.text(node, "email");
        url = resolver.text(node, "url");
        contentHash = resolver.contenthash(node);
    }
}

Security Considerations

The PublicResolver trusts the trustedETHController and trustedReverseRegistrar addresses. These addresses can modify any records without ownership checks. Ensure these are set to the correct, trusted contracts.
When using operators or delegates, remember that:
  • Operators can modify ALL names owned by an address
  • Delegates can only modify the specific name they’re approved for
  • The owner can always revoke these permissions

Next Steps

Resolver Profiles

Explore individual resolver profiles and their functions

Resolvers Overview

Back to resolvers overview

Build docs developers (and LLMs) love