4. Ethereum Request for Comment

When building decentralized systems, one of the most crucial parts is how they interact. You learned that a smart contract has public functions, but you also need an ABI to call them.

You might be able to reverse engineer the ABI in some way, for example, by reading the source code. But sadly, not all smart contracts are open source. This makes the task quite cumbersome.

Enter Ethereum Request for Comments

Luckily the creators of Ethereum saw this problem early on and focused on interopt between smart contracts. The result is the Ethereum request for comments or short ERCs. They can contain specs for interfaces a smart contract has to implement.

The process goes as follows: Things aren't going smoothly for some reason or another, and users want to solve it. They write an Ethereum Improvement Proposal (EIP) and layout what could be better. Next, the Ethereum Committee will approve the EIP if it's okay and create an ERC based on the EIP.

This process leads to new ways for smart contract interopt and makes it easier to mix and match smart contracts in Web3 applications.

ERC-20 & ERC-721 Token Standards

The most prominent ERCs are ERC-20 for fungible tokens, commonly known as cryptocurrencies or simply tokens, and ERC-721 for non-fungible tokens, known as NFTs.

While critical functions of smart contracts are usually bound to specific addresses, many are external without any permissions so that you can call them via Ethers.js.

Creating an ERC-721 Read-Only ABI

If you know the address of a smart contract that mints NFTs, you can go and search for an ERC-721 ABI and use it together with the address to call it.

The standardized Solidity interface for an ERC-721 contract looks like this:

interface ERC721 {
    event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
    event Approval(address indexed _owner, address indexed _approved, uint256 indexed _tokenId);
    event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);

    function balanceOf(address _owner) external view returns (uint256);
    function ownerOf(uint256 _tokenId) external view returns (address);
    function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable;
    function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable;
    function transferFrom(address _from, address _to, uint256 _tokenId) external payable;
    function approve(address _approved, uint256 _tokenId) external payable;
    function setApprovalForAll(address _operator, bool _approved) external;
    function getApproved(uint256 _tokenId) external view returns (address);
    function isApprovedForAll(address _owner, address _operator) external view returns (bool);
}

We learned in lesson 2 that we can convert this to an ABI for Ethers.js by simply copying the function signatures as strings into a JavaScript array.

const erc721Abi = [
  "function balanceOf(address _owner) external view returns (uint256)",
  "function ownerOf(uint256 _tokenId) external view returns (address)",
  "function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes data) external payable",
  "function safeTransferFrom(address _from, address _to, uint256 _tokenId) external payable",
  "function transferFrom(address _from, address _to, uint256 _tokenId) external payable",
  "function approve(address _approved, uint256 _tokenId) external payable",
  "function setApprovalForAll(address _operator, bool _approved) external",
  "function getApproved(uint256 _tokenId) external view returns (address)",
  "function isApprovedForAll(address _owner, address _operator) external view returns (bool)",
]

Now, since we don't have covered wallets yet, we are only able to call the view functions, so let's throw the rest out.

const erc721ReadOnlyAbi = [
  "function balanceOf(address _owner) external view returns (uint256)",
  "function ownerOf(uint256 _tokenId) external view returns (address)",
  "function getApproved(uint256 _tokenId) external view returns (address)",
  "function isApprovedForAll(address _owner, address _operator) external view returns (bool)",
]

Try to get the owner of tokenId 5666 for the NFT contract located at 0x25ed58c027921E14D86380eA2646E3a1B5C55A8b, using the ABI we defined above and print the result!

You can use the lookupAddress method of the provider to get the ENS domain that points to an address. Try that for the owner address of the token.

const provider = ethers.getDefaultProvider() // Write your code here!

const erc721ReadOnlyAbi = [ "function balanceOf(address _owner) external view returns (uint256)", "function ownerOf(uint256 _tokenId) external view returns (address)", "function getApproved(uint256 _tokenId) external view returns (address)", "function isApprovedForAll(address _owner, address _operator) external view returns (bool)", ] const contractAddress = "0x25ed58c027921E14D86380eA2646E3a1B5C55A8b" const smartContract = new ethers.Contract( contractAddress, erc721ReadOnlyAbi, provider ) const ownerAddress = await smartContract.ownerOf(5666) const ownerDomain = await provider.lookupAddress(ownerAddress) print(ownerDomain)

Summary

In this lesson you learned about ERCs. How they standardize interfaces and behavior to help you using these contracts and also allow other contrats to interact with them.

We also converted a Solitidy interface defined in the ERC-721 to a ABI you can use with Ethers.js.

In the next lesson, you will build your very first Web3 app!