DNS Ownership Oracle

This oracle checks Google’s DNS service to determine if a given domain is owned by a given blockchain address. Each address is stored in a TXT record. This guide explains how to call the DNS ownership oracle and verify that a given address owns a specific domain. For instance, we will confirm that the address 0xf75519f611776c22275474151a04183665b7feDe owns www5.infernos.io. Note that the source of data is Google DNS.

Requirements

This guide assumes that you know how to create and deploy smart contracts on the Goerli Testnet using the following tools:

You should be familiar with the Chainlink Basic Request Model. If you are new to developing smart contracts on Ethereum, see the Getting Started guide to learn the basics.

DNS ownership contract

This example operates using the following steps:

  1. When you deploy the contract, the constructor() initializes the address of oracle, the jobId, and the fees oraclePayment. The code example is configured for the Goerli testnet. Check the Network Details section for other networks.
  2. Fund the contract with LINK tokens. Each request requires 0.1 LINK.
  3. Run the requestProof() function to check that an address owns a domain name. For this example, you can use www5.infernos.io for the _name and 0xf75519f611776c22275474151a04183665b7feDe for the _record. Notice how these parameters are used to build the Chainlink request. The selector of the fulfill() function is also passed so that the oracle knows which function to call back with the proof.
  4. After few seconds, check the value of proof. It should return true.
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "@chainlink/contracts/src/v0.8/ChainlinkClient.sol";
import "@chainlink/contracts/src/v0.8/ConfirmedOwner.sol";

/**
 * Request testnet LINK and ETH here: https://faucets.chain.link/
 * Find information on LINK Token Contracts and get the latest ETH and LINK faucets here: https://docs.chain.link/docs/link-token-contracts/
 */

/**
 * THIS IS AN EXAMPLE CONTRACT THAT USES HARDCODED VALUES FOR CLARITY.
 * THIS IS AN EXAMPLE CONTRACT THAT USES UN-AUDITED CODE.
 * DO NOT USE THIS CODE IN PRODUCTION.
 */

contract DnsOwnershipChainlink is ChainlinkClient, ConfirmedOwner {
    using Chainlink for Chainlink.Request;

    bytes32 private jobId;
    bool public proof;
    uint256 oraclePayment;

    /**
     * Network: Goerli
     * Oracle:
     *      Name:           LinkPool
     *      Listing URL:    https://market.link/nodes/323602b9-3831-4f8d-a66b-3fb7531649eb?network=42
     *      Address:        0xB9756312523826A566e222a34793E414A81c88E1
     * Job:
     *      Name:           DNS Record Check
     *      ID:             791bd73c8a1349859f09b1cb87304f71
     *      Fee:            0.1 LINK
     */
    constructor() ConfirmedOwner(msg.sender) {
        setChainlinkToken(0x326C977E6efc84E512bB9C30f76E30c160eD06FB);
        setChainlinkOracle(0xB9756312523826A566e222a34793E414A81c88E1);
        jobId = "791bd73c8a1349859f09b1cb87304f71";
        oraclePayment = (1 * LINK_DIVISIBILITY) / 10; // 0,1 * 10**18 (Varies by network and job)
    }

    /**
     *  Request proof to check if the address `_record` owns the DNS name `_name`
     *
     */
    function requestProof(
        string memory _name,
        string memory _record
    ) public onlyOwner {
        Chainlink.Request memory req = buildChainlinkRequest(
            jobId,
            address(this),
            this.fulfill.selector
        );
        req.add("name", _name);
        req.add("record", _record);
        sendChainlinkRequest(req, oraclePayment);
    }

    /**
     * Callback function called by the oracle to submit the `_proof` . If true, this means that the address owns the dns name
     *
     */
    function fulfill(
        bytes32 _requestId,
        bool _proof
    ) public recordChainlinkFulfillment(_requestId) {
        proof = _proof;
    }

    /**
     * Allow withdraw of Link tokens from the contract
     */
    function withdrawLink() public onlyOwner {
        LinkTokenInterface link = LinkTokenInterface(chainlinkTokenAddress());
        require(
            link.transfer(msg.sender, link.balanceOf(address(this))),
            "Unable to transfer"
        );
    }
}

