NFT 101. ERC721 Scratch

閱讀時間約 31 分鐘

1. Architecture of NFTs

2. Documentation

3. Hardhat set up

  1. run "npm init -y"
  2. run "npm install --save-dev hardhat"
  3. run "npx hardhat init"
  4. rename the file to "deploy.js" in the folder "scripts"
  5. delete the file the folder "test"
  6. 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"
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

contract ERC721{

mapping(address => uint256) internal _balances;
// Returns the number of NFTs assigned to an owner
function balanceOf(address owner) public view returns(uint256){
require(owner != address(0), "Address is zere");
return _balances[owner];
}
}

6. The OwnerOf Function

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

contract ERC721{

mapping(address => uint256) internal _balances;

mapping(uint256 => address) internal _owners;

// Returns the number of NFTs assigned to an owner
function balanceOf(address owner) public view returns(uint256){
require(owner != address(0), "Address is zere");
return _balances[owner];
}

// Finds the owner of an NFT
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "TokenID does not exist");
return owner;
}
}


7. The Operator Functions

  • operator manages NFTs on behalf of someone else(E.g A market place like OpenSea)
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

contract ERC721{

// event Transfer();
// event Approval();
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;

// Returns the number of NFTs assigned to an owner
function balanceOf(address owner) public view returns(uint256){
require(owner != address(0), "Address is zere");
return _balances[owner];
}

// Finds the owner of an NFT
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "TokenID does not exist");
return owner;
}

// Enables or disables an operator to manage all of msg.senders assets
function setApprovalForAll(address operator, bool approved) public {
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}

// Check if an address is an operator for another address
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}

}


8. The Approval Functions

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.23;

contract ERC721{

// event Transfer();
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;

// Returns the number of NFTs assigned to an owner
function balanceOf(address owner) public view returns(uint256){
require(owner != address(0), "Address is zere");
return _balances[owner];
}

// Finds the owner of an NFT
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "TokenID does not exist");
return owner;
}

// Enables or disables an operator to manage all of msg.senders assets
function setApprovalForAll(address operator, bool approved) public {
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}

// Check if an address is an operator for another address
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}

// Updates an approved address for an NFT
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);
}

// Gets the approved address for a single NFT
function getApproved(uint256 tokenId) public view returns(address){
require(_owners[tokenId] != address(0), "Token ID does not exist");
return _tokenApprovals[tokenId];
}
}


9. The Transfer Functions

// SPDX-License-Identifier: MIT
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;

// Returns the number of NFTs assigned to an owner
function balanceOf(address owner) public view returns(uint256){
require(owner != address(0), "Address is zere");
return _balances[owner];
}

// Finds the owner of an NFT
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "TokenID does not exist");
return owner;
}

// Enables or disables an operator to manage all of msg.senders assets
function setApprovalForAll(address operator, bool approved) public {
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}

// Check if an address is an operator for another address
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}

// Updates an approved address for an NFT
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);
}

// Gets the approved address for a single NFT
function getApproved(uint256 tokenId) public view returns(address){
require(_owners[tokenId] != address(0), "Token ID does not exist");
return _tokenApprovals[tokenId];
}

// Transfer ownership of an NFT
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);
}

// Standard transferFrom
// Check is on ERC721Received is implemented WHEN sending to smart contracts
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, "");
}

// Oversimplified
function _checkOnERC721Received() private pure returns(bool){
return true;
}
}


10. Adding The ERC165 Standard

// SPDX-License-Identifier: MIT
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;

// Returns the number of NFTs assigned to an owner
function balanceOf(address owner) public view returns(uint256){
require(owner != address(0), "Address is zere");
return _balances[owner];
}

// Finds the owner of an NFT
function ownerOf(uint256 tokenId) public view returns (address) {
address owner = _owners[tokenId];
require(owner != address(0), "TokenID does not exist");
return owner;
}

// Enables or disables an operator to manage all of msg.senders assets
function setApprovalForAll(address operator, bool approved) public {
_operatorApprovals[msg.sender][operator] = approved;
emit ApprovalForAll(msg.sender, operator, approved);
}

// Check if an address is an operator for another address
function isApprovedForAll(address owner, address operator) public view returns (bool) {
return _operatorApprovals[owner][operator];
}

// Updates an approved address for an NFT
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);
}

// Gets the approved address for a single NFT
function getApproved(uint256 tokenId) public view returns(address){
require(_owners[tokenId] != address(0), "Token ID does not exist");
return _tokenApprovals[tokenId];
}

// Transfer ownership of an NFT
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);
}

// Standard transferFrom
// Check is on ERC721Received is implemented WHEN sending to smart contracts
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, "");
}

// Oversimplified
function _checkOnERC721Received() private pure returns(bool){
return true;
}

// EIP165: Query if a contract implements another interface
function supportsInterface(bytes4 interfaceId) public pure virtual returns(bool){
return interfaceId == 0x80ac58cd;
}
}


[Reference] Eattheblocks - NFT101

尋大神腳印, 亦步亦趨。
留言0
查看全部
發表第一個留言支持創作者!
從 Google News 追蹤更多 vocus 的最新精選內容