区块链学习: https://github.com/xianfeng92/Love-Ethereum
ERC20是以太坊定义的一个代币标准, 利用以太坊的智能合约可以轻松编写出属于自己的代币,这里的代币可以代表在平台或者社区的所拥有的权益。
太坊的代币标准
使用代币合约标准可以在以太坊上发行在区块链应用(Dapp)上所使用的代币。
Token
Methods
NOTE: Callers MUST handle false from returns (bool success). Callers MUST NOT assume that false is never returned!
调用者必须处理返回值为false的方法
name
Returns the name of the token
symbol
Returns the symbol of the token
decimals
Returns the number of decimals the token uses
totalSupply
Returns the total token supply
balanceOf
Returns the account balance of another account with address _owner
返回账户owner中的余额
transfer
Transfers _value amount of tokens to address _to, and MUST fire the Transfer event. The function SHOULD throw if the _from account balance does not have enough tokens to spend
Note Transfers of 0 values MUST be treated as normal transfers and fire the Transfer event
transferFrom
Transfers _value amount of tokens from address _from to address _to, and MUST fire the Transfer event
The transferFrom method is used for a withdraw workflow, allowing contracts to transfer tokens on your behalf. This can be used for example to allow a contract to transfer tokens on your behalf and/or to charge fees in sub-currencies
transferFrom方法用于"转账"流程,允许合约代表您转让token。 例如,这可用于允许合同代表您转让代币或者以子货币收取费用。
Note Transfers of 0 values MUST be treated as normal transfers and fire the Transfer event
approve
Allows _spender to withdraw from your account multiple times, up to the _value amount. If this function is called again it overwrites the current allowance with _value
允许spender从你的账户中多次"提取"token,直到规定的金额value
allowance
Returns the amount which _spender is still allowed to withdraw from _owner
返回账户spender可以从合约所有者owner中提取的token数量
Events
Transfer
MUST trigger when tokens are transferred, including zero value transfers.
涉及到token转移时必须触发此事件
A token contract which creates new tokens SHOULD trigger a Transfer event with the _from address set to 0x0 when tokens are created
代币合约部署时也需要触发此事件
Approval
MUST trigger on any successful call to approve(address _spender, uint256 _value)
代币合约实例
下面是一个 token 合约的例子,可以直接复制到remix进行合约部署的。
事先说明两点:
1token的发行很简单的,其价值还是要看和token所关联的项目。有很多组织没有项目就胡乱发token。
2token这个概论本身是有很大的价值的,如何发行和利用token,对项目成败也有很多影响。
pragma solidity ^0.4.20;
interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) public; }
contract TokenLBJ {
// 代币名称,如:"LeBron James"
string public name;
// 代币符合,一般用代币名称的缩写,如 LBJ
string public symbol;
// 每个代币可细分的到多少位,即最小代币单位。18为默认值
uint8 public decimals = 18;
// 代币总供应量,这里指的是一共有多少个以最小单位所计量的代币
uint256 public totalSupply;
// 用mapping保存每个地址对应的余额
mapping (address => uint256) public balanceOf;
// 存储对账号的控制,有两点需要注意的:1本合约账户可以授权其他账户转移token数量 2 其他账户授权该合约账户转移token数量
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 TokenLBJ(uint256 initialSupply, string tokenName, string tokenSymbol) public {
totalSupply = initialSupply * 10 ** uint256(decimals); // 供应的份额,份额跟最小的代币单位有关,份额 = 币数 * 10 ** decimals
balanceOf[msg.sender] = totalSupply; // 创建者拥有所有的代币
name = tokenName; // 代币名称
symbol = tokenSymbol; // 代币符号
}
/**
* 代币交易转移的内部实现,只能被本合约调用
*/
function _transfer(address _from, address _to, uint _value) internal {
// 确保目地地址不为0x0,因为0x0地址代表的是销毁
require(_to != 0x0);
// 确保发送者账户有足够的余额
require(balanceOf[_from] >= _value);
// 确保_value为正数,如果为负数,那相当于付款者账户钱越买越多~哈哈~
require(balanceOf[_to] + _value > balanceOf[_to]);
// 交易前,双方账户余额总和
uint previousBalances = balanceOf[_from] + balanceOf[_to];
// 将发送方账户余额减value
balanceOf[_from] -= _value;
// 将接收方账户余额加value
balanceOf[_to] += _value;
//通知客户端交易发生
Transfer(_from, _to, _value);
// 用assert来检查代码逻辑,即交易前后双发账户余额的和应该是相同的
assert(balanceOf[_from] + balanceOf[_to] == previousBalances);
}
/**
* 从合约创建交易者账号发送`_value`个代币到 `_to`账号
* 这种类似于现在很多项目的空头吧,很多空气项目其实也就是在以太坊上发个币,然后就割韭菜,所以学学区块链技术还是很有必要的
* @param _to 接收者地址
* @param _value 转移数额
*/
function transfer(address _to, uint256 _value) public {
//_from始终是合约创建者的地址
_transfer(msg.sender, _to, _value);
}
/**
*批量转帐固定金额
/
function batchTransfer(address[] _to, uint _value) public{
require(address.length > 0)
for(uint i=0 ; i< address.length; i++){
_transfer(msg.sender,address[i], _value);
}
}
/**
* Transfer tokens from other address
* Send '_value' tokens to '_to' on behalf of '_from'
* @param _from 发送者地址
* @param _to 接收者地址
* @param _value 转移数额
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
// _from 所授权 msg.sender 转移的代币数量
require(_value <= allowance[_from][msg.sender]);
allowance[_from][msg.sender] -= _value;
_transfer(_from, _to, _value);
return true;
}
/**
*
* 允许发送者`_spender` 花费不多于 `_value` 个代币
*
* @param _spender The address authorized to spend
* @param _value the max amount they can spend
*/
function approve(address _spender, uint256 _value) public
returns (bool success) {
//具体结构如下:(msg.sender --> allowance[msg.sender])--> mapping(address => uint)
// msg.sender 允许 _spender 最多花费的代币数量
allowance[msg.sender][_spender] = _value;
return true;
}
/**
* 设置允许一个地址(合约)以我(创建交易者)的名义可最多花费的代币数。
*
* @param _spender 被授权的地址(合约)
* @param _value 最大可花费代币数
* @param _extraData 发送给合约的附加数据
*/
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);
// 将合约账户余额减少value
balanceOf[msg.sender] -= _value;
// 对应代币总供应量也应该减少
totalSupply -= _value;
Burn(msg.sender, _value);
return true;
}
/**
* 销毁用户账户中指定个代币
*
* Remove `_value` tokens from the system irreversibly on behalf of `_from`.
*
* @param _from the address of the sender
* @param _value the amount of money to burn
*/
function burnFrom(address _from, uint256 _value) public returns (bool success) {
require(balanceOf[_from] >= _value); // Check if the targeted balance is enough
require(_value <= allowance[_from][msg.sender]); // Check allowance
balanceOf[_from] -= _value; // Subtract from the targeted balance
allowance[_from][msg.sender] -= _value; // Subtract from the sender's allowance
totalSupply -= _value; // Update totalSupply
Burn(_from, _value);
return true;
}
}
给代币增加可管理、增发、兑换、冻结等功能
contract owned{
address public owner;
function owned(){
owner = msg.sender;
}
//
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) onlyOwner{
owner = newOwner;
}
}
该合约中加入了一个函数修改器(Function Modifiers)onlyOwner,函数修改器是一个合约属性,可以被继承,还能被重写。它用于在函数执行前检查某种前置条件。
此时让TokenLBJ继承owned,使其拥有onlyOwner修改器。
代币增发
function mintToken(address target, uint256 mintedAmount) onlyOwner{
balanceof[target] += mintedAmount;
totalSupply += mintedAmount;
Transfer(0,owner,mintedAmount);
Transfer(owner, target, mintedAmount);
}
注意onlyOwner修改器添加在函数末尾,这表示只有ower才能调用这用函数。他的功能很简单,就是给指定的账户增加代币,同时增加总供应量。
资产冻结
有时为了监管的需要,需要实现冻结某些账户,冻结后,其资产仍在账户,但是不允许交易,直到解除冻结。给合约添加以下的变量和方法(可以添加到合约的任何地方,但是建议把mapping加到和其他mapping一起,event也是如此):
mapping(address => bool) public frozenAccount;
event FrozenFunds(address target, bool frozen);
function freezeAccount(address target, bool freeze) onlyOwner {
frozenAccount[target] = freeze;
FrozenFunds(target, freeze);
}
单单以上的代码还无法冻结,需要把他加入到transfer函数中才能真正生效,因此修改transfer函数。
function transfer(address _to, uint256 _value){
require(!frozenAccount[msg.sender])
...
}
这样在转账前,对发起交易的账号做一次检查,只有没被冻结的账号才能转账。
代币买卖(兑换)
可以自己的货币中实现代币与其他数字货币(ether 或其他tokens)的兑换机制。有了这个功能,我们的合约就可以在一买一卖中赚利润了。
先来设置下买卖价格:
uint256 public sellPrice;
uint256 public buyPrice;
function setPrice (uint256 newSellPrice, uint256 newBuyPrice) onlyOwner{
sellPrice = newSellPrice;
buyPrice = newBuyPrice;
}
setPrices()添加了onlyOwner修改器,注意买卖的价格单位是wei(最小的货币单位: 1 eth = 1000000000000000000 wei)。
添加买卖函数:
function buy() payable returns (uint amount){
amount = msg.value / buyPrice;
require(balanceof[this] >= amount);
balanceof[msg.sender] += amount;
balanceof[this] -= amount;
Transfer(this, msg.sender,amount);
return amount;
}
function sell(uint amount) payable returns (uint revenue){
require(balanceof[msg.sender] >= amount);
balanceof[this] += amount;
balanceof[msg.sender] -= amount;
revenue = amount * sellPrice;
msg.sender.transfer(revenue);
Transfer(msg.sender, this, amount);
return revenue;
}
加入了买卖功能后,要求我们在创建合约时发送足够的以太币,以便合约有能力回购市面上的代币,否则合约将破产,用户没法先合约卖代币。
参考:Create your own crypto-currency