首頁
以太坊代幣進階
發布時間:2018-04-01 00:00:00     

我先后寫了4篇關于區塊鏈和以太坊的文章,比較零散,有需要的同學可以翻看歷史記錄。繼上一篇發行一個簡單的代幣之后,接下來準備寫三篇在以太坊上開發Dapp的文章,想學習區塊鏈開發的同學可以關注下公眾號。

  1. 以太坊代幣進階(本篇);

  2. 以太坊上創建眾籌合約;

  3. 以太坊上建立去中心自動化組織(DAO);

上一篇文章我們實現了一個簡單的代幣MyToken,作為一個demo入門,大家應該都了解了代幣發行的基本流程。如果要實際發行MyToken的話,則需要做更多的工作,如實現合約的買賣功能等。

ERC20代幣

先放上合約代碼。

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;    } }

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() 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);    }

所有涉及轉賬功能的函數,都需要先做各自的數據安全性檢查,然后調用_transfer()函數實現轉賬。

代幣管理

所有的dapp都是分布式的,但為了防止代幣被亂用、失去控制,我們需要實現相關管理功能,如凍結用戶,凍結資產,發行更多代幣等。與一般app不同的是,所有這些規則都需要在代幣部署前完成,一經部署,規則就無法改變了。

管理員可以是一個賬戶,也可以是另一個合約。如果合約實現是一個去中心化組織的話,將可以有效限制合約擁有者的權利。

我們使用繼承來實現代幣管理合約。

contract owned {    address public owner;
  
function owned() {        owner = msg.sender;    }    modifier onlyOwner {
       
require(msg.sender == owner);        _;    }
   
function transferOwnership(address newOwner) onlyOwner {        owner = newOwner;    } }

這個合約定義了誰擁有合約,并提供一個modifier修飾器,被修飾的函數只有擁有者才有調用權限。接下來讓代幣合約繼承owed合約就可以了。