Network details

The DNS Ownership Contract example works on the Goerli Testnet. Below are the configuration for other chains.

Ethereum Mainnet

  • Payment Amount: 2 LINK
  • LINK Token Address: 0x514910771AF9Ca656af840dff83E8264EcF986CA
  • Oracle Address: 0x1152c76A0B3acC9856B1d8ee9EbDf2A2d0a01cC3
  • JobID: 6ca2e68622bd421d98c648f056ee7c76

Ethereum Goerli Testnet

  • Payment Amount: 0.1 LINK
  • LINK Token Address: 0x326C977E6efc84E512bB9C30f76E30c160eD06FB
  • Oracle Address: 0xB9756312523826A566e222a34793E414A81c88E1
  • JobID: 791bd73c8a1349859f09b1cb87304f71

BNB Chain mainnet

  • Payment Amount: 0.1 LINK
  • LINK Token address: 0x404460C6A5EdE2D891e8297795264fDe62ADBB75
  • Oracle Address: 0x63f9459471804835E35EFeB296314153063c25E3
  • JobID: fb06afd5a9df4e6cb156f6b797b63a24

Polygon (Matic) mainnet

  • Payment Amount: 0.1 LINK
  • LINK Token Address: 0xb0897686c545045aFc77CF20eC7A532E3120E0F1
  • Oracle Address: 0x2984beb1d35d11B56973148A9022210Aecc26CE5
  • JobID: f3daed2990114e98906aaf21c4172da3

Job

The DNS Ownership node uses a Chainlink v2 direct-request job. It is composed by the following taks:

type = "directrequest"
schemaVersion = 1
contractAddress = "0x0000000000000000000000000000000000000000"
maxTaskDuration = "0s"
observationSource = """
    decode_log          [type=ethabidecodelog
                         abi="OracleRequest(bytes32 indexed specId, address requester, bytes32 requestId, uint256 payment, address callbackAddr, bytes4 callbackFunctionId, uint256 cancelExpiration, uint256 dataVersion, bytes data)"
                         data="$(jobRun.logData)"
                         topics="$(jobRun.logTopics)"]

    decode_cbor         [type=cborparse data="$(decode_log.data)"]

    dnsproof            [type=bridge
                         name="dnsproof"
                         requestData="{\\"data\\": {\\"endpoint\\": \\"dnsProof\\", \\"name\\": $(decode_cbor.name), \\"record\\": $(decode_cbor.record)}}"]


    result_parse        [type=jsonparse data="$(dnsproof)" path="result"]

    encode_data         [type=ethabiencode
                         abi="(bool _result)"
                         data="{\\"_requestId\\": $(decode_log.requestId),\\"_result\\": $(result_parse)}"]

    encode_tx           [type=ethabiencode
                         abi="fulfillOracleRequest(bytes32 requestId, uint256 payment, address callbackAddress, bytes4 callbackFunctionId, uint256 expiration, bytes32 data)"
                         data="{\\"requestId\\": $(decode_log.requestId),\\"payment\\": $(decode_log.payment),\\"callbackAddress\\": $(decode_log.callbackAddr),\\"callbackFunctionId\\": $(decode_log.callbackFunctionId),\\"expiration\\": $(decode_log.cancelExpiration),\\"data\\": $(encode_data)}"]

    submit_tx           [type=ethtx to="0x0000000000000000000000000000000000000000" data="$(encode_tx)" minConfirmations="2"]

    decode_log -> decode_cbor -> dnsproof -> result_parse -> encode_data -> encode_tx -> submit_tx
"""

Stay updated on the latest Chainlink news