首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

以太坊代币进阶

我先后写了4篇关于区块链和以太坊的文章,比较零散,有需要的同学可以翻看历史记录。继上一篇发行一个简单的代币之后,接下来准备写三篇在以太坊上开发Dapp的文章,想学习区块链开发的同学可以关注下公众号。

在以太坊上发行自己的代币

以太坊代币进阶(本篇);

以太坊上创建众筹合约;

以太坊上建立去中心自动化组织(DAO);

上一篇文章我们实现了一个简单的代币MyToken,作为一个demo入门,大家应该都了解了代币发行的基本流程。如果要实际发行MyToken的话,则需要做更多的工作,如实现合约的买卖功能等。

ERC20代币

先放上合约代码。

pragma solidity ^0.4.16;

interfacetokenRecipient{

functionreceiveApproval(

address _from,

uint256 _value,

address _token,

bytes _extraData)public;

}contract TokenERC20 {// Public variables of the tokenstringpublicname; stringpublicsymbol;

// 18 decimals is the strongly suggested default, avoid changing ituint8publicdecimals =18;uint256publictotalSupply;

// This creates an array with all balancesmapping (address => uint256)publicbalanceOf;

// This generates a public event on the blockchain that will notify clientsmapping (address => mapping (address => uint256))publicallowance;event Transfer(address indexed from, address indexed to, uint256 value);// This notifies clients about the amount burntevent Burn(address indexed from, uint256 value);

/** * Constructor function * * Initializes contract with initial supply tokens to the creator of the contract */functionTokenERC20( uint256 initialSupply, string tokenName, string tokenSymbol )public{

// Update total supply with the decimal amounttotalSupply = initialSupply *10** uint256(decimals);balanceOf[msg.sender] = totalSupply;name = tokenName;symbol = tokenSymbol;}

/** * Internal transfer, only can be called by this contract */function_transfer(address _from, address _to, uint _value)internal{

// Prevent transfer to 0x0 address. Use burn() insteadrequire(_to !=0x0);

// Check if the sender has enoughrequire(balanceOf[_from] >= _value);

// Check for overflowsrequire(balanceOf[_to] + _value > balanceOf[_to]);

// Save this for an assertion in the futureuint previousBalances = balanceOf[_from] + balanceOf[_to];

// Subtract from the senderbalanceOf[_from] -= _value;// Add the same to the recipientbalanceOf[_to] += _value; Transfer(_from, _to, _value);

// Asserts are used to use static analysis to find bugs in your code. They should never failassert(balanceOf[_from] + balanceOf[_to] == previousBalances); }

/** * Transfer tokens * * Send `_value` tokens to `_to` from your account * *@param_to The address of the recipient *@param_value the amount to send */functiontransfer(address _to, uint256 _value)public{ _transfer(msg.sender, _to, _value); }

/** * Transfer tokens from other address * * Send `_value` tokens to `_to` on behalf of `_from` * *@param_from The address of the sender *@param_to The address of the recipient *@param_value the amount to send */functiontransferFrom(address _from, address _to, uint256 _value)publicreturns(bool success){

require(_value

/** * Set allowance for other address * * Allows `_spender` to spend no more than `_value` tokens on your behalf * *@param_spender The address authorized to spend *@param_value the max amount they can spend */functionapprove(address _spender, uint256 _value)publicreturns(bool success){ allowance[msg.sender][_spender] = _value;

returntrue; }

/** * Set allowance for other address and notify * * Allows `_spender` to spend no more than `_value` tokens on your behalf, and then ping the contract about it * *@param_spender The address authorized to spend *@param_value the max amount they can spend *@param_extraData some extra information to send to the approved contract */functionapproveAndCall(address _spender, uint256 _value, bytes _extraData)publicreturns(bool success){ tokenRecipient spender = tokenRecipient(_spender);

if(approve(_spender, _value)) { spender.receiveApproval(msg.sender, _value, this, _extraData);

returntrue; } }

/** * Destroy tokens * * Remove `_value` tokens from the system irreversibly * *@param_value the amount of money to burn */functionburn(uint256 _value)publicreturns(bool success){

require(balanceOf[msg.sender] >= _value);// Check if the sender has enoughbalanceOf[msg.sender] -= _value;// Subtract from the sendertotalSupply -= _value;// Updates totalSupplyBurn(msg.sender, _value);

returntrue; }

/** * Destroy tokens from other account * * 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 */functionburnFrom(address _from, uint256 _value)publicreturns(bool success){

require(balanceOf[_from] >= _value);// Check if the targeted balance is enoughrequire(_value

returntrue; }}

ERC20合约中提供了更多的函数,如approve(),sendFrom()等。sendFrom()函数允许其他人从我们的账户中转账给其他账户,这在现实场景中是真实存在的,如财务人员用公司的账户给员工发工资等。但我们必须给予限额,避免他人把我们账户的钱全部转走了。approve()函数就提供了这个功能,但approve()函数不会通知对方操作限额,所以如果你在设置限额的时候希望对方可以收到通知,可以使用approveAndCall()。

基于安全性考虑,转账必须只有合约本身才可调用,所以这里把转账功能单独出来并声明为internal。

/* Internal transfer, can only be called by this contract */function_transfer(address _from, address_to, uint _value) internal { require (_to !=0x0);// Prevent transfer to 0x0 address. Use burn() insteadrequire (balanceOf[_from] >= _value);// Check if the sender has enoughrequire (balanceOf[_to] + _value > balanceOf[_to]);// Check for overflowsrequire(!frozenAccount[_from]);// Check if sender is frozenrequire(!frozenAccount[_to]);// Check if recipient is frozenbalanceOf[_from] -= _value;// Subtract from the senderbalanceOf[_to] += _value;// Add the same to the recipientTransfer(_from,_to, _value); }

所有涉及转账功能的函数,都需要先做各自的数据安全性检查,然后调用_transfer()函数实现转账。

代币管理

所有的dapp都是分布式的,但为了防止代币被乱用、失去控制,我们需要实现相关管理功能,如冻结用户,冻结资产,发行更多代币等。与一般app不同的是,所有这些规则都需要在代币部署前完成,一经部署,规则就无法改变了。

管理员可以是一个账户,也可以是另一个合约。如果合约实现是一个去中心化组织的话,将可以有效限制合约拥有者的权利。

我们使用继承来实现代币管理合约。

contract owned { addresspublicowner;

functionowned(){ owner = msg.sender; } modifier onlyOwner {

require(msg.sender == owner); _; }

functiontransferOwnership(address newOwner)onlyOwner{ owner = newOwner; }}

这个合约定义了谁拥有合约,并提供一个modifier修饰器,被修饰的函数只有拥有者才有调用权限。接下来让代币合约继承owed合约就可以了。

contractTokenERC20isowned {

/* the rest of the contract as usual */

这样我们就实现了一个简单的管理合约。

代币发行模型

目前市场上很多的代币发行策略要么是通胀模型,要么是固定最大值。接下来我们实现这个功能。

首先,定义一个状态变量totalSupply,在构造函数中赋值。

contractTokenERC20{ uint256publictotalSupply;

functionTokenERC20(...){ totalSupply = initialSupply; ... } ...}

接下来添加一个代币发行函数:

functionmintToken(address target, uint256 mintedAmount)onlyOwner{ balanceOf[target] += mintedAmount; totalSupply += mintedAmount; Transfer(, owner, mintedAmount); Transfer(owner, target, mintedAmount); }

函数通过给目标地址增加一定数量的代币,从而增加代币的整体供应量。

冻结账户资产

几乎所有的资产发行都存在冻结功能,冻结一定数量的资产可以获得更多的代币收益。我们也给代币实现冻结功能。添加如下代码:

mapping (address =>bool)publicfrozenAccount;

eventFrozenFunds(address target,boolfrozen);

functionfreezeAccount(address target,boolfreeze) onlyOwner{ frozenAccount[target] = freeze; FrozenFunds(target, freeze); }

所有账户默认不会被冻结,owner可以通过调用freezenAccount()来冻结某些账户。被冻结的账户不能转账,在transfer()函数中添加一行代码:

functiontransfer(address _to, uint256 _value){

require(!frozenAccount[msg.sender]);

代币交易

一个完善的代币应该可以用以太币购买,也可以兑换成以太币。接下来为我们的代币实现交易功能。

首先,定义买卖价格:

uint256public sellPrice;

uint256public buyPrice;

functionsetPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner {

sellPrice= newSellPrice;

buyPrice= newBuyPrice; }

接下来定义买卖函数:

functionbuy()payablereturns(uint amount){ amount = msg.value / buyPrice;// calculates the amountrequire(balanceOf[this] >= amount);// checks if it has enough to sellbalanceOf[msg.sender] += amount;// adds the amount to buyer's balancebalanceOf[this] -= amount;// subtracts amount from seller's balanceTransfer(this, msg.sender, amount);// execute an event reflecting the changereturnamount;// ends function and returns}

functionsell(uint amount)returns(uint revenue){

require(balanceOf[msg.sender] >= amount);// checks if the sender has enough to sellbalanceOf[this] += amount;// adds the amount to owner's balancebalanceOf[msg.sender] -= amount;// subtracts the amount from seller's balancerevenue = amount * sellPrice; msg.sender.transfer(revenue);// sends ether to the seller: it's important to do this last to prevent recursion attacksTransfer(msg.sender,this, amount);// executes an event reflecting on the changereturnrevenue;// ends function and returns}

这里买卖的价格单位不是Ether,而是wei,1 ether = 10^18 wei。

最后

为了代码结构清晰,我们把本文添加的代币功能放在一个新合约MyAdvancedToken中,并继承owned和TokenERC20,最后的代码如下:

pragma solidity ^0.4.16;contract owned { addresspublicowner;

functionowned()public{ owner = msg.sender; } modifier onlyOwner {

require(msg.sender == owner); _; }

functiontransferOwnership(address newOwner)onlyOwnerpublic{ owner = newOwner; }}

interfacetokenRecipient{functionreceiveApproval(address _from, uint256 _value, address _token, bytes _extraData)public; }contract TokenERC20 {// Public variables of the tokenstringpublicname; stringpublicsymbol; uint8publicdecimals =18;// 18 decimals is the strongly suggested default, avoid changing ituint256publictotalSupply;// This creates an array with all balancesmapping (address => uint256)publicbalanceOf; mapping (address => mapping (address => uint256))publicallowance;// This generates a public event on the blockchain that will notify clientsevent Transfer(address indexed from, address indexed to, uint256 value);// This notifies clients about the amount burntevent Burn(address indexed from, uint256 value);

/** * Constrctor function * * Initializes contract with initial supply tokens to the creator of the contract */functionTokenERC20( uint256 initialSupply, string tokenName, string tokenSymbol )public{ totalSupply = initialSupply *10** uint256(decimals);// Update total supply with the decimal amountbalanceOf[msg.sender] = totalSupply;// Give the creator all initial tokensname = tokenName;// Set the name for display purposessymbol = tokenSymbol;// Set the symbol for display purposes}

/** * Internal transfer, only can be called by this contract */function_transfer(address _from, address _to, uint _value)internal{

// Prevent transfer to 0x0 address. Use burn() insteadrequire(_to !=0x0);

// Check if the sender has enoughrequire(balanceOf[_from] >= _value);

// Check for overflowsrequire(balanceOf[_to] + _value > balanceOf[_to]);

// Save this for an assertion in the futureuint previousBalances = balanceOf[_from] + balanceOf[_to];// Subtract from the senderbalanceOf[_from] -= _value;// Add the same to the recipientbalanceOf[_to] += _value; Transfer(_from, _to, _value);// Asserts are used to use static analysis to find bugs in your code. They should never failassert(balanceOf[_from] + balanceOf[_to] == previousBalances); }

/** * Transfer tokens * * Send `_value` tokens to `_to` from your account * *@param_to The address of the recipient *@param_value the amount to send */functiontransfer(address _to, uint256 _value)public{ _transfer(msg.sender, _to, _value); }

/** * Transfer tokens from other address * * Send `_value` tokens to `_to` in behalf of `_from` * *@param_from The address of the sender *@param_to The address of the recipient *@param_value the amount to send */functiontransferFrom(address _from, address _to, uint256 _value)publicreturns(bool success){

require(_value

/** * Set allowance for other address * * Allows `_spender` to spend no more than `_value` tokens in your behalf * *@param_spender The address authorized to spend *@param_value the max amount they can spend */functionapprove(address _spender, uint256 _value)publicreturns(bool success){ allowance[msg.sender][_spender] = _value;

returntrue; }

/** * Set allowance for other address and notify * * Allows `_spender` to spend no more than `_value` tokens in your behalf, and then ping the contract about it * *@param_spender The address authorized to spend *@param_value the max amount they can spend *@param_extraData some extra information to send to the approved contract */functionapproveAndCall(address _spender, uint256 _value, bytes _extraData)publicreturns(bool success){ tokenRecipient spender = tokenRecipient(_spender);

if(approve(_spender, _value)) { spender.receiveApproval(msg.sender, _value, this, _extraData);

returntrue; } }

/** * Destroy tokens * * Remove `_value` tokens from the system irreversibly * *@param_value the amount of money to burn */functionburn(uint256 _value)publicreturns(bool success){

require(balanceOf[msg.sender] >= _value);// Check if the sender has enoughbalanceOf[msg.sender] -= _value;// Subtract from the sendertotalSupply -= _value;// Updates totalSupplyBurn(msg.sender, _value);returntrue; }

/** * Destroy tokens from other account * * 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 */functionburnFrom(address _from, uint256 _value)publicreturns(bool success){

require(balanceOf[_from] >= _value);// Check if the targeted balance is enoughrequire(_value

/******************************************/

/* ADVANCED TOKEN STARTS HERE */

/******************************************/

contract MyAdvancedToken is owned, TokenERC20 { uint256publicsellPrice; uint256publicbuyPrice; mapping (address => bool)publicfrozenAccount;

/* This generates a public event on the blockchain that will notify clients */event FrozenFunds(address target, bool frozen);

/* Initializes contract with initial supply tokens to the creator of the contract */functionMyAdvancedToken( uint256 initialSupply, string tokenName, string tokenSymbol )TokenERC20(initialSupply, tokenName, tokenSymbol)public{}

/* Internal transfer, only can be called by this contract */function_transfer(address _from, address _to, uint _value)internal{

require(_to !=0x0);// Prevent transfer to 0x0 address. Use burn() insteadrequire(balanceOf[_from] >= _value);// Check if the sender has enoughrequire(balanceOf[_to] + _value > balanceOf[_to]);// Check for overflowsrequire(!frozenAccount[_from]);// Check if sender is frozenrequire(!frozenAccount[_to]);// Check if recipient is frozenbalanceOf[_from] -= _value;// Subtract from the senderbalanceOf[_to] += _value;// Add the same to the recipientTransfer(_from, _to, _value); }

/// @notice Create `mintedAmount` tokens and send it to `target`/// @param target Address to receive the tokens/// @param mintedAmount the amount of tokens it will receivefunctionmintToken(address target, uint256 mintedAmount)onlyOwnerpublic{ balanceOf[target] += mintedAmount; totalSupply += mintedAmount; Transfer(, this, mintedAmount); Transfer(this, target, mintedAmount); }

/// @notice `freeze? Prevent | Allow` `target` from sending & receiving tokens/// @param target Address to be frozen/// @param freeze either to freeze it or notfunctionfreezeAccount(address target, bool freeze)onlyOwnerpublic{ frozenAccount[target] = freeze; FrozenFunds(target, freeze); }

/// @notice Allow users to buy tokens for `newBuyPrice` eth and sell tokens for `newSellPrice` eth/// @param newSellPrice Price the users can sell to the contract/// @param newBuyPrice Price users can buy from the contractfunctionsetPrices(uint256 newSellPrice, uint256 newBuyPrice)onlyOwnerpublic{ sellPrice = newSellPrice; buyPrice = newBuyPrice; }

/// @notice Buy tokens from contract by sending etherfunctionbuy()payablepublic{ uint amount = msg.value / buyPrice;// calculates the amount_transfer(this, msg.sender, amount);// makes the transfers}

/// @notice Sell `amount` tokens to contract/// @param amount amount of tokens to be soldfunctionsell(uint256 amount)public{

require(this.balance >= amount * sellPrice);// checks if the contract has enough ether to buy_transfer(msg.sender, this, amount);// makes the transfersmsg.sender.transfer(amount * sellPrice);// sends ether to the seller. It's important to do this last to avoid recursion attacks}}

到这里我们的代币已经开发完成了,使用以太坊钱包部署体验下吧。接下来两篇文章是在以太坊上创建众筹合约和去中心自动化组织DAO,欢迎关注本公众号。

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180401A10BQE00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券