Onchain.ENS (onchain v0.7.0)

Copy Markdown View Source

ENS (Ethereum Name Service) resolution and namehash computation.

Does

  • Compute EIP-137 namehash for ENS names (namehash/1)
  • Normalize names via UTS-46 / ENSIP-15 before hashing (case-fold, NFC, strip trailing dot)
  • Validate name structure (reject empty labels, disallowed code points)
  • Forward resolution: ENS name → ETH address (resolve/2)
  • Multi-coin address resolution via addr(bytes32,uint256) (address/3, ENSIP-9/11)
  • ENSIP-10 wildcard resolution + EIP-3668 CCIP-Read off-chain lookups (address/3)
  • Reverse resolution: ETH address → ENS name (reverse/2)
  • Text record queries (text/3), contenthash, ABI, pubkey retrieval
  • Look up resolver contracts (resolver/2)
  • UTS-46 / ENSIP-15 name normalization before namehash (normalize/1)
  • DNS wire-format name encoding (dns_encode/1, ENSIP-10)
  • Configurable registry address via :registry opt

Does Not

  • ENS name registration or management (write operations)
  • Caching — consumers manage their own cache
  • The ENSIP-15 security filters (confusable / script-mixing / NSM checks) — normalize/1 applies the deterministic Unicode steps (NFC, case-fold, ignored/disallowed code points) but not the data-table-driven confusable detection. See the internal Onchain.ENS.Normalize module for the scope boundary.

Functions

FunctionPurpose
namehash/1ENS name -> 32-byte EIP-137 node hash (normalized first)
namehash!/1Same, raises on error
normalize/1Apply UTS-46 / ENSIP-15 normalization to a name
normalize!/1Same, raises on error
dns_encode/1ENS name -> DNS wire format (ENSIP-10)
dns_encode!/1Same, raises on error
evm_coin_type/1EVM chain id -> ENSIP-11 coin type
resolver/2ENS name -> resolver contract address
resolver!/2Same, raises on error
resolve/2ENS name -> ETH address (forward resolution)
resolve!/2Same, raises on error
address/3ENS name + coin type -> raw address bytes (multi-coin, wildcard + CCIP)
address!/3Same, raises on error
reverse/2ETH address -> ENS name (reverse resolution)
reverse!/2Same, raises on error
text/3Retrieve a text record (avatar, url, etc.)
text!/3Same, raises on error
contenthash/2Retrieve the contenthash record
contenthash!/2Same, raises on error
pubkey/2Retrieve the ECDSA public key
pubkey!/2Same, raises on error
abi/3Retrieve ABI data (ENSIP-7)
abi!/3Same, raises on error

API Functions

FunctionArityDescriptionParam Kinds
abi!3Retrieve ABI data. Raises on error.name: value, content_types: value, opts: value
abi3Retrieve ABI data from an ENS name's resolver (ENSIP-7).name: value, content_types: value, opts: value
pubkey!2Retrieve the ECDSA public key. Raises on error.name: value, opts: value
pubkey2Retrieve the ECDSA public key from an ENS name's resolver.name: value, opts: value
contenthash!2Retrieve the contenthash record. Raises on error.name: value, opts: value
contenthash2Retrieve the contenthash record from an ENS name's resolver.name: value, opts: value
text!3Retrieve a text record. Raises on error.name: value, key: value, opts: value
text3Retrieve a text record from an ENS name's resolver.name: value, key: value, opts: value
reverse!2Reverse-resolve an ETH address to an ENS name. Raises on error.address: value, opts: value
reverse2Reverse-resolve an ETH address to an ENS name.address: value, opts: value
address!3Resolve an ENS name to a chain-specific address. Raises on error.name: value, coin_type: value, opts: value
address3Resolve an ENS name to a chain-specific address (ENSIP-9 multi-coin).name: value, coin_type: value, opts: value
resolve!2Resolve an ENS name to an ETH address. Raises on error.name: value, opts: value
resolve2Resolve an ENS name to an ETH address (forward resolution).name: value, opts: value
resolver!2Look up the resolver contract address. Raises on error.name: value, opts: value
resolver2Look up the resolver contract address for an ENS name.name: value, opts: value
evm_coin_type1Derive an ENSIP-11 coin type from an EVM chain id.chain_id: value
dns_encode!1Encode an ENS name to DNS wire format. Raises on error.name: value
dns_encode1Encode an ENS name to DNS wire format (ENSIP-10).name: value
normalize!1Apply UTS-46 / ENSIP-15 normalization. Raises on error.name: value
normalize1Apply UTS-46 / ENSIP-15 normalization to an ENS name.name: value
namehash!1Compute the EIP-137 namehash. Raises on error.name: value
namehash1Compute the EIP-137 namehash for an ENS name.name: value

