---
# System prepended metadata

title: 1/23 智能合約 Solidity 新手學習分享
tags: [Demi]

---

---
tags: Demi
---
# 1/23 智能合約 Solidity 新手學習分享
## Agenda
1. 智能合約
2. Solidity
3. ERC20簡介
4. 簡單範例導讀
## About Me
Demi-Boralin #2125
資工研究生  非區塊鏈專業 
被DeFi朋友拉進圈  被Demi-Chubbytank拉進Demily
Solidity學習中
## 智能合約
* 運行在鏈上的程式碼
* 呼叫 functions 跟合約互動 (Read/Write Contract)
* 不智能 也不是合約
* 可以想像成 自動販賣機
* 部屬合約時僅 binary code上鏈， 可上傳源碼到 etherscan 做驗證並公開

![](https://i.imgur.com/CMWOvd2.png)
(Source: https://youtu.be/qhy1drKh8Z4?list=PLHmOMPRfmOxQ3HSlId8KAKxnt8yuyTZVk&t=143)
* Demi 實例:
合約上鏈的交易紀錄: https://etherscan.io/tx/0x51c4735c66abac467a248430dbe5f1b8b472cfbb77eccc1d01f9f0daac706846
上鏈後(有驗證)
https://etherscan.io/address/0xa6916545a56f75acd43fb6a1527a73a41d2b4081#code
## 程式語言
* Ethereum Virtual Machine (EVM)兼容鏈: Solidity/Vyper
    * ETH, Polygon, BSC
* Solana: Rust
* Tezos: Michelson
## Solidity 
- Solidity 教學 (v0.8.0)
    - Hello World
    ``` solidity=
    // SPDX-License-Identifier: MIT    
    pragma solidity >=0.7.0 <0.9.0;
    contract HelloWorld {
        function hi() public pure returns (string){
            return 'Hello, World!'
        }
    }
    ```
    
    - SPDX License Identifier

    ``` solidity=
    // SPDX-License-Identifier: MIT
    ```
    If you do not want to specify a license or if the source code is not open-source
    ``` solidity=
    // SPDX-License-Identifier: UNLICENSED
    ```

   - Version pragma
    ```solidity=
    pragma solidity >=0.7.0 <0.9.0;
    //0.7.0~0.9.0之間的任何版本都可以執行
    ```
    
    ```solidity=
    pragma solidity ^0.8.2;
    //0.8.2~0.8.x都可以執行
    ```
   - Constract
    ```solidity=
    contract HelloWorld{
    //合約內容
    }
    ```
    - Function
    ```solidity=
    function <name>(<parameters>) visibility modifiers returns(return_value_type){
    }
   ```
   - Function Visibility Specifiers 可視性
        
        * **public**: visible externally and internally (creates a getter function for storage/state variables)
        * **private**: only visible in the current contract
        * **external**: only visible externally (only for functions) - i.e. can only be message-called (via this.func)
        * **internal**: only visible internally

    ![](https://i.imgur.com/VV9jahl.png)
    ![](https://i.imgur.com/y8bWvnr.png)
    ![](https://i.imgur.com/GGadwHB.png)
    ![](https://i.imgur.com/F3FZ83T.png)
    (Source: Solidity function visibility, explained https://bitsofco.de/solidity-function-visibility-explained)
    - Modifiers 前置修飾詞
        - Functions:
            * **pure** for functions: Disallows modification or access of state.
            * **view** for functions: Disallows modification of state.
            * **payable** for functions: Allows them to receive Ether together with a call.
        - State Variables:
            * **constant** for state variables: Disallows assignment (except initialisation), does not occupy storage slot.
            * **immutable** for state variables: Allows exactly one assignment at construction time and is constant afterwards. Is stored in code.
        - Events
            * **anonymous** for events: Does not store event signature as topic.
            * **indexed** for event parameters: Stores the parameter as topic.
        - Others:
            * **virtual** for functions and modifiers: Allows the function’s or modifier’s behaviour to be changed in derived contracts.
            * **override**: States that this function, modifier or public state variable changes the behaviour of a function or modifier in a base contract.
    - Mapping
        
        ``` solidity=       //宣告mapping
        //mapping(key型別=> Value型別) <name>
        mapping(string =>uint) pubic map;
        map[key] = value;

        //mapping的刪除
        delete map[key];
        ```

    - Message
        1. **msg.sender** (address): sender of the message (current call)
        1. **msg.value** (uint): number of wei sent with the message

## ERC20 簡介
- Interfaces
    - totalSupply
    - balanceOf
    - transfer
    - transferFrom
    - approve
    - allowance
- Events
    - Transfer
    - Approval
``` solidity =
 1 // ----------------------------------------------------------------------------
 2 // ERC Token Standard #20 Interface
 3 // https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20-token-standard.md
 4 // ----------------------------------------------------------------------------
 5 contract ERC20Interface {
 6     function totalSupply() public constant returns (uint);
 7     function balanceOf(address tokenOwner) public constant returns (uint balance);
 8     function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
 9     function transfer(address to, uint tokens) public returns (bool success);
10     function approve(address spender, uint tokens) public returns (bool success);
11     function transferFrom(address from, address to, uint tokens) public returns (bool success);
12 
13     event Transfer(address indexed from, address indexed to, uint tokens);
14     event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
15 }
```
- https://eips.ethereum.org/EIPS/eip-20
Methods
1. name (Optional)
Returns the name of the token - e.g. "MyToken".
2. symbol (Optional)
Returns the symbol of the token. E.g. “HIX”.
3. decimals (Optional)
Returns the number of decimals the token uses - e.g. 8, means to divide the token amount by 100000000 to get its user representation.
4. totalSupply
Returns the total token supply.
5. balanceOf
Returns the account balance of another account with address _owner.
6.  transfer
Transfers _value amount of tokens to address _to, and MUST fire the Transfer event.
7.  transferFrom
Transfers _value amount of tokens from address _from to address _to, and MUST fire the Transfer event.
8. approve
Allows _spender to withdraw from your account multiple times, up to the _value amount.
9. allowance
Returns the amount which _spender is still allowed to withdraw from _owner.

## Simple Contracts
1. 產生自己的 ERC2O Tokens
``` solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import "@openzeppelin/contracts/access/Ownable.sol";

contract MyToken is ERC20, Ownable {
    constructor() ERC20("MyToken", "MTK") {
        _mint(msg.sender, 10000 * 10 ** decimals());
    }

    function mint(address to, uint256 amount) public onlyOwner {
        _mint(to, amount);
    }
}
```

2. 買賣 ERC20 (1:1)
收 ETH 發 ERC20 Token 出去
收 ERC20 燒掉 還 ETH回去
Tips:  conditions -> effects -> interaction
``` solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract MyWETH is ERC20 {
    constructor() ERC20("MyWETH", "MWETH") {}

    event  Deposit(address indexed dst, uint amount);
    event  Withdrawal(address indexed src, uint amount);
    
    receive() external payable {
        require(msg.value > 0);
        _mint(msg.sender, msg.value);
        emit Deposit(msg.sender, msg.value);
    }
    function deposit() public payable {
        require(msg.value > 0);
        _mint(msg.sender, msg.value);
        emit Deposit(msg.sender, msg.value);
    }
    function withdraw(uint amount) public {
        // 1. conditions
        require(amount > 0);  
        // 2. effect
        _burn(msg.sender, amount);
        // 3. interaction
        payable(msg.sender).transfer(amount);
        emit Withdrawal(msg.sender, amount);
    }
}
```
3. Token Presale 賣幣
用別的 ERC20 Token 進來買幣 (1:00) 
``` solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.2;

import "@openzeppelin/contracts/token/ERC20/ERC20.sol";

contract TokenPresale is ERC20 {
    uint256 currentFund = 0;
    
    constructor() ERC20("MyWETH", "MWETH") {
        _mint(address(this), 10000 * 10 ** decimals()); // totalSupply = 10000 Token 
    }
    
    event BUY(address indexed dst, uint amountTobuy);
    
    address _tokenContract = 0x93a7e3aDF5A4042c050Bb8b26726264ab4fE8901; // Goerli HSU
    IERC20 token = IERC20(_tokenContract);

    function buy(uint256 amount) public  {
        require(amount > 0, "buy more than 0");  
        require(token.balanceOf(msg.sender) >= amount, "Need HSU tokens more than amount"); 
        uint256 allowance = token.allowance(msg.sender, address(this));
        require(allowance >= amount, "Approve more HSU token than amount first");

        uint256 amountTobuy = amount * 100;  
        require(currentFund + amountTobuy <= totalSupply(), "totalSupply Exceed!!");  
             
        token.transferFrom(msg.sender, address(this), amount);  //轉HSU token 進來
        currentFund += amountTobuy; 
        this.transfer(msg.sender, amountTobuy); // 轉presale token 出去
        

        emit BUY(msg.sender, amountTobuy);
    }

}

```
4. ForceSaving
要存到一定量才可以出金
且會收取手續費
``` solidity
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.7;

import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract ForcedSaving {
    event  Deposit(address indexed dst, uint amount);
    event  Withdrawal(address indexed src, uint amount, uint fee);
    event  COLLECT(address indexed src, uint fee);
    
    mapping(address => uint256) deposit_balance;
    uint256 public total_deposit;
    uint256 public threshold =  0.001 * 10 ** 18;
    address public owner;
    
    //address _tokenContract = 0xB4FBF271143F4FBf7B91A5ded31805e42b2208d6; //Goerli WETH9
    address _tokenContract = 0x93a7e3aDF5A4042c050Bb8b26726264ab4fE8901; // Goerli HSU
    IERC20 token = IERC20(_tokenContract);
    constructor() {
        owner = msg.sender;
    }

    function deposit(uint amount) external  {
        require(amount>0);
        // transfer the token from address of the user to address of this contract
        require(token.balanceOf(msg.sender) >= amount, "You need to have tokens more than amount");
        require(token.allowance(msg.sender,address(this)) >= amount, "You need to approve tokens more than amount");
        token.transferFrom(msg.sender,address(this), amount); 
        
        deposit_balance[msg.sender] += amount;
        total_deposit += amount;

        emit Deposit(msg.sender,  amount);
    }
   
    function withdraw(uint256 amount) external {
        require(amount>0);
        require(deposit_balance[msg.sender] >= threshold, "You need to deposit tokens more than threshold");
        
        token.transfer(msg.sender, 999 * amount /1000);

        deposit_balance[msg.sender] -= amount;
        total_deposit -= amount;
        emit Withdrawal(msg.sender, amount, amount/1000);
    }

    function collcect() external {
        require(msg.sender == owner, "Only owner");
        uint256 fee = token.balanceOf(address(this)) - total_deposit;
        
        require(fee > 0, "No fee to collect");
        
        token.transfer(owner, fee);
        emit COLLECT(msg.sender, fee);
    }
    
}
```
5. KingOftheEther
放錢進來比前一個王多可以變成王
新的王可以把錢拿走
會被合約管理者收管理費
``` solidity
pragma solidity ^0.8.0;
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";

contract KingOftheEther {

 
    uint256 public highest_amount;    // 出價最高的金額
    address owner;      // 管理者
    address currentKing;    // 現任國王

    address[] kingIndexs;   //歷屆國王地址
    mapping (address => King) public kings; //歷屆國王資料
 
    event Donate(address indexed addr, uint256 amount);
    event NoticeNewKing(address indexed addr, uint256 amount, string name);    // 通知有新任國王上任
    struct King {
        address addr;
        uint amount;
        string name;
        uint createdAt;
        uint withdrawalAmount;
    }

    address _tokenContract = 0x93a7e3aDF5A4042c050Bb8b26726264ab4fE8901; // Goerli HSU
    IERC20 token = IERC20(_tokenContract);

    constructor() {
        owner = msg.sender;
    }
  
    // 篡位
    function replaceKing(string memory _name, uint256 amount)  public {
        require(amount > kings[currentKing].amount+ 0.1 * 10 **18, "Must more than current king");
        require(currentKing != msg.sender,"You can't not replace yourself");
        
        // transfer the token from address of the user to address of this contract
        require(token.balanceOf(msg.sender) >= amount, "You need to have tokens more than amount");
        require(token.allowance(msg.sender,address(this)) >= amount, "You need to approve tokens more than amount");
        token.transferFrom(msg.sender,address(this), amount); 

        if(kingIndexs.length > 0) {
            kings[currentKing].withdrawalAmount += amount - 0.05 * 10**18 ;  // 抽手續費0.05 
        }
        kingIndexs.push(msg.sender);
        kings[msg.sender] = King(msg.sender, amount, _name, block.timestamp, 0);
        currentKing = msg.sender;
        highest_amount = amount;
        
        emit NoticeNewKing(msg.sender, amount, _name);
    }



    // 提領管理費
    function ownerWithdrawal()  public {
        require(msg.sender == owner); 
        token.transfer(msg.sender, token.balanceOf(address(this)));
    }

    // 被篡位的人，可以拿走篡位的人的錢，但要先扣除管理費。
    function playerWithdrawal()  public {
        require(kings[msg.sender].withdrawalAmount > 0);    //Condition
        uint256 amount = kings[msg.sender].withdrawalAmount;    
        kings[msg.sender].withdrawalAmount = 0;        //Effect
        token.transfer(msg.sender,kings[msg.sender].withdrawalAmount);    //Interaction 
    }
}
```

## Framework
測試/部屬合約
1. Remix - Ethereum IDE https://remix.ethereum.org
2. Brownie (Python)
3. Hardhat (JavaScript)
4. Truffle (JavaScript)

Truffle vs Hardhat vs Brownie — Let’s have a look https://mirabdulhaseeb.medium.com/truffle-vs-hardhat-vs-brownie-lets-compare-ea08b927d084

## 參考資料來源:
1.ERC20 Token Standard
https://theethereum.wiki/erc20_token_standard/
2. EIPs https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md
3. Introduction to Smart Contracts
https://docs.soliditylang.org/en/v0.8.10/introduction-to-smart-contracts.html#a-simple-smart-contract

## 教學影片
1. Hung-Ying Tai - Solidity 30 Days (2018) https://www.youtube.com/playlist?list=PLHmOMPRfmOxSJcrlwyandWYiuP9ZAMYoF
2. KryptoCamp https://youtu.be/ljN46jx3oVQ
    - All In One Solidity https://hackmd.io/@ChiHaoLu/HymtJS_oF
4. ChainLink https://youtu.be/M576WGiDBdQ

## 入門教程
1. https://cryptozombies.io/en/course
2. https://www.useweb3.xyz/?fbclid=IwAR2OGJwk8MMnYgz7gEpsTDRqii11NwA4Updc3Ak_qxJMqnQTMAZe8gNf4ho
    - https://solidity-by-example.org/

## OpenZeppelin
[Github](https://github.com/OpenZeppelin/openzeppelin-contracts)
[GUI builder](https://wizard.openzeppelin.com/)

## 無情業配
兔子理財小幫手 https://rabbithelpers.com/home
邀請碼: rbt0000343
https://rabbithelpers.com/createUser?c=rbt0000343