---
# System prepended metadata

title: 第二週作業
tags: [Solidity 工程師實戰營第 5 期]

---

# 第二週作業

## 基本題

### 1. 部署一個自定義的 ERC20 Token 在 Goerli 鏈上

* 開源合約並提交合約地址
合約地址URL：https://goerli.etherscan.io/address/0xd0fa61a4c4fa1057b9104b390fa2221b9eebb561
Token Name : RyanToken
Token Symbol：RT
* 實作 mint 和 burn 功能
```solidity=
/**
 *Submitted for verification at Etherscan.io on 2023-03-05
 */

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.18;

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

contract MyToken is ERC20 {

    uint8 private _decimals; //Token最小單位

    //初始化
    constructor(
        string memory name_,
        string memory symbol_,
        uint8 decimals_
    ) ERC20(name_, symbol_) {
        _decimals = decimals_;
    }

    //取得Token最小單位
    function decimals() public view override returns (uint8) {
        return _decimals;
    }

    //實作 mint 鑄造
    function mint(address account, uint256 amount) external {
        super._mint(account, amount);
    }

    //實作 burn 燒毀
    function burn(address account, uint256 amount) external {
        super._burn(account, amount);
    }
}

```
* 轉移 100 個 Token 到以下地址 0x6e24f0fF0337edf4af9c67bFf22C402302fc94D3
Transaction Details URL：https://goerli.etherscan.io/tx/0xb25311cac96754ea2bf158073bd09938756bf8af67e9d2a9c3fbaae66074cd20
![](https://i.imgur.com/IxebPit.png)

* 轉移 Token 給所有組員
![](https://i.imgur.com/oA8eTsF.png)


### 2.部署一個 ERC721 NFT 合約，同時擁有付費 mint 功能
* 開源合約並提交合約地址
* 使用 OpenSea Metadata 標準，並提交 OpenSea頁面
* 將檔案上傳至 ipfs
* 擁有白名單機制並且將組員加入白名單
* 設定總量上限
* 加入至少一種自定義功能，例如：荷蘭拍、盲盒、返佣、融合…

合約地址：https://goerli.etherscan.io/address/0xe5d30e6e2f4a20d243e971dc31055c664203fd46#code

```solidity=
/**
 *Submitted for verification at Etherscan.io on 2023-03-05
 */

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.18;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyNFT is ERC721, Ownable {
    bytes32 private root; //root hash
    uint256 private tokenId = 0; //NFT Token ID
    bool private isBoxOpened = false; //盲盒控制打開與否
    string private baseUri; //圖片uri
    string private unrevealedURI; //盲盒圖片uri
    uint256 private cost; // Mint 價格
    uint256 private maxCount; //最大鑄造數量

    constructor(
        string memory _name,
        string memory _symbol,
        string memory _baseUri,
        string memory _unrevealedURI,
        uint256 _cost,
        uint256 _maxCount
    ) ERC721(_name, _symbol) {
        baseUri = _baseUri;
        unrevealedURI = _unrevealedURI;
        cost = _cost;
        maxCount = _maxCount;
    }

    //驗證proof
    modifier verifyProof(bytes32[] memory proof) {
        require(
            MerkleProof.verify(
                proof,
                root,
                keccak256(abi.encodePacked(msg.sender))
            ),
            "Invalid proof"
        );
        _;
    }

    //驗證是否超過最大鑄造數量
    modifier verifyMintCount() {
        require(tokenId < maxCount, "Can't mint more");
        _;
    }

    //設定鑄造價格
    function setCost(uint256 _cost) external onlyOwner {
        cost = _cost;
    }

    //取得鑄造價格
    function getCost() external view returns (uint256) {
        return cost;
    }

    //設定鑄造最大數量
    function setMaxCount(uint256 _maxCount) external onlyOwner {
        maxCount = _maxCount;
    }

    //取得鑄造最大數量
    function getMaxCount() external view returns (uint256) {
        return maxCount;
    }

    //取得base Uri
    function _baseURI() internal view override returns (string memory) {
        return isBoxOpened ? baseUri : unrevealedURI;
    }

    //打開盲盒
    function openBox() external onlyOwner {
        isBoxOpened = true;
    }

    //關閉盲盒
    function closeBox() external onlyOwner {
        isBoxOpened = false;
    }

    //取得token Uri
    function tokenURI(uint256 _tokenId)
        public
        view
        override
        returns (string memory)
    {
        _requireMinted(_tokenId);

        string memory baseURI = _baseURI();
        return
            bytes(baseURI).length > 0
                ? string(
                    abi.encodePacked(
                        baseURI,
                        Strings.toString(_tokenId),
                        ".json"
                    )
                )
                : "";
    }

    //鑄造給一般人，但要收錢
    function mint() external payable verifyMintCount {
        require(msg.value == cost, "Not enough ether sent");
        tokenId++;
        _safeMint(msg.sender, tokenId);
    }

    //免費鑄造給白名單
    function whitelistMint(bytes32[] calldata _proof)
        external
        verifyProof(_proof)
        verifyMintCount
    {
        tokenId++;
        _safeMint(msg.sender, tokenId);
    }

    //設定Root
    function setRoot(bytes32 _root) external onlyOwner {
        root = _root;
    }

    //提取錢給合約擁有者
    function withdraw() public onlyOwner {
        require(address(this).balance > 0, "Balance is 0");
        payable(owner()).transfer(address(this).balance);
    }
}
```

盲盒
![](https://i.imgur.com/e1UEUch.png)

解盲盒
![](https://i.imgur.com/pMkbjjq.png)




## 進階題

### 1.研究 ERC721A 合約

* 寫下 ERC721A 及 ERC721 差異
ERC-721 是一種合約協議的代號，他是目前 NFT 的基礎協議，用作發行擁有不同代號的有限量或無限量代幣(Token)。而因為他的唯一編碼方式以及使用 Metadata 來提供更多資訊的特色，因此被廣泛應用在各領域。
ERC-721A 是由 ERC-721 的改良，因原本使用的 ERC-721 其語法的因素，導致在鑄造(Mint)多個 NFT 時會產生大量的手續費(Gas Fee)，而 ERC-721A 大幅度地減少了手續費的消耗，因此在目前的 NFT 世界被廣泛使用。


* 實際部署 ERC721A 合約比較所花費的 gas fee
ERC721A
![](https://i.imgur.com/vrO7s09.png)
ERC721
![](https://i.imgur.com/MDvMxp9.png)




### 2.Contract Factory 使用合約來部署多個 ERC20 或 ERC721 合約
* 開源合約並提交合約地址
* 每個產出的子合約可以設定不同的參數，name, symbol...

合約地址 : https://goerli.etherscan.io/address/0xccb00d27ad3e98c492a4ee66c9bef994797fffcb

```solidity=
/**
 *Submitted for verification at Etherscan.io on 2023-03-05
 */

// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.18;

import "./ERC20.sol";
import "./ERC721A.sol";

contract Factory {
    address[] public erc20ContractArr;
    address[] public erc721ContractArr;

    //建立ERC20合約
    function createERC20(
        string memory name_,
        string memory symbol_,
        uint8 decimals_
    ) external payable {
        MyToken token = new MyToken(name_, symbol_, decimals_);
        erc20ContractArr.push(address(token));
    }

    function getAllERC20Contract() external view returns (address[] memory) {
        return erc20ContractArr;
    }

    function createERC721A(
        string memory name_,
        string memory symbol_,
        string memory baseUri_,
        string memory unrevealedURI_,
        uint256 cost_,
        uint256 maxCount_
    ) external payable {
        MyNFT nft = new MyNFT(name_,symbol_,baseUri_,unrevealedURI_,cost_,maxCount_);
        erc721ContractArr.push(address(nft));
    }

    function getAllERC721AContract() external view returns (address[] memory) {
        return erc721ContractArr;
    }
}
```

###### tags: `Solidity 工程師實戰營第 5 期`