# 第二週作業
## 基本題
### 1. 部署一個自定義的 ERC20 Token 在 Goerli 鏈上
* 開源合約並提交合約地址
合約地址URL:https://goerli.etherscan.io/address/0xd0fa61a4c4fa1057b9104b390fa2221b9eebb561
Token Name : RyanToken
Token Symbol:RT
* 實作 mint 和 burn 功能
```solidity=
/**
*Submitted for verification at Etherscan.io on 2023-03-05
*/
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.18;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
contract MyToken is ERC20 {
uint8 private _decimals; //Token最小單位
//初始化
constructor(
string memory name_,
string memory symbol_,
uint8 decimals_
) ERC20(name_, symbol_) {
_decimals = decimals_;
}
//取得Token最小單位
function decimals() public view override returns (uint8) {
return _decimals;
}
//實作 mint 鑄造
function mint(address account, uint256 amount) external {
super._mint(account, amount);
}
//實作 burn 燒毀
function burn(address account, uint256 amount) external {
super._burn(account, amount);
}
}
```
* 轉移 100 個 Token 到以下地址 0x6e24f0fF0337edf4af9c67bFf22C402302fc94D3
Transaction Details URL:https://goerli.etherscan.io/tx/0xb25311cac96754ea2bf158073bd09938756bf8af67e9d2a9c3fbaae66074cd20

* 轉移 Token 給所有組員

### 2.部署一個 ERC721 NFT 合約,同時擁有付費 mint 功能
* 開源合約並提交合約地址
* 使用 OpenSea Metadata 標準,並提交 OpenSea頁面
* 將檔案上傳至 ipfs
* 擁有白名單機制並且將組員加入白名單
* 設定總量上限
* 加入至少一種自定義功能,例如:荷蘭拍、盲盒、返佣、融合…
合約地址:https://goerli.etherscan.io/address/0xe5d30e6e2f4a20d243e971dc31055c664203fd46#code
```solidity=
/**
*Submitted for verification at Etherscan.io on 2023-03-05
*/
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.18;
import "@openzeppelin/contracts/token/ERC721/ERC721.sol";
import "@openzeppelin/contracts/utils/Strings.sol";
import "@openzeppelin/contracts/utils/cryptography/MerkleProof.sol";
import "@openzeppelin/contracts/access/Ownable.sol";
contract MyNFT is ERC721, Ownable {
bytes32 private root; //root hash
uint256 private tokenId = 0; //NFT Token ID
bool private isBoxOpened = false; //盲盒控制打開與否
string private baseUri; //圖片uri
string private unrevealedURI; //盲盒圖片uri
uint256 private cost; // Mint 價格
uint256 private maxCount; //最大鑄造數量
constructor(
string memory _name,
string memory _symbol,
string memory _baseUri,
string memory _unrevealedURI,
uint256 _cost,
uint256 _maxCount
) ERC721(_name, _symbol) {
baseUri = _baseUri;
unrevealedURI = _unrevealedURI;
cost = _cost;
maxCount = _maxCount;
}
//驗證proof
modifier verifyProof(bytes32[] memory proof) {
require(
MerkleProof.verify(
proof,
root,
keccak256(abi.encodePacked(msg.sender))
),
"Invalid proof"
);
_;
}
//驗證是否超過最大鑄造數量
modifier verifyMintCount() {
require(tokenId < maxCount, "Can't mint more");
_;
}
//設定鑄造價格
function setCost(uint256 _cost) external onlyOwner {
cost = _cost;
}
//取得鑄造價格
function getCost() external view returns (uint256) {
return cost;
}
//設定鑄造最大數量
function setMaxCount(uint256 _maxCount) external onlyOwner {
maxCount = _maxCount;
}
//取得鑄造最大數量
function getMaxCount() external view returns (uint256) {
return maxCount;
}
//取得base Uri
function _baseURI() internal view override returns (string memory) {
return isBoxOpened ? baseUri : unrevealedURI;
}
//打開盲盒
function openBox() external onlyOwner {
isBoxOpened = true;
}
//關閉盲盒
function closeBox() external onlyOwner {
isBoxOpened = false;
}
//取得token Uri
function tokenURI(uint256 _tokenId)
public
view
override
returns (string memory)
{
_requireMinted(_tokenId);
string memory baseURI = _baseURI();
return
bytes(baseURI).length > 0
? string(
abi.encodePacked(
baseURI,
Strings.toString(_tokenId),
".json"
)
)
: "";
}
//鑄造給一般人,但要收錢
function mint() external payable verifyMintCount {
require(msg.value == cost, "Not enough ether sent");
tokenId++;
_safeMint(msg.sender, tokenId);
}
//免費鑄造給白名單
function whitelistMint(bytes32[] calldata _proof)
external
verifyProof(_proof)
verifyMintCount
{
tokenId++;
_safeMint(msg.sender, tokenId);
}
//設定Root
function setRoot(bytes32 _root) external onlyOwner {
root = _root;
}
//提取錢給合約擁有者
function withdraw() public onlyOwner {
require(address(this).balance > 0, "Balance is 0");
payable(owner()).transfer(address(this).balance);
}
}
```
盲盒

解盲盒

## 進階題
### 1.研究 ERC721A 合約
* 寫下 ERC721A 及 ERC721 差異
ERC-721 是一種合約協議的代號,他是目前 NFT 的基礎協議,用作發行擁有不同代號的有限量或無限量代幣(Token)。而因為他的唯一編碼方式以及使用 Metadata 來提供更多資訊的特色,因此被廣泛應用在各領域。
ERC-721A 是由 ERC-721 的改良,因原本使用的 ERC-721 其語法的因素,導致在鑄造(Mint)多個 NFT 時會產生大量的手續費(Gas Fee),而 ERC-721A 大幅度地減少了手續費的消耗,因此在目前的 NFT 世界被廣泛使用。
* 實際部署 ERC721A 合約比較所花費的 gas fee
ERC721A

ERC721

### 2.Contract Factory 使用合約來部署多個 ERC20 或 ERC721 合約
* 開源合約並提交合約地址
* 每個產出的子合約可以設定不同的參數,name, symbol...
合約地址 : https://goerli.etherscan.io/address/0xccb00d27ad3e98c492a4ee66c9bef994797fffcb
```solidity=
/**
*Submitted for verification at Etherscan.io on 2023-03-05
*/
// SPDX-License-Identifier: GPL-3.0
pragma solidity 0.8.18;
import "./ERC20.sol";
import "./ERC721A.sol";
contract Factory {
address[] public erc20ContractArr;
address[] public erc721ContractArr;
//建立ERC20合約
function createERC20(
string memory name_,
string memory symbol_,
uint8 decimals_
) external payable {
MyToken token = new MyToken(name_, symbol_, decimals_);
erc20ContractArr.push(address(token));
}
function getAllERC20Contract() external view returns (address[] memory) {
return erc20ContractArr;
}
function createERC721A(
string memory name_,
string memory symbol_,
string memory baseUri_,
string memory unrevealedURI_,
uint256 cost_,
uint256 maxCount_
) external payable {
MyNFT nft = new MyNFT(name_,symbol_,baseUri_,unrevealedURI_,cost_,maxCount_);
erc721ContractArr.push(address(nft));
}
function getAllERC721AContract() external view returns (address[] memory) {
return erc721ContractArr;
}
}
```
###### tags: `Solidity 工程師實戰營第 5 期`