區塊鏈特輯——solidity語言基礎(七)

2023-01-21 06:00:23

Solidity語法基礎學習

十、實戰專案(二):

3.專案實操:

ERC20 代幣實戰

①轉賬篇

總髮行量函數

totalSupply() return(uint256)

·回傳代幣的發行總量

·使用狀態變數uint256_totalSupply來儲存

 

賬戶餘額查詢函數

Balance0f(address) returns(uint256)

·給定一個賬戶(address),回傳該賬戶擁有的代幣餘額(uint256)

·使用mapping來儲存:

  ·mapping(address => uint256) _balance;

 

轉賬函數

Transfer(address,uint256) returns(bool);

·呼叫者「msg.sender」,轉移「amount」數量的代幣給特定賬戶「to」

·成功時回傳true,反之,回傳false

·有些檢查需要做:

  ·amount是否超過餘額

  ·是否轉移給address 0x0代表銷燬的意思

 

轉賬事件

event Transfer(

    address indexed from,

    address indexed to,

    uint256 value,

);

·當發生代幣轉移時,必須觸發此事件,即使轉移的數量為「0」也是

 

②授權篇

授權餘額查詢函數

allowance(address owner,address spender) returns(uint256);

·給定兩個賬戶(address),回傳「owner」授權給「spender」的額度(uint256)

·使用mapping來儲存:

  ·mapping(address =>

    ·mapping(address => uint256) _allowance;

注:mapping查詢節省燃料

 

授權函數

approve(address spender,uint256 amount) returns(bool);

·呼叫者「msg.sender」,授權「amount」數量的代幣額度給第三方賬戶「spender」

·成功時回傳true,反之,回傳false

 

授權事件

event Approval(

    address indexed owner,

    address indexed spender,

    uint256 value,

);

 

·當授權額度時,必須觸發此事件,即使數量為「0」也要觸發

 

③花別人的錢

從第三方賬戶轉賬的函數

transferFrom(address from,address to,uint256 amount);

·呼叫者(msg.sender)從代幣持有者(from)轉賬給接收者(to)「amount」數量的代幣

·其中:

  ·需檢查呼叫者是否擁有足夠的額度可用

  ·轉賬時要檢查持有者是否足夠的餘額

  ·轉賬時需要同時減少額度

4.補充概念:

ERC20代幣實戰

元資料(metadata)

鑄造(mint)與銷燬(burn)篇

ERC20 Meta介面

Interface IERCMetadata{……}

interface IERC20Metadata{
    function name() public view returns(string memory);
    function symbol() public view returns(string memory);
    function decimals() public view returns(uint8); 
}

 

代幣名稱

function name() public view returns(string memory);

·回傳一個字元,代表這個代幣的名稱

·儲存是以string來儲存

·通常在constructor的時候就給定

 

代幣的簡稱/縮寫/象徵

function symbol() public view returns(string memory);

·回傳一個字串,代表這個代幣的簡稱

  ·Ethereum(name)→ETH(symbol)

  ·Apple(name)→AAPL(symbol)

·儲存時以string來儲存

·通常在constructor的時候就給定

 

代幣小數點位置

function decimals() public view returns(uint8);

·回傳一個uint8,代表這個代幣的小數點位置

·這個函數只用來顯示用

  ·decimals=3,則balance=1234,在顯示為1.234

·基本上代幣都會把decimals設定為18

  ·這是因為最開始就是設計的18,後來因為人類天性,能抄就抄

  ·1ether=1018wei

  ·1token=1018uint => decimals=18

 

ERC20輔助函數

鑄造(mint)與銷燬(burn)

interface IERC20{
    function mint(address account,uin256 amount);
    function burn(address account,uint256 amount);
}

 

鑄造新代幣

function mint(address account,uint256 amount);

·鑄造,即「無中生有」

·只有合約擁有者或者特殊許可權的人才能呼叫

·他同時也是一種轉賬,由address 0x0轉到目標賬號(account)

·由於是轉賬,因此也要觸發「Transfer」事件

 

銷燬代幣

function burn(address account,uint256 account);

·銷燬,即「迴歸虛無」

·可以根據使用情況決定誰可以呼叫

  ·若只有合約擁有者可以呼叫,則通常會有account引數,用來銷燬特定人的代幣

  ·若任何人都可以呼叫,則不會有account引數,用來銷燬特定人的代幣

  ·若任何人都可以呼叫,則不會有account引數,主要目的是請求呼叫者(msg.sender)銷燬自己的代幣同時也是一種轉賬,由account/msg.sender轉到address 0x0

·由於是轉賬,因此也要觸發「Transfer」事件

 

Example:範例程式碼

 

//SPDX-License-Identifier:MIT
pragma solidity ^0.8.17;

interface IERC20 {
    event Transfer(address indexed from, address indexed to, uint256 value);
    event Approval(address indexed owner,address indexed spender, uint256 value);

    function totalSupply() external view returns (uint256);

    function balance0f(address account) external view returns (uint256);
    function allowance(address owner,address spender) external view returns (uint256);

    function approve(address spender, uint256 amount) external returns (bool);
    function transfer(address to, uint amount) external returns (bool);
    function trnasferFrom(address from, address to, uint256 amount) external returns (bool);
}

contract ERC20 is IERC20 {
    uint _totalSupply;//定義一個數
    mapping(address => uint256) _balance;//定義了一個賬號
    mapping(address => mapping(address => uint256)) _allowance;
    //查詢授權額度
    function allowance(address owner, address spender) public view returns (uint256) {
        return _allowance[owner][spender];//返回自己和第三方
    }

    function _approve(address owner, address spender, uint256 amount) internal {
        _allowance[owner][spender] = amount;
        emit Approval(owner, spender, amount);
    }

    //授權
    function approve(address spender, uint256 amount) public returns (bool) {
        _approve(msg.sender, spender, amount);
        return true;
    }

    //發行代幣,啟動最初代幣
    constructor () {
        _balance[msg.sender] = 10000;
        _totalSupply = 10000;
    }

    function totalSupply() public view returns (uint256) {
        //回傳總髮行量
        return _totalSupply;
    }

    function balance0f(address account) public view returns (uint256) {
        //回傳查詢餘額
        return _balance[account];
    }

    function _transfer(address from, address to, uint256 amount) internal {
        uint256 myBalance = _balance[from];
        require(myBalance >= amount,"No money to transfer!");//已經沒錢轉賬了
        require(to != address(0),"Transfer to address 0");//不準轉賬到地址0
        _balance[from] = myBalance - amount;//我的賬戶總額計算
        _balance[to] = _balance[to] + amount;//你的賬戶怎麼計算
        emit Transfer(from, to, amount);
    }
    
    //實現轉賬功能
    function transfer(address to, uint256 amount) public returns (bool) {
        _transfer(msg.sender, to , amount);
        return true;
    }

    //檢查額度花人家的錢
    function trnasferFrom(address from, address to, uint256 amount) external returns (bool) {
        uint256 myAllowance = _allowance[from] [msg.sender]; 
        require(myAllowance >= amount,"ERROR:myAllowance < amount"); //我們允許的額度是否小於了他花的額度

        _approve(from, msg.sender, myAllowance - amount);//花去以後是否允許的額度有減少
        _transfer(from, to, amount);
        //檢查花銷是否從原來賬戶轉移到被授權者賬戶
        return true;
    }
}