---
# System prepended metadata

title: 【05/28 Learning Meeting Workshop】

---


# 【05/28 Learning Meeting Workshop】

主題：NFT工作坊#1 - 手把手發NFT
講者：@Simon Chu
時間：05/28 (六) 11:00 ~ 15:00
地點：台北市光復南路102號11樓
(@perpetual protocol)

----
## ==masteryNFT.SOL程式碼說明==

----

### Units
* uint(uint256)

   * 1 eth == 1 wei;
   * 1 eth == 1e9 gwei; //10 ** 9 
   * 1 eth == 1e18 ether; //10 ** 18

* [Gas Used/Gas limit](https://ithelp.ithome.com.tw/articles/10217483)
  * 若想提早處理交易,可提高Gas price,礦工會依照Gas price高低先後處理
  * Gas Used>>Gas limit && 如果你不想在 Gas 上花費太多，降低 Gas Limit 不會有多大幫助。您必須包含足夠的 Gas 來涵蓋您使用的計算資源，否則您的交易將因 Out of Gas 而失敗
    * 一般標準交易的 Gas Limit 為21000 
* 交易手續費(Tx Fee)
   * 交易手續費（Tx Fee）<= Gas Limit * Gas Price(先以Gas limit計算,若未超過Gas limit則以Gas Used計算)
   * Eth交易手續費則為 21,000(Gas Limit)*20 Gwei(Gas Price)=420,000 Gwei，因此，交易手續費 Tx Fee=420,000 *0.000000001Eth=0.00042Eth

* 參考資料:
   * [Gas](https://zombit.info/%E5%88%B0%E5%BA%95%E4%BB%80%E9%BA%BC%E6%98%AFgas%E3%80%81gas-limit%E3%80%81gas-price%EF%BC%9F/)

----
### payable
* 函式可以收取以太幣
* 程式碼說明--->
```solidity=
function mintMysteryNFT(uint256 tokenQuantity) public payable {
//mintMysteryNFT() 這個function可以交易eth
        require(
            totalSupply() + tokenQuantity <= MAX_SUPPLY,
            "Sale would exceed max supply"
        );
        require(_isSaleActive, "Sale must be active to mint Mystery NFT");
        require(
            balanceOf(msg.sender) + tokenQuantity <= maxBalance,
            "Sale would exceed max balance"
        );
        require(
            tokenQuantity * mintPrice <= msg.value,
            "Not enough ether sent"
        );
        require(tokenQuantity <= maxMint, "Can only mint 1 tokens at a time");

        _mintMysteryNFT(tokenQuantity);
    }
```
---
## Gas 的錯誤處理
#### require
* require 的用途就是進行條件判斷，如果條件不成立，會將剩餘的 Gas 歸還，並將合約狀態回復，也是最常使用到的錯誤處理方式。
* 概念有點類似下方的 JavaScript 範例，針對輸入值做驗證，不成立就return來終止運行：
    ![](https://i.imgur.com/0qpXpqe.png)
* require 範例，寫在修飾詞中，並判斷是否為合約部署者：
![](https://i.imgur.com/jQuOsBx.png)
* [require revert assert 三大Gas錯誤處理語法 和 gas/ico 關聯](https://medium.com/taipei-ethereum-meetup/%E6%AF%94%E8%BC%83-require-assert-%E5%92%8C-revert-%E5%8F%8A%E5%85%B6%E9%81%8B%E4%BD%9C%E6%96%B9%E5%BC%8F-30c24d534ce4)
* 程式碼驗證1--->
```solidity=
 function mintMysteryNFT(uint256 tokenQuantity) public payable {
        require(
            totalSupply() + tokenQuantity <= MAX_SUPPLY,
            "Sale would exceed max supply"
        );
        require(_isSaleActive, "Sale must be active to mint Mystery NFT");//若不能sale,則秀出"Sale must be active to mint Mystery NFT"這段log
        require(
            balanceOf(msg.sender) + tokenQuantity <= maxBalance,
            "Sale would exceed max balance"
        );
        require(
            tokenQuantity * mintPrice <= msg.value,
            "Not enough ether sent"
        );
        require(tokenQuantity <= maxMint, "Can only mint 1 tokens at a time");

        _mintMysteryNFT(tokenQuantity);
    }

```
----
### 與合約互動者身份

#### onlyOwner(migrate合約到鏈上者)
![](https://i.imgur.com/aTJEG0E.png =x20) 
 
```solidity=
    function flipSaleActive() public onlyOwner {
        _isSaleActive = !_isSaleActive;
    }
```
----

#### msg.sender(連錢包與呼叫合約與合約互動者)
```solidity=
 function mintMysteryNFT(uint256 tokenQuantity) public payable {
        require(
            totalSupply() + tokenQuantity <= MAX_SUPPLY,
            "Sale would exceed max supply"
        );
        require(_isSaleActive, "Sale must be active to mint Mystery NFT");
        require(
            balanceOf(msg.sender) + tokenQuantity <= maxBalance,
            "Sale would exceed max balance"
        );// balanceOf(msg.sender)--->連錢包與合約互動者的錢包目前有多少錢
        require(
            tokenQuantity * mintPrice <= msg.value,
            "Not enough ether sent"
        );
        require(tokenQuantity <= maxMint, "Can only mint 1 tokens at a time");

        _mintMysteryNFT(tokenQuantity);
    }

```
----
### 修飾字
* 比較
    * static-->母程式可使用 子程式不可 
    * public-->母程式可 子程式可       其他皆可
    * private-->母程式可 子程式不可    其他不可
    * internal-->母程式可 子程式可     其他不可
      * 這與多數物件導向語言的 protected 相似，可以在合約內部使用，包含了繼承的合約，但無法由非此合約的外部函式呼叫 
    * external-->母程式不可 子程式不可  其他可
      * 要在參數帶上 calldata 
      * 由於只開放給外部使用，比起同時要開放給內部使用的 public 還要==省 Gas==。 
    ![](https://i.imgur.com/9xnZBXb.png)

* 參考資料--->
    * [Solidity函数view,pure,constant的用法](https://www.programminghunter.com/article/7171459237/)
    * [Solidity修飾詞 view,pure,payable](https://ithelp.ithome.com.tw/articles/10219510)
    
* internal 程式碼驗證--->
 ```solidity=
  
    function _mintMysteryNFT(uint256 tokenQuantity) internal {
        for (uint256 i = 0; i < tokenQuantity; i++) {
            uint256 mintIndex = totalSupply();
            if (totalSupply() < MAX_SUPPLY) {
                _safeMint(msg.sender, mintIndex);
            }
        }
    }
```
##### view

* view --->gas optimization(不改變鏈上的值 ---> View functions doesn't cost gas 只讀data)
   * view 會告訴 web3.js 對應的 function 要query自己的 local ethereum node,不用在區塊鏈上每個node上run產生transaction
     * external view --->gas optimization(read-only)  
     * internal view --->若internal有個非view的function call view function,還是會在鏈上每個node上跑--->依舊會產生gas

``` solidity=

  function _baseURI() internal view virtual override returns (string memory) {
        return baseURI;
    }

```

``` solidity=

 contract mysteryNFT is ERC721Enumerable, Ownable {
    using Strings for uint256;
 }
```
#### function
##### constructor()
* 第一次執行且只執行一次的function
``` solidity=
    constructor(string memory initBaseURI, string memory initNotRevealedUri)
        ERC721("Mystery NFT", "MN")
    {
        setBaseURI(initBaseURI);
        setNotRevealedURI(initNotRevealedUri);
    }

```
##### mapping

![](https://i.imgur.com/BJCw7U3.png)

``` solidity=
mapping(uint256 => string) private _tokenURIs;
``` 
#### 繼承
![](https://i.imgur.com/ubbX9ym.png)


* is (A is B)----> A合約繼承B合約

