Similar to the ENS look-up, we can also query the user's NFT ownership. In this example, we will display basic information about the user's NFTs in a table, via the OpenSea API . However, this could also be extended to even give the user a visual gallery view of their NFTs once connected.
First, we need to change index.html
to include a new table. We'll use the same structure as in the last chapter, separating the two tables with an <hr>
tag:
Copy <! DOCTYPE html >
< html >
< head >
< meta charset = "utf-8" />
< title >SIWE Quickstart</ title >
< style >
.hidden {
display : none
}
table ,
th ,
td {
border : 1 px black solid ;
}
.avatar {
border-radius : 8 px ;
border : 1 px solid #ccc ;
width : 20 px ;
height : 20 px ;
}
</ 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 Found.
</ div >
< div class = "hidden" id = "nft" >
< h3 >NFT Ownership</ h3 >
< div id = "nftLoader" ></ div >
< div id = "nftContainer" class = "hidden" >
< table id = "nftTable" >
</ table >
</ div >
</ div >
</ body >
</ html >
Copy 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' );
const nftElm = document .getElementById ( 'nft' );
const nftLoaderElm = document .getElementById ( 'nftLoader' );
const nftContainerElm = document .getElementById ( 'nftContainer' );
const nftTableElm = document .getElementById ( 'nftTable' );
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 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 = '' ;
}
async function getNFTs () {
try {
let res = await fetch ( `https://api.opensea.io/api/v1/assets?owner= ${ address } ` );
if ( ! res .ok) {
throw new Error ( res .statusText)
}
let body = await res .json ();
if ( ! body .assets || ! Array .isArray ( body .assets) || body . assets . length === 0 ) {
return []
}
return body . assets .map ((asset) => {
let {name , asset_contract , token_id} = asset;
let {address} = asset_contract;
return {name , address , token_id};
});
} catch (err) {
console .error ( `Failed to resolve nfts: ${ err .message } ` );
return [];
}
}
async function displayNFTs () {
nftLoaderElm .innerHTML = 'Loading NFT Ownership...' ;
nftElm .classList = '' ;
let nfts = await getNFTs ();
if ( nfts . length === 0 ) {
nftLoaderElm .innerHTML = 'No NFTs found' ;
return ;
}
let tableHtml = "<tr><th>Name</th><th>Address</th><th>Token ID</th></tr>" ;
nfts .forEach ((nft) => {
tableHtml += `<tr><td> ${ nft .name } </td><td> ${ nft .address } </td><td> ${ nft .token_id } </td></tr>`
});
nftTableElm .innerHTML = tableHtml;
nftContainerElm .classList = '' ;
nftLoaderElm .innerHTML = '' ;
}
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 ();
displayNFTs ();
}
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 ();
displayNFTs ();
}
const connectWalletBtn = document .getElementById ( 'connectWalletBtn' );
const siweBtn = document .getElementById ( 'siweBtn' );
const infoBtn = document .getElementById ( 'infoBtn' );
connectWalletBtn .onclick = connectWallet;
siweBtn .onclick = signInWithEthereum;
infoBtn .onclick = getInformation;
Now, when a user signs in, information on NFT holdings is displayed below the ENS information (if available).