第一週作業 [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]