---
# System prepended metadata

title: 區塊鏈全端工程師實戰營  week1

---

第一週作業 [2024年1月]
基礎：
1. 發行總量100億顆、位數18的代幣！
2. 發行總量10張的SBT。
3. 開發猜數字的合約（一個人設定，大家猜，有人猜中，就結束不讓其他人猜了）

進階： 
1. 發行有盲盒機制的NFT！
2. 理解ERC721A與ERC721的差異，並實作ERC721A合約，實際比較差異！
 =================================================
 
1.發行總量100億顆、位數18的代幣  
```
 // SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyCoin01 is ERC20 {
    address public admin;
    uint256 maxSup;
    constructor(string memory _name , string memory _symbol , uint256 _maxSup) ERC20(_name, _symbol) {
        admin = msg.sender;
        maxSup = _maxSup;
    }

    function mint(uint256 _amount) public {
        //如果今天是Line Pay改用虛擬貨幣，那充值TWD的時候應該就會從官方錢包去mint，所以下面也要有後台burn的功能   應該就可以這樣去設計?
        require(msg.sender == admin , "u r not admin");
        require(totalSupply()+_amount < maxSup , "mint > maxSup");
        _mint(msg.sender, _amount);
    }


    function burn(address user, uint256 x) public { 
        _burn(user,x);
    }
}
```

2.發行總量10張的SBT。

疑問點1:safe版沒改revert也轉移不了，但沒safe的transferFrom就很正常，由於safe會檢查接收方是不是合約、if是合約->有沒有操作NFT的功能。 會不會是remix是虛擬錢包的關系?

疑問點2:部署的合約不一定會公開合約代碼-->safetransferFrom無法判斷對方能不能操作NFT，這樣會不會造成實戰時使用safetransferFrom的場景限制？ 

疑問點3:我看大部份人的邏輯都是mint給msg.sender，如果有一個場景：有一個女團要發NFT，條件是本人有到現場+後援會成員+限領一個  -> 所以會是人工處理，這種情況mint的方式應該是在constructor裡加一個address Owner，然後require只能由Owner去mint給address?
還是全部mint給Owner然後require由Owner去transfer 已有的NFT給申請領取的人? 
以上2種好像都是可行，但個人偏好後者

疑問點4:好像不一定要import Counters.sol，但網絡上有些做法是用這個，單純是比較方便而已嗎
```
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.20;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";

contract MyNFT is ERC721 {

    uint256 MaxSupply = 10;
    uint256 counter = 0;
    constructor(string memory _name ,string memory _symbol , uint256 _MaxSupply) ERC721(_name, _symbol) {
        MaxSupply = _MaxSupply;
    }
    function mint(uint256 num) public {
        require(num + counter<=MaxSupply , "Cant mint any more");
        for(uint256 i=0;i<num;i++){
            uint256 tokenId = counter;
            _safeMint(msg.sender, tokenId);
            counter++;
        }
    }

    function transferFrom(address from, address to, uint256 tokenId) public override {
        revert();
    }
}
```


3.開發猜數字的合約（一個人設定，大家猜，有人猜中，就結束不讓其他人猜了）

```
// SPDX-License-Identifier: GPL-3.0

pragma solidity ^0.8.20;
import "hardhat/console.sol";

contract w1h3 {
    address private owner;
    address private admin;
    address public Winner;
    bool public game_start = false;
    int private number;
    constructor(){
        owner = msg.sender;
    }

    function getOwner()public view returns (address){
        return owner;
    }
    function getAdmin()public view returns (address){
        return admin;
    }
    function setAdmin(address i_admin)public{
        require(msg.sender == owner , "u r not owner");
        admin = i_admin;
    }
    function gameStart(int i_number)public{
        require(msg.sender == admin , "u r not admin");
        Winner = address(0);
        number = i_number;
        game_start = true;
    }
    function guessNum(int i_number)public{
        require(game_start == true , "game offline");
        if(i_number ==number){
            console.log ("you win");
            game_start = false;
            Winner = msg.sender;
        }
        else{
            console.log ("try again");
        }
        
    }
}
```

進階
1. 發行有盲盒機制的NFT！
**疑問點1**:我希望->抽中2張黑底可以拿回來換成5.png，到時候會burn掉2張nft去獨立mint一個限定版有角的NFTP[5.PNG]
我是不是不應該設計成從Solidity硬讀鏈以外的json->"bgcolor" ?但如果不這樣做，是不是只能在合約裡列出黑底的tokenId,人家要來換的時候再拿出來對比檢查?
![image](https://hackmd.io/_uploads/Hyqwkz4KT.png)
![image](https://hackmd.io/_uploads/rkouJzVtp.png)

```
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Counters.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
contract Nft is ERC721 {
    using Counters for Counters.Counter;
    Counters.Counter private _tokenIdCounter;
    
    bool private packed = true;
    uint256 public maxSupply = 5;
    address Admin = address(0);
    string private packedURI = "ipfs://QmUkfvu8iWhsxRtAteZGLSDvDZnUQSvgrkxHD34TPPL3on/";
    string private unpackedURI = "ipfs://QmX3bSTy4ankSQPBV1u516GCHN7eXHj3QVpgx3KymQwLHf/";

    constructor() ERC721("KTC", "KC") {
        Admin = msg.sender;
    }
    
    function safeMint(address to) internal {
        uint256 tokenId = _tokenIdCounter.current();
        _tokenIdCounter.increment();
        _safeMint(to, tokenId);
    }

    function mintNFT(uint256 _amount) public{
        require(_tokenIdCounter.current()+_amount <= maxSupply, "sold out");
        for(uint256 i=0;i<_amount;i++){
            safeMint(msg.sender);
        }

    }

    //盲盒開關
    function switchRevealed(bool _status)public {
        require(msg.sender == Admin,"admin only");
        packed = _status;   
    }

    function tokenURI(uint256 tokenId) public view virtual override returns (string memory) {
        //看開盒了沒
        if (packed) {
            return packedURI;
        }
        return string(abi.encodePacked(unpackedURI, Strings.toString(tokenId), ".json"));
    }
}
```

進階2
[404error]