web3 solidity 基础 ERC20 大白话搞懂
一、什么是標(biāo)準(zhǔn)什么是ERC20標(biāo)準(zhǔn)
ERC20 是 eth 的一個(gè)標(biāo)準(zhǔn),怎么理解標(biāo)準(zhǔn)一詞呢?
標(biāo)準(zhǔn)是大家遵循的一個(gè)協(xié)議,根據(jù)這個(gè)協(xié)議大家都知道該怎么去做,例如去吃飯的時(shí)候人多,你就需要排隊(duì),然后去窗口跟阿姨說你要吃什么,阿姨就會幫你打;若你不準(zhǔn)守這個(gè)標(biāo)準(zhǔn),直接沖進(jìn)后廚,翻開泔水,大喊著我要吃飯…這個(gè)時(shí)候就完全背離了這個(gè)標(biāo)準(zhǔn),所以被趕走了。
以上所述以開玩笑的方式講述了什么是標(biāo)準(zhǔn),所以在我們要使用 ERC20 標(biāo)準(zhǔn)完成這個(gè)標(biāo)準(zhǔn)的結(jié)果時(shí),就需要遵守這個(gè)標(biāo)準(zhǔn)。
ERC20 是以太坊上的一種代幣合約標(biāo)準(zhǔn),你要實(shí)現(xiàn)這個(gè)代幣或者說你要發(fā)個(gè)幣那么就得給這個(gè)代幣一個(gè)名字、怎么轉(zhuǎn)賬、總量、授權(quán) 等這些功能(標(biāo)準(zhǔn)),否則別人拿你的幣都不能轉(zhuǎn)賬,難道就是看嘛,所以標(biāo)準(zhǔn)我們得實(shí)現(xiàn)。
具體標(biāo)準(zhǔn)我們可以看這個(gè):https://eips.ethereum.org/EIPS/eip-20
以上的標(biāo)準(zhǔn)簡而言之就是實(shí)現(xiàn)一些接口就可以了,以下是一個(gè)標(biāo)準(zhǔn)的ERC20標(biāo)準(zhǔn):
// SPDX-License-Identifier:MIT pragma solidity 0.8.17; interface IERC20 {//發(fā)行的代幣總量function totalSupply() external view returns (uint256);//某地址余額function balanceOf(address account) external view returns (uint256);//從當(dāng)前賬戶對某個(gè)地址轉(zhuǎn)amount的錢function transfer(address account, uint256 amount) external returns (bool);//授權(quán)某個(gè)賬戶可以用你的錢(用多少錢是指定的)function approve(address spender, uint256 amount) external returns (bool);//你授權(quán)的賬戶還可以有多少你授權(quán)的錢可以用function allowance(address owner, address spender) external view returns (uint256);//授權(quán)用戶的轉(zhuǎn)賬方法,只針對授權(quán)用戶使用function transferFrom(address from,address to,uint256 amount) external returns (bool);//轉(zhuǎn)賬時(shí)觸發(fā)轉(zhuǎn)賬事件event Transfer(address indexed from, address indexed to, uint256 value);//授權(quán)時(shí)觸發(fā)授權(quán)事件event Approval(address indexed owner, address indexed spender, uint256 value); }用文字列出來,那么這些接口方法就包括:
- 代幣總量 totalSupply
- 某地址余額 balanceOf
- 轉(zhuǎn)賬 transfer
- 授權(quán) approve
- 查看授權(quán)賬戶余額 allowance
- 授權(quán)用戶轉(zhuǎn)賬 transferFrom
- 轉(zhuǎn)賬事件 Transfer
- 授權(quán)事件Approval
二、簡單 ERC20標(biāo)準(zhǔn)實(shí)現(xiàn)
2.1 父合約
實(shí)現(xiàn) ERC20 標(biāo)準(zhǔn)首先我們創(chuàng)建一個(gè)合約,在合約中將第一點(diǎn)的接口作為父合約之后我們將其繼承:
// SPDX-License-Identifier:MIT pragma solidity 0.8.17; interface IERC20 {//發(fā)行的代幣總量function totalSupply() external view returns (uint256);//某地址余額function balanceOf(address account) external view returns (uint256);//從當(dāng)前賬戶對某個(gè)地址轉(zhuǎn)amount的錢function transfer(address account, uint256 amount) external returns (bool);//授權(quán)某個(gè)賬戶可以用你的錢(用多少錢是指定的)function approve(address spender, uint256 amount) external returns (bool);//你授權(quán)的賬戶還可以有多少你授權(quán)的錢可以用function allowance(address owner, address spender) external view returns (uint256);//授權(quán)用戶的轉(zhuǎn)賬方法,只針對授權(quán)用戶使用function transferFrom(address from,address to,uint256 amount) external returns (bool);//轉(zhuǎn)賬時(shí)觸發(fā)轉(zhuǎn)賬事件event Transfer(address indexed from, address indexed to, uint256 value);//授權(quán)時(shí)觸發(fā)授權(quán)事件event Approval(address indexed owner, address indexed spender, uint256 value); }2.2 創(chuàng)建合約
接著編寫一個(gè)合約叫做 BitCoinDemo:
contract BitCoinDemo is IERC20{}2.3 代幣名稱、總量、余額等狀態(tài)變量編寫
接下來是不是應(yīng)該到我們需要用到一些變量來存儲這個(gè)代幣名稱、總量以及余額了?
此時(shí)創(chuàng)建一個(gè)變量用來存儲一個(gè)地址與余額的關(guān)系,那么使用 map 類型的數(shù)據(jù):
//余額 mapping(address => uint256)public balances;那么接下來就是對應(yīng)的總量了:
//總量 uint256 public total = 100000000;那么就還有你的代幣全名、簡稱和小數(shù)了:
//代幣名 string public constant name = "1BITCOINERC20DEMO"; //簡稱 string public constant symbol = "1BitCoin"; //小數(shù)點(diǎn) uint8 public constant decimals = 18;其中小數(shù)點(diǎn)位置我們?yōu)?18就好,一般都是寫18。
當(dāng)然,你這些內(nèi)容都可以在合約部署的時(shí)候再傳入,在這里我就簡單編寫了。
2.4 構(gòu)造函數(shù)給自己好多錢
接著,我們可以編寫一個(gè)構(gòu)造函數(shù),將即將我們要?jiǎng)?chuàng)建的代幣給與當(dāng)前合約的創(chuàng)建者:
constructor() {balances[msg.sender] = total; }畢竟這個(gè)關(guān)系就是某個(gè)地址又多少余額,那么我總量是 total,那不就是給當(dāng)前創(chuàng)建合約的人所有余額就好了。
2.5 實(shí)現(xiàn) totalSupply() 方法
接著開始實(shí)現(xiàn) totalSupply() 方法,我們只需要返回當(dāng)前合約的總量即可,那么就可以寫成:
//返回總量 function totalSupply()override public view returns (uint256) {return total; }記得,一定要寫 override,畢竟是父接口的方法重寫。
2.6 實(shí)現(xiàn) 轉(zhuǎn)賬 方法
既然我有了幣,那么接下來就應(yīng)該有一個(gè)轉(zhuǎn)賬方法,我們實(shí)現(xiàn) transfer 方法:
//轉(zhuǎn)賬代幣 function transfer(address account, uint256 amount) public override returns (bool) {require(balances[msg.sender]>amount);//判斷錢夠不夠balances[msg.sender] = balances[msg.sender]-amount;//原賬戶減去給自己balances[account] = balances[account]+amount;//給別人賬戶加上轉(zhuǎn)賬的錢emit Transfer(msg.sender, account, amount);//響應(yīng)這個(gè)事件return true; }以上的轉(zhuǎn)賬方法很簡單,接收兩個(gè)參數(shù),一個(gè)是你要轉(zhuǎn)給誰的賬戶 account,還有一個(gè)你要轉(zhuǎn)多少錢 amount,最后返回 bool 是否轉(zhuǎn)賬成功
在方法中首先判斷錢是否足夠,夠的話就給原賬戶減去轉(zhuǎn)出去的錢,別人賬戶加上轉(zhuǎn)出去的錢就ok了。
2.7 查看余額
既然已經(jīng)轉(zhuǎn)賬到別人賬戶了,那么此時(shí)還需要對應(yīng)的查看一下別人的余額,那么此時(shí)就實(shí)現(xiàn) balanceOf 方法:
//余額查看 function balanceOf(address account)override public view returns (uint256) {return balances[account]; }直接返回那個(gè) balances 的映射結(jié)果就得到余額了。
2.8 指定授權(quán)賬戶
授權(quán)賬戶需要一個(gè) map 來存儲,但是跟之前的 map 不太一樣,如下:
//授權(quán)用戶及余額 mapping(address => mapping (address => uint256)) appbalances;為啥要這樣寫這個(gè) map 呢?那是因?yàn)槟闶跈?quán)肯定是 A賬戶 授權(quán)給了 B賬戶 多少錢,所以此時(shí)就是兩個(gè) address,最外層的 address 就是授權(quán)人,這個(gè)授權(quán)人下的 address 就是授權(quán)給的某人,二 對應(yīng)的 uint256 數(shù)據(jù)則是授權(quán)的金額,方法如下:
//授權(quán)方法 function approve(address spender, uint256 amount)override public returns (bool) {appbalances[msg.sender][spender] = amount;emit Approval(msg.sender, spender, amount);return true; }以上這個(gè)方法呢 spender 就是需要授權(quán)給的地址,amount 就是給這個(gè)地址授權(quán)的金額數(shù)量,那么 appbalances[msg.sender][spender] = amount; 就表示在這個(gè) appbalances 授權(quán)金額 map 中添加一個(gè)記錄,msg.sender 是我自己的地址,那意思就是我自己授權(quán)給了 spender 一個(gè)金額,這個(gè)金額是 amount。
隨后再用 emit 觸發(fā)一個(gè)事件。
2.9 指定授權(quán)賬戶
查看授權(quán)賬戶余額也很簡單了,傳入兩個(gè)地址,一個(gè)地址是授權(quán)人,另一個(gè)是被授權(quán)人,返回對應(yīng)的 appbalances 數(shù)據(jù),那么就得到值了,那么這個(gè)方法編寫如下:
//查看授權(quán)賬戶余額 function allowance(address owner, address spender)override public view returns (uint) {return appbalances[owner][spender]; }2.10 授權(quán)用戶的專用轉(zhuǎn)賬方法
授權(quán)用戶是有一個(gè)專用的轉(zhuǎn)賬方法的,畢竟這兩者存儲的結(jié)構(gòu)都不一樣,那么此時(shí)就實(shí)現(xiàn)最后一個(gè)需要實(shí)現(xiàn)的授權(quán)用戶的轉(zhuǎn)賬方法:
//授權(quán)用戶的轉(zhuǎn)賬方法 function transferFrom(address from, address to, uint256 amount)override public returns (bool) {require(amount <= balances[from]);require(amount <= appbalances[from][msg.sender]);balances[from] = balances[from]-amount;appbalances[from][msg.sender] = appbalances[from][msg.sender]-amount;balances[to] = balances[to]+amount;emit Transfer(from, to, amount);return true; }以上代碼中的第一行,為啥要判斷 balances 非授權(quán)用戶的余額呢?那是因?yàn)槲沂跈?quán)給你的錢那也是我的錢,那么肯定我的錢都不夠,你肯定也不夠了。要注意這個(gè)關(guān)系,是授權(quán)而不是轉(zhuǎn)賬給你。
知道以后那就明白為什么要寫 require(amount <= balances[from]); 了,那么判斷完授權(quán)用戶的余額后開始判斷被授權(quán)用戶的余額是否足夠,足夠了就繼續(xù)往下走。
首先得從授權(quán)賬戶的余額里面扣除要支出的部分 balances[from] = balances[from]-amount;,接著再從被授權(quán)的人那里扣除支出部分 appbalances[from][msg.sender] = appbalances[from][msg.sender]-amount;;有些同學(xué)可能會問不是已經(jīng)從授權(quán)賬戶扣除了為什么還要從被授權(quán)賬戶扣除呢?這是因?yàn)檫@是被授權(quán)用戶發(fā)起的支出,而不是授權(quán)賬戶發(fā)起的支出,所以被授權(quán)賬戶要減去已經(jīng)授權(quán)的余額。
接下來就直接導(dǎo) balances 中為獲得方添加余額即可,記住這個(gè)金額不是授權(quán)金額,所以直接 balances 進(jìn)行添加即可。
最后響應(yīng)一個(gè)事件及解決。
2.11 完整代碼
那么此時(shí)的完整代碼如下:
// SPDX-License-Identifier:MIT pragma solidity 0.8.17; interface IERC20 {//發(fā)行的代幣總量function totalSupply() external view returns (uint256);//某地址余額function balanceOf(address account) external view returns (uint256);//從當(dāng)前賬戶對某個(gè)地址轉(zhuǎn)amount的錢function transfer(address account, uint256 amount) external returns (bool);//授權(quán)某個(gè)賬戶可以用你的錢(用多少錢是指定的)function approve(address spender, uint256 amount) external returns (bool);//你授權(quán)的賬戶還可以有多少你授權(quán)的錢可以用function allowance(address owner, address spender) external view returns (uint256);//授權(quán)用戶的轉(zhuǎn)賬方法,只針對授權(quán)用戶使用function transferFrom(address from,address to,uint256 amount) external returns (bool);//轉(zhuǎn)賬時(shí)觸發(fā)轉(zhuǎn)賬事件event Transfer(address indexed from, address indexed to, uint256 value);//授權(quán)時(shí)觸發(fā)授權(quán)事件event Approval(address indexed owner, address indexed spender, uint256 value); }contract BitCoinDemo is IERC20{//余額mapping(address => uint256)public balances;//總量uint256 public total = 10000000000000000;//代幣名string public constant name = "1BITCOINERC20DEMO";//簡稱string public constant symbol = "1BitCoin";//小數(shù)點(diǎn)uint8 public constant decimals = 18;//授權(quán)用戶及余額mapping(address => mapping (address => uint256)) appbalances;constructor() {balances[msg.sender] = total;}//返回總量function totalSupply()override public view returns (uint256) {return total;}//轉(zhuǎn)賬代幣function transfer(address account, uint256 amount) public override returns (bool) {require(balances[msg.sender]>amount);//判斷錢夠不夠balances[msg.sender] = balances[msg.sender]-amount;//原賬戶減去給自己balances[account] = balances[account]+amount;//給別人賬戶加上轉(zhuǎn)賬的錢emit Transfer(msg.sender, account, amount);//響應(yīng)這個(gè)事件return true;}//余額查看function balanceOf(address account)override public view returns (uint256) {return balances[account];}//授權(quán)方法function approve(address spender, uint256 amount)override public returns (bool) {appbalances[msg.sender][spender] = amount;emit Approval(msg.sender, spender, amount);return true;}//查看授權(quán)賬戶余額function allowance(address owner, address spender)override public view returns (uint) {return appbalances[owner][spender];}//授權(quán)用戶的轉(zhuǎn)賬方法function transferFrom(address from, address to, uint256 amount)override public returns (bool) {require(amount <= balances[from]);require(amount <= appbalances[from][msg.sender]);balances[from] = balances[from]-amount;appbalances[from][msg.sender] = appbalances[from][msg.sender]-amount;balances[to] = balances[to]+amount;emit Transfer(from, to, amount);return true;} }三、新增功能
3.1 增發(fā)鑄幣
此時(shí)我們還可以對合約增加一些新的功能,例如鑄幣功能。你可以理解為鑄幣就是對代幣進(jìn)行增發(fā)(不是增頭發(fā)),通過鑄幣可以創(chuàng)造出更多的代幣,但是在代幣總量上我們需要對其進(jìn)行增加,否則你的發(fā)行跟實(shí)際記錄不符,這樣你的合約將會不可信。
//增發(fā) function mint(address account, uint256 amount) external virtual {total += amount;balances[account] += amount;emit Transfer(address(0), account, amount); }以上是一個(gè)增發(fā)方法,通過傳入增發(fā)后代幣增加的賬戶地址,以及一個(gè)增發(fā)量,再到方法內(nèi)對總量進(jìn)行增加,并且記錄增發(fā)的代幣存儲到哪一個(gè)賬戶之中,當(dāng)然也要響應(yīng)一個(gè)對應(yīng)的交易事件(你也可以再搞一個(gè)增發(fā)事件)。
3.2 代幣銷毀
既然有了代幣增發(fā),那就來一個(gè)代幣銷毀。代表銷毀可以銷毀指定賬戶的代幣,當(dāng)然你也可以設(shè)定為只有“owner”賬戶,最開始存儲的賬戶代幣都行,在此只是做一個(gè)示例。
//銷毀 function burn(address account, uint256 amount) external virtual {require(balances[account] >= amount, "burn error");balances[account] = balances[account] - amount;total -= amount;emit Transfer(account, address(0), amount); }代幣銷毀的方式跟增發(fā)的方式相反,當(dāng)然還需要判斷你指定的賬戶的余額否大于或等于需要銷毀的量,接著就是往對應(yīng)的 balances 里面去減去對應(yīng)的 amount 了,總量也要對應(yīng)的減去值,最后觸發(fā)一個(gè) Transfer 事件。
在此我們可以看到我們通過 發(fā)送方或者是接收方 為 0 地址表示銷毀和增加,發(fā)送方為 0 地址則是增發(fā),接收方為 0 地址則是銷毀。
四、優(yōu)化合約
通過以上的最基礎(chǔ)的 ERC20 合約內(nèi)容大概已經(jīng)明白了怎么玩了,接下來我們?yōu)槠湫略鲆稽c(diǎn)內(nèi)容,循序漸進(jìn)感覺挺棒。
4.1 增發(fā)及銷毀條件
增發(fā)及銷毀條件需要滿足是否是合約的 owner 調(diào)用,否則任意一個(gè)人都可以增發(fā)和銷毀就亂套了,在此我們增加對應(yīng)的 require:
require(owner==msg.sender,"sender error");在此我們還需要?jiǎng)?chuàng)建一個(gè)狀態(tài)變量存儲當(dāng)前的 owner:
address owner; owner=msg.sender;4.2 銷毀代幣優(yōu)化
銷毀代幣之前是隨便一個(gè)賬戶指定了就可以銷毀那個(gè)賬戶代幣,這明顯就不對,不然我的錢就非常不安全了,在此我們設(shè)置只能夠銷毀自己的代碼:
還需要把多余的參數(shù)刪除喲。
4.3 完整代碼
其他情況就增加判斷是否是 0 地址就ok了,畢竟本篇文章只是介紹基礎(chǔ)的 ERC20,并不是做一個(gè)“完善”的ERC20 合約:
接著我們部署完畢后(測試網(wǎng))導(dǎo)入代幣:
接著我們來個(gè)增發(fā),輸入地址和增加量:
等待交易完成:
現(xiàn)在錢多多了,并且使 Mint 方法發(fā)送的。
在測試一下銷毀:
銷毀成功:
基本上都操作正常:
最終代碼完整如下:
// SPDX-License-Identifier:MIT pragma solidity 0.8.17; interface IERC20 {//發(fā)行的代幣總量function totalSupply() external view returns (uint256);//某地址余額function balanceOf(address account) external view returns (uint256);//從當(dāng)前賬戶對某個(gè)地址轉(zhuǎn)amount的錢function transfer(address account, uint256 amount) external returns (bool);//授權(quán)某個(gè)賬戶可以用你的錢(用多少錢是指定的)function approve(address spender, uint256 amount) external returns (bool);//你授權(quán)的賬戶還可以有多少你授權(quán)的錢可以用function allowance(address owner, address spender) external view returns (uint256);//授權(quán)用戶的轉(zhuǎn)賬方法,只針對授權(quán)用戶使用function transferFrom(address from,address to,uint256 amount) external returns (bool);//轉(zhuǎn)賬時(shí)觸發(fā)轉(zhuǎn)賬事件event Transfer(address indexed from, address indexed to, uint256 value);//授權(quán)時(shí)觸發(fā)授權(quán)事件event Approval(address indexed owner, address indexed spender, uint256 value); }contract BitCoinDemo is IERC20{//余額mapping(address => uint256)public balances;//總量uint256 public total = 10000000000000000;//代幣名string public constant name = "1BITCOINERC20DEMO";//簡稱string public constant symbol = "1BitCoin";//小數(shù)點(diǎn)uint8 public constant decimals = 18;//授權(quán)用戶及余額mapping(address => mapping (address => uint256)) appbalances;//擁有者address owner;constructor() {owner=msg.sender;balances[msg.sender] = total;}//返回總量function totalSupply()override public view returns (uint256) {return total;}//轉(zhuǎn)賬代幣function transfer(address account, uint256 amount) public override returns (bool) {require(account==address(0),"sender error");require(balances[msg.sender]>amount);//判斷錢夠不夠balances[msg.sender] = balances[msg.sender]-amount;//原賬戶減去給自己balances[account] = balances[account]+amount;//給別人賬戶加上轉(zhuǎn)賬的錢emit Transfer(msg.sender, account, amount);//響應(yīng)這個(gè)事件return true;}//余額查看function balanceOf(address account)override public view returns (uint256) {return balances[account];}//授權(quán)方法function approve(address spender, uint256 amount)override public returns (bool) {require(spender==address(0),"sender error");appbalances[msg.sender][spender] = amount;emit Approval(msg.sender, spender, amount);return true;}//查看授權(quán)賬戶余額function allowance(address owner, address spender)override public view returns (uint) {return appbalances[owner][spender];}//授權(quán)用戶的轉(zhuǎn)賬方法function transferFrom(address from, address to, uint256 amount)override public returns (bool) {require(amount <= balances[from]);require(amount <= appbalances[from][msg.sender]);require(to==address(0),"sender error");balances[from] = balances[from]-amount;appbalances[from][msg.sender] = appbalances[from][msg.sender]-amount;balances[to] = balances[to]+amount;emit Transfer(from, to, amount);return true;}//增發(fā)function mint(address account, uint256 amount) external virtual {require(owner==msg.sender,"sender error");total += amount;balances[account] += amount;emit Transfer(address(0), account, amount);}//銷毀function burn(uint256 amount) external virtual {require(owner==msg.sender,"sender error");require(balances[owner] >= amount, "burn error");balances[owner] = balances[owner] - amount;total -= amount;emit Transfer(owner, address(0), amount);} }總結(jié)
以上是生活随笔為你收集整理的web3 solidity 基础 ERC20 大白话搞懂的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 商业智能BI财务分析,如何从财务指标定位
- 下一篇: 2011年总结