第一週作業 --- 基礎: 1. 發行總量100億顆、位數18的代幣! 2. 發行總量10張的SBT。 3. 開發猜數字的合約(一個人設定,大家猜,有人猜中,就結束不讓其他人猜了) 進階: 1. 發行有盲盒機制的NFT! 2. 理解ERC721A與ERC721的差異,並實作ERC721A合約,實際比較差異! --- ## 基礎​1 ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract Basic0106_1 is ERC20 { constructor() ERC20("Basic One", "BO") { _mint(msg.sender, 10 ** 10); } } ``` --- ## ***# Refactor*** ```solidity // SPDX-License-Identifier: GPL-3.0 pragma solidity ^0.8.4; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract Basic0106_1 is ERC20 { uint8 public decimal; uint256 public maxSupply; constructor( string memory _name, string memory _symbol, uint8 _decimal, uint256 _maxSupply ) ERC20(_name, _symbol){ decimal = _decimal; maxSupply = _maxSupply; } function decimals() public view override returns (uint8) { return decimal; } function mint (uint256 amount) external { require(amount + totalSupply() <= maxSupply, "over max supply."); _mint(msg.sender, amount); } } ``` ## 基礎​2 ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; contract Advanced0106_2 is ERC721 { uint256 public availableQuantity = 10; constructor() ERC721("SBT Token", "SBT") {} function mint(address to, uint256 quantity) external payable { require(0 <= availableQuantity-quantity , "Not available quantity"); availableQuantity = availableQuantity-quantity; _mint(to, quantity);//使用錯誤 } function transferFrom(address from, address to, uint256 tokenId) public virtual override { require( from == address(0) || to == address(0), "You cannot transfer this token" ); } } ``` --- ## ***# Refactor*** ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract Advanced0106_2 is ERC721 { using Counters for Counters.Counter; Counters.Counter private _tokenIds; uint256 public maxSupply; modifier avaialbeMint(uint256 amount) { require(amount + _tokenIds.current() <= maxSupply, "over max supply."); _; } constructor( string memory _name, string memory _symbol, uint256 _maxSupply) ERC721(_name, _symbol){ maxSupply = _maxSupply; } function mint (uint256 amount) external avaialbeMint(amount){ for(uint256 i=0; i < amount ; i++){ uint256 tokenId = _tokenIds.current(); _mint(msg.sender, tokenId); _tokenIds.increment(); } } function transferFrom(address from, address to, uint256 tokenId) public override { require( from == address(0) || to == address(0), "You cannot transfer this token" ); } } ``` ## 基礎​3 ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; contract Basic0106_3 { event GuessResult(string message); address public owner; uint256 public targetNumber; bool public result; constructor() { owner = msg.sender; } modifier onlyOwner { require(msg.sender == owner); _; } function setTargetNumber(uint256 _newTargetNumber) external onlyOwner{ targetNumber = _newTargetNumber; result = false; } function guess(uint256 guessNumber) external { require(result == false, "Not started yet!"); if(targetNumber == guessNumber) { emit GuessResult("Bingo!"); result = true; } else { emit GuessResult("Not correct number!"); } } } ``` --- ## ***# Refactor*** ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; contract Basic0106_3 { event GuessResult(string message); mapping (address => bool) public guessRecord; struct GameInfo{ uint256 minValue; uint256 maxValue; uint256 target; address winner; bool result; } GameInfo gameInfo; constructor ( uint256 _min, uint256 _max, uint256 _target ){ gameInfo = GameInfo( { minValue: _min, maxValue: _max, target: _target, winner: msg.sender, result: false } ); } modifier onlyWinner { require(gameInfo.winner == msg.sender); _; } function setTargetNumber(uint256 _newTargetNumber) external onlyWinner { require(gameInfo.result == true, "The game is still going!"); gameInfo.target = _newTargetNumber; gameInfo.result = false; } function guess(uint256 guessNumber) external { require(gameInfo.result == false, "The game hasn't started yet!"); require(gameInfo.winner != msg.sender, "You are the host!"); require(!guessRecord[msg.sender], "Already guessed."); guessRecord[msg.sender] = true; if(gameInfo.target == guessNumber) { gameInfo.winner = msg.sender; gameInfo.result = true; emit GuessResult("Bingo!"); } else { emit GuessResult("Not correct number!"); } } function getWinner () external view returns (address){ return gameInfo.winner; } } ``` ## 進階​1 ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract Advanced0106_1 is ERC721 { using Counters for Counters.Counter; Counters.Counter private _tokenIds; address public owner; string url = ""; constructor() ERC721("BlindBox", "BOX") { owner = msg.sender; } modifier onlyOwner { require(msg.sender == owner); _; } function _baseURI() internal view override returns (string memory) { return url; } function setUrl(string calldata _url) public onlyOwner { url = _url; } function mint(address to) public { uint256 tokenId = _tokenIds.current(); _mint(to, tokenId); _tokenIds.increment(); } } ``` --- ## ***# Refactor*** ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract Advanced0106_1 is ERC721 { using Counters for Counters.Counter; Counters.Counter private _tokenIds; using Strings for uint256; address owner; uint256 public maxSupply = 10; bool private isOpened = false; modifier onlyOwner{ require(msg.sender == owner); _; } constructor (string memory _name, string memory _symbol) ERC721(_name, _symbol){ owner = msg.sender; } function openBlindBox() external onlyOwner{ isOpened = true; } function _baseURI() internal pure override returns (string memory) { return "ipfs://QmXxZBg4RnGxC2dDxfUSAmxgGooHsoncPQgCLiNw8kj3Ls/"; } function tokenURI(uint256 tokenId) public view override returns (string memory) { if (!isOpened){ return _baseURI(); } return string(abi.encodePacked("ipfs://QmWQcaFFCm9ofyVN2ZwGbTGopLEbQ6QSc2Xn1C7ekKAYDF/", tokenId.toString(), ".json")); } function mint (uint256 amount) external{ require(amount + _tokenIds.current() < maxSupply, "Over max supply."); for(uint256 i = 0; i < amount ; i++){ uint256 tokenId = _tokenIds.current(); _mint(msg.sender, tokenId); _tokenIds.increment(); } } } ``` ## 進階​2 ```solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.13; import "erc721a/contracts/ERC721A.sol"; contract Advanced0106_2 is ERC721A { constructor() ERC721A("ERC721A Token", "721AT") {} function mint(address to, uint256 quantity) external payable { _mint(to, quantity); } function transferFromByERC721A( address from, address to, uint256 tokenId ) external payable { transferFrom(from, to, tokenId); } } import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract Advanced0106_2_2 is ERC721 { using Counters for Counters.Counter; Counters.Counter private _tokenIds; constructor() ERC721("ERC721 Token", "721T") {} function mint(address to) public { uint256 tokenId = _tokenIds.current(); _mint(to, tokenId); _tokenIds.increment(); } function batchMint(address to, uint256 quantity) external payable { for (uint i = 0; i < quantity; i++) { mint(to); } } function transferFromByERC721( address from, address to, uint256 tokenId ) external payable { _transfer(from, to, tokenId); } } ``` --- ## ***# Refactor*** ```solidity ```