# 基本題一 部署 ERC721 1.我找到 mytoken721.sol 2.我修改了 name(Charles) 跟 symbol(CS) ``` solidity // contracts/GameItem.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.0; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract CharlesNFT is ERC721 { constructor() ERC721("Charles", "CS") {} function mint(uint256 tokenId) public { _safeMint(msg.sender, tokenId); } } ``` 3.調整 mint 數量,確定能夠拿到 2 個 ERC721 Token ![](https://i.imgur.com/mUA10s6.png) ![](https://i.imgur.com/B6P1wPX.png) 4.添加一個功能並且傳送 n 個數量 ERC721 Token 給二號(ex. 0x977e01DDd064e404227eea9E30a5a36ABFDeF93D)地址 功能內容:添加一個function 可以轉 token 數量給某個錢包,例:購買Token、mintToken # 基本題二 部署 ERC20 部署方式:Remix 部署方式選擇 JavaScript VM(Lodon) 1.找到檔案中的 myToken.sol 2.嘗試修改 name(CharlesToken) 與 symbol(CS) 3 調整 mint 數量,確定能夠拿到 10,000 個 ERC20 Token ``` solidity // contracts/GLDToken.sol // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; contract CharlesToken is ERC20 { // 初始設好 10000 ERC20 TOKEN 在程式碼上 uint256 public initialSupply = 10000; // 嘗試修改 name 跟 symbol constructor() ERC20("CharlesToken", "CS") { _mint(msg.sender, initialSupply); } // 添加一個功能並且傳送 n 個數量個 ERC20 TOKEN // 給二號(ex. 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2)地址 function buyCoin(address _to, uint256 _amount) public { _mint(_to, _amount); } } ``` 4.添加一個功能並且傳送 n 個數量 ERC20 Token 給二號(ex. 0xAb8483F64d9C6d1EcF9b849Ae677dD3315835cb2)地址 功能內容:添加一個function 可以轉 token 數量給某個錢包,例:購買Token、mintToken ![](https://i.imgur.com/00ksnax.png) # 進階題一 ERC721 自行添加功能優化,並部署至 Goerli 測試鏈 部署方式:Remix 部署方式改選使用 Injected Web3(Metamask),部署至 Goerli 測試鏈 1.選擇一個測試用的錢包(請新開錢包盡量不要用原本自己在使用的) 0xC3096eACEd76Bf8140920Cfc532191a818FAcB45 2.Metamask 中選擇 Goerli 網路進行部署 ![](https://i.imgur.com/v8bzoP9.png) 3.將必要完成作業的合約部署至 Goerli 測試鏈 ![](https://i.imgur.com/kkWZWBF.png) 4.進行呼叫,並且傳送 1 個 ERC721 Token 至(0x977e01DDd064e404227eea9E30a5a36ABFDeF93D)地址 ![](https://i.imgur.com/lYYXd3K.png) 5.自行嘗試是否能新增額外功能,使用最直覺撰寫方式即可,不需要針對燃料費用或程式內容進行優化 ``` solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; import "@openzeppelin/contracts/token/ERC721/ERC721.sol"; import "@openzeppelin/contracts/utils/Counters.sol"; contract HardHazelERC721 is ERC721 { /* 將必要完成作業的合約部署至 Goerli 測試鏈 進行呼叫,並且傳送 1 個 ERC721 Token 至(0x977e01DDd064e404227eea9E30a5a36ABFDeF93D)地址 自行嘗試是否能新增額外功能,使用最直覺撰寫方式即可,不需要針對燃料費用或程式內容進行優化 */ using Counters for Counters.Counter; Counters.Counter private _tokenIds; // 定義 NFT 價格 uint256 public mintPrice = 0.001 ether; address private owner; // TODO: name, symbol 改成喜歡的名字 constructor() ERC721("CharlesNFT", "CS") { owner = msg.sender; } event GiveChange(address _to, uint256 _amount, uint256 balance); // emit GiveChange(_to, _amount, balance); // 紀錄退款事件 event Refund(uint256 _to, uint256 mintPrice); // 紀錄提款事件 event Withdraw(address _to, uint256 _amount); // TODO: 買 NFT function mintMultiple(address _to, uint256 _amount) payable public { // 驗證:金額 require(msg.value >= (_amount * mintPrice), "Not enough ether!!"); // 驗證:至少 Mint 一個 NFT require(_amount > 0, "Must mint at lease 1 NFT !!"); // 1) 鑄造 mint _amount 個 NFT (forloop) for (uint256 i = 0; i < _amount; i++) { uint256 tokenId = _tokenIds.current(); safeMint(_to, tokenId); _tokenIds.increment(); } // 2) 購買時,將多餘的金額進行退款 uint256 balance = msg.value - (mintPrice * _amount); // 3) 若餘額超過 0,將進行退款 實作 if (balance > 0) { payable(_to).transfer(balance); // 事件紀錄 GiveChange emit GiveChange(_to, _amount, balance); } } function safeMint(address _to, uint256 _amount) private { _safeMint(_to, _amount); } // TODO: 轉傳多個 NFT // _tokens = [1,2,5] function transferNft(uint256[] memory _tokens, address _to) public { for (uint256 i = 0; i < _tokens.length; i++) { safeTransferFrom(msg.sender, _to, _tokens[i]); // 1, 2, 5 } } // 銷毀 Token function burn(uint256 _tokenId) public { _burn(_tokenId); } // TODO: 退款 function refund(uint256 _tokenId) external { // 1) 銷毀指定的 NFT 編號 burn(_tokenId); // 2) 退款 mintPrice payable(msg.sender).transfer(mintPrice); // event 紀錄退款 emit Refund(_tokenId, mintPrice); } // TODO: 項目方領錢 function withdraw () external { // 1) 確認他是不是 owner,只能 owner 才可以領錢 require(owner == msg.sender, "Only OWNER !!!!!"); // 2) 取得合約的餘額 uint256 amount = address(this).balance; // 3) 轉錢出去給 owner payable(msg.sender).transfer(amount); // 4) 紀錄 Withdraw emit Withdraw(msg.sender, amount); } } ``` # 進階題二-不引用原有標準 ERC20 檔案,完成部署 ERC20 合約,並將 Token 傳送至指定錢包地址 部署方式:Remix 部署方式改選使用 Injected Web3(Metamask),部署至 Goerli 測試鏈 此作業目的為可更理解 Token 實際是什麼 *不透過原有的標準 ERC20 檔案,思考如何用學習到的語法自行撰寫一個可以自訂 name, symbol *該智能合約必須完成 totalSupply, balanceOf 與 transfer 的方法使用。 *傳送完成的 ERC20 Token 至(0x977e01DDd064e404227eea9E30a5a36ABFDeF93D)地址 *貼 程式碼 Gist 連結或是 Verify 開源的智能合約 & 交易成功的截圖 1.創建 CharlesCOIN 合約在 Goerli鏈上 ![](https://i.imgur.com/o7dKwMB.png) ![](https://i.imgur.com/G6MbsdU.png) 2.傳送 499TOKEN 地址到0x977e01DDd064e404227eea9E30a5a36ABFDeF93D ![](https://i.imgur.com/zqUIbCL.png) ``` solidity // SPDX-License-Identifier: MIT pragma solidity ^0.8.4; // TODO: 改合約名稱 contract CharlesCoin { // ERC20 Token 幣的名稱、符號 string public name; string public symbol; // 總供應量 uint256 public totalSupply; // 查詢餘額 mapping(address => uint256) private accountBalances; address owner; // 可以自訂 name, symbol constructor(string memory _name, string memory _symbol, uint256 _initialSupply){ name = _name; symbol = _symbol; totalSupply += _initialSupply; owner = msg.sender; accountBalances[owner] = _initialSupply; } event Transfer(address _from, address _to, uint256 _amount); // TODO: Transfer 事件 function transfer(address _to, uint256 _amount) public { // 檢查餘額夠不夠 require(accountBalances[msg.sender] >= _amount, "Yout don't have enough balance!"); // msg.sender 轉帳者 // _to 接收者 // TODO: 轉錢給別人 accountBalances[msg.sender] -= _amount; accountBalances[_to] += _amount; // 事件紀錄 Transfer emit Transfer(msg.sender, _to, _amount); } // TODO: 查詢某人錢包的餘額 function balanceOf(address _address) public view returns (uint256){ return accountBalances[_address]; } } ```