继续区块链的学习了,今天来学习如何发一个ERC20标准的代币
什么是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代币合约就创建成功了