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.abiToken.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'

定義兩個用戶alicebob,並解鎖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

如圖所示

1602945325

相關說明:

1、VIP會員無限製任意下載,免積分。立即前往開通>>

2、下載積分可通過日常 簽到綁定郵箱 以及 積分兌換 等途徑獲得!

3、本站資源大多存儲在雲盤,如出現鏈接失效請評論反饋,如有密碼,均為:www.ipipn.com。

4、所有站內資源僅供學習交流使用。未經原版權作者許可,禁止用於任何商業環境,否則後果自負。為尊重作者版權,請購買正版作品。

5、站內資源來源於網絡公開發表文件或網友分享,如侵犯您的權益,請聯係管理員處理。

6、本站提供的源碼、模板、軟件工具等其他資源,都不包含技術服務,請大家諒解!

7、源碼、模板等資源會隨著技術、壞境的升級而存在部分問題,還請慎重選擇。

PS.源碼均收集自網絡,如有侵犯閣下權益,請發信件至: admin@ipipn.com .


源站網 » Geth在以太坊測試鏈(rinkeby testnet)發布ERC20 Token智能合約

發表評論

讚助本站發展 維持服務器消耗

全站源碼免費下載 立刻讚助