一、什么是標(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ì),然后去窗口跟阿姨說你要吃什么,阿姨就會(huì)幫你打;若你不準(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)該到我們需要用到一些變量來存儲(chǔ)這個(gè)代幣名稱、總量以及余額了?
此時(shí)創(chuàng)建一個(gè)變量用來存儲(chǔ)一個(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 來存儲(chǔ),但是跟之前的 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)賬方法的,畢竟這兩者存儲(chǔ)的結(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é)可能會(huì)問不是已經(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í)際記錄不符,這樣你的合約將會(huì)不可信。
//增發(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ā)的代幣存儲(chǔ)到哪一個(gè)賬戶之中,當(dāng)然也要響應(yīng)一個(gè)對應(yīng)的交易事件(你也可以再搞一個(gè)增發(fā)事件)。
3.2 代幣銷毀
既然有了代幣增發(fā),那就來一個(gè)代幣銷毀。代表銷毀可以銷毀指定賬戶的代幣,當(dāng)然你也可以設(shè)定為只有“owner”賬戶,最開始存儲(chǔ)的賬戶代幣都行,在此只是做一個(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)變量存儲(chǔ)當(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ā)送的。
在測試一下銷毀:
銷毀成功:
基本上都操作正常:文章來源:http://www.zghlxwxcb.cn/news/detail-815428.html
最終代碼完整如下:文章來源地址http://www.zghlxwxcb.cn/news/detail-815428.html
// 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);
}
}
到了這里,關(guān)于web3 solidity 基礎(chǔ) ERC20 大白話搞懂的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!