主页 > imtoken钱包收款地址 > 以太坊发行代币(二)
以太坊发行代币(二)
在上一节中,我已经谈到了如何发行一个简单的令牌。 没看过的请看以太坊发行代币(一)
在本节中,我们将对之前的令牌进行升级。
如果你想把你的代币卖给交易所,仅仅发送到其他合约的以太坊地址是不够的,因为它不会知道这个新代币或是谁发送的,因为这个合约没有发布一些事件或接口. 所以对于合约,你应该先批准他们从你的账户中转出代币,然后让他们知道他们应该做什么。
由于里面的一些函数必须做一些交易操作,所以需要将它们整合成一个内部函数(只有合约本身可以调用)。
/* 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() instead
require (balanceOf[_from] >= _value); // Check if the sender has enough
require (balanceOf[_to] + _value >= balanceOf[_to]); // Check for overflows
require(!frozenAccount[_from]); // Check if sender is frozen
require(!frozenAccount[_to]); // Check if recipient is frozen
balanceOf[_from] -= _value; // Subtract from the sender
balanceOf[_to] += _value; // Add the same to the recipient
Transfer(_from, _to, _value);
}
现在你的其他涉及交易操作的函数可以自己做验证以太坊发行,然后用正确的参数调用传递函数。
注意这个函数不需要任何权限验证就可以转账,因为这个函数是一个内部函数,只有合约本身可以调用。
所以如果你添加任何调用它的函数,你需要确保调用者在调用它之前有转移令牌的权限。
中心管理
默认情况下,所有 dapp 都是去中心化的,这并不意味着它们不能有某种中央管理,比如如果你想拥有铸造更多代币的能力,或者你想禁止某人使用你的代币。 你可以在你的合约中添加你想要的任何功能,但你只能在开始时添加,让那些代币持有者知道你的游戏规则,然后决定是否拥有你的代币。
在这里我们将学习合约的一个特别有用的属性:继承
继承允许合约获取父合约的属性而无需重新定义它们。 这可以使我们的代码更加简洁和可重用。
将以下代码添加到代码的第一行,在 contract MyToken {
contract owned {
address public owner;
function owned() {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) onlyOwner {
owner = newOwner;
}
}
这里创建了一个非常基本的合约,只是定义了关于这个合约的一些公共函数。 下一步就是将 is owner 添加到您的合同中。
contract MyToken is owned {
/* the rest of the contract as usual */
这样所有MyToken函数都可以使用owner变量和修饰符onlyOnwer。 合约还可以获得修改合约所有者的功能。 即使合约最初设置了合约所有者,您也可以在构造函数中添加此代码:
function MyToken(
uint256 initialSupply,
string tokenName,
uint8 decimalUnits,
string tokenSymbol,
address centralMinter
) {
if(centralMinter != 0 ) owner = centralMinter;
}
中央代币发行
支持您控制流通的代币数量。
一种情况是当你的货币实际上代表区块链资产(如金币或政府货币),然后你希望你的虚拟库存真正反映其中之一。 或者你想控制代币的价格,适当增减流通数量。
首先,我们需要添加一个变量来存储总发行量,然后在我们的构造函数中指定它。
contract MyToken {
uint256 public totalSupply;
function MyToken(...) {
totalSupply = initialSupply;
...
}
...
}
现在添加一个新功能以允许合约所有者创建新代币。
function mintToken(address target, uint256 mintedAmount) onlyOwner {
balanceOf[target] += mintedAmount;
totalSupply += mintedAmount;
Transfer(0, owner, mintedAmount);
Transfer(owner, target, mintedAmount);
}
注意在函数名末尾添加了onlyOwner,意思是这个函数只能被合约所有者的账户调用,这样就可以创建更多的token。
冻结资产
根据您的用例,您可能需要遇到一些管理障碍,例如谁可以拥有您的代币,谁不能。 在这种情况下,您需要添加一个参数以允许合约所有者冻结或解冻资产。
在这个合约的任何地方添加这个变量和函数。 但是,建议您将映射与其他映射放在一起,将事件与其他事件放在一起。
mapping (address => bool) public frozenAccount;
event FrozenFunds(address target, bool frozen);
function freezeAccount(address target, bool freeze) onlyOwner {
frozenAccount[target] = freeze;
FrozenFunds(target, freeze);
}
对于这段代码,默认情况下所有账户都是解冻的,但合约所有者可以通过调用 freezeAccount 为他们设置冻结状态。
不幸的是,冻结没有任何实际效果,因为我们还没有在传递函数中做任何事情。 现在修改它:
function transfer(address _to, uint256 _value) {
require(!frozenAccount[msg.sender]);
任何被冻结的账户现在都可以保留他们的代币,但不能转移它们。 任何账户在您冻结之前默认是解冻的,您也可以通过白名单进行管理。
只需将 freezeAccount 重命名为 approvedAccount 并将最后一行代码更改为:
require(approvedAccount[msg.sender]);
自动交易
至此,您可以信任您的代币并将其应用到实际场景中。 但是如果你想通过以市场价格自动买卖来通过以太币(或其他代币)赎回。
首先,您需要设置买卖价格。
uint256 public sellPrice;
uint256 public buyPrice;
function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner {
sellPrice = newSellPrice;
buyPrice = newBuyPrice;
}
这是一个可以接受的价格,不经常变化,因为每次价格变化都需要你执行一次交易并花费一点以太币。如果你需要一个带小数位的常量结构,我建议你参考
下一步是创建买卖功能:
function buy() payable returns (uint amount){
amount = msg.value / buyPrice; // calculates the amount
require(balanceOf[this] >= amount); // checks if it has enough to sell
balanceOf[msg.sender] += amount; // adds the amount to buyer's balance
balanceOf[this] -= amount; // subtracts amount from seller's balance
Transfer(this, msg.sender, amount); // execute an event reflecting the change
return amount; // ends function and returns
}
function sell(uint amount) returns (uint revenue){
require(balanceOf[msg.sender] >= amount); // checks if the sender has enough to sell
balanceOf[this] += amount; // adds the amount to owner's balance
balanceOf[msg.sender] -= amount; // subtracts the amount from seller's balance
revenue = amount * sellPrice;
msg.sender.transfer(revenue); // sends ether to the seller: it's important to do this last to prevent recursion attacks
Transfer(msg.sender, this, amount); // executes an event reflecting on the change
return revenue; // ends function and returns
}
这些操作不会添加新代币,但会更改合约所有者的余额。
请注意,价格集不是以以太币为单位,而是以 wei 为单位。 一个以太币相当于 1000000000000000000 wei,所以在以太坊中设置您的代币价格时,您需要在其后添加 18 个零。
创建合约时,需要支付足够的以太币才能买回市场上的所有代币。 否则您的合约将破产,您的用户将无法出售他们的代币。
一个更有趣的合约可以允许交易所中的任何人出价,或者直接从外部获取价格。
自动充值
每次你在以太坊进行交易,你都需要给矿工一个小费来计算你的合约结果。 这一切都可能在未来改变,但目前只能支付以太币,所以你所有的代币用户都需要持有以太币。 如果用户的以太币数量不足以支付小费,它将被冻结,直到合约所有者支付为止。 但在某些场景下,你可能不想让你的用户去想以太坊、区块链或如何拥有以太坊,所以一个有效的方法是,如果检测到用户余额不足,可以自动为用户余额充值。
首先,您需要创建一个变量来存储帐户的阈值和一个函数来设置它。 如果你不知道设置多少,你可以设置 5 finney (0.005 ether)
uint public minBalanceForAccounts;
function setMinBalance(uint minimumBalanceInFinney) onlyOwner {
minBalanceForAccounts = minimumBalanceInFinney * 1 finney;
}
然后,在 tresfer 函数中添加以下代码,让卖家退还一些代币来充值以太币:
/* Send coins */
function transfer(address _to, uint256 _value) {
...
if(msg.sender.balance < minBalanceForAccounts)
sell((minBalanceForAccounts - msg.sender.balance) / sellPrice);
}
您还可以将小费从卖家转移到买家:
/* Send coins */
function transfer(address _to, uint256 _value) {
...
if(_to.balance<minBalanceForAccounts)
_to.send(sell((minBalanceForAccounts - _to.balance) / sellPrice));
}
这确保每个人都有足够的以太币来支付小费。
工作证明
以下是一些通过数学公式增加代币供应的方法。 最简单的一种方式就是绑定以太坊的挖矿,也就是说,一旦有人找到了一个以太坊区块,它也会相应地获得你的代币奖励。 你可以通过一些得到找到块的帐号。
function giveBlockReward() {
balanceOf[block.coinbase] += 1;
}
也可以添加一个数学公式,让任何人知道如何解决这个公式以获得奖励。 这是您必须解决当前挑战并正确设置下一个挑战的示例。
uint public currentChallenge = 1; // Can you figure out the cubic root of this number?
function rewardMathGeniuses(uint answerToCurrentReward, uint nextChallenge) {
require(answerToCurrentReward**3 == currentChallenge); // If answer is wrong do not continue
balanceOf[msg.sender] += 1; // Reward the player
currentChallenge = nextChallenge; // Set the next challenge
}
当然,上面的结果手动算出来是非常困难的,但是用计算器就很简单了,所以这个游戏很容易被电脑破解。 也因为最后的获胜者可以选择下一次挑战的难度,他们可以选择自己知道的以太坊发行,所以对其他玩家来说是很不公平的。 一个更公平的系统需要设计一个对计算机来说很难的挑战,但并非不可能成功。 一个很好的解决方案是要求挑战者从众多数字中找到一个低于难度值的哈希值。
该方案最早由 Adam Back 于 1997 年提出,称为 Hashcash,随后中本聪于 2008 年将其应用于比特币的工作量证明。
当然,您也可以创建自己的工作量证明。
bytes32 public currentChallenge; // The coin starts with a challenge
uint public timeOfLastProof; // Variable to keep track of when rewards were given
uint public difficulty = 10**32; // Difficulty starts reasonably low
function proofOfWork(uint nonce){
bytes8 n = bytes8(sha3(nonce, currentChallenge)); // Generate a random hash based on input
require(n >= bytes8(difficulty)); // Check if it's under the difficulty
uint timeSinceLastProof = (now - timeOfLastProof); // Calculate time since last reward was given
require(timeSinceLastProof >= 5 seconds); // Rewards cannot be given too quickly
balanceOf[msg.sender] += timeSinceLastProof / 60 seconds; // The reward to the winner grows by the minute
difficulty = difficulty * 10 minutes / timeSinceLastProof + 1; // Adjusts the difficulty
timeOfLastProof = now; // Reset the counter
currentChallenge = sha3(nonce, currentChallenge, block.blockhash(block.number - 1)); // Save a hash that will be used as the next proof
}
构造函数也需要修改,添加如下一行代码:
timeOfLastProof = now;
运行合约后,选择工作量证明功能,添加您的幸运数字,然后运行。 如果确认窗口显示红色警告“数据无法执行”,则需要返回上一步重新设置数字,直到找到满足交易可以执行的数字。 找到这个数字后,您将每分钟获得一个代币奖励,具体取决于您上次挑战后的时间。 然后调整难度,平均每10分钟计算一次。
完整代码
pragma solidity ^0.4.16;
contract owned {
address public owner;
function owned() public {
owner = msg.sender;
}
modifier onlyOwner {
require(msg.sender == owner);
_;
}
function transferOwnership(address newOwner) onlyOwner public {
owner = newOwner;
}
}
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;
uint8 public decimals = 18;
// 18 decimals is the strongly suggested default, avoid changing it
uint256 public totalSupply;
// This creates an array with all balances
mapping (address => uint256) public balanceOf;
mapping (address => mapping (address => uint256)) public allowance;
// This generates a public event on the blockchain that will notify clients
event Transfer(address indexed from, address indexed to, uint256 value);
// This notifies clients about the amount burnt
event Burn(address indexed from, uint256 value);
/**
* Constrctor function
*
* Initializes contract with initial supply tokens to the creator of the contract
*/
function TokenERC20(
uint256 initialSupply,
string tokenName,
string tokenSymbol
) public {
totalSupply = initialSupply * 10 ** uint256(decimals); // Update total supply with the decimal amount
balanceOf[msg.sender] = totalSupply; // Give the creator all initial tokens
name = tokenName; // Set the name for display purposes
symbol = 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() 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` in 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 in 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 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
*/
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;
}
}
/******************************************/
/* ADVANCED TOKEN STARTS HERE */
/******************************************/
contract MyAdvancedToken is owned, TokenERC20 {
uint256 public sellPrice;
uint256 public buyPrice;
mapping (address => bool) public frozenAccount;
/* 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 */
function MyAdvancedToken(
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() instead
require (balanceOf[_from] >= _value); // Check if the sender has enough
require (balanceOf[_to] + _value >= balanceOf[_to]); // Check for overflows
require(!frozenAccount[_from]); // Check if sender is frozen
require(!frozenAccount[_to]); // Check if recipient is frozen
balanceOf[_from] -= _value; // Subtract from the sender
balanceOf[_to] += _value; // Add the same to the recipient
Transfer(_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 receive
function mintToken(address target, uint256 mintedAmount) onlyOwner public {
balanceOf[target] += mintedAmount;
totalSupply += mintedAmount;
Transfer(0, 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 not
function freezeAccount(address target, bool freeze) onlyOwner public {
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 contract
function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner public {
sellPrice = newSellPrice;
buyPrice = newBuyPrice;
}
/// @notice Buy tokens from contract by sending ether
function buy() payable public {
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 sold
function sell(uint256 amount) public {
require(this.balance >= amount * sellPrice); // checks if the contract has enough ether to buy
_transfer(msg.sender, this, amount); // makes the transfers
msg.sender.transfer(amount * sellPrice); // sends ether to the seller. It's important to do this last to avoid recursion attacks
}
}