# 基本題一 部署 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


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

# 進階題一 ERC721 自行添加功能優化,並部署至 Goerli 測試鏈
部署方式:Remix 部署方式改選使用 Injected Web3(Metamask),部署至 Goerli 測試鏈
1.選擇一個測試用的錢包(請新開錢包盡量不要用原本自己在使用的)
0xC3096eACEd76Bf8140920Cfc532191a818FAcB45
2.Metamask 中選擇 Goerli 網路進行部署

3.將必要完成作業的合約部署至 Goerli 測試鏈

4.進行呼叫,並且傳送 1 個 ERC721 Token 至(0x977e01DDd064e404227eea9E30a5a36ABFDeF93D)地址

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鏈上


2.傳送 499TOKEN 地址到0x977e01DDd064e404227eea9E30a5a36ABFDeF93D

``` 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];
}
}
```