contract TokenERC20 is owned {
    
/* the rest of the contract as usual */

這樣我們就實現了一個簡單的管理合約。

代幣發行模型

目前市場上很多的代幣發行策略要么是通脹模型,要么是固定最大值。接下來我們實現這個功能。

首先,定義一個狀態變量totalSupply,在構造函數中賦值。

contract TokenERC20 {    uint256 public totalSupply;
  
function TokenERC20(...) {        totalSupply = initialSupply;        ...    }    ... }

接下來添加一個代幣發行函數:

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

函數通過給目標地址增加一定數量的代幣,從而增加代幣的整體供應量。

凍結賬戶資產

幾乎所有的資產發行都存在凍結功能,凍結一定數量的資產可以獲得更多的代幣收益。我們也給代幣實現凍結功能。添加如下代碼:

   mapping (address => bool) public frozenAccount;
  
event FrozenFunds(address target, bool frozen);
   
function freezeAccount(address target, bool freeze) onlyOwner {        frozenAccount[target] = freeze;        FrozenFunds(target, freeze);    }

所有賬戶默認不會被凍結,owner可以通過調用freezenAccount()來凍結某些賬戶。被凍結的賬戶不能轉賬,在transfer()函數中添加一行代碼:

   function transfer(address _to, uint256 _value) {
       
require(!frozenAccount[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    }

這里買賣的價格單位不是Ether,而是wei,1 ether = 10^18 wei。

最后

為了代碼結構清晰,我們把本文添加的代幣功能放在一個新合約MyAdvancedToken中,并繼承owned和TokenERC20,最后的代碼如下:

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    } }

到這里我們的代幣已經開發完成了,使用以太坊錢包部署體驗下吧。接下來兩篇文章是在以太坊上創建眾籌合約和去中心自動化組織DAO,歡迎關注本公眾號。

專題

本專題做內容收集,如果你信了,希望不要上當,如果不信,希望不要錯過發財的機會

最新資訊

拍拍貸,互聯網金融的技術化生存

堅守合規底線、互聯網技術創新,是以拍拍貸為代表的第一批互聯網金融公司率先登陸資本市場的原因。美國東部時間11月10日,中國首家網貸平臺——拍拍貸(股票代碼為:PPDF)登陸美國紐約證券交易所。上市當天,拍拍貸發行價為13美元,總市值40億美元,本次融資總額達2.7億美元,其中包括公開發行股票融資2.2億美元和向新鴻基私募融資5000萬美元。拍拍貸的上市,使我想起了2013年11月的一天。彼時,成立

英國“監管沙盒”項目對我國金融創新監管的啟示

摘要:英國金融監管局在2016年推出了監管沙盒項目,該項目旨在促進金融科技創新,增強金融市場競爭活力,推動金融監管升級,為金融消費者提供更低成本更高效高質量的金融服務。對該項目的研究,為完善我國金融創新監管具有一定的幫助。建議我國金融監管機構樹立服務型監管觀念,變被動監管為主動服務;堅持法治原則,依法監管;加強不同監管部門之間的協調合作;充分調動社會資源,在金融監管領域開展協同共治。關鍵詞:監管沙

底薪4920元,入職購買五險一金,晉升空間大

點擊上方香聘杭州→點擊右上角...→點選設為星標★不然可能會錯過每天推送的最新優質崗位哦香聘51信用卡,中國領先金融科技創新企業,2018年香港主板上市。2012年5月,51信用卡推出了一款可以一鍵智能管理信用卡賬單的APP——51信用卡管家,極大地滿足了用戶、尤其是多卡用戶對自身負債進行管理的需求。發展至今,51信用卡逐步開發了一個動態及自我強化的生態系統,為用戶提供涵蓋個人信用管理服務、信用卡

隨機資訊

就明天!20+行業老兵,300+汽修老板,決戰汽配供應鏈!

距2016全國門店盈利能力提升研修會「廣州站-汽配供應鏈專場」就在明天!活動報名截止到今天18:00點,開放最后20個現場免費參會名額!想要免費參會,與后市場大佬面對面?趕緊仔細閱讀搶票攻略↓↓↓,就有機會免費獲得價值299RMB的論壇門票哦!真的是最后一波名額......小編只能幫到這里了,這次再搶不到的老板們...我們明年見!·究竟什么是汽配供應鏈的核心?·供應鏈如何支撐維修連鎖發展?·變革的

央行未發行法定數字貨幣,將引入私人部門;區塊鏈首次應用到雙11

定期打卡閱讀更快更專業解讀FinTech企業動態拾點FinTech由中國人民大學金融科技研究所(微信ID:ruc_fintech)整理發布,關注金融科技理論、應用與政策前沿動態,分享監管動態與行業變革。區塊鏈央行:法定數字貨幣仍處于研究測試中央行公告稱,近期網傳消息稱人民銀行已發行法定數字貨幣,更有個別機構冒用人民銀行名義,將相關數字產品冠以DC/EP或DCEP在數字資產交易平臺上進行交易。現就有

消費變革時期,產業鏈上下游這樣實現精準對接

新春伊始,無數變革正在含苞吐穗、蓄力待發。在新一輪消費變革驅動下,紡織面料企業亟需一個平臺,展示自我創新實力、洞察國內外市場風向、與下游采購商緊密互動。無疑,每年舉行的中國國際紡織面料及輔料(春夏)博覽會(以下簡稱intertextile春夏面輔料展)作為業界開年第一大展,成為紡織企業捕捉行業內外信息,收獲早春訂單的絕佳機會。3月14日-16日,來自22個國家和地區的近3400家行業領先面輔料企業

福利彩票快乐十分计划 做山货野菜赚钱吗 山东11选5基本任选 决策天机炒股软件 养蛋鸡卖鸡蛋赚钱吗 浙江舟山飞鱼开奖号码 号码查询 陕西11选5开奖直播现场 日本东京股票指数 股票交易 双色球蓝球分区走势图 北京快中彩玩法说明 888棋牌游戏中心旗舰平台 海南环岛赛彩票攻略 湖南幸运赛车投注网站 零点棋牌充值 收废塑料能赚钱吗
資訊評價
經典 | 垃圾
資訊評價