---
# System prepended metadata

title: Solidity (0.8.4)

---

[TOC]
# Solidity (0.8.4)

# Link
## 1. [Remix (Online IDE)](http://remix.ethereum.org/)

## 2. [MetaMask](https://metamask.io/)

## 3. [Goeril TestNet](https://goerli.etherscan.io/)
- [Goerli 水龍頭 (發錢用)](https://faucet.goerli.mudit.blog/)

# Remix Env Settings
- Deploy & run transaction -> Environment -> Injected Web3 (Connect with MetaMask)

# Solidity 合約佈署

- Deploy & run transaction
1. 按下 Deploy 佈署合約

# Simple Solidity code

- SPDX 幫助減少軟體的衝突
```=Solidity
// SPDX-License-Identifier: GPL-3.0
```
- 定義版本範圍
```=Solidity
pragma solidity >=0.7.0 <0.9.0;
```

```=Solidity
/**
 * @title Storage
 * @dev Store & retrieve value in a variable
 */
contract Storage {

    uint256 number;

    /**
     * @dev Store value in variable
     * @param num value to store
     */
    function store(uint256 num) public {
        number = num;
    }

    /**
     * @dev Return value 
     * @return value of 'number'
     */
    function retrieve() public view returns (uint256){
        return number;
    }
}
```
# Solidity Explanation
## 1. Visibility (可視性)

*public, private, external, internal*

- public
    - 被宣告為 public 的 function 將成為 ABI 的一部分，可被大家看見
    - **能**被 internal call (來自自己的合約呼叫)
    - **能**被**外部**呼叫 message call
    - public 宣告 state variables 將產生 getter function

- private
    - 不會出現在 ABI 裡面
    - 只能被定義的 contract 內成員呼叫
    - **不能**被**外部**呼叫 message call
    - 外部不能互動，不代表不能被看見。數值仍然可被外部觀察者看見

- external
    - 被宣告為 external 的 function 將成為 ABI 的一部分，可被大家看見
    - **不能**被 internal call (來自自己的合約呼叫)
        - function f() external {}
        - f() -> 失敗
        - this.f() -> 成功，通過 message call 呼叫
    - **能**被 [**外部的合約**] 或 [**Transaction**] 呼叫
    - 處理[大陣列]的時候很有效率，external function 不會做 memory copy 的操作

- internal
    - 被宣告 internal 的 function 或 state variables 只能被定義的合約 (defined contract) 或者派生合約 (derived contract) 呼叫

## 2. State Mutability (變異性)
*pure, view, default*



|     | pure | view               | default            |
| --- | ---- | ------------------ | ------------------ |
| 讀  | :x:  | :heavy_check_mark: | :heavy_check_mark: |
| 寫  | :x:  | :x:                | :heavy_check_mark: |

## 3. 如何宣告 function
- 回傳值
    ```=solidity
    function name(args) visibility mutability returns(return variables) {
        ...
    }
    ```
- 不回傳值
    ```=solidity
    function name(args) visibility mutability {
        ...
    }
    ```
    
## 4. Getter Function
- 當自己定義
    ```=solidity 
    public int a;
    ```
- Solidity 將自動產生 function
    ```=solidity
    function a() public view returns (int) {
        return a;
    }
    ```
    
## 5. Constructor
- 建構子，為 contract 被建立時自動被執行的 function
- 通常在建構子裡設定資料的初始值
- 不宣告，Solidity會自動插入空的 constructor
    ```=solidity
    constructor() {}
    ```
- 建構 constructor
    ```=solidity
    constructor(args) {
        ...
    }
    ```
- 解構子
    ```=solidity
    selfdestruct()
    ```
    
## 6. Receive Function
- 接收 ether
- 一個 contract 只能有一個 receive function
    ```=solidity
    receive() external payable {
        ...
    }
     ```
- send(), transfer() 會觸發這個 function
- EOA message call
- 若 receive 不存在，尋找 fallback function

## 7. Fallback Function
- 一個 contract 只能有一個 fallback function
- 收 ether
    ```=solidity
    fallback() external payable
    ```
- 不收 ether
    ```=solidity
    fallback() external
    ```
- 若沒有 payable，收到 ether 會觸發 exception 並且退回 ether
- fallback 可能是無意見觸發的，因此 fallback function 內最好減少 gas 的消耗，盡量少於 2300 gass
- 以下這些行為可能大於 2300 gas，fallback 中盡量不要使用:
    - Modify storage
    - Create a contract
    - Call an external function
    - Send Ethers

### Example (Ether simulator)
```=solidity
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

contract etherSim {
    mapping(address => uint256) public balances;
    address payable wallet;
    
    constructor(address payable _wallet) {
        wallet = _wallet;
    }
    
    // Any call with non-empty calldata to this contract will execute
    // the fallback function (even if Ether is sent along with the call).
    fallback() external payable {
        
    }
    
    // This function is called for plain Ether transfers, i.e.
    // for every call with empty calldata
    receive() external payable {
        buyToken();
    }
    
    function buyToken() public payable {
        // buy a buyToken
        balances[msg.sender] += 1;
        // send ether to the wallet
        wallet.transfer(msg.value);
    }
}
```
- CALLDATA 如果**沒有**值，執行 **receive function**
- CALLDATA 如果**有**值，執行 **fallback function**

## 8. Event
- 監聽事件
```=solidity
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

contract etherSim {
    mapping(address => uint256) public balances;
    address payable wallet;
    
    event Purchase (
        address indexed _buyer,
        uint256 _amount
    );
    
    constructor(address payable _wallet) {
        wallet = _wallet;
    }

    receive() external payable {
        buyToken();
    }
    
    function buyToken() public payable {
        // buy a buyToken
        balances[msg.sender] += 1;
        // send ether to the wallet
        wallet.transfer(msg.value);
        emit Purchase(msg.sender, 1);
    }
}
```
- logs
```=json
[
	{
		"from": "0x3328358128832A260C76A4141e19E2A943CD4B6D",
		"topic": "0x2499a5330ab0979cc612135e7883ebc3cd5c9f7a8508f042540c34723348f632",
		"event": "Purchase",
		"args": {
			"0": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
			"1": "1",
			"_buyer": "0x5B38Da6a701c568545dCfcB03FcB875f56beddC4",
			"_amount": "1"
		}
	}
]
```

# Example 1 (簡易計算機)
```=Solidity
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

contract Calc {
    
    int private result;
    
    function add(int a, int b) public returns(int c) {
        result = a + b;
        c = result;
    }
    
    function minus(int a, int b) public returns(int) {
        result = a - b;
        return result;
    }
    
    function mul(int a, int b) public returns(int) {
        result = a * b;
        return result;
    }
    
    function div(int a, int b) public returns(int) {
        result = a / b;
        return result;
    }
    
    function getResult() public view returns(int) {
        return result;
    }
    
}
```

# Example 2 (小豬撲滿)

```=solidity
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

contract PiggyBank {
    
    uint public goal;
    
    constructor(uint myGoal) {
        goal = myGoal;
    }
    
    // 收取 ether
    receive() external payable {}
    
    function getMyBalance() public view returns(uint) {
        return address(this).balance;
    }
    
    function withdraw() public {
        if (getMyBalance() > goal) {
            // 銷毀合約
            selfdestruct(payable(msg.sender));
        }
    }
    
}
```

# Value Type

## 1. Boolean
- true or false
- ! && || == !=

## 2. Integer

- int (int256) | 以太輸出最大 256 bit
- uint (uint256)
- (u)intX, X $\in$ 8 的倍數, where X <= 256
- Type 資訊
    - type(T).min
    - type(T).max

## 3. Fixed-size Byte Arrays

- bytesX where X $\in$ $[1,32]$
- byte = bytes1
- bytes[k] 讀取締 k 個 byte

## 4. Literals
- Address Literal: 0xD05Ebe8E7BB56450d34784996Db6D22909E035Af

# Enum
- enum讓使用者可以自定義變數而不需要指定型態
- example
```=solidity
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

contract MyContract {
    enum State {Waiting, Ready, Active}
    State public state;
    
    constructor() {
        state = State.Waiting;
    }
    
    function active() public {
        state = State.Active;
    }
    
    function isActive() public view returns(bool) {
        return state == State.Active;
    }

}
```

# Storage Area

## 1. storage
- 永久儲存在區塊練中，存儲是一個 key/value 庫
- 每個 account 都會有 storage data area
- 存在在 function 和 transactions 之中
- 讀取成本相對較高，初始化和修改存儲的成本更高
- 佔用一個256位的槽需要消耗20000 gas
- 修改一個已經使用的存儲槽的值，需要消耗5000 gas
## 2. memory
- 臨時儲存
- 合約調用完成即被移除
- 讀或寫一個內存槽都會消耗 3 gas
- 為了避免礦工的工作量過大，memory 使用越大越耗費 gas fee
- **calldata**
- **stack**

# Example 3 (People List)
```=solidity
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

contract myContract {
    Person[] public people;
    
    uint256 public peopleCount;
    
    struct Person {
        string _firstName;
        string _lastName;
    }
    
    function addPerson(string memory _firstName, string memory _lastName) public {
        people.push(Person(_firstName, _lastName));
        peopleCount += 1;
    }
}
```
    
使用 [Mapping](https://docs.soliditylang.org/en/v0.8.4/types.html?highlight=mapping#mapping-types)

```=solidity
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

contract MyContract {
    
    uint256 public peopleCount;
    mapping(uint => Person) public people;
    
    struct Person {
        uint _id;
        string _firstName;
        string _lastName;
    }
    
    function addPerson(string memory _firstName, string memory _lastName) public {
        peopleCount += 1;
        people[peopleCount] = Person(peopleCount, _firstName, _lastName);
    }
}
```

## Modifier example
只讓 owner 調用 addPerson 函數
```=solidity
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

contract MyContract {
    
    uint256 public peopleCount;
    mapping(uint => Person) public people;
    
    address owner;
    
    modifier onlyOwner() {
        require(msg.sender == owner);
        _;
    }
    
    struct Person {
        uint _id;
        string _firstName;
        string _lastName;
    }
    
    constructor() {
        // msg.sender is the account deploy this contract
        owner = msg.sender;
    }

    
    function addPerson(
        string memory _firstName,
        string memory _lastName
    )
        public
        onlyOwner
    {
        incrementCount();
        people[peopleCount] = Person(peopleCount, _firstName, _lastName);
    }
    
    function incrementCount() internal {
        peopleCount += 1;
    }
    
}
```

## Timer example
設定過了 openingTime (epoch time) 才能執行 addPerson
```=solidity
// SPDX-License-Identifier: MIT

pragma solidity >=0.7.0 <0.9.0;

contract MyContract {
    
    uint256 public peopleCount;
    mapping(uint => Person) public people;
    
    uint256 openingTime = 1624039337;
    
    modifier onlyWhileOpen() {
        require(block.timestamp >= openingTime);
        _;
    }
    
    struct Person {
        uint _id;
        string _firstName;
        string _lastName;
    }


    
    function addPerson(
        string memory _firstName,
        string memory _lastName
    )
        public
        onlyWhileOpen
    {
        incrementCount();
        people[peopleCount] = Person(peopleCount, _firstName, _lastName);
    }
    
    function incrementCount() internal {
        peopleCount += 1;
    }
    
}
```

# Multiple Smart Contract



# Solidity Security
- [原文](https://blog.sigmaprime.io/solidity-security.html)
- [中文](https://paper.seebug.org/632/#%E7%9C%9F%E5%AE%9E%E4%B8%96%E7%95%8C%E7%9A%84%E4%BE%8B%E5%AD%90dao)