Summary

Functions

Retrieve ABI data from an ENS name's resolver (ENSIP-7).

Retrieve ABI data. Raises on error.

Resolve an ENS name to a chain-specific address (ENSIP-9 addr(bytes32,uint256)).

Resolve an ENS name to a chain-specific address. Raises on error.

Retrieve the contenthash record from an ENS name's resolver.

Retrieve the contenthash record. Raises on error.

Encode an ENS name to DNS wire format (ENSIP-10).

Encode an ENS name to DNS wire format. Raises on error.

Derive an ENSIP-11 coin type from an EVM chain id.

Compute the EIP-137 namehash for an ENS name.

Compute the EIP-137 namehash. Raises on error.

Apply UTS-46 / ENSIP-15 normalization to an ENS name.

Apply UTS-46 / ENSIP-15 normalization. Raises on error.

Retrieve the ECDSA public key from an ENS name's resolver.

Retrieve the ECDSA public key. Raises on error.

Resolve an ENS name to an ETH address (forward resolution).

Resolve an ENS name to an ETH address. Raises on error.

Look up the resolver contract address for an ENS name.

Look up the resolver contract address. Raises on error.

Reverse-resolve an ETH address to an ENS name.

Reverse-resolve an ETH address to an ENS name. Raises on error.

Retrieve a text record from an ENS name's resolver.

Retrieve a text record. Raises on error.

Functions

abi(name, content_types, opts \\ [])

@spec abi(String.t(), non_neg_integer(), keyword()) ::
  {:ok, {non_neg_integer(), binary()}} | {:error, term()}

Retrieve ABI data from an ENS name's resolver (ENSIP-7).

Parameters

  • name - ENS name (e.g., "vitalik.eth") (value)
  • content_types - Bitmask of content types: 1=JSON, 2=zlib, 4=CBOR, 8=URI (value)
  • opts - Options: :rpc_url, :timeout, :registry (default: [], value)

Returns

Tuple of {content_type, abi_data} ({:ok, {non_neg_integer(), binary()}} | {:error, term()})

# descripex:contract
%{
  params: %{
    name: %{description: "ENS name (e.g., \"vitalik.eth\")", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :registry",
      kind: :value
    },
    content_types: %{
      description: "Bitmask of content types: 1=JSON, 2=zlib, 4=CBOR, 8=URI",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, {non_neg_integer(), binary()}} | {:error, term()}",
    description: "Tuple of {content_type, abi_data}"
  }
}

abi!(name, content_types, opts \\ [])

@spec abi!(String.t(), non_neg_integer(), keyword()) :: {non_neg_integer(), binary()}

Retrieve ABI data. Raises on error.

Parameters

  • name - ENS name (e.g., "vitalik.eth") (value)
  • content_types - Bitmask of content types: 1=JSON, 2=zlib, 4=CBOR, 8=URI (value)
  • opts - Options: :rpc_url, :timeout, :registry (default: [], value)

Returns

Tuple of {content_type, abi_data} ({non_neg_integer(), binary()})

