Geth在以太坊測試鏈(rinkeby testnet)發布ERC20 Token智能合約
接上麵文章Geth部署以太坊測試鏈(testnet),下麵我們將在太坊rinkeby測試鏈發布一套自己的數字幣,當然隻是在測試環境。
ERC20
以太坊是一個智能合約平台,其中ERC20是以太坊應用程序級別的標準和約定,隻要按照這個約定我們都可以在以太坊平台發幣,以太坊支持的協議列表,這裏麵定了很多種標準規範,而ERC20就是其中的一種,定義的一些標準如下
Methods
- name
function name() constant returns (string name)
返回string類型的ERC20代幣的名字,例如:StatusNetwork
- symbol
function symbol() constant returns (string symbol)
返回string類型的ERC20代幣的符號,也就是代幣的簡稱,例如:SNT
- decimals
function decimals() constant returns (uint8 decimals)
支持幾位小數點後幾位。如果設置為3。也就是支持0.001表示
- totalSupply
function totalSupply() constant returns (uint256 totalSupply)
發行代幣的總量,可以通過這個函數來獲取。所有智能合約發行的代幣總量是一定的,totalSupply必須設置初始值。如果不設置初始值,這個代幣發行就說明有問題。
- balanceOf
function balanceOf(address _owner) constant returns (uint256 balance)
輸入地址,可以獲取該地址代幣的餘額
- transfer
function transfer(address _to, uint256 _value) returns (bool success)
調用transfer函數將自己的token轉賬給_to地址,_value為轉賬個數
- approve
function approve(address _spender, uint256 _value) returns (bool success)
批準_spender賬戶從自己的賬戶轉移_value個token。可以分多次轉移
- transferFrom
function transferFrom(address _from, address _to, uint256 _value) returns (bool success)
與approve搭配使用,approve批準之後,調用transferFrom函數來轉移token
- allowance
function allowance(address _owner, address _spender) constant returns (uint256 remaining)
返回_spender還能提取token的個數
Events
- Transfer
event Transfer(address indexed _from, address indexed _to, uint256 _value)
當成功轉移token時,一定要觸發Transfer事件
- Approval
event Approval(address indexed _owner, address indexed _spender, uint256 _value)
當調用approval函數成功時,一定要觸發Approval事件
部署
合約是使用Solidity語言進行編寫,如果你不了解Solidity,你可以看一下下麵的代碼,如果有不懂的可以參考官方文檔,我感覺語法還是挺簡單的,非常簡潔明了,創建一個名為token.sol
的合約文件
$ vim token.sol
pragma solidity ^0.7.0;
library SafeMath {
function sub(uint256 a, uint256 b) internal pure returns (uint256) {
assert(b <= a);
return a - b;
}
function add(uint256 a, uint256 b) internal pure returns (uint256) {
uint256 c = a + b;
assert(c >= a);
return c;
}
}
interface ERC20Standard {
// 代幣的名字,例如:StatusNetwork
function name() external view returns (string memory);
// 代幣的符號,也就是代幣的簡稱,例如:SNT
function symbol() external view returns (string memory);
// 支持幾位小數點後幾位。如果設置為3。也就是支持0.001表示
function decimals() external view returns (uint8);
// 發行代幣的總量
function totalSupply() external view returns (uint256);
// 獲取該地址代幣的餘額
function balanceOf(address _owner) external view returns (uint256 balance);
// 將自己的token轉賬給_to地址,_value為轉賬個數
function transfer(address _to, uint256 _value)
external
returns (bool success);
// 與approve搭配使用,approve批準之後,調用transferFrom函數來轉移token
function transferFrom(
address _from,
address _to,
uint256 _value
) external returns (bool success);
// 批準_spender賬戶從自己的賬戶轉移_value個token。可以分多次轉移
function approve(address _spender, uint256 _value)
external
returns (bool success);
// 返回_spender還能提取token的個數
function allowance(address _owner, address _spender)
external
view
returns (uint256 remaining);
event Transfer(address indexed _from, address indexed _to, uint256 _value);
event Approval(
address indexed _owner,
address indexed _spender,
uint256 _value
);
}
contract Token is ERC20Standard {
string name_;
string symbol_;
uint8 decimals_;
uint256 totalSupply_;
using SafeMath for uint256;
mapping(address => uint256) balances;
mapping(address => mapping(address => uint256)) allowed;
// 初始化,並把初始化的發行數量分配給擁有者
constructor(
// string memory name,
// string memory symbol,
// uint8 decimals,
// uint256 totalSupply
) public {
// name_ = name;
// symbol_ = symbol;
// decimals_ = decimals;
// totalSupply_ = totalSupply;
// balances[msg.sender] = totalSupply;
name_ = "ansheng";
symbol_ = "as";
decimals_ = 18;
totalSupply_ = 10000000000000000000;
balances[msg.sender] = 10000000000000000000;
}
function name() public override view returns (string memory) {
return name_;
}
function symbol() public override view returns (string memory) {
return symbol_;
}
function decimals() public override view returns (uint8) {
return decimals_;
}
function totalSupply() public override view returns (uint256) {
return totalSupply_;
}
function balanceOf(address _owner)
public
override
view
returns (uint256 balance)
{
return balances[_owner];
}
function transfer(address _to, uint256 _value)
public
override
returns (bool success)
{
// 檢查發送者賬戶餘額是否足夠
require(_value <= balances[msg.sender]);
// 發送者減少餘額
balances[msg.sender] -= _value;
// 接受者增加餘額
balances[_to] += _value;
emit Transfer(msg.sender, _to, _value);
return true;
}
function transferFrom(
address _from,
address _to,
uint256 _value
) public override returns (bool success) {
// 檢查發送者的餘額是否足夠
require(_value <= balances[_from]);
require(_value <= allowed[_from][msg.sender]);
balances[_from] -= _value;
allowed[_from][msg.sender] -= _value;
balances[_to] += _value;
Transfer(_from, _to, _value);
return true;
}
function approve(address _spender, uint256 _value)
public
override
returns (bool success)
{
allowed[msg.sender][_spender] = _value;
emit Approval(msg.sender, _spender, _value);
return true;
}
function allowance(address _owner, address _spender)
public
override
view
returns (uint256 remaining)
{
return allowed[_owner][_spender];
}
}
由於我的客戶端是macOS,我還需要通過brew安裝Solidity來進行代碼編譯,如果你是其他平台請參考官方文檔Installing the Solidity Compiler
$ brew update
$ brew upgrade
$ brew tap ethereum/ethereum
$ brew install solidity
$ geth solc --version
solc, the solidity compiler commandline interface
Version: 0.7.2+commit.51b20bc0.Darwin.appleclang
- 代碼編譯
忽略Warning:
$ solc --abi --bin -o solcoutput token.sol
$ ls solcoutput
ERC20Standard.abi ERC20Standard.bin SafeMath.abi SafeMath.bin Token.abi Token.bin combined.json
對我們有用的文件隻有Token.abi
和Token.bin
,下麵登陸節點服務器並進入console
$ geth attach /usr/local/etc/geth/geth.ipc
# 我把之前的用戶都刪掉了,目前是空的
> eth.accounts
[]
# 創建用戶
> personal.newAccount("ansheng.me")
"0xdf3d8df4ee370e96a2338d389c5821e154b092e9"
> personal.newAccount("ansheng")
"0xda0cfe3a0772995f83399b1ae82afbbddf5aedd2"
# 解鎖
> personal.unlockAccount("0xdf3d8df4ee370e96a2338d389c5821e154b092e9", "ansheng.me", 0)
true
# 發布合約需要有餘額才可以,剛創建的用戶餘額是0,所以你需要去https://www.rinkeby.io/#faucet弄點幣才行
> eth.getBalance('0xdf3d8df4ee370e96a2338d389c5821e154b092e9')
2000000000000000000
# 發布合約
# solcoutput/Token.abi 的內容
var abi = [{ "inputs": [], "stateMutability": "nonpayable", "type": "constructor" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "_owner", "type": "address" }, { "indexed": true, "internalType": "address", "name": "_spender", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "_value", "type": "uint256" }], "name": "Approval", "type": "event" }, { "anonymous": false, "inputs": [{ "indexed": true, "internalType": "address", "name": "_from", "type": "address" }, { "indexed": true, "internalType": "address", "name": "_to", "type": "address" }, { "indexed": false, "internalType": "uint256", "name": "_value", "type": "uint256" }], "name": "Transfer", "type": "event" }, { "inputs": [{ "internalType": "address", "name": "_owner", "type": "address" }, { "internalType": "address", "name": "_spender", "type": "address" }], "name": "allowance", "outputs": [{ "internalType": "uint256", "name": "remaining", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_spender", "type": "address" }, { "internalType": "uint256", "name": "_value", "type": "uint256" }], "name": "approve", "outputs": [{ "internalType": "bool", "name": "success", "type": "bool" }], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_owner", "type": "address" }], "name": "balanceOf", "outputs": [{ "internalType": "uint256", "name": "balance", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "decimals", "outputs": [{ "internalType": "uint8", "name": "", "type": "uint8" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "name", "outputs": [{ "internalType": "string", "name": "", "type": "string" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "symbol", "outputs": [{ "internalType": "string", "name": "", "type": "string" }], "stateMutability": "view", "type": "function" }, { "inputs": [], "name": "totalSupply", "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], "stateMutability": "view", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_to", "type": "address" }, { "internalType": "uint256", "name": "_value", "type": "uint256" }], "name": "transfer", "outputs": [{ "internalType": "bool", "name": "success", "type": "bool" }], "stateMutability": "nonpayable", "type": "function" }, { "inputs": [{ "internalType": "address", "name": "_from", "type": "address" }, { "internalType": "address", "name": "_to", "type": "address" }, { "internalType": "uint256", "name": "_value", "type": "uint256" }], "name": "transferFrom", "outputs": [{ "internalType": "bool", "name": "success", "type": "bool" }], "stateMutability": "nonpayable", "type": "function" }];
# 0x後麵加編譯的字節碼
var bytecode = "0x608060405234801561001057600080fd5b506040518060400160405280600781526020017f616e7368656e67000000000000000000000000000000000000000000000000008152506000908051906020019061005c929190610125565b506040518060400160405280600281526020017f6173000000000000000000000000000000000000000000000000000000000000815250600190805190602001906100a8929190610125565b506012600260006101000a81548160ff021916908360ff160217905550678ac7230489e80000600381905550678ac7230489e80000600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055506101c2565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061016657805160ff1916838001178555610194565b82800160010185558215610194579182015b82811115610193578251825591602001919060010190610178565b5b5090506101a191906101a5565b5090565b5b808211156101be5760008160009055506001016101a6565b5090565b610b18806101d16000396000f3fe608060405234801561001057600080fd5b50600436106100935760003560e01c8063313ce56711610066578063313ce5671461022157806370a082311461024257806395d89b411461029a578063a9059cbb1461031d578063dd62ed3e1461038157610093565b806306fdde0314610098578063095ea7b31461011b57806318160ddd1461017f57806323b872dd1461019d575b600080fd5b6100a06103f9565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156100e05780820151818401526020810190506100c5565b50505050905090810190601f16801561010d5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6101676004803603604081101561013157600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019092919050505061049b565b60405180821515815260200191505060405180910390f35b61018761058d565b6040518082815260200191505060405180910390f35b610209600480360360608110156101b357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610597565b60405180821515815260200191505060405180910390f35b610229610802565b604051808260ff16815260200191505060405180910390f35b6102846004803603602081101561025857600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610819565b6040518082815260200191505060405180910390f35b6102a2610862565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156102e25780820151818401526020810190506102c7565b50505050905090810190601f16801561030f5780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b6103696004803603604081101561033357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610904565b60405180821515815260200191505060405180910390f35b6103e36004803603604081101561039757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610a5b565b6040518082815260200191505060405180910390f35b606060008054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156104915780601f1061046657610100808354040283529160200191610491565b820191906000526020600020905b81548152906001019060200180831161047457829003601f168201915b5050505050905090565b600081600560003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b6000600354905090565b6000600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020548211156105e557600080fd5b600560008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482111561066e57600080fd5b81600460008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600560008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff168473ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a3600190509392505050565b6000600260009054906101000a900460ff16905090565b6000600460008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b606060018054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156108fa5780601f106108cf576101008083540402835291602001916108fa565b820191906000526020600020905b8154815290600101906020018083116108dd57829003601f168201915b5050505050905090565b6000600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205482111561095257600080fd5b81600460003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600460008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600560008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205490509291505056fea2646970667358221220a2485e1b926d17cf9231b535aab86329faa059bc67d41f8c6868ea77274bb46464736f6c63430007020033";
var simpleContract = web3.eth.contract(abi);
var simple = simpleContract.new(42, {
from: "0xdf3d8df4ee370e96a2338d389c5821e154b092e9",
data: bytecode,
gas: 0x47b760
}, function (e, contract) {
if (e) {
console.log("err creating contract", e);
} else {
if (!contract.address) {
console.log("Contract transaction send: TransactionHash: " + contract.transactionHash + " waiting to be mined...");
} else {
console.log("Contract mined! Address: " + contract.address);
}
}
});
# Contract transaction send: TransactionHash: 0x5d45beeb2337d3fcc8b95d4c2e6545ce6ca890db16b91b3c962b7385595bb82e waiting to be mined...
# ......
# 這就是我們的合約地址,需要記錄下麵
# > Contract mined! Address: 0xe16c5613b71edaa9bf2d0edd1f7ce90708904d48
# 隻要得到上麵的合約地址就可以推出了
> exit
如果剛創建的賬戶餘額不足,會提示以下錯誤,這個時候就需要賬戶有幣才可以
err creating contract Error: insufficient funds for gas * price + value
操作
我們可以通過下麵的地址查看測試鏈的合約信息
https://rinkeby.etherscan.io/token/0xe16c5613b71edaa9bf2d0edd1f7ce90708904d48
合約發布之後我們就來試試下麵的功能把,當然還是通過web3py進行操作,當然你可以可以參考管飯文檔Working with an ERC20 Token Contract
- 創建連接
>>> from web3 import Web3
>>> web3 = Web3(Web3.HTTPProvider("http://34.92.29.146:23456", request_kwargs={'timeout': 60}))
- 創建contract factory
>>> import json
>>> with open('./solcoutput/Token.abi') as f:
... ABI = json.load(f)
...
>>> contract_address = Web3.toChecksumAddress('0xe16c5613b71edaa9bf2d0edd1f7ce90708904d48')
>>> contract = web3.eth.contract(contract_address, abi=ABI)
>>> contract.address
'0xe16c5613B71edaA9bF2d0EdD1f7CE90708904d48'
定義兩個用戶alice
和bob
,並解鎖account,不然無法進行轉賬交易
# alice是合約的發布者,賬戶自帶餘額
>>> alice = Web3.toChecksumAddress("0xdf3d8df4ee370e96a2338d389c5821e154b092e9")
>>> bob = Web3.toChecksumAddress("0xDa0CFe3A0772995f83399b1AE82AFBbDDf5aeDD2")
>>> web3.geth.personal.unlock_account(alice, 'ansheng.me', 0)
True
>>> web3.geth.personal.unlock_account(bob, 'ansheng', 0)
True
代幣的名字
>>> contract.functions.name().call()
'ansheng'
代幣的簡稱
>>> contract.functions.symbol().call()
'as'
支持幾位小數點後幾位
>>> decimals = contract.functions.decimals().call()
>>> decimals
18
發行代幣的總量
>>> contract.functions.totalSupply().call()
10000000000000000000
賬戶餘額查詢
>>> contract.functions.balanceOf(alice).call()
10000000000000000000
>>> contract.functions.balanceOf(bob).call()
0
alice轉賬100到bob賬戶
>>> from web3.middleware import geth_poa_middleware
>>> web3.middleware_onion.inject(geth_poa_middleware, layer=0)
>>> contract.functions.transfer(bob, 100).transact({'from': alice})
HexBytes('0x1b811756a580e4ad7bb9fcf87549a5f24545c8589fb3af0f08fd0df93830bc1c')
>>> contract.functions.balanceOf(alice).call()
9999999999999999900
>>> contract.functions.balanceOf(bob).call()
100
alice將批準bob允許消費200,錢從alice中扣除
>>> contract.functions.allowance(alice, bob).call()
0
>>> contract.functions.approve(bob, 200).transact({'from': alice})
HexBytes('0x0471a04b21655e951e63904111fd812503abbc730c475680ffabdd9206d6a48e')
>>> contract.functions.allowance(alice, bob).call()
0
bob轉賬的時候從alice賬戶扣
# 轉賬交易是需要消耗ETH的,我們的bob賬號目前餘額是0
>>> web3.eth.getBalance(bob)
0
# alice給bob轉1個ETH
>>> web3.eth.sendTransaction({'to': bob,'from': alice,'value': web3.toWei('1','ether')})
HexBytes('0x56dd28db03b8a1b64affcf82f080d4879e7683a912d0e6711f30cd010558e86a')
# 等待礦工操作完成之後就有餘額了
>>> web3.eth.getBalance(bob)
1000000000000000000
# 查看bob可以透支的餘額
>>> contract.functions.allowance(alice, bob).call()
200
# bob現在的餘額
>>> contract.functions.balanceOf(bob).call()
100
>>> contract.functions.transferFrom(alice, bob, 75).transact({'from': bob})
HexBytes('0x1c316e4156dcb8b7edefd2f08c4309169ef77358aacdb0d25af4ebd8d98fe89a')
>>> contract.functions.allowance(alice, bob).call()
125
>>> contract.functions.balanceOf(bob).call()
175
至此,本片結束,可以通過下麵的連接查看交易的記錄
https://rinkeby.etherscan.io/token/0xe16c5613b71edaa9bf2d0edd1f7ce90708904d48
如圖所示
相關說明:
1、VIP會員無限製任意下載,免積分。立即前往開通>>
2、下載積分可通過日常 簽到、綁定郵箱 以及 積分兌換 等途徑獲得!
3、本站資源大多存儲在雲盤,如出現鏈接失效請評論反饋,如有密碼,均為:www.ipipn.com。
4、所有站內資源僅供學習交流使用。未經原版權作者許可,禁止用於任何商業環境,否則後果自負。為尊重作者版權,請購買正版作品。
5、站內資源來源於網絡公開發表文件或網友分享,如侵犯您的權益,請聯係管理員處理。
6、本站提供的源碼、模板、軟件工具等其他資源,都不包含技術服務,請大家諒解!
7、源碼、模板等資源會隨著技術、壞境的升級而存在部分問題,還請慎重選擇。
PS.源碼均收集自網絡,如有侵犯閣下權益,請發信件至: admin@ipipn.com .
源站網 » Geth在以太坊測試鏈(rinkeby testnet)發布ERC20 Token智能合約