继续区块链的学习了,今天来学习如何发一个ERC20标准的代币
然后就可以开始写合约了
pragma solidity ^0.4.11; contract ERC20Standard { uint256 public totalSupply; string public name; uint256 public decimals; string public symbol; address public owner; mapping (address => uint256) balances; mapping (address => mapping (address => uint256)) allowed; function ERC20Standard(uint256 _totalSupply, string _symbol, string _name) public { decimals = 18; symbol = _symbol; name = _name; owner = msg.sender; totalSupply = _totalSupply * (10 ** decimals); balances[msg.sender] = totalSupply; } //Fix for short address attack against ERC20,避免短地址攻击 modifier onlyPayloadSize(uint size) { assert(msg.data.length == size + 4); _; } function balanceOf(address _owner) constant public returns (uint256) { return balances[_owner]; } function transfer(address _recipient, uint256 _value) onlyPayloadSize(2*32) public { require(balances[msg.sender] >= _value && _value > 0); balances[msg.sender] -= _value; balances[_recipient] += _value; Transfer(msg.sender, _recipient, _value); } function transferFrom(address _from, address _to, uint256 _value) public { require(balances[_from] >= _value && allowed[_from][msg.sender] >= _value && _value > 0); balances[_to] += _value; balances[_from] -= _value; allowed[_from][msg.sender] -= _value; Transfer(_from, _to, _value); } function approve(address _spender, uint256 _value) public { allowed[msg.sender][_spender] = _value; Approval(msg.sender, _spender, _value); } function allowance(address _owner, address _spender) constant public returns (uint256) { return allowed[_owner][_spender]; } event Transfer( address indexed _from, address indexed _to, uint256 _value ); event Approval( address indexed _owner, address indexed _spender, uint256 _value ); }
知道基本的solidity语法应该就能看懂这个合约了,除了实现基本的ERC20标准以外还增加了一个修改器,用来限制参数位数,防止短地址攻击
关于短地址攻击,因为EVM在识别参数时并没有严格地校验参数的位数,而且还会自动补充消失的位数,如果调用合约的tranferFrom方法时有一个地址结尾是0的参数,当有人故意少写结尾的0时,EVM会把value前面的0当作地址的结尾0,而value缺少的位数就会自动补充,这时如果方法里还没有校验参数那就杯具了。
合约有了,接下来就开始编译和部署了,因为全节点太大,所以选择用infura去部署
编译合约我们需要用到solc(注意web3.eth.compile.solidity等相关编译方法在新版的geth中已经移除了),infura的使用可以参考我的另一篇文章
除了基本模块以外还需要引入以下几个模块
var fs = require("fs"); var Web3 = require('web3'); var solc = require('solc'); var Tx = require('ethereumjs-tx');
接下来就是运行代码
app.get('/Compile',function(req,res){ var sourceCode = fs.readFileSync('source.sol');//读取合约文件 var output = solc.compile(sourceCode.toString(), 1);//编译合约 var bytecode = output.contracts[':ERC20Standard'].bytecode;//提取编译成的bytecode var abi = output.contracts[':ERC20Standard'].interface;//提取编译成的abi var ERC20Standard =new web3.eth.Contract( JSON.parse(abi));//通过abi创建合约实例 var transactionObject=ERC20Standard.deploy({//获取合约部署交易对象 data: '0x'+bytecode, arguments: [10000000, 'MET',"MyERC20StandardToken"]//合约构造方法的参数 }) var transactiondata=transactionObject.encodeABI();//转化成交易代码 web3.eth.estimateGas({//估算gas,estimateGas估算的代码不准却,一般都会少一些,所以gaslimit要加上一点gas,大概加上十万 data: '0x'+bytecode }).then(function(gas){ var privateKey = new Buffer('这里是你的密钥', 'hex');//infura不掌控账户,发起任何交易都需要自己打包签名 var rawTx = { nonce: '0x0C',//nonce代表这个账户的交易次数,查看一下你账户的交易次数再填,nonce填错了也会报错 gasPrice: '0x3B9ACA00',//1gwei gasLimit: gas+100000, data: transactiondata } var tx = new Tx(rawTx); tx.sign(privateKey);//打包并用私钥签名 var serializedTx = tx.serialize();//序列化交易编码 var r= web3.eth.sendSignedTransaction('0x' + serializedTx.toString('hex'),function(err,hash){//发送交易 if(err!=null){ console.log(err); } else{ console.log("success"+hash); web3.eth.getTransaction(hash).then(console.log); } }); console.log(gas);//打印一下估算的gas }); res.send("智能合约"); })
node运行代码后浏览器访问端口/Compile就会在控制台打印交易发送信息,结果是
可以看到gas估算是519836
因为是测试网络,我设置的gas是3000000,实际上也只需要大概600000就行了,交易发送后可以在etherscan上查看,可以在测试网络实验一下
这是刚刚发送的交易,到这里一个ERC20代币合约就创建成功了