# descripex:contract
%{
  params: %{
    name: %{description: "ENS name (e.g., \"vitalik.eth\")", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :registry",
      kind: :value
    },
    content_types: %{
      description: "Bitmask of content types: 1=JSON, 2=zlib, 4=CBOR, 8=URI",
      kind: :value
    }
  },
  returns: %{
    type: "{non_neg_integer(), binary()}",
    description: "Tuple of {content_type, abi_data}"
  }
}

address(name, coin_type \\ 60, opts \\ [])

@spec address(String.t(), non_neg_integer(), keyword()) ::
  {:ok, binary()} | {:error, term()}

Resolve an ENS name to a chain-specific address (ENSIP-9 addr(bytes32,uint256)).

Unlike resolve/2 (which returns an EIP-55 checksummed string for Ethereum mainnet only), this returns the raw address bytes for any SLIP-44 / ENSIP-11 coin type. Resolution goes through the ENSIP-10 extended-resolver path when the resolver supports it (interface 0x9061b923), which transparently handles wildcard subdomains and EIP-3668 CCIP-Read off-chain lookups.

address!(name, coin_type \\ 60, opts \\ [])

@spec address!(String.t(), non_neg_integer(), keyword()) :: binary()

Resolve an ENS name to a chain-specific address. Raises on error.

Parameters

  • name - ENS name (e.g., "vitalik.eth") (value)
  • coin_type - SLIP-44 / ENSIP-11 coin type (default 60 = ETH) (default: 60, value)
  • opts - Options: :rpc_url, :timeout, :registry (default: [], value)

Returns

Raw address bytes for the coin type (binary())

# descripex:contract
%{
  params: %{
    name: %{description: "ENS name (e.g., \"vitalik.eth\")", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :registry",
      kind: :value
    },
    coin_type: %{
      default: 60,
      description: "SLIP-44 / ENSIP-11 coin type (default 60 = ETH)",
      kind: :value
    }
  },
  returns: %{
    type: "binary()",
    description: "Raw address bytes for the coin type"
  }
}

contenthash(name, opts \\ [])

@spec contenthash(
  String.t(),
  keyword()
) :: {:ok, binary()} | {:error, term()}

Retrieve the contenthash record from an ENS name's resolver.

Parameters

  • name - ENS name (e.g., "vitalik.eth") (value)
  • opts - Options: :rpc_url, :timeout, :registry (default: [], value)

Returns

Raw contenthash bytes (ENSIP-7 encoded) ({:ok, binary()} | {:error, term()})

# descripex:contract
%{
  params: %{
    name: %{description: "ENS name (e.g., \"vitalik.eth\")", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :registry",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, binary()} | {:error, term()}",
    description: "Raw contenthash bytes (ENSIP-7 encoded)"
  }
}

contenthash!(name, opts \\ [])

@spec contenthash!(
  String.t(),
  keyword()
) :: binary()

Retrieve the contenthash record. Raises on error.

Parameters

  • name - ENS name (e.g., "vitalik.eth") (value)
  • opts - Options: :rpc_url, :timeout, :registry (default: [], value)

Returns

Raw contenthash bytes (binary())

# descripex:contract
%{
  params: %{
    name: %{description: "ENS name (e.g., \"vitalik.eth\")", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :registry",
      kind: :value
    }
  },
  returns: %{type: "binary()", description: "Raw contenthash bytes"}
}

dns_encode(name)

@spec dns_encode(String.t()) :: {:ok, binary()} | {:error, term()}

Encode an ENS name to DNS wire format (ENSIP-10).

Parameters

  • name - ENS name, e.g. "foo.eth" (value)

Returns

Length-prefixed labels with a null terminator, e.g. "foo.eth" -> <<3, "foo", 3, "eth", 0>>. The name is normalized first. ({:ok, binary()} | {:error, term()})

# descripex:contract
%{
  params: %{name: %{description: "ENS name, e.g. \"foo.eth\"", kind: :value}},
  returns: %{
    type: "{:ok, binary()} | {:error, term()}",
    description: "Length-prefixed labels with a null terminator, e.g. \"foo.eth\" -> <<3, \"foo\", 3, \"eth\", 0>>. The name is normalized first."
  }
}

dns_encode!(name)

@spec dns_encode!(String.t()) :: binary()

Encode an ENS name to DNS wire format. Raises on error.

Parameters

  • name - ENS name to encode (value)

Returns

DNS wire-format encoded name (binary())

# descripex:contract
%{
  params: %{name: %{description: "ENS name to encode", kind: :value}},
  returns: %{type: "binary()", description: "DNS wire-format encoded name"}
}

evm_coin_type(chain_id)

@spec evm_coin_type(pos_integer()) :: pos_integer()

Derive an ENSIP-11 coin type from an EVM chain id.

Parameters

  • chain_id - EVM chain id, e.g. 1 (mainnet), 10 (Optimism), 8453 (Base) (value)

Returns

ENSIP-11 coin type = 0x80000000 | chain_id. Pass this to address/3 to resolve an L2 address. Note Ethereum mainnet's canonical coin type is the SLIP-44 value 60, not the ENSIP-11 form. (pos_integer())

# descripex:contract
%{
  params: %{
    chain_id: %{
      description: "EVM chain id, e.g. 1 (mainnet), 10 (Optimism), 8453 (Base)",
      kind: :value
    }
  },
  returns: %{
    type: "pos_integer()",
    description: "ENSIP-11 coin type = 0x80000000 | chain_id. Pass this to address/3 to resolve an L2 address. Note Ethereum mainnet's canonical coin type is the SLIP-44 value 60, not the ENSIP-11 form."
  }
}

namehash(name)

@spec namehash(String.t()) :: {:ok, binary()} | {:error, {:invalid_name, term()}}

Compute the EIP-137 namehash for an ENS name.

Parameters

  • name - ENS name, e.g. "vitalik.eth" (value)

Returns

32-byte keccak256 node hash per EIP-137 ({:ok, <<_::256>>} | {:error, term})

# descripex:contract
%{
  params: %{
    name: %{description: "ENS name, e.g. \"vitalik.eth\"", kind: :value}
  },
  returns: %{
    type: "{:ok, <<_::256>>} | {:error, term}",
    description: "32-byte keccak256 node hash per EIP-137"
  }
}

namehash!(name)

@spec namehash!(String.t()) :: binary()

Compute the EIP-137 namehash. Raises on error.

Parameters

  • name - ENS name, e.g. "vitalik.eth" (value)

Returns

32-byte keccak256 node hash (<<_::256>>)

# descripex:contract
%{
  params: %{
    name: %{description: "ENS name, e.g. \"vitalik.eth\"", kind: :value}
  },
  returns: %{type: "<<_::256>>", description: "32-byte keccak256 node hash"}
}

normalize(name)

@spec normalize(String.t()) :: {:ok, String.t()} | {:error, {:invalid_name, term()}}

Apply UTS-46 / ENSIP-15 normalization to an ENS name.

Parameters

  • name - ENS name, e.g. "VITALIK.eth" or "café.eth" (value)

Returns

Normalized name (case-folded, NFC, ignored code points stripped). See Onchain.ENS.Normalize for the scope boundary — the ENSIP-15 confusable/script-mixing filters are NOT applied. ({:ok, String.t()} | {:error, {:invalid_name, term()}})

# descripex:contract
%{
  params: %{
    name: %{
      description: "ENS name, e.g. \"VITALIK.eth\" or \"café.eth\"",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, String.t()} | {:error, {:invalid_name, term()}}",
    description: "Normalized name (case-folded, NFC, ignored code points stripped). See Onchain.ENS.Normalize for the scope boundary — the ENSIP-15 confusable/script-mixing filters are NOT applied."
  }
}

normalize!(name)

@spec normalize!(String.t()) :: String.t()

Apply UTS-46 / ENSIP-15 normalization. Raises on error.

Parameters

  • name - ENS name to normalize (value)

Returns

Normalized name (String.t())

# descripex:contract
%{
  params: %{name: %{description: "ENS name to normalize", kind: :value}},
  returns: %{type: "String.t()", description: "Normalized name"}
}

pubkey(name, opts \\ [])

@spec pubkey(
  String.t(),
  keyword()
) :: {:ok, {binary(), binary()}} | {:error, term()}

Retrieve the ECDSA public key from an ENS name's resolver.

Parameters

  • name - ENS name (e.g., "vitalik.eth") (value)
  • opts - Options: :rpc_url, :timeout, :registry (default: [], value)

Returns

Tuple of {x, y} 32-byte coordinates ({:ok, {binary(), binary()}} | {:error, term()})

# descripex:contract
%{
  params: %{
    name: %{description: "ENS name (e.g., \"vitalik.eth\")", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :registry",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, {binary(), binary()}} | {:error, term()}",
    description: "Tuple of {x, y} 32-byte coordinates"
  }
}

pubkey!(name, opts \\ [])

@spec pubkey!(
  String.t(),
  keyword()
) :: {binary(), binary()}

Retrieve the ECDSA public key. Raises on error.

Parameters

  • name - ENS name (e.g., "vitalik.eth") (value)
  • opts - Options: :rpc_url, :timeout, :registry (default: [], value)

Returns

Tuple of {x, y} 32-byte coordinates ({binary(), binary()})

# descripex:contract
%{
  params: %{
    name: %{description: "ENS name (e.g., \"vitalik.eth\")", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :registry",
      kind: :value
    }
  },
  returns: %{
    type: "{binary(), binary()}",
    description: "Tuple of {x, y} 32-byte coordinates"
  }
}

resolve(name, opts \\ [])

@spec resolve(
  String.t(),
  keyword()
) :: {:ok, String.t()} | {:error, term()}

Resolve an ENS name to an ETH address (forward resolution).

Parameters

  • name - ENS name (e.g., "vitalik.eth") (value)
  • opts - Options: :rpc_url, :timeout, :registry (default: [], value)

Returns

EIP-55 checksummed ETH address ({:ok, String.t()} | {:error, term()})

# descripex:contract
%{
  params: %{
    name: %{description: "ENS name (e.g., \"vitalik.eth\")", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :registry",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, String.t()} | {:error, term()}",
    description: "EIP-55 checksummed ETH address"
  }
}

resolve!(name, opts \\ [])

@spec resolve!(
  String.t(),
  keyword()
) :: String.t()

Resolve an ENS name to an ETH address. Raises on error.

Parameters

  • name - ENS name (e.g., "vitalik.eth") (value)
  • opts - Options: :rpc_url, :timeout, :registry (default: [], value)

Returns

EIP-55 checksummed ETH address (String.t())

# descripex:contract
%{
  params: %{
    name: %{description: "ENS name (e.g., \"vitalik.eth\")", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :registry",
      kind: :value
    }
  },
  returns: %{type: "String.t()", description: "EIP-55 checksummed ETH address"}
}

resolver(name, opts \\ [])

@spec resolver(
  String.t(),
  keyword()
) :: {:ok, String.t()} | {:error, term()}

Look up the resolver contract address for an ENS name.

Parameters

  • name - ENS name (e.g., "vitalik.eth") (value)
  • opts - Options: :rpc_url, :timeout, :registry (default: [], value)

Returns

EIP-55 checksummed resolver address ({:ok, String.t()} | {:error, term()})

# descripex:contract
%{
  params: %{
    name: %{description: "ENS name (e.g., \"vitalik.eth\")", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :registry",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, String.t()} | {:error, term()}",
    description: "EIP-55 checksummed resolver address"
  }
}

resolver!(name, opts \\ [])

@spec resolver!(
  String.t(),
  keyword()
) :: String.t()

Look up the resolver contract address. Raises on error.

Parameters

  • name - ENS name (e.g., "vitalik.eth") (value)
  • opts - Options: :rpc_url, :timeout, :registry (default: [], value)

Returns

EIP-55 checksummed resolver address (String.t())

# descripex:contract
%{
  params: %{
    name: %{description: "ENS name (e.g., \"vitalik.eth\")", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :registry",
      kind: :value
    }
  },
  returns: %{
    type: "String.t()",
    description: "EIP-55 checksummed resolver address"
  }
}

reverse(address, opts \\ [])

@spec reverse(
  String.t() | binary(),
  keyword()
) :: {:ok, String.t()} | {:error, term()}

Reverse-resolve an ETH address to an ENS name.

Parameters

  • address - ETH address as 0x hex string or 20-byte binary (value)
  • opts - Options: :rpc_url, :timeout, :registry (default: [], value)

Returns

ENS name (e.g., "vitalik.eth") ({:ok, String.t()} | {:error, term()})

# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :registry",
      kind: :value
    },
    address: %{
      description: "ETH address as 0x hex string or 20-byte binary",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, String.t()} | {:error, term()}",
    description: "ENS name (e.g., \"vitalik.eth\")"
  }
}

reverse!(address, opts \\ [])

@spec reverse!(
  String.t() | binary(),
  keyword()
) :: String.t()

Reverse-resolve an ETH address to an ENS name. Raises on error.

Parameters

  • address - ETH address as 0x hex string or 20-byte binary (value)
  • opts - Options: :rpc_url, :timeout, :registry (default: [], value)

Returns

ENS name (e.g., "vitalik.eth") (String.t())

# descripex:contract
%{
  params: %{
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :registry",
      kind: :value
    },
    address: %{
      description: "ETH address as 0x hex string or 20-byte binary",
      kind: :value
    }
  },
  returns: %{
    type: "String.t()",
    description: "ENS name (e.g., \"vitalik.eth\")"
  }
}

text(name, key, opts \\ [])

@spec text(String.t(), String.t(), keyword()) :: {:ok, String.t()} | {:error, term()}

Retrieve a text record from an ENS name's resolver.

Parameters

  • name - ENS name (e.g., "vitalik.eth") (value)
  • key - Text record key (e.g., "avatar", "url", "com.twitter") (value)
  • opts - Options: :rpc_url, :timeout, :registry (default: [], value)

Returns

Text record value ({:ok, String.t()} | {:error, term()})

# descripex:contract
%{
  params: %{
    name: %{description: "ENS name (e.g., \"vitalik.eth\")", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :registry",
      kind: :value
    },
    key: %{
      description: "Text record key (e.g., \"avatar\", \"url\", \"com.twitter\")",
      kind: :value
    }
  },
  returns: %{
    type: "{:ok, String.t()} | {:error, term()}",
    description: "Text record value"
  }
}

text!(name, key, opts \\ [])

@spec text!(String.t(), String.t(), keyword()) :: String.t()

Retrieve a text record. Raises on error.

Parameters

  • name - ENS name (e.g., "vitalik.eth") (value)
  • key - Text record key (e.g., "avatar", "url", "com.twitter") (value)
  • opts - Options: :rpc_url, :timeout, :registry (default: [], value)

Returns

Text record value (String.t())

# descripex:contract
%{
  params: %{
    name: %{description: "ENS name (e.g., \"vitalik.eth\")", kind: :value},
    opts: %{
      default: [],
      description: "Options: :rpc_url, :timeout, :registry",
      kind: :value
    },
    key: %{
      description: "Text record key (e.g., \"avatar\", \"url\", \"com.twitter\")",
      kind: :value
    }
  },
  returns: %{type: "String.t()", description: "Text record value"}
}