Resolve ENS Profiles

Here we learn how to resolve a user's ENS profile data.

A completed version of the updated frontend can be found here: (04_ens_resolution/frontend).

Now that the application knows the user's connected account, a basic profile can be built using additional information from ENS if available. Because the frontend is already is using ethers, it is simple to add this functionality and retrieve this data.

Update the frontend/src/index.html file to the following:

src/index.html
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title>SIWE Quickstart</title>
  <style>
    .hidden {
      display: none
    }

    table, th, td {
      border: 1px black solid;
    }

    .avatar {
      border-radius: 8px;
      border: 1px solid #ccc;
      width: 20px;
      height: 20px;
    }
  </style>
</head>

<body>
  <div><button id="connectWalletBtn">Connect wallet</button></div>
  <div><button id="siweBtn">Sign-in with Ethereum</button></div>
  <div><button id="infoBtn">Get session information</button></div>
  <div class="hidden" id="welcome">
  </div>
  <div class="hidden" id="profile">
    <h3>ENS Metadata:</h3>
    <div id="ensLoader"></div>
    <div id="ensContainer" class="hidden">
      <table id="ensTable">
      </table>
    </div> 
  </div>
  <div class="hidden" id="noProfile">
    No ENS Profile detected.
  </div>
</body>

</html>

This will create a table with data based on the user's ENS information if it exists. Otherwise, if there isn't any data, it will remain hidden and a "No ENS Profile detected." message will be displayed.

Finally, we can finish by updating the index.js file to include what's needed.

Update the frontend/src/index.js file to the following:

src/index.js
import { BrowserProvider } from 'ethers';
import { SiweMessage } from 'siwe';

const domain = window.location.host;
const origin = window.location.origin;
const provider = new BrowserProvider(window.ethereum);

const profileElm = document.getElementById('profile');
const noProfileElm = document.getElementById('noProfile');
const welcomeElm = document.getElementById('welcome');

const ensLoaderElm = document.getElementById('ensLoader');
const ensContainerElm = document.getElementById('ensContainer');
const ensTableElm = document.getElementById('ensTable');

let address;

const BACKEND_ADDR = "http://localhost:3000";
async function createSiweMessage(address, statement) {
    const res = await fetch(`${BACKEND_ADDR}/nonce`, {
        credentials: 'include',
    });
    const message = new SiweMessage({
        domain,
        address,
        statement,
        uri: origin,
        version: '1',
        chainId: '1',
        nonce: await res.text()
    });
    return message.prepareMessage();
}

function connectWallet() {
    provider.send('eth_requestAccounts', [])
        .catch(() => console.log('user rejected request'));
}

async function signInWithEthereum() {
    const signer = await provider.getSigner();
    profileElm.classList = 'hidden';
    noProfileElm.classList = 'hidden';
    welcomeElm.classList = 'hidden';

    address = await signer.getAddress()
    const message = await createSiweMessage(
        address,
        'Sign in with Ethereum to the app.'
    );
    const signature = await signer.signMessage(message);

    const res = await fetch(`${BACKEND_ADDR}/verify`, {
        method: "POST",
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({ message, signature }),
        credentials: 'include'
    });

    if (!res.ok) {
        console.error(`Failed in getInformation: ${res.statusText}`);
        return 
    }
    console.log(await res.text());

    displayENSProfile();
}

async function getInformation() {
    const res = await fetch(`${BACKEND_ADDR}/personal_information`, {
        credentials: 'include',
    });

    if (!res.ok) {
        console.error(`Failed in getInformation: ${res.statusText}`);
        return 
    }

    let result = await res.text();
    console.log(result);
    address = result.split(" ")[result.split(" ").length - 1];
    displayENSProfile();
}

async function displayENSProfile() {
    const ensName = await provider.lookupAddress(address);

    if (ensName) {
        profileElm.classList = '';

        welcomeElm.innerHTML = `Hello, ${ensName}`;
        let avatar = await provider.getAvatar(ensName);
        if (avatar) {
            welcomeElm.innerHTML += ` <img class="avatar" src=${avatar}/>`;
        }

        ensLoaderElm.innerHTML = 'Loading...';
        ensTableElm.innerHTML.concat(`<tr><th>ENS Text Key</th><th>Value</th></tr>`);
        const resolver = await provider.getResolver(ensName);

        const keys = ["email", "url", "description", "com.twitter"];
        ensTableElm.innerHTML += `<tr><td>name:</td><td>${ensName}</td></tr>`;
        for (const key of keys)
            ensTableElm.innerHTML += `<tr><td>${key}:</td><td>${await resolver.getText(key)}</td></tr>`;
        ensLoaderElm.innerHTML = '';
        ensContainerElm.classList = '';
    } else {
        welcomeElm.innerHTML = `Hello, ${address}`;
        noProfileElm.classList = '';
    }

    welcomeElm.classList = '';
}

const connectWalletBtn = document.getElementById('connectWalletBtn');
const siweBtn = document.getElementById('siweBtn');
const infoBtn = document.getElementById('infoBtn');
connectWalletBtn.onclick = connectWallet;
siweBtn.onclick = signInWithEthereum;
infoBtn.onclick = getInformation;

The above adds a look-up for some ENS metadata (email, url, description and twitter), then converts the result into content that is displayed in the table. Other functionality includes the showing and hiding of UI elements to make the page dynamic.

By doing this we're grabbing the label to each of the text records for a user's ENS profile, resolving each of them, and placing the result into a basic table.

To see the result, go into frontend and run:

yarn
yarn start

Then go into backend and run:

yarn
yarn start

And run through the updated example!

Now once the Sign-In with Ethereum flow is complete and there's an ENS profile associated with the account, the ENS name and avatar will appear along with all additional metadata from the profile in a new table.

Last updated