如果你對區塊鏈和ERC20代幣沒有概念,請先閱讀入門知識
批量轉賬,指的是在一筆 ETH 交易中轉多筆代幣到不同的賬戶,一般用於 ERC20 代幣專案啟動時對使用者進行空投。
批量空投的好處主要有兩個,一是省 GAS 費,但事實上省得不多;二是省時間,這是最主要目的。以太坊是以交易為粒度打包,如果轉賬只能單對單,即使一次發起多筆單對單的交易,等待這些交易被打包的時間也非常漫長,而且還有筆數上限限制。將多筆轉賬放到同一個交易中,被打包確認的速度就會非常快。一般 ERC20 代幣專案啟動時都會大撒幣,空投地址動輒都是幾萬幾十萬,批量空投介面對效率會有上百倍的提升。
文章主要內容
- 在本機部署ETH私有鏈
- 在ETH私有鏈上釋出ERC20代幣
- 三種批量轉賬方式的原理
- 三種批量轉賬方式的實現
- 如何解析批量轉賬
由於文章較長,所以這裡分為兩篇,本篇主要講前兩個部署環境的部分,熟悉部署的同學可以跳過本章
在本機部署ETH私有鏈
本機預設是在Mac OS X上進行安裝,別的系統的安裝可以在github上檢視Ethereum Installation Instructions
geth安裝
推薦使用Homebrew進行安裝,簡單快捷
brew 是 Mac 下的包管理工具,和Ubuntu裡的apt-get類似
brew tap ethereum/ethereum
brew install ethereum
複製程式碼
建立私有鏈
首先新建一個檔案目錄,例如ethprivate
要搭建私有鏈必須有一個創世區塊,創世區塊的資訊寫在一個json檔案中,例如genesis.json檔案,在ethprivate/genesis.json
中寫入下面的內容
{
"config": {
"chainID": 1024,
"homesteadBlock": 0,
"eip155Block": 0,
"eip158Block": 0
},
"alloc": {},
"coinbase": "0x0000000000000000000000000000000000000000",
"difficulty": "0x400",
"extraData": "0x",
"gasLimit": "0x84c060",
"nonce": "0xdeadbeefdeadbeef",
"mixhash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"parentHash": "0x0000000000000000000000000000000000000000000000000000000000000000",
"timestamp": "0x00"
}
複製程式碼
這裡將gasLimit設定大一點,防止後面部署合約的時候報
Error: exceeds block gas limit undefined
的錯誤資訊,這是因為合約所需的gas超過了區塊的最大gas
建立好創世區塊後,我們需要利用該創世區塊去初始化區塊鏈,需要新建一個data0
目錄存放區塊資訊
接著在主目錄下,執行初始化區塊鏈的命令
geth --datadir data0 init genesis.json
複製程式碼
看到如下資訊則表明初始化成功
啟動私有鏈
geth --datadir 'data0' --identity 'my_block' --port '30304' console 2>>eth_output.log
複製程式碼
引數含義
–identity:指定節點 ID;
–rpc:表示開啟 HTTP-RPC 服務;
–rpcport:指定 HTTP-RPC 服務監聽埠號(預設為 8545);
–datadir:指定區塊鏈資料的儲存位置;
–port:指定和其他節點連線所用的埠號(預設為 30303);
–networkid: 指定私有鏈網路號;
–nodiscover:關閉節點發現機制,防止加入有同樣初始配置的陌生節點。
複製程式碼
看到下面的資訊表示私有鏈成功啟動
建立賬戶
現在剛啟動還沒有任何賬戶
我們利用personal
建立賬戶
personal.newAccount()
複製程式碼
Passphrase 就是密碼,連續輸入兩次密碼便可以建立一個賬戶
除了personal
,geth命令列中還包含了另外的物件
eth:包含一些跟操作區塊鏈相關的方法
net:包含以下檢視p2p網路狀態的方法
admin:包含一些與管理節點相關的方法
miner:包含啟動&停止挖礦的一些方法
personal:主要包含一些管理賬戶的方法
txpool:包含一些檢視交易記憶體池的方法
web3:包含了以上物件,還包含一些單位換算的方法
複製程式碼
按照這中方式可以多建立幾個賬戶,在後面使用
挖礦
我們剛開始還沒有進行挖礦,所以eth.accounts[0]
中的餘額為0
eth.getBalance(eth.accounts[0])
複製程式碼
挖礦所得的ETH會預設存在
eth.accounts[0]
的賬戶當中
通過miner.start()
來啟動挖礦,利用miner.stop()
停止挖礦
現在我們檢視賬戶的餘額就不在是0了
miner.start()
會返回null
,推薦使用tail -f eth_output.log
檢視日誌,這樣就可以看到挖礦的資訊
每挖到一個區塊會獎勵5個以太幣,日誌中可以看到到目前為止挖到了5個區塊,那麼就應該是20個以太幣,可是檢視的餘額卻不是這樣,這是因為這裡預設的單位是Wei,有關單位可以去移步以太幣(Ether)單位
可以利用下面的命令轉換單位
這裡只做簡單的介紹,其餘的命令大家有感興趣的可以自行查詢
在ETH私有鏈上釋出ERC20代幣
ERC20是Fabian Vogelsteller在2015年末提出的以太坊改進建議,它是許多流行的合約都在遵循的標準。
ERC20使通證智慧合約的行為非常類似於傳統的加密貨幣,例如在不同賬戶之間傳送和接收、檢視通證總供應量或者檢視某個地址可用的通證餘額,就像比特幣或以太幣一樣。這類似於用以太坊錢包 傳送和接收以太幣、檢視流通中的以太幣總量、檢視特定錢包的貨幣餘額等。
ERC20規定了一些標準的介面
contract ERC20Interface {
string public constant name = "Token Name";
string public constant symbol = "SYM";
uint8 public constant decimals = 18; // 18 is the most common number of decimal places
function totalSupply() public constant returns (uint);
function balanceOf(address tokenOwner) public constant returns (uint balance);
function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
function transfer(address to, uint tokens) public returns (bool success);
function approve(address spender, uint tokens) public returns (bool success);
function transferFrom(address from, address to, uint tokens) public returns (bool success);
event Transfer(address indexed from, address indexed to, uint tokens);
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}
複製程式碼
簡單的介紹一下,更加詳細的介紹可以在github檢視
name : 代幣名稱
symbol: 代幣符號
decimals: 代幣小數點位數,代幣的最小單位
totalSupply() : 發行代幣總量。
balanceOf(): 檢視對應賬號的代幣餘額。
transfer(): 實現代幣交易,用於給使用者傳送代幣(從我們的賬戶裡)
transferFrom(): 實現代幣使用者之間的交易
allowance(): 控制代幣的交易,如可交易賬號及資產
approve(): 允許使用者可花費的代幣數
複製程式碼
在ETH私有鏈上釋出ERC20代幣
ERC20是Fabian Vogelsteller在2015年末提出的以太坊改進建議,它是許多流行的合約都在遵循的標準。
ERC20使通證智慧合約的行為非常類似於傳統的加密貨幣,例如在不同賬戶之間傳送和接收、檢視通證總供應量或者檢視某個地址可用的通證餘額,就像比特幣或以太幣一樣。這類似於用以太坊錢包 傳送和接收以太幣、檢視流通中的以太幣總量、檢視特定錢包的貨幣餘額等。
ERC20規定了一些標準的介面
contract ERC20Interface {
string public constant name = "Token Name";
string public constant symbol = "SYM";
uint8 public constant decimals = 18; // 18 is the most common number of decimal places
function totalSupply() public constant returns (uint);
function balanceOf(address tokenOwner) public constant returns (uint balance);
function allowance(address tokenOwner, address spender) public constant returns (uint remaining);
function transfer(address to, uint tokens) public returns (bool success);
function approve(address spender, uint tokens) public returns (bool success);
function transferFrom(address from, address to, uint tokens) public returns (bool success);
event Transfer(address indexed from, address indexed to, uint tokens);
event Approval(address indexed tokenOwner, address indexed spender, uint tokens);
}
複製程式碼
簡單的介紹一下,更加詳細的介紹可以在github檢視
name : 代幣名稱
symbol: 代幣符號
decimals: 代幣小數點位數,代幣的最小單位
totalSupply() : 發行代幣總量。
balanceOf(): 檢視對應賬號的代幣餘額。
transfer(): 實現代幣交易,用於給使用者傳送代幣(從我們的賬戶裡)
transferFrom(): 實現代幣使用者之間的交易
allowance(): 控制代幣的交易,如可交易賬號及資產
approve(): 允許使用者可花費的代幣數
複製程式碼
編寫合約程式碼
先編寫一個符合ERC20標準的程式碼
pragma solidity ^0.4.16;
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }
contract TokenERC20 {
string public name;
string public symbol;
uint8 public decimals = 18; // 18 是建議的預設值
uint256 public totalSupply;
mapping (address => uint256) public balanceOf; //
mapping (address => mapping (address => uint256)) public allowance;
event Transfer(address indexed from, address indexed to, uint256 value);
event Burn(address indexed from, uint256 value);
function TokenERC20(uint256 initialSupply, string tokenName, string tokenSymbol) public {
totalSupply = initialSupply * 10 ** uint256(decimals);
balanceOf[msg.sender] = totalSupply;
name = tokenName;
symbol = tokenSymbol;
}
function _transfer(address _from, address _to, uint _value) internal {
require(_to != 0x0);
require(balanceOf[_from] >= _value);
require(balanceOf[_to] + _value > balanceOf[_to]);
uint previousBalances = balanceOf[_from] + balanceOf[_to];
balanceOf[_from] -= _value;
balanceOf[_to] += _value;
Transfer(_from, _to, _value);
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}
function transfer(address _to, uint256 _value) public {
_transfer(msg.sender, _to, _value);
}
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
require(_value <= allowance[_from][msg.sender]); // Check allowance
allowance[_from][msg.sender] -= _value;
_transfer(_from, _to, _value);
return true;
}
function approve(address _spender, uint256 _value) public
returns (bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}
function approveAndCall(address _spender, uint256 _value, bytes _extraData) public returns (bool success) {
tokenRecipient spender = tokenRecipient(_spender);
if (approve(_spender, _value)) {
spender.receiveApproval(msg.sender, _value, this, _extraData);
return true;
}
}
function burn(uint256 _value) public returns (bool success) {
require(balanceOf[msg.sender] >= _value);
balanceOf[msg.sender] -= _value;
totalSupply -= _value;
Burn(msg.sender, _value);
return true;
}
function burnFrom(address _from, uint256 _value) public returns (bool success) {
require(balanceOf[_from] >= _value);
require(_value <= allowance[_from][msg.sender]);
balanceOf[_from] -= _value;
allowance[_from][msg.sender] -= _value;
totalSupply -= _value;
Burn(_from, _value);
return true;
}
}
複製程式碼
然後把這段程式碼拷貝到Solidity,點選旁邊的Details獲取部署程式碼
然後在彈出層中複製WEB3DEPLOY中的程式碼到編輯器中
修改程式碼如圖中所示
然後將程式碼複製到geth命令列中,按回車。然後執行miner.start()
,如果出現以下資訊則表明代幣部署成功
如果出現
Error: authentication needed: password or unlock undefined
錯誤,是因為賬戶沒有被解鎖,執行personal.unlockAccount(eth.accounts[0])
然後輸入密碼解鎖賬戶
接著我們在命令列中輸入tokenerc20
則可以看到返回的介面資訊
address,表示合約地址
可以利用balanceOf
函式檢視地址的代幣餘額
至此,合約部署完畢
下一篇文章說說ERC20空投合約的三種實現方式