---
# System prepended metadata

title: 智能合約solidity開發基礎實戰教學文件
tags: [區塊鏈, Dapp]

---

# 智能合約solidity開發基礎實戰教學文件

[TOC]

###### tags: `Dapp` `區塊鏈`
## 智能合約硬核

:::spoiler

* [深入智能合約 ABI](https://medium.com/taipei-ethereum-meetup/ethereum-%E6%99%BA%E8%83%BD%E5%90%88%E7%B4%84%E9%96%8B%E7%99%BC%E7%AD%86%E8%A8%98-%E6%B7%B1%E5%85%A5%E6%99%BA%E8%83%BD%E5%90%88%E7%B4%84-abi-268ececb70ae)
![](https://i.imgur.com/sozOu2H.png =x300)
* [Ethereum 開發整體脈絡](https://medium.com/fukuball-murmur/ethereum-%E9%96%8B%E7%99%BC%E7%AD%86%E8%A8%98-2-1-ethereum-%E9%96%8B%E7%99%BC%E5%B7%A5%E5%85%B7%E6%A6%82%E5%BF%B5-834b611d11ac)
![](https://i.imgur.com/v1X19wH.png =x300)
* [以太坊區塊鏈智能合約基礎介紹](https://medium.com/@daniel.mars622/%E4%BB%A5%E5%A4%AA%E5%9D%8A%E5%8D%80%E5%A1%8A%E9%8F%88%E6%99%BA%E8%83%BD%E5%90%88%E7%B4%84%E5%9F%BA%E7%A4%8E%E4%BB%8B%E7%B4%B9-b7317347a612)


[RESTful API](https://ithelp.ithome.com.tw/articles/10251579?sc=pt)
[RESTful API與MVC名詞介紹](https://ithelp.ithome.com.tw/articles/10191925)
[Blocto 錢包sdk](https://docs.blocto.app/blocto-sdk/overview)


[程式語法參考資料](https://ithelp.ithome.com.tw/articles/10203645)
[solidity遊戲教學](https://cryptozombies.io/en/lesson/2/chapter/3)
---
![](https://i.imgur.com/N5MGjnn.png)
![](https://i.imgur.com/xeHUt9w.png)
![](https://i.imgur.com/OX0sgvV.png)

:::


:::spoiler

### global variables

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

:::

---

:::spoiler


### 修飾字
* 繼承了public(function/variable)部分

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

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

![](https://i.imgur.com/D2sOuPj.png =x200)

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

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

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


----


#### 修飾字

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

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


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

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


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


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


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


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


* 結論--->
    * 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)
    
* 程式碼驗證--->
 ```solidity=
    pragma solidity  ^0.5.7;

contract  test{
　　//属性
    uint  p;
    address _owner;
    
　　//构造函数
    function  car()  public{
        p=100;
        _owner=msg.sender;
    }
    
    //public的话可以外部调用
    function  get()  public  returns(uint){
        return  p;
    }
    //view，当不返回存粹的值或者是变量的时候，比如msg.sender使用
    function  get1()  view public returns(address){
        return  msg.sender
    }
    
    //pure，返回存粹的值使用,123,'hello'
    function  get2()  pure public  returns(uint){
        return  123
        
    }
    //constant, 返回带有状态变量的值,_owner
    function  get3()  constant public  returns(address){
        return  _owner
    }
}  
 ```
:::
##### view

* view --->gas optimization(不改變區塊鏈上任何值 ---> View functions don'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=
pragma solidity >=0.5.0 <0.6.0;

import "./zombiefeeding.sol";

contract ZombieHelper is ZombieFeeding {

  modifier aboveLevel(uint _level, uint _zombieId) {
    require(zombies[_zombieId].level >= _level);
    _;
  }

  function changeName(uint _zombieId, string calldata _newName) external aboveLevel(2, _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    zombies[_zombieId].name = _newName;
  }

  function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    zombies[_zombieId].dna = _newDna;
  }

  function getZombiesByOwner(address _owner) external view  returns (uint[] memory) {

  }

}
```
##### pure
* 不寫也不讀data進來
##### payable
* 讓函式可以收以太幣
* 程式碼驗證1--->
```solidity=
contract OnlineStore {
  function buySomething() external payable {//有加payable的function才可以送錢過去
    // Check to make sure 0.001 ether was sent to the function call:
    require(msg.value == 0.001 ether);
     // msg.value是看有多少ether送到contract裡
    // If so, some logic to transfer the digital item to the caller of the function:
    transferThing(msg.sender);
  }
}
```
* 程式碼驗證2--->
```solidity=
pragma solidity >=0.5.0 <0.6.0;

import "./zombiefeeding.sol";

contract ZombieHelper is ZombieFeeding {

  uint levelUpFee = 0.001 ether;
  modifier aboveLevel(uint _level, uint _zombieId) {
    require(zombies[_zombieId].level >= _level);
    _;
  }

  function levelUp(uint _zombieId)external payable {
    require(msg.value == levelUpFee);
    zombies[_zombieId].level++;

  }

  function changeName(uint _zombieId, string calldata _newName) external aboveLevel(2, _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    zombies[_zombieId].name = _newName;
  }

  function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    zombies[_zombieId].dna = _newDna;
  }

  function getZombiesByOwner(address _owner) external view returns(uint[] memory) {
    uint[] memory result = new uint[](ownerZombieCount[_owner]);
    uint counter = 0;
    for (uint i = 0; i < zombies.length; i++) {
      if (zombieToOwner[i] == _owner) {
        result[counter] = i;
        counter++;
      }
    }
    return result;
  }

}
```
##### withdraws
* 當轉 ether 到 contract 後,ether會 store 在 contract 裡 且 trapped --->必須要加一個function 去 ==withdraw== trap 在 contract 裡面的ether
* 程式碼驗證--->
```solidity=
  contract GetPaid is Ownable {
  function withdraw() external onlyOwner {
    address payable _owner = address(uint160(owner()));
    _owner.transfer(address(this).balance);
    }// _owner (uint160)--->cast to "address payable" 
  //this--->當前的合約,可轉換為合約的地址
  //address(this).balance--->為當前contract中總ether
  //會將contract中總ether傳到_owner中
}
```
```solidity=
uint itemFee = 0.001 ether;//若msg.sender送過多錢,理論只要送itemfee就好
msg.sender.transfer(msg.value - itemFee);
//msg.value--->呼叫訊息的人先前總共錯誤打賞多少以太幣
//用transfer()函式去轉回(msg.value-itemFee)的錢

```
----
#### function
##### constructor()
* 第一次執行且只執行一次的function
##### modifier()
* 程式碼驗證--->
```solidity=
pragma solidity >=0.5.0 <0.6.0;

import "./zombiefeeding.sol";

contract ZombieHelper is ZombieFeeding {

  modifier aboveLevel(uint _level, uint _zombieId) {
    require(zombies[_zombieId].level >= _level);//檢查有無滿足
    _;//else
  }
  function changeName(uint _zombieId, string calldata _newName) external aboveLevel(2, _zombieId){
   //aboveLevel這個modifier,連帶寫在function之後
   //calldata類似external用法
    require(msg.sender == zombieToOwner[_zombieId]);
    zombies[_zombieId].name = _newName;
  
  }
  function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId){
    require(msg.sender == zombieToOwner[_zombieId]);//確定deploy smart contract的是_zombieId本人
    zombies[_zombieId].dna = _newDna;
    
  }

}
```
----
#### variables
* smart contract 上 variables 可以存在 
  * storage 
  * memory 

##### storage 
* variable which store permanently on the blockchain
* State variables (variables declared outside of functions) are by default storage and written permanently to the blockchain
* 類似hard disk 

##### memory
* variable that are temporary, and are erased between external function calls to your contract.
* variables declared inside functions are memory and will disappear when the function call ends
* 類似RAM
* 程式碼驗證 1--->
```solidity=
function getArray() external pure returns(uint[] memory) {
  // Instantiate a new array in memory with a length of 3
  uint[] memory values = new uint[](3);
  //array存在memory中為fixed size array

  // Put some values to it
  values[0] = 1;
  values[1] = 2;
  values[2] = 3;

  return values;
}
```
* 程式碼驗證 2--->
```solidity=
pragma solidity >=0.5.0 <0.6.0;

import "./zombiefeeding.sol";

contract ZombieHelper is ZombieFeeding {

  modifier aboveLevel(uint _level, uint _zombieId) {
    require(zombies[_zombieId].level >= _level);
    _;
  }

  function changeName(uint _zombieId, string calldata _newName) external aboveLevel(2, _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    zombies[_zombieId].name = _newName;
  }

  function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    zombies[_zombieId].dna = _newDna;
  }

  function getZombiesByOwner(address _owner) external view returns(uint[] memory) {
    
    uint[] memory result = new uint[](ownerZombieCount[_owner]);//這個result array大小為owner所擁有的zombie個數
    return result;

  }

}
```
```solidity=
pragma solidity >=0.5.0 <0.6.0;

import "./zombiefeeding.sol";

contract ZombieHelper is ZombieFeeding {

  modifier aboveLevel(uint _level, uint _zombieId) {
    require(zombies[_zombieId].level >= _level);
    _;
  }

  function changeName(uint _zombieId, string calldata _newName) external aboveLevel(2, _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    zombies[_zombieId].name = _newName;
  }

  function changeDna(uint _zombieId, uint _newDna) external aboveLevel(20, _zombieId) {
    require(msg.sender == zombieToOwner[_zombieId]);
    zombies[_zombieId].dna = _newDna;
  }

  function getZombiesByOwner(address _owner) external view returns(uint[] memory) {
    uint[] memory result = new uint[](ownerZombieCount[_owner]);//result[counter++]這個array
    uint counter=0;
    for(uint i=0;i<zombies.length;i++)
    {
      if(zombieToOwner[i]==_owner)
      { 
        result[counter]=i;//counter計算個數總共多少個zombie's ID==_owner , result這個array存"zombieToOwner[i]==_owner"的第幾個zombie index
        counter++;
      }
    }
    return result;
  }

}
```

###### code examples

```solidity=
contract SandwichFactory {
  struct Sandwich {
    string name;
    string status;
  }

  Sandwich[] sandwiches;

  function eatSandwich(uint _index) public {
    // Sandwich mySandwich = sandwiches[_index];

    // ^ Seems pretty straightforward, but solidity will give you a warning
    // telling you that you should explicitly declare `storage` or `memory` here.

    // So instead, you should declare with the `storage` keyword, like:
    Sandwich storage mySandwich = sandwiches[_index];
    // ...in which case `mySandwich` is a pointer to `sandwiches[_index]`
    // in storage, and...
    mySandwich.status = "Eaten!";
    // ...this will permanently change `sandwiches[_index]` on the blockchain.

    // If you just want a copy, you can use `memory`:
    Sandwich memory anotherSandwich = sandwiches[_index + 1];
    // ...in which case `anotherSandwich` will simply be a copy of the 
    // data in memory, and...
    anotherSandwich.status = "Eaten!";
    // ...will just modify the temporary variable and have no effect 
    // on `sandwiches[_index + 1]`. But you can do this:
    sandwiches[_index + 1] = anotherSandwich;
    // ...if you want to copy the changes back into blockchain storage.
  }
}
 ```
 * zombiefactory.sol
 ```solidity=
 pragma solidity >=0.5.0 <0.6.0;

contract ZombieFactory {

    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    mapping (uint => address) public zombieToOwner;
    mapping (address => uint) ownerZombieCount;

    function _createZombie(string memory _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        zombieToOwner[id] = msg.sender;
        ownerZombieCount[msg.sender]++;
        emit NewZombie(id, _name, _dna);
    }

    function _generateRandomDna(string memory _str) private view returns (uint) {
        uint rand = uint(keccak256(abi.encodePacked(_str)));
        return rand % dnaModulus;
    }

    function createRandomZombie(string memory _name) public {
        require(ownerZombieCount[msg.sender] == 0);
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}

 ```
* ZombieFeeding.sol

 ```solidity=
 pragma solidity >=0.5.0 <0.6.0;

import "./zombiefactory.sol";

contract ZombieFeeding is ZombieFactory {

 function feedAndMultiply(uint _zombieId,uint _targetDna)public
 {
   require(zombieToOwner[_zombieId]==msg.sender);//檢查為smart contract本人
   Zombie storage myZombie=zombies[_zombieId];

 }
}
 ```

----
* 2個paused不會collide(因為1個private/1個public)

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

----
#### Operators
![](https://i.imgur.com/tWew2g6.png)

----

#### 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/)


----
#### Struct 
* 一般來講uint size變小 不會省多少gas
* 但在struct裡 gas fee費用---> uint8 < uint16 < uint32
* "==Struct packing"== more tightly--->to save gas
##### example
```solidity=
struct NormalStruct {
  uint a;
  uint b;
  uint c;
}

struct MiniMe {
  uint32 a;
  uint32 b;
  uint c;
}

// `mini` will cost less gas than `normal` because of struct packing
NormalStruct normal = NormalStruct(10, 20, 30);
MiniMe mini = MiniMe(10, 20, 30); 

```
* 上面例子下面的struct total data size 也較少
   * uint<--->uint256 
* 上面例子下面的struct==cluster identical data type(uint size相同的綁在一起)==
   * ex: uint32 a uint32 b 寫在一起--->storage space更加減少
##### code examples

```solidity=
pragma solidity >=0.5.0 <0.6.0;

contract ZombieFactory {

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;//zombie struct存成叫zombies的array

    function _createZombie(string memory _name, uint _dna) private {
        zombies.push(Zombie(_name, _dna));//將新zombie push到Zombies後面
        
    }

    function _generateRandomDna(string memory _str) private view returns (uint) {
        uint rand = uint(keccak256(abi.encodePacked(_str)));//keccak256 hash algo can convert _str(string) inato rand(int)
        return rand % dnaModulus;//回傳16位數字的randomNum
    }

    function createRandomZombie(string memory _name) public {
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);//將新計算好的_name, randDna push到zombies struct後面
    }

}

```
```solidity=

pragma solidity >=0.5.0 <0.6.0;

import "./ownable.sol";

contract ZombieFactory is Ownable {

    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;
    uint cooldownTime = 1 days;

    struct Zombie {
        string name;
        uint dna;
        uint32 level;
        uint32 readyTime;
    }

    Zombie[] public zombies;

    mapping (uint => address) public zombieToOwner;
    mapping (address => uint) ownerZombieCount;

    function _createZombie(string memory _name, uint _dna) internal {
        uint id = zombies.push(Zombie(_name, _dna,1,uint32(now+cooldownTime))) - 1;// uint32(now_cooldownTime) is necessary because now returns a uint256 by default. So we need to explicitly convert it to a uint32.
        //zombies這個struct有4個參數分別傳入要一一對應 
        zombieToOwner[id] = msg.sender;
        ownerZombieCount[msg.sender]++;
        emit NewZombie(id, _name, _dna);
    }

    function _generateRandomDna(string memory _str) private view returns (uint) {
        uint rand = uint(keccak256(abi.encodePacked(_str)));
        return rand % dnaModulus;//回傳最大小於16位數
    }

    function createRandomZombie(string memory _name) public {
        require(ownerZombieCount[msg.sender] == 0);// makes sure each user can't own more than one zombie:
        uint randDna = _generateRandomDna(_name);
        randDna = randDna - randDna % 100;
        _createZombie(_name, randDna);
    }

}
```
##### Passing structs as arguments
```solidity=
 function _triggerCooldown(Zombie storage _zombie)internal{
   uint32 _zombie.readyTime=uint32(now + cooldownTime);

 }
 function _isReady(Zombie storage _zombie) internal view returns (bool) {//參數傳入struct _zombie
   return (_zombie.readyTime <= now);//return true or false
 }


```
```solidity=
struct Zombie {
      string name;
      uint dna;
      uint32 level;
      uint32 readyTime;
    }

    Zombie[] public zombies;
```
----

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

##### Msg.sender(就是 call address ==deploy智能合約的人==)
* the address of the person(smart contract) who called the function 
* 只要有人call function必定會有對應Msg.sender
```solidity=
pragma solidity >=0.5.0 <0.6.0;

contract ZombieFactory {

    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    mapping (uint => address) public zombieToOwner;
    mapping (address => uint) ownerZombieCount;

    function _createZombie(string memory _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        zombieToOwner[id]=msg.sender;
        ownerZombieCount[msg.sender]++;//call function的counter++
        emit NewZombie(id, _name, _dna);
    }

    function _generateRandomDna(string memory _str) private view returns (uint) {
        uint rand = uint(keccak256(abi.encodePacked(_str)));
        return rand % dnaModulus;
    }

    function createRandomZombie(string memory _name) public {
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}
```
* 用solidity寫智能合約常做狀態檢查
![](https://i.imgur.com/GqZG4Rt.png)

* 在 transferOwnership 的時候合約需要檢查交易發起人是否為 owner , 若不是則執行 throw。throw 會使整個交易終止，並讓合約狀態回到交易執行前的狀態，並取走“所有”交易手續費，及 msg.sender 會被抽走 gasLimit * gasPrice 這麼多 ether。
* 補充：若是一筆沒有遇到 throw 的正常交易, 則會被抽走 gasUsed * gasPrice 這麼多 ether。
* ICO 很常會也一些奇怪的投標規則。像是會限制投標時間，或是 gasPrice，或是個人投標上限等等。如果你沒有遵守這些規則，那你發出去的交易會失敗，還會被抽走所有的交易手續費，而通常都不少錢，因為很多投標需要大量的 gas，使 gasLimit 不能設太低。像我只是搞錯了投標時間，提早發出了投標交易，就被抽走這麼多手續費，實在是叫我情何以堪。所以需要不同的狀態檢查機制(require,assert,revert)，能夠分辨是簡單的錯誤，或是較嚴重的系統錯誤，兩者會顯示不同的錯誤，抽不同的手續費。

#### 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=
pragma solidity >=0.5.0 <0.6.0;

contract ZombieFactory {

    event NewZombie(uint zombieId, string name, uint dna);

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    mapping (uint => address) public zombieToOwner;
    mapping (address => uint) ownerZombieCount;

    function _createZombie(string memory _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;
        zombieToOwner[id] = msg.sender;
        ownerZombieCount[msg.sender]++;
        emit NewZombie(id, _name, _dna);
    }

    function _generateRandomDna(string memory _str) private view returns (uint) {
        uint rand = uint(keccak256(abi.encodePacked(_str)));
        return rand % dnaModulus;
    }

    function createRandomZombie(string memory _name) public {
        require(ownerZombieCount[msg.sender]==0);//make sure this function only gets executed one time per user, when they create their first zombie.
        //otherwise return error
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}
```
----
* 程式碼驗證2--->
```solidity=
function _triggerCooldown(Zombie storage _zombie) internal {
    _zombie.readyTime = uint32(now + cooldownTime);
  }

  function _isReady(Zombie storage _zombie) internal view returns (bool) {
      return (_zombie.readyTime <= now);
  }

  function feedAndMultiply(uint _zombieId, uint _targetDna, string memory _species) internal {
    require(msg.sender == zombieToOwner[_zombieId]);
    Zombie storage myZombie = zombies[_zombieId];
  
    require(_isReady(myZombie));//檢查myZombie是否為true 
    //是就結束
    

    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
    if (keccak256(abi.encodePacked(_species)) == keccak256(abi.encodePacked("kitty"))) {
      newDna = newDna - newDna % 100 + 99;
    }
    _createZombie("NoName", newDna);
   
    _triggerCooldown(myZombie);
  }
  ```
----
##### revert
* 跟 require 十分接近但沒有條件判斷，當呼叫時會歸還剩餘的 Gas 並回復合約狀態。
![](https://i.imgur.com/61RGaXd.png)


##### assert
* 跟 require 一樣做判斷，但當條件不成立時，會將 Gas 全數消耗，不會返回任何的 Gas ，並回復合約狀態。通常用來處理較嚴重的錯誤，如：程式 bug ，為最不該被經常使用到的錯誤處理。
![](https://i.imgur.com/Cu8H6vx.png)


----

#### event(事件)
* event name(宣告name這個事件)
* emit(呼叫name這個事件)--->fire name 這個事件

* ==event== ---> gas cost 比 storage 低
![](https://i.imgur.com/a2WfQk0.png)

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

[solidity events和web3.js合作達到監聽事件](https://me.tryblockchain.org/blockchain-solidity-event.html)


##### code examples
```solidity=
pragma solidity >=0.5.0 <0.6.0;

contract ZombieFactory {

    event NewZombie(uint zombieId, string name, uint dna);//宣告非同步事件

    uint dnaDigits = 16;
    uint dnaModulus = 10 ** dnaDigits;

    struct Zombie {
        string name;
        uint dna;
    }

    Zombie[] public zombies;

    function _createZombie(string memory _name, uint _dna) private {
        uint id = zombies.push(Zombie(_name, _dna)) - 1;//-1--->因為push進去zombies array的 index 要 -1
        emit NewZombie(id,_name,_dna);// and fire event here(呼叫event)
    }

    function _generateRandomDna(string memory _str) private view returns (uint) {
        uint rand = uint(keccak256(abi.encodePacked(_str)));
        return rand % dnaModulus;
    }

    function createRandomZombie(string memory _name) public {
        uint randDna = _generateRandomDna(_name);
        _createZombie(_name, randDna);
    }

}
```
#### Inheritance



----
#### Import



----
#### Interface(接口)
* your contract can interact with any other contract on the Ethereum blockchain, as long they expose those functions as public or external.
* [interface用法](https://blog.syhlion.tw/2021/09/2021-13th-ithome30day-day14-solidity-interfaces/)
* 規則
    *  不能繼承其他contract,但可以繼承其他interface
    *  interface內所有function必須為external
    *  不能宣告constructor
    *  不能宣告state variable
    

##### code examples
```solidity=
pragma solidity >=0.5.0 <0.6.0;

import "./zombiefactory.sol";

contract KittyInterface {//creat an interface
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

contract ZombieFeeding is ZombieFactory {

  address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  // Initialize kittyContract here using `ckAddress` from above

  function feedAndMultiply(uint _zombieId, uint _targetDna) public {
    require(msg.sender == zombieToOwner[_zombieId]);
    Zombie storage myZombie = zombies[_zombieId];
    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
    _createZombie("NoName", newDna);
  }
   KittyInterface kittyContract= KittyInterface(ckAddress);
}
```
----

```solidity=
pragma solidity >=0.5.0 <0.6.0;

import "./zombiefactory.sol";

contract KittyInterface {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

contract ZombieFeeding is ZombieFactory {

  address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  KittyInterface kittyContract = KittyInterface(ckAddress);
//設立一個interface kittyContract
  function feedAndMultiply(uint _zombieId, uint _targetDna) public {
    require(msg.sender == zombieToOwner[_zombieId]);
    Zombie storage myZombie = zombies[_zombieId];
    _targetDna = _targetDna % dnaModulus;//dnaModulus-->10**16 
    //所以為了使_targetDna小於16位數
    uint newDna = (myZombie.dna + _targetDna) / 2;
    _createZombie("NoName", newDna);
  }
  function feedOnKitty(uint _zombieId,uint _kittyId) public {
    uint kittyDna;//declare a uint
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);//取得只有一個回傳值genes到kittyDna
    feedAndMultiply(_zombieId, kittyDna);
  }
  
 
}
```
----

```solidity=
pragma solidity >=0.5.0 <0.6.0;

import "./zombiefactory.sol";

contract KittyInterface {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

contract ZombieFeeding is ZombieFactory {

  address ckAddress = 0x06012c8cf97BEaD5deAe237070F9587f8E7A266d;
  KittyInterface kittyContract = KittyInterface(ckAddress);

  // Modify function definition here:
  function feedAndMultiply(uint _zombieId, uint _targetDna,string memory _species) public {
    require(msg.sender == zombieToOwner[_zombieId]);
    Zombie storage myZombie = zombies[_zombieId];
    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
    if(keccak256(abi.encodePacked(_species))==keccak256(abi.encodePacked("kitty")))//若最後傳入species為kitty
    {
      newDna=newDna-newDna%100+99;//newDna最後兩位換99
    }
    _createZombie("NoName", newDna);
  }

  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    // And modify function call here:
    feedAndMultiply(_zombieId, kittyDna,"kitty");
  }

}

```
```solidity=

pragma solidity >=0.5.0 <0.6.0;

import "./zombiefactory.sol";

contract KittyInterface {
  function getKitty(uint256 _id) external view returns (
    bool isGestating,
    bool isReady,
    uint256 cooldownIndex,
    uint256 nextActionAt,
    uint256 siringWithId,
    uint256 birthTime,
    uint256 matronId,
    uint256 sireId,
    uint256 generation,
    uint256 genes
  );
}

contract ZombieFeeding is ZombieFactory {

  
  KittyInterface kittyContract;//don't hardcode into the kittyInterface 

  function setKittyContractAddress(address _address)external
  {
      kittyContract=KittyInterface(_address);
  }
  function feedAndMultiply(uint _zombieId, uint _targetDna, string memory _species) public {
    require(msg.sender == zombieToOwner[_zombieId]);
    Zombie storage myZombie = zombies[_zombieId];
    _targetDna = _targetDna % dnaModulus;
    uint newDna = (myZombie.dna + _targetDna) / 2;
    if (keccak256(abi.encodePacked(_species)) == keccak256(abi.encodePacked("kitty"))) {
      newDna = newDna - newDna % 100 + 99;
    }
    _createZombie("NoName", newDna);
  }

  function feedOnKitty(uint _zombieId, uint _kittyId) public {
    uint kittyDna;
    (,,,,,,,,,kittyDna) = kittyContract.getKitty(_kittyId);
    feedAndMultiply(_zombieId, kittyDna, "kitty");
  }

}

```
#### Random number generation via keccak256
```solidity=
// Generate a random number between 1 and 100:
uint randNonce = 0;//Nonce必須獨一無二
uint random = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % 100;// "pack" the 3 inputs and use keccak to convert them to a random hash
//Next, it would convert that hash to a uint, 
//and then use % 100 to take only the last 2 digits. 
//This will give us a totally random number between 0 and 99.
//now-->timestamp(時間戳) msg.sender-->玩合約的人
randNonce++;
uint random2 = uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % 100;

```
* [How can I securely generate a random number in my smart contract?](https://ethereum.stackexchange.com/questions/191/how-can-i-securely-generate-a-random-number-in-my-smart-contract)
   * use ==oracle== (a secure way to pull data in from outside of Ethereum) to access a random number function from outside of the Ethereum blockchain
* 程式碼驗證 1 --->
 ```solidity=
pragma solidity >=0.5.0 <0.6.0;

import "./zombiehelper.sol";

contract ZombieAttack is ZombieHelper {
    uint randNonce = 0;
    function randMod(uint _modulus) internal returns(uint) {
      randNonce++;
      return uint(keccak256(abi.encodePacked(now, msg.sender, randNonce))) % _modulus;
    }
}

```
----
### ERC-721

----
### Contract security enhancements: Overflows and Underflows
* uint256(2**256夠大 , 很難 overflow 或 underflow)
* uint8
  * (二進位最大就11111111--->decimal:256 ) 
     * 11111111 加 1 --->overflow-->(全歸0) 
     * 0 減 1 --->underflow-->(變 11111110--->decimal:255)
* 程式碼驗證 1--->
```solidity=
using SafeMath for uint256;

uint256 a = 5;
uint256 b = a.add(3); // 5 + 3 = 8
uint256 c = a.mul(2); // 5 * 2 = 10
```
----
### comments(natspec)
* 程式碼驗證 1--->
```solidity=
/// @title A contract for basic math operations
/// @author H4XF13LD MORRIS 💯💯😎💯💯
/// @notice For now, this contract just adds a multiply function
contract Math {
  /// @notice Multiplies 2 numbers together
  /// @param x the first uint.
  /// @param y the second uint.
  /// @return z the product of (x * y)
  /// @dev This function does not currently check for overflows
  function multiply(uint x, uint y) returns (uint z) {
    // This is just a normal comment, and won't get picked up by natspec
    z = x * y;
  }
}
@title and @author are straightforward.

@notice explains to a user what the contract / function does. @dev is for explaining extra details to developers.

@param and @return are for describing what each parameter and return value of a function are for.

```
----

---

##### Reference 
[What the hack is Memory and Storage in Solidity?](https://medium.com/coinmonks/what-the-hack-is-memory-and-storage-in-solidity-6b9e62577305)
[什麼時候用 storage，什麼時候該用 memory?](https://medium.com/taipei-ethereum-meetup/solidity-weekly-9-beae006cee2)
[layer 2](https://www.blocktempo.com/the-state-of-eth2-june-2020/)