DNS Registrar

At PNS, we have two smart contracts, DNSSECOracle and DNSRegistrar.

DNSSEC (The Domain Name System Security Extensions) establishes a chain of trust from the root key which is signed by ICANN (.) and down through each key. We start off knowing the hash of the root key of DNS (this is hard coded in the smart contract oracle). Given the hashes of that key, we can pass in the actual key, we can verify that it matches the hash and we can add it to the set of the trusted records.

Given that key, we can now verify any record that is signed with that key, so in this case, it’s the hash of the root of the xyz top-level domain. Given that, we can recognize the key, and so on and so forth.

DNSSEC oracle allows anyone to submit proof of any DNSSEC-signed DNS record on the Ethereum blockchain, as long as it was signed using supported public key schemes and digests. DNSRegistrar grants PNS domains to anyone who can prove ownership of the corresponding domain in DNS through DNSSEC Oracle to prove this.

Deployed DNSRegistrar addresses

  • Mainnet: 0xdD91caB95681CD5494afC41FeF5C3760c0932F1E.

  • Testnet (v4):0xa06BF6b95d664DC3c1592b6a3276F30E566F38Ea.

When you register PNS names, you can look up the registrar contract address by looking up its parent domain owner (eg: .pls, for .matoken.pls). However, when you register via DNSSEC Registrars, the parent domain owner may not exist if you are the first person to register under the TLD.

Gas cost

Submitting proof to DNSSEC Oracle takes up a lot of gas because it is heavy computation work. It will take up even more gas if you submit the first domain under the specific TLD. This is because it submits proof of both your domain and its parent domain(eg: matoken.live, as well as .live). When tested on testnet (v3) network, it cost ~1,663,953 gas

Typescript/Javascript Libraries

To help you interact with DNSSEC data and the DNSRegistrar, we provide two libraries.

  • DNSProvejs = A library for querying and validating DNSSEC data from DNS

  • dnssecoraclejs = A library for generating proof data for the PNS DNSSEC Oracle.


Retrieving proof from DNS

import { Oracle } from '@pnsdomains/dnssecoraclejs'
import { DNSProver } from '@pnsdomains/dnsprovejs'

const textDomain = '_pns.matoken.xyz'
const prover = DNSProver.create("https://cloudflare-dns.com/dns-query")
const result = await prover.queryWithProof('TXT', textDomain)

Retrieving the DNS text record

const result = {
  answer: SignedSet {
    records: [{
      name: '_pns.matoken.xyz',
      type: 'TXT',
      ttl: 300,
      class: 'IN',
      flush: false,
      data: [Array]
    signature: {
      name: '_pns.matoken.xyz',
      type: 'RRSIG',
      ttl: 300,
      class: 'IN',
      flush: false,
      data: [Object]
  proofs: [
    SignedSet { records: [Array], signature: [Object] },
    SignedSet { records: [Array], signature: [Object] },
    SignedSet { records: [Array], signature: [Object] },
    SignedSet { records: [Array], signature: [Object] },
    SignedSet { records: [Array], signature: [Object] }

// Retrieving the text record
// 'a=0xa5313060f9fa6b607ac8ca8728a851166c9f612'

queryWithProof returns answer and proofs. answer contains the human-readable record of the DNS record and its signing signature (RRSIG). The example above shows that the leaf of the chain (the first returned record) contains the TXT record type in a=$PULSECHAIN_ADDRESS format.

Submitting the proof to the DNSRegistrar

import { Oracle } from '@pnsdomains/dnssecoraclejs'
import { abi } from '@pnsdomains/contracts/abis/dnsregistrar/DNSRegistrar.json'
import { Contract } from 'ethers'

// The registrar address nees to be hard-coded
const registrarAddress = 'TBD'
const registrar new Contract(registrarAddress, abi, provider)
const oracleAddress = await registrar.oracle()
const oracle = new Oracle(oracleAddress, provider)
const { data, proof } = oracle.getProofData(result)

if(data.length === 0){
    // This happens if someone has submitted the proof directly to DNSSECOracle, hence only claim a name on the registrar.
    return registrar.claim(claim.encodedName, proof)
    // This submits proof to DNSSECOracle, then claim a name.
    return registrar.proveAndClaim(claim.encodedName, data, proof)


It is currently missing the Typescript/JS library support to delete the record from DNSSECOracle by providing NSEC/NSEC3(Next Secure Record) proof.

Last updated