---
# System prepended metadata

title: ERC20 範例
tags: [Lab 研究]

---

# ERC20 範例 

> 直接繼承 import 的 Openzepplin ERC20代幣
```solidity=
// contracts/GLDToken.sol
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.0;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";


contract CharmingFish is ERC20 {
    constructor(uint256 initialSupply) ERC20("CharmingFish", "CMF") {
        _mint(msg.sender, initialSupply);
    }
    function buyToken(address to, uint256 amount) public{
        address owner = msg.sender;
        _transfer(owner,to,amount);
    }

}

```


> 直接手動刻出 ERC20標準 須具備的 Function (非完整 要補完interface)
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17; 

//持有者A ONWER : 0x5B38Da6a701c568545dCfcB03FcB875f56beddC4
//持有者B  : 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2
//持有者C  : 0x17F6AD8Ef982297579C203069C1DbfFE4348c372


interface IERC20 {

    function totalSupply() external view returns (uint256); //V

    function balanceOf(address account) external view returns (uint256); //V

    function transfer(address recipient, uint256 amount) external returns (bool); //V

    function allowance(address owner, address spender) external view returns (uint256); //V

    function approve(address spender, uint256 amount) external returns (bool); //V
   
    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool); //V

    event Transfer(address indexed from, address indexed to, uint256 value); //V

    event Approval(address indexed owner, address indexed spender, uint256 value); //V
}


contract ERC20TEST is IERC20
{

    uint public totalSupply;
    string public name;
    string public symbol;
    int public decimals;
    address public owner;

    mapping (address => uint256) public balanceOf;
    mapping (address => mapping(address => uint256)) public allowance;

    constructor(){
        totalSupply = 10000;
        name = "TOM";
        decimals = 18;
        symbol = "tom";
        balanceOf[msg.sender]=10000;
        owner = msg.sender;
    }

    function transfer(address recipient, uint256 amount) external returns (bool){
        balanceOf[msg.sender] -= amount;
        balanceOf[recipient] += amount;
        emit Transfer(msg.sender,recipient,amount);
        return true;
    }

    function approve(address spender, uint256 amount) external returns (bool){
        allowance[msg.sender][spender] +=amount;
        emit Approval(msg.sender,spender,amount);
        return true;
    }

    function transferFrom(address sender, address recipient, uint256 amount) external returns (bool){
        allowance[sender][msg.sender] -= amount;
        balanceOf[recipient] += amount;
        balanceOf[sender] -= amount;
        emit Transfer(sender,recipient,amount);
        return true;
    }
}
```
<BR>

> ERC721 示範
```solidity=
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.17;

interface IERC721Receiver {
    function onERC721Received(
        address operator,
        address from,
        uint256 tokenId,
        bytes calldata data
    ) external returns (bytes4);
}

interface IERC721Metadata {
    function name() external view returns (string memory);
    function symbol() external view returns (string memory);
    function tokenURI(uint256 tokenId) external view returns(string memory);
}

interface IERC165 {
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

interface IERC721 {
    // Event
    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);
    // Query
    function balanceOf(address owner) external view returns (uint256 balance);
    function ownerOf(uint256 tokenId) external view returns (address owner);
    // Transfer
    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
    function safeTransferFrom(address from, address to, uint256 tokenId) external;
    function transferFrom(address from, address to, uint256 tokenId) external;
    //Approve
    function approve(address to, uint256 tokenId) external;
    function setApprovalForAll(address operator, bool _approved) external;
    function getApproved(uint256 tokenId) external view returns (address operator);
    function isApprovedForAll(address owner, address operator) external view returns (bool);
    //下面這些並非標準的ERC721必備Function
    //mint
    function mint(address to, uint256 tokenId) external;
    function safemint(address to, uint256 tokenId, bytes memory data) external;
    function safemint(address to, uint256 tokenId) external;
    //burn
    function burn(uint256 tokenId) external ;

}

contract ERC721 is IERC721, IERC721Metadata, IERC165{

    string _name;
    string _symbol;
    
    constructor(string memory name_, string memory symbol_){
        _name = name_;
        _symbol = symbol_;
    }

    function name() public view returns (string memory){
        return _name;
    }

    function symbol() public view returns (string memory){
        return _symbol;
    }

    //檢查之用
    function supportsInterface(bytes4 interfaceId) public pure returns (bool){
        return interfaceId == type(IERC721).interfaceId ||
            interfaceId == type(IERC721Metadata).interfaceId ||
            interfaceId == type(IERC165).interfaceId;
    }
    

    mapping(address => uint256) _balances;
    mapping(uint256 => address) _owners;
    mapping(uint256 => address) _tokenApprovals;
    mapping(address => mapping(address => bool)) _operatorApprovals; 
    mapping(uint256 => string) _tokenURIs;

    function balanceOf(address owner) public view returns (uint256 balance){
        require(owner != address(0),"ERROR: address 0 can't be owner!");
        return _balances[owner];
    }

    function ownerOf(uint256 tokenId) public view returns (address){
        address owner = _owners[tokenId];
        require(owner != address(0),"ERROR: tokenId is not valid Id!");
        return owner;
    }

    function approve(address to, uint256 tokenId) public{
        address owner = _owners[tokenId];
        require(owner != to,"ERROR: owner == to");
        require(owner == msg.sender || isApprovedForAll(owner,msg.sender),"ERROR: Caller is not owner / approved for all");
        _tokenApprovals[tokenId] = to;
        emit Approval(owner,to,tokenId);
    }

    function getApproved(uint256 tokenId) public view returns (address operator){
        address owner = _owners[tokenId];
        require(owner != address(0),"ERROR: token is not minted or is burn already");
        return _tokenApprovals[tokenId];
    }

    function setApprovalForAll(address operator, bool _approved) public{
        address owner = msg.sender;
        require(owner != operator,"ERROR: Owner cannot be the operator!");
        //下面這樣多此一舉又慢 思考!
        // if(_approved)
        // {
        //     _operatorApprovals[owner][operator] = true;
        //     emit ApprovalForAll(owner,operator,true);
        // }
        _operatorApprovals[owner][operator] = _approved;
        emit ApprovalForAll(owner,operator,_approved);
    }

    function isApprovedForAll(address owner, address operator) public view returns (bool){
        return _operatorApprovals[owner][operator];
    }

    function transferFrom(address from, address to, uint256 tokenId) public{
        _transer(from,to,tokenId);
    }

    function _transer(address from, address to, uint256 tokenId) internal{
        address owner = _owners[tokenId];
        require(owner == from , "ERROR: Owner is not from address!");
        require(msg.sender==owner || isApprovedForAll(owner,msg.sender) || getApproved(tokenId)==msg.sender,"ERROR: Permission denied!");
        delete _tokenApprovals[tokenId];
        _balances[from]-=1;
        _balances[to]+=1;
        _owners[tokenId] = to; 
        emit Transfer(from,to,tokenId);
    }

    function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) public{
        _safeTransfer(from,to,tokenId,data);
    }


    function safeTransferFrom(address from, address to, uint256 tokenId) public{
        _safeTransfer(from, to, tokenId, "");
    }

    function _safeTransfer(address from, address to, uint256 tokenId,bytes memory data) internal{
        _transer(from,to,tokenId);
        require(_checkOnERC721Received(from,to,tokenId,data),"ERROR: ERC721 receiver is not implmeneted!");
    }

    //reference 
    function _checkOnERC721Received(address from, address to, uint256 tokenId, bytes memory data) private returns (bool) {
        if (to.code.length > 0 /* to is a contract*/) {
            try IERC721Receiver(to).onERC721Received(msg.sender, from, tokenId, data) returns (bytes4 retval) {
                return retval == IERC721Receiver.onERC721Received.selector;
            } catch (bytes memory reason) {
                if (reason.length == 0) {
                    revert("ERC721: transfer to non ERC721Receiver implementer");
                } else {
                    /// @solidity memory-safe-assembly
                    assembly {
                        revert(add(32, reason), mload(reason))
                    }
                }
            }
        } 
        else {
            return true;
        }
    }

    function mint(address to, uint256 tokenId) public{
        require(to != address(0),"ERROR: You can't mint the NFT to address 0x00");
        address owner = _owners[tokenId];
        require(owner == address(0),"ERROR: TokenId has been used!");
        // _transer(address(0),to,tokenId); 用這段會比較浪費 不需要做這麼多檢查
        _balances[to] += 1;
        _owners[tokenId] = to;
        emit Transfer(address(0),to,tokenId);
    }

    function safemint(address to, uint256 tokenId, bytes memory data) public{
        mint(to,tokenId);
        require(_checkOnERC721Received(address(0),to,tokenId,data),"ERROR: ERC721 receiver is not implmeneted!");
    }

    function safemint(address to, uint256 tokenId) public{
        safemint(to,tokenId,"");
    }

    function burn(uint256 tokenId) public{
        address owner = _owners[tokenId];
        require(msg.sender==owner,"ERROR: You don't have this permission!");
        _balances[owner] -= 1;
        _owners[tokenId] = address(0);
        //or delete _owners[tokenId];
        delete _tokenApprovals[tokenId];
        emit Transfer(owner,address(0),tokenId);
    }
 
    function tokenURI(uint256 tokenId) external view returns(string memory){
        address owner = _owners[tokenId];
        require(owner != address(0),"ERROR: tokenId is not valid");
        return _tokenURIs[tokenId];
    }

    function setTokenURI (uint256 tokenId,string memory URI) public{
        _tokenURIs[tokenId]=URI;
    }

}
```

###### tags: `Lab 研究`