1. Architecture of NFTs
2. Documentation
3. Hardhat set up
- run "npm init -y"
- run "npm install --save-dev hardhat"
- run "npx hardhat init"
- rename the file to "deploy.js" in the folder "scripts"
- delete the file the folder "test"
- delete the file the folder "contracts"
4. Target
- event Transfer();
- event Approval();
- event ApprovalForAll();
- function balanceOf();
- function safeTransferForm();
- function safeTransferForm();
- function transferfrom();
- function approve();
- function setApprovalForAll();
- function getApproved();
- function isApprovedForAll();
5. The balanceOf Function
- Create a file named "ERC721.sol" in the folder "contracts"
pragma solidity ^0.8.23;
contract ERC721{
mapping(address => uint256) internal _balances;
function balanceOf(address owner) public view returns(uint256){
require(owner != address(0), "Address is zere");
return _balances[owner];
}
}
6. The OwnerOf Function
pragma solidity ^0.8.23;
contract ERC721{
mapping(address => uint256) internal _balances;
mapping(uint256 => address) internal _owners;
function balanceOf(address owner) public view returns(uint256){
require(owner != address(0), "Address is zere");
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "TokenID does not exist");
return owner;
}
}
- operator manages NFTs on behalf of someone else(E.g A market place like OpenSea)
pragma solidity ^0.8.23;
contract ERC721{
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
mapping(address => uint256) internal _balances;
mapping(uint256 => address) internal _owners;
mapping(address => mapping(address => bool))private _operatorApprovals;
function balanceOf(address owner) public view returns(uint256){
require(owner != address(0), "Address is zere");
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "TokenID does not exist");
return owner;
}
function setApprovalForAll(address operator, bool approved) public {
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}
}
8. The Approval Functions
pragma solidity ^0.8.23;
contract ERC721{
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
mapping(address => uint256) internal _balances;
mapping(uint256 => address) internal _owners;
mapping(address => mapping(address => bool))private _operatorApprovals;
mapping(uint256 => address) private _tokenApprovals;
function balanceOf(address owner) public view returns(uint256){
require(owner != address(0), "Address is zere");
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "TokenID does not exist");
return owner;
}
function setApprovalForAll(address operator, bool approved) public {
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}
function approve(address to, uint256 tokenId) public {
address owner = ownerOf(tokenId);
require(msg.sender == owner || isApprovedForAll(owner, msg.sender), "Msg.sender is not the owner or an approved operator");
_tokenApprovals[tokenId] = to;
emit Approval(owner, to, tokenId);
}
function getApproved(uint256 tokenId) public view returns(address){
require(_owners[tokenId] != address(0), "Token ID does not exist");
return _tokenApprovals[tokenId];
}
}
pragma solidity ^0.8.23;
contract ERC721{
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
mapping(address => uint256) internal _balances;
mapping(uint256 => address) internal _owners;
mapping(address => mapping(address => bool))private _operatorApprovals;
mapping(uint256 => address) private _tokenApprovals;
function balanceOf(address owner) public view returns(uint256){
require(owner != address(0), "Address is zere");
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "TokenID does not exist");
return owner;
}
function setApprovalForAll(address operator, bool approved) public {
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}
function approve(address to, uint256 tokenId) public {
address owner = ownerOf(tokenId);
require(msg.sender == owner || isApprovedForAll(owner, msg.sender), "Msg.sender is not the owner or an approved operator");
_tokenApprovals[tokenId] = to;
emit Approval(owner, to, tokenId);
}
function getApproved(uint256 tokenId) public view returns(address){
require(_owners[tokenId] != address(0), "Token ID does not exist");
return _tokenApprovals[tokenId];
}
function transferFrom(address from, address to, uint256 tokenId) public {
address owner = ownerOf(tokenId);
require(
msg.sender == owner ||
getApproved(tokenId) == msg.sender ||
isApprovedForAll(owner, msg.sender),
"Msg.sender is not the owner or approved from transfer "
);
require(owner == from, "From address is not the owner");
require(to != address(0), "Address is zero");
require(_owners[tokenId] != address(0), "TokenId does not exist");
approve(address(0), tokenId);
_balances[from] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
transferFrom(from, to, tokenId);
require(_checkOnERC721Received(), "Receiver not implemented");
}
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
function _checkOnERC721Received() private pure returns(bool){
return true;
}
}
10. Adding The ERC165 Standard
pragma solidity ^0.8.23;
contract ERC721{
event Transfer(address indexed _from, address indexed _to, uint256 indexed _tokenId);
event Approval(address indexed _owner, address indexed _approved, uint256 _tokenId);
event ApprovalForAll(address indexed _owner, address indexed _operator, bool _approved);
mapping(address => uint256) internal _balances;
mapping(uint256 => address) internal _owners;
mapping(address => mapping(address => bool))private _operatorApprovals;
mapping(uint256 => address) private _tokenApprovals;
function balanceOf(address owner) public view returns(uint256){
require(owner != address(0), "Address is zere");
return _balances[owner];
}
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "TokenID does not exist");
return owner;
}
function setApprovalForAll(address operator, bool approved) public {
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}
function approve(address to, uint256 tokenId) public {
address owner = ownerOf(tokenId);
require(msg.sender == owner || isApprovedForAll(owner, msg.sender), "Msg.sender is not the owner or an approved operator");
_tokenApprovals[tokenId] = to;
emit Approval(owner, to, tokenId);
}
function getApproved(uint256 tokenId) public view returns(address){
require(_owners[tokenId] != address(0), "Token ID does not exist");
return _tokenApprovals[tokenId];
}
function transferFrom(address from, address to, uint256 tokenId) public {
address owner = ownerOf(tokenId);
require(
msg.sender == owner ||
getApproved(tokenId) == msg.sender ||
isApprovedForAll(owner, msg.sender),
"Msg.sender is not the owner or approved from transfer "
);
require(owner == from, "From address is not the owner");
require(to != address(0), "Address is zero");
require(_owners[tokenId] != address(0), "TokenId does not exist");
approve(address(0), tokenId);
_balances[from] -= 1;
_balances[to] += 1;
_owners[tokenId] = to;
emit Transfer(from, to, tokenId);
}
function safeTransferFrom(address from, address to, uint256 tokenId, bytes memory _data) public {
transferFrom(from, to, tokenId);
require(_checkOnERC721Received(), "Receiver not implemented");
}
function safeTransferFrom(address from, address to, uint256 tokenId) public {
safeTransferFrom(from, to, tokenId, "");
}
function _checkOnERC721Received() private pure returns(bool){
return true;
}
function supportsInterface(bytes4 interfaceId) public pure virtual returns(bool){
return interfaceId == 0x80ac58cd;
}
}