主页 > 苹果手机安装imtoken > 在以太坊上发行你自己的代币
在以太坊上发行你自己的代币
上一篇从区块链、以太坊的概念,到去中心化应用,再到语言、框架,最后列举了一些开源项目,相信大家已经开始着手开发以太坊了。 如果您还没有看过,请点击链接了解详情。
接下来怎么在以太坊上发行代币,我们一步步创建和发行自己的代币。 以太坊生态系统中的代币可以代表任何可交易的商品,例如硬币、金币、借条和游戏道具。 以太坊中的所有代币都遵循基本规则和协议,因此我们的代币与以太坊钱包以及遵循相同规则的其他客户端和智能合约兼容。
代币合约
标准代币合约会比较复杂,我们先来个简单的shock。
pragma solidity ^0.4.16;
interface tokenRecipient {
function receiveApproval(
address _from,
uint256 _value,
address _token,
bytes _extraData) public;
}
contract TokenERC20 { // Public variables of the token
string public name;
string public symbol;
// 18 decimals is the strongly suggested default, avoid changing it
uint8 public decimals = 18;
uint256 public totalSupply;
// This creates an array with all balances
mapping (address => uint256) public balanceOf;
// This generates a public event on the blockchain that will notify clients
mapping (address => mapping (address => uint256)) public allowance;
event Transfer(address indexed from, address indexed to, uint256 value);
// This notifies clients about the amount burnt
event Burn(address indexed from, uint256 value);
/**
* Constructor function
*
* Initializes contract with initial supply tokens to the creator of the contract
*/
function TokenERC20(
uint256 initialSupply,
string tokenName,
string tokenSymbol
) public {
// Update total supply with the decimal amount
totalSupply = 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() instead
require(_to != 0x0);
// Check if the sender has enough
require(balanceOf[_from] >= _value);
// Check for overflows
require(balanceOf[_to] + _value > balanceOf[_to]);
// Save this for an assertion in the future
uint previousBalances = balanceOf[_from] + balanceOf[_to];
// Subtract from the sender
balanceOf[_from] -= _value;
// Add the same to the recipient
balanceOf[_to] += _value;
Transfer(_from, _to, _value);
// Asserts are used to use static analysis to find bugs in your code. They should never fail
assert(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
*/
function transfer(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
*/
function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) {
require(_value <= allowance[_from][msg.sender]); // Check allowance
allowance[_from][msg.sender] -= _value;
_transfer(_from, _to, _value);
return true;
}
/**
* 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
*/
function approve(address _spender, uint256 _value) public
returns (bool success) {
allowance[msg.sender][_spender] = _value;
return true;
}
/**
* 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
*/
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;
}
}
/**
* Destroy tokens
*
* Remove `_value` tokens from the system irreversibly
*
* @param _value the amount of money to burn
*/
function burn(uint256 _value) public returns (bool success) {
require(balanceOf[msg.sender] >= _value); // Check if the sender has enough
balanceOf[msg.sender] -= _value; // Subtract from the sender
totalSupply -= _value; // Updates totalSupply
Burn(msg.sender, _value);
return true;
}
/**
* 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
*/
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 MyToken {
/* This creates an array with all balances */
mapping (address => uint256) public balanceOf;
/* Initializes contract with initial supply tokens to the creator of the contract */
function MyToken(uint256 initialSupply) {
balanceOf[msg.sender] = initialSupply;
// Give the creator all initial tokens
}
/* Send coins */
function transfer(address _to, uint256 _value) {
require(balanceOf[msg.sender] >= _value);
// Check if the sender has enough
require(balanceOf[_to] + _value >= balanceOf[_to]);
// Check for overflows
balanceOf[msg.sender] -= _value;
// Subtract from the sender
balanceOf[_to] += _value;
// Add the same to the recipient
}
}
复制代码
这个合约很简单,一个状态变量用来存储所有账户的余额,并提供一个传递函数transfer()。
接下来怎么在以太坊上发行代币,我们将一步步编写MyToken的代码。 去以太坊官网下载以太坊钱包,安装钱包,选择rinkeby testnet。 进入钱包后,首先选择钱包,下面有添加账户,然后输入密码直接添加即可。
点击合约标签进入合约界面,点击部署新合约部署新合约。
在 Solidity Contract Source 代码编辑框中输入以下代码:
contract MyToken {
/* This creates an array with all balances */
mapping (address => uint256) public balanceOf;
}
复制代码
这里声明了一个映射类型的变量balanceOf,映射类型可以理解为Java中的HashMap。 public 关键字表示变量可以被区块链上的所有节点访问。 Solidity 自动为公共类型的变量生成 getter 方法。
目前这个合约是没有用的,因为我们还没有生成任何代币,所以如果有合约试图获取我们代币的数量,结果将为0。我们在部署合约时创建一些代币,并添加以下代码代码的结尾:
function MyToken() {
balanceOf[msg.sender] = 21000000;
}
复制代码
该函数与合约同名,相当于C语言中的构造函数,只有在第一次部署合约时才会执行。 部署合约后,合约创建者 msg.sender 将拥有 21000000 个代币。 这里我们已经在代码中写入了值。 更好的方法是给构造函数提供一个参数,让合约创建者填写。代码如下:
function MyToken(uint256 initialSupply) public {
balanceOf[msg.sender] = initialSupply;
}
复制代码
看钱包右侧,点击下拉列表,选择MyToken,你会看到一个编辑框,让你输入构造函数的参数。
接下来为我们的代币添加转账功能。
/* Send coins */
function transfer(address _to, uint256 _value) { /* Add and subtract new balances */
balanceOf[msg.sender] -= _value;
balanceOf[_to] += _value;
}
复制代码
调用该函数时,调用者msg.sender会直接减去相应数量的代币,接收方会得到相应数量的代币。 这里有两个问题:
为了避免这两种情况,根据之前的经验,我们可以判断return或者throw在不满足条件的时候抛出异常。 反正以后可以出新版本升级。 但合同不同。 合约一旦部署,就不能更改或下线。 而 throw 会白白浪费调用者的 gas。 基于以上两方面的考虑,我们应该在部署合约之前检查所有的数据,确保不会出现异常。 Solidity提供了require()方法,我们修改代码如下:
function transfer(address _to, uint256 _value) {
/* Check if sender has balance and for overflows */
require(balanceOf[msg.sender] >= _value && balanceOf[_to] + _value >= balanceOf[_to]);
balanceOf[_to] += _value;
}
复制代码
传递函数现在可用。 但是我们的代币还缺少名称和符号,直接在代码中声明对应的状态变量:
string public name;
string public symbol;
uint8 public decimals;
复制代码
在构造函数中给变量赋值,方便我们在部署时灵活命名我们的token。
/* Initializes contract with initial supply tokens to the creator of the contract */
function MyToken(uint256 initialSupply, string tokenName, string tokenSymbol, uint8 decimalUnits) {
balanceOf[msg.sender] = initialSupply; // Give the creator all initial tokens
name = tokenName; // Set the name for display purposes
symbol = tokenSymbol; // Set the symbol for display purposes
decimals = decimalUnits; // Amount of decimals for display purposes
}
复制代码
为了让钱包等客户端能够收到token通知,我们最后添加了token的Events。 Event是使用EVM日志内置功能的便捷工具。 在DAPP的接口中,可以依次调用Javascript的回调来监听事件。 事件声明:
event Transfer(address indexed from, address indexed to, uint256 value);
复制代码
在 transfer() 函数中调用事件:
/* Notify anyone listening that this transfer took place */
Transfer(msg.sender, _to, _value);
复制代码
至此,MyToken 代币开发完成。
部署代币
填写构造函数需要的参数,你的钱包界面应该大致如下:
拉到底部并设置您愿意为令牌部署花费的以太币数量。 这里可以保持默认也可以设置多一点,因为剩余的以太币会返还给我们。 如果设置较少的以太币,则需要等待更多时间才能完成部署。
单击部署。
输入密码以提交部署。 部署完成后,您可以体验自己的代币,比如转发代币给好友。 点击WALLET标签旁边的SEND标签,输入好友的钱包地址,代币数量,选择自己的代币(默认为ether),点击发送。
但是现在你朋友的钱包将无法看到你发送给他的代币,因为以太坊钱包只会跟踪它知道的货币,我们需要手动将我们刚刚创建的代币添加到钱包中。
首先进入刚刚创建的合约界面,复制合约地址,点击Watch Token,在弹出的对话框中粘贴合约地址,名称,符号等字段应该会自动填写。
点击确认,当我们的代币有任何变化时,钱包会收到通知。
最后
部署合约需要以太币,Rinkeby测试网的以太币可以免费获取。 如果不能科学上网,可以直接在remix网站上体验。