ERC 6551: Token Bound Accounts (TBA)
Basic Understanding
ERC-6551 allows NFTs to have associated smart contract accounts. These accounts can hold tokens, interact with decentralized applications (dApps), and execute transactions—just like a regular Ethereum account.
Before ERC-6551, NFTs were static assets. With TBAs, an NFT can own ERC-20 tokens, other NFTs, or interact with DeFi protocols.
How it works:
Each NFT gets an associated Token Bound Account (TBA), which is a smart contract wallet.
The TBA is linked to the NFT and managed by the NFT's owner.
The NFT can store tokens or execute on-chain actions.
Use Cases
GameFi & Metaverse
A game character NFT can hold in-game assets (weapons, potions, tokens).
When the character is sold, the new owner also gets the stored items.
On-Chain Identity
Users can own an NFT-based identity with on-chain history, achievements, and credentials.
DAO & Governance
An NFT membership card can hold voting power and interact with DAO governance contracts.
DeFi & Financial Instruments
NFT-backed loans: An NFT with a TBA can hold staked assets or act as collateral.
Composable NFTs
A fashion NFT (e.g., a digital avatar) can own wearable NFTs (clothing, accessories).
Advantages
NFTs Become Wallets – NFTs can own assets, removing the need for separate wallets.
Better Composability – Enables richer interactions between NFTs and DeFi, DAOs, gaming.
Improved Security – Smart contract-controlled accounts reduce risks of external wallets.
Gas Efficiency – Reduces the need for multiple transactions to move assets.
Disadvantages
Smart Contract Complexity – More complex than standard NFTs, requiring secure contract audits.
Adoption Curve – Wallets, marketplaces, and dApps need to support ERC-6551.
Higher Gas Costs – Deploying smart contract accounts for NFTs adds gas costs.
Architecture & Code Structure
1. Key Components
ERC-721 NFT: The main NFT that will have an associated wallet.
Token Bound Account (TBA) Smart Contract: A contract that acts as the NFT’s wallet.
Registry Contract: Deploys and manages TBAs for NFTs.
Diagram
+----------------------+
| ERC-721 NFT |
| (e.g., Game Avatar) |
+----------------------+
|
| Links to TBA
v
+----------------------+
| Token Bound Account |
| (Smart Contract) |
+----------------------+
|
| Can store tokens, interact with dApps
v
+----------------------+
| ERC-20, NFTs, dApps |
| (Assets & Contracts)|
+----------------------+
3. Code Structure
Here’s a basic implementation of an ERC-6551 Token Bound Account:
A. ERC-6551 Registry (Factory to deploy TBAs)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
interface IERC6551Registry {
function createAccount(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external returns (address);
}
contract ERC6551Registry {
mapping(bytes32 => address) public accounts;
function createAccount(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId
) external returns (address) {
bytes32 salt = keccak256(abi.encodePacked(chainId, tokenContract, tokenId));
require(accounts[salt] == address(0), "Account already exists");
ERC6551Account account = new ERC6551Account();
accounts[salt] = address(account);
return address(account);
}
}
B. Token Bound Account (Smart Contract Wallet for NFT)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/access/Ownable.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";
contract ERC6551Account is Ownable {
event Executed(address target, uint256 value, bytes data);
constructor() {
transferOwnership(msg.sender);
}
function execute(address target, uint256 value, bytes calldata data) external onlyOwner {
(bool success, ) = target.call{value: value}(data);
require(success, "Execution failed");
emit Executed(target, value, data);
}
receive() external payable {} // Accept Ether
}
C. NFT Contract (Uses ERC-6551)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC721/extensions/ERC721URIStorage.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyNFT is ERC721URIStorage, Ownable {
IERC6551Registry public registry;
address public implementation;
constructor(address _registry, address _implementation) ERC721("MyNFT", "MNFT") {
registry = IERC6551Registry(_registry);
implementation = _implementation;
}
function mint(address to, uint256 tokenId, string memory tokenURI) public onlyOwner {
_mint(to, tokenId);
_setTokenURI(tokenId, tokenURI);
// Create Token Bound Account for NFT
registry.createAccount(implementation, block.chainid, address(this), tokenId);
}
}
Last updated
Was this helpful?