Revolutionizing NFTs: How ERC6551 Token Bound Accounts Drive NFT Innovation

Klaytn
Klaytn
Published in
13 min readNov 24, 2023

Overview

Considering the different possibilities that could ensue from NFT’s, different teams built blue-chip projects in the gaming ecosystem and metaverse. We saw the rise of different NFT projects like Cryptokitties, Bored Apes, Azuki, Doodles, NBA TopShot, etc., which, on the one hand, has majorly fostered a speculative trend where people buy, sell, and trade JPEGs, popularly known as an act of collecting NFTs.

For the past few years, we have seen how NFTs have transformed the way we own and represent unique digital items on-chain. During this time, NFTs saw broad adoption. Moreso, we experienced an era where different actors (NFT projects, creators, and traders) actively participated and are still participating in this next big thing powered by ERC721.

On the other hand, there should be more to NFTs than just collecting them, right? This is where the ERC6551 Token Bound Account (TBA) comes in. It unlocks a new paradigm where any single NFT is attached to its own wallet or account. With TBAs, NFTs transform from their static nature to being a living creature, i.e., an on-chain identity that can interact with dApps, own things, and change states just like any externally owned account.

In this article, we will be exploring what TBAs are and how they work under the hood. We then delve into the different use cases of ERC6551 and go over the process of creating a token bound account. To conclude, we go over the future of ERC-6551.

What is ERC6551: Non-fungible Token Bound Account?

The ERC6551 standard is a groundbreaking proposal by Future Primitive, an on-chain product studio headed by Benny Giang, Steve Jang, and Jayden Windle. This standard puts forth a new concept of Non-Fungible Token Bound Account (TBA), which revolutionizes the functionalities of an NFT by attaching a smart contract account to every NFT that has ever existed or that will be created in the future. Imagine an NFT taking up the powers of an externally owned account (EOA), where it can own assets, execute transactions, and interact with dApps. These are the possibilities that TBA opens up.

Token Bound Account is a complete inverse of Soul Bound Token (SBT), in that a token (NFT) is permanently bound to an account other than an account bound to a token (NFT). Having said that, ERC6551 ultimately transforms into a token bound account, where it transcends being used as collectibles or for speculative purposes (buying and selling) to being a digital identity on-chain. Now, with TBAs, NFTs become an on-chain creature with complete control over the assets and transactions associated with their accounts.

As we stand on the cusp of a new digital era, from ERC721 to ERC6551, you can imagine a world where this standard is set to transform NFTs and open up compelling new use cases for blockchain-based digital assets. Additionally, as an innovative solution, TBA is fully backward-compatible with ERC721. Meaning that existing NFTs can be transformed to ERC-6551 without undergoing any significant alterations.

Inside ERC-6551: Delving into Its Operational Mechanism

As stated earlier, TBA, by design, creates a new smart contract wallet for every NFT, thus making it an on-chain agent. Having said that, it is important to note that the account that owns this NFT has control over its assets and can execute on-chain actions via the TBA.

Did you just whisper, How is this made possible?

Well, the operational system of ERC6551 can be broken into two major parts:

Source

Registry

The registry stands as the creation point for all token-bound accounts. To deliver on this, it has two major functions: createAcount and account. The createAccount function is solely responsible for deploying a TBA for an NFT given an implementation address. While the account function computes the token bound account address for an NFT given an implementation address.

It is important to note that when you create a TBA using the createAccount function, it deploys the TBA as an ERC1167 Minimal Proxy Contract (MPC) with immutable constant data appended to the bytecode. MPC’s are instrumental for TBA for two reasons:

  • Save gas cost: Gas costs can be saved by using clones of contracts rather than repeatedly deploying NFT contracts.
  • Less effort: With MPCs, only one deployment of the proxy contract is required. Simply point the proxy at the same logic but with new data each time you wish to deploy a new instance.

A token bound account interface

The account interface is crucial for the core functionality attached to token bound accounts as it implements the following:

  • receive function: allows TBAs to receive Ether
  • execute function: this function allows TBAs to execute arbitrary calls on behalf of the NFT owner. Also, this function can support additional operations or restrict a signer’s ability to execute certain operations. This is important for security purposes, as it helps prevent unauthorized access to the TBA

Token Bound Accounts in Action: Practical Use Cases

Now that NFTs can own assets like KLAY, ETH, ERC20, ERC721, and ERC1155, and execute on-chain actions, the possibilities are endless. As it is, TBAs will set the pace for groundbreaking and innovative solutions in the Web3 ecosystem. The following are some of the practical use cases of the ERC6551 Non-Fungible Token Bound Account:

  • Gaming: With TBAs, in-game NFT characters can now collect various game accessories while exploring the game. This makes each game character function like a backpack and inventory, used for collecting and viewing in-game accessories. TBAs in games could also help facilitate decentralized game inventory. Other than that, in-game NFT characters will be able to perform actions in the game autonomously.
  • Autonomous on-chain identities: With TBAs, digital assets can become on-chain agents that can autonomously interact with services in a secure and transparent manner. Could be a signer in a multi-signature, own an ENS sub-domain, and also cast votes on proposals.
  • DAOs and Community Building: Most DAO’s and communities are always on the lookout for who participated most in community activities. To do this, most DAO’s and communities have represented membership cards and community experiences with soul-bound tokens, which are limited in their own way. For instance, you can sell your membership card and still retain some of the money your other community has collected over time. However, with ERC6551, DAO’s and communities can now represent their membership cards as TBA, which will also facilitate collecting and holding reputations and DAO standing in one NFT: TBA.
  • Decentralised Socials and Messaging: As stated earlier, TBAs open up new possibilities for integrating NFTs into everyday life. TBA could be used to enhance identity verification in a social app, meaning that it proves genuine participation. Also, TBAs could facilitate micropayments for content creators on decentralized social platforms where users can tip or pay content creators with their TBAs.

Creating and Deploying Token Bound Accounts (TBA)

After describing TBA’s operational mechanism, it is crucial to examine how its code actually works. In this section, we will examine the essential steps for creating up a token bound account.

Prerequisites

1. Remix IDE & Metamask
- In this tutorial, we’re going to use the Remix IDE to compile and deploy our smart contract. But the same can be done using Hardhat or any other Solidity Smart Contract development framework in your favorite code editor.

2. Faucets and Testnet Tokens
- Make sure your MetaMask wallet is connected to Baobab. After connecting to the right network, get test KLAY from Klay Faucet.

Step 1: Mint your Legacy ERC721 NFT

Token bound accounts are, as already stated, an NFT that serves as a smart contract wallet. Therefore, you need a unique ERC 721 NFT if you want to create a Token Bound account. In this step, we will deploy and mint a legacy ERC 721 NFT generated from the Klaytn-contracts library on Remix IDE.

Here is a working example of ERC-721 token contract:

solidity

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.7;

import "@klaytn/contracts/token/ERC721/ERC721.sol";

contract ERC6551BasicNft is ERC721

string public constant TOKEN_URI = "ipfs://bafybeig37ioir76s7mg5oobetncojcm3c3hxasyd4rvid4jqhy4gkaheg4/?filename=0-PUG.json";

uint256 private s_tokenCounter;

event Minted(uint256 indexed tokenId);

constructor() ERC721("ERC6551BasicNft", "EBN")

s_tokenCounter = 0;



function mintNft() public

_safeMint(msg.sender, s_tokenCounter);

emit Minted(s_tokenCounter);

s_tokenCounter = s_tokenCounter + 1;



function tokenURI(uint256 tokenId) public view override returns (string memory)

require(_exists(tokenId), "ERC721Metadata: URI query for nonexistent token");

return TOKEN_URI;



function getTokenCounter() public view returns (uint256)

return s_tokenCounter;

To deploy and mint this contract, navigate to Remix IDE.

Remix IDE

  1. On Remix IDE, navigate to contract folder in the File explorer tab
  2. Create a new file named ERC6551BasicNFT.sol in the contract folder.
  3. In the Solidity Compiler tab, click Compile contract.
  4. Click the Klaytn tab on your left having installed the plugin
  5. Select Environment > Injected ProviderMetaMask.
  6. In Contract, select your contract. For example, ERC6551BasicNFT.sol.
  7. Click Deploy.
  8. Once your contract is deployed, expand the contract interface in the Deployed Contract Tab and click the mintNft button

After successfully minting, you can verify your mint by viewing the NFT on OpenSea

Step 2: Creating and Deploying the Registry

The registry contract, as already stated, serves as the bedrock of the ERC 6551 Token Bound Account, as it is solely in charge of creating and attaching wallets to ERC 721 NFTs. In this section, we will be deploying the registry contract and implementing its core functions: createAccount and account.

  1. The `account` function for precomputing the TBA address given the specified parameters
  2. The `createAccount` function creates a TBA by deploying a smart wallet with specified parameters.

To derive a TBA address, the following unique parameters are hashed using the `Create2` function: TBA implementation address, token (ERC721) contract address, token ID, chain ID, and salt.

Also, the `_creationCode` function in the code below generates the bytecode for the smart wallet using the provided parameters. The `createAccount` function emits an `AccountCreated` event containing relevant information about the newly created TBA.

Here’s a snippet of the registry contract:

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/Create2.sol";

import "./interfaces/IERC6551Registry.sol";

library ERC6551BytecodeLib

function getCreationCode(

address implementation_,

uint256 chainId_,

address tokenContract_,

uint256 tokenId_,

uint256 salt_

) internal pure returns (bytes memory)

return

abi.encodePacked(

hex"3d60ad80600a3d3981f3363d3d373d3d3d363d73",

implementation_,

hex"5af43d82803e903d91602b57fd5bf3",

abi.encode(salt_, chainId_, tokenContract_, tokenId_)

);





contract ERC6551Registry is IERC6551Registry

error AccountCreationFailed();

function createAccount(

address implementation,

uint256 chainId,

address tokenContract,

uint256 tokenId,

uint256 salt,

bytes calldata initData

) external returns (address)

bytes memory code = ERC6551BytecodeLib.getCreationCode(

implementation,

chainId,

tokenContract,

tokenId,

salt

);

address _account = Create2.computeAddress(bytes32(salt), keccak256(code));

if (_account.code.length != 0) return _account;

emit AccountCreated(_account, implementation, chainId, tokenContract, tokenId, salt);

assembly

_account := create2(0, add(code, 0x20), mload(code), salt)



if (_account == address(0)) revert AccountCreationFailed();

if (initData.length != 0)

(bool success, bytes memory result) = _account.call(initData);

if (!success)

assembly

revert(add(result, 32), mload(result))







return _account;



function account(

address implementation,

uint256 chainId,

address tokenContract,

uint256 tokenId,

uint256 salt

) external view returns (address)

bytes32 bytecodeHash = keccak256(

ERC6551BytecodeLib.getCreationCode(

implementation,

chainId,

tokenContract,

tokenId,

salt

)

);

return Create2.computeAddress(bytes32(salt), bytecodeHash);

To deploy this contract, navigate to Remix IDE.

Remix IDE

  1. On Remix IDE, navigate to contract folder in the File explorer tab
  2. Create a new file named ERC6551Registry.sol in the contract folder.
  3. In the Solidity Compiler tab, click Compile contract.
  4. Click the Klaytn tab on your left having installed the plugin
  5. Select Environment > Injected ProviderMetaMask.
  6. In Contract, select your contract. For example, ERC6551Registry.sol.
  7. Click Deploy.

Step 3: Token Bound Account Implementation Deployment

To create a TBA, we need to pass in a unique parameter: the implementation address, as mentioned in the previous section. This contract gives NFT their wallet functionality, which comprises functions to receive Klay and execute transactions, hence unlocking the full potential of their assets. Simply put, the functionality of token bound accounts is enabled by this contract.

Here’s a snippet of the Token Bound Account Implementation contract:

solidity

// SPDX-License-Identifier: MIT

pragma solidity ^0.8.0;

import "@openzeppelin/contracts/utils/introspection/IERC165.sol";

import "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import "@openzeppelin/contracts/interfaces/IERC1271.sol";

import "@openzeppelin/contracts/utils/cryptography/SignatureChecker.sol";

import "./interfaces/IERC6551Account.sol";

import "./interfaces/IERC6551Executable.sol";

contract ExampleERC6551Account is IERC165, IERC1271, IERC6551Account, IERC6551Executable

uint256 public state;

receive() external payable

function execute(

address to,

uint256 value,

bytes calldata data,

uint256 operation

) external payable returns (bytes memory result)

require(_isValidSigner(msg.sender), "Invalid signer");

require(operation == 0, "Only call operations are supported");

++state;

bool success;

(success, result) = to.callvalue: value(data);

if (!success)

assembly

revert(add(result, 32), mload(result))







function isValidSigner(address signer, bytes calldata) external view returns (bytes4)

if (_isValidSigner(signer))

return IERC6551Account.isValidSigner.selector;



return bytes4(0);



function isValidSignature(bytes32 hash, bytes memory signature)

external

view

returns (bytes4 magicValue)



bool isValid = SignatureChecker.isValidSignatureNow(owner(), hash, signature);

if (isValid)

return IERC1271.isValidSignature.selector;



return "";



function supportsInterface(bytes4 interfaceId) external pure returns (bool)

function token()

public

view

returns (

uint256,

address,

uint256

)



bytes memory footer = new bytes(0x60);

assembly

extcodecopy(address(), add(footer, 0x20), 0x4d, 0x60)



return abi.decode(footer, (uint256, address, uint256));



function owner() public view returns (address)

(uint256 chainId, address tokenContract, uint256 tokenId) = token();

if (chainId != block.chainid) return address(0);

return IERC721(tokenContract).ownerOf(tokenId);



function _isValidSigner(address signer) internal view returns (bool)

return signer == owner();

To deploy this contract, navigate to Remix IDE.

Remix IDE

  1. On Remix IDE, navigate to contract folder in the File explorer tab
  2. Create a new file named ExampleERC6551Account.sol in the contract folder.
  3. In the Solidity Compiler tab, click Compile contract.
  4. Click the Klaytn tab on your left having installed the plugin
  5. Select Environment > Injected ProviderMetaMask.
  6. In Contract, select your contract. For example, ExampleERC6551Account.sol.
  7. Click Deploy.
  8. Copy the address of the newly created contract. Note: this address will be passed in the Registry contract as Implementation address while calling the createAccount and account functions.

Step 4: Computing and deploying the Token Bound Account Address

In this section, we will be pre-computing the token bound account address and eventually deploying it. Let’s go over it in the following steps:

Calling the `account` function in the Registry Contract

  1. On Remix IDE, expand the deployed Registry contract in the Deployed Contracts tab
  2. Click on the `account` function and pass in the following arguments:
  • Implementation address: the address of ExampleERC6551Account.sol deployed in the third step above.
  • chainId: 1001 in the case of Baobab
  • tokenContract: the address of ERC721 deployed in the first step
  • tokenId: 0 in the case of this NFT
  • salt: 0
  1. After inputting all the arguments, click on call to precompute the Token Bound Account Address

Executing the createAccount function in the Registry Contract

  1. On Remix IDE, expand the deployed Registry contract in the Deployed Contracts tab
  2. Click on the createAccount function and pass in the following arguments:
  • Implementation address: the address of ExampleERC6551Account.sol deployed in the third step above.
  • chainId: 1001 in the case of Baobab
  • tokenContract: the address of ERC721 deployed in the first step
  • tokenId: 0 in the case of this NFT
  • salt: 0
  • initData: empty array [] in this case

3. After inputting all the arguments, click on transact to deploy the Token Bound Account.

After successfully deploying the TBA, you can airdrop NFTs, new tokens (ERC-20, ERC-1155, etc.), send Klay to this newly created Token Bound Account. Also, you can view their transaction history on a block explorer like Klaytnscope.

Instantiating the Token Bound Account / Wallet

Recall that an account implementation address must be provided each time the registry uses the ‘createAccount’ method to create a new TBA? The reason for this is that a token bound account proxy shall delegate execution to a contract that implements the IERC6551Account interface. Given the TBA address computed above, you can instantiate the Token Bound Account using a contract that implements the IERC6551Account interface, in this instance ExampleERC6551Account.sol.

  • On Remix IDE, click on the ExampleERC6551Account contract file.
  • In the Solidity Compiler tab, click Compile contract.
  • In Contract, select your contract. For example, ExampleERC6551Account.sol.
  • To load the TBA contract interface, paste the address in the At Address field and click on the At Address button.
  • You should now see the Token Bound Account instance in the Deployed Contracts tab

To validate our TBA deployment, the owner of the legacy ERC721 NFT minted in step 1, should be the owner of the Token Bound Account. This means that the owner of the legacy NFT also controls the token bound account.

The Future of ERC6551

The birth of the ERC6551 Token Bound Account seeks to re-imagine digital ownership and provenance, paving the way for innovative solutions and blue-chip projects. As it is, this standard will not only be useful for gaming, NFT projects, DAO’s, real estate, etc., but can also power up amazing functionalities in the future. One such future use case is the birth of network playable characters (NPCs) in the metaverse and gaming platforms. The sole purpose of these NPCs is to populate the metaverse, which would have been originally empty, and perform on-chain actions. This concept was drawn from how non-playing characters (NPC) work in the traditional gaming system. Another amazing functionality that we can envision with TBA is one where artificial intelligence is merged with the ERC6551 Token Bound Account. With AI models and LLMs merged with TBAs, you can programme your NFTs or NPCs to perform onchain actions for you autonomously. We cannot help but envision a future of boundless possibilities with the ERC6551 Token Bound Account.

Conclusion

In the ever-growing space of blockchain technology, the ERC6551 Token Bound Account stands to unlock a new paradigm in the space. The implementation of this standard opens up an influx of use cases, enhancing NFT utility and adoption.

ERC 6551, being a new primitive, is still in its infant stage, and the tools and infrastructure around it are gradually being developed. As such, we urge business operators, developers, decentralized marketplaces, and crypto enthusiasts to take a look at the security considerations stated in its proposal.

The ERC-6551 standard is Ethereum Virtual Machine (EVM) compatible, meaning it’s operable on EVM-compatible chains, including Klaytn. Having said that, if you are planning to build any web3 solutions, such as a gaming platform, NFT marketplaces, wallet-as-a-service platform, lending solutions, or RWA tokenization, now is the time to leverage the possibilities of the ERC6551 Non-Fungible Token Bound Account and build the next unicorn on Klaytn. The complete source code for the example in this tutorial can be found in this Github Repository.

--

--