Erc-20

乙太坊代幣創建和眾售範例

  • August 30, 2018

我的目標是創建一個代幣並通過眾籌合約出售它。我使用了以下範例:

除了建構子命名之外,下面的程式碼與乙太坊首頁上的程式碼完全相同。

pragma solidity ^0.4.16;

interface tokenRecipient { function receiveApproval(address _from, uint256 _value, address _token, bytes _extraData) external; }

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 generates a public event on the blockchain that will notify clients
   event Approval(address indexed _owner, address indexed _spender, 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
    */
   constructor (
       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;
       emit 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 returns (bool success) {
       _transfer(msg.sender, _to, _value);
       return true;
   }

   /**
    * 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;
       emit Approval(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
       emit 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
       emit Burn(_from, _value);
       return true;
   }
}

它創建了一個新的代幣合約(地址 1)。所有創建的令牌都屬於message.sender.

然後我使用眾籌合約出售這些 ERC20 代幣。我部署了眾籌合約(在本地使用 Ganache)並將tokenAddress建構子中的address 1. 通過ETH從第二個賬戶 ( <acc2>) 發送到合約地址(地址 2),呼叫預設函式並向我顯示發送到合約balanceOf[<acc2>]的 ETH 餘額。<acc2>

除了建構子命名之外,下面的程式碼與乙太坊首頁上的程式碼完全相同。

pragma solidity ^0.4.18;

interface token {
   function transfer(address receiver, uint amount) external;
}

contract Crowdsale {
   address public beneficiary;
   uint public fundingGoal;
   uint public amountRaised;
   uint public deadline;
   uint public price;
   token public tokenReward;
   mapping(address => uint256) public balanceOf;
   bool fundingGoalReached = false;
   bool crowdsaleClosed = false;

   event GoalReached(address recipient, uint totalAmountRaised);
   event FundTransfer(address backer, uint amount, bool isContribution);

   /**
    * Constructor function
    *
    * Setup the owner
    */
   constructor(
       address ifSuccessfulSendTo,
       uint fundingGoalInEthers,
       uint durationInMinutes,
       uint etherCostOfEachToken,
       address addressOfTokenUsedAsReward
   ) public {
       beneficiary = ifSuccessfulSendTo;
       fundingGoal = fundingGoalInEthers * 1 ether;
       deadline = now + durationInMinutes * 1 minutes;
       price = etherCostOfEachToken * 1 ether;
       tokenReward = token(addressOfTokenUsedAsReward);
   }

   /**
    * Fallback function
    *
    * The function without name is the default function that is called whenever anyone sends funds to a contract
    */
   function () payable public {
       require(!crowdsaleClosed);
       uint amount = msg.value;
       balanceOf[msg.sender] += amount;
       amountRaised += amount;
       tokenReward.transfer(msg.sender, amount / price);
      emit FundTransfer(msg.sender, amount, true);
   }

   modifier afterDeadline() { if (now >= deadline) _; }

   /**
    * Check if goal was reached
    *
    * Checks if the goal or time limit has been reached and ends the campaign
    */
   function checkGoalReached() public afterDeadline {
       if (amountRaised >= fundingGoal){
           fundingGoalReached = true;
           emit GoalReached(beneficiary, amountRaised);
       }
       crowdsaleClosed = true;
   }


   /**
    * Withdraw the funds
    *
    * Checks to see if goal or time limit has been reached, and if so, and the funding goal was reached,
    * sends the entire amount to the beneficiary. If goal was not reached, each contributor can withdraw
    * the amount they contributed.
    */
   function safeWithdrawal() public afterDeadline {
       if (!fundingGoalReached) {
           uint amount = balanceOf[msg.sender];
           balanceOf[msg.sender] = 0;
           if (amount > 0) {
               if (msg.sender.send(amount)) {
                  emit FundTransfer(msg.sender, amount, false);
               } else {
                   balanceOf[msg.sender] = amount;
               }
           }
       }

       if (fundingGoalReached && beneficiary == msg.sender) {
           if (beneficiary.send(amountRaised)) {
              emit FundTransfer(beneficiary, amountRaised, false);
           } else {
               //If we fail to send the funds to beneficiary, unlock funders balance
               fundingGoalReached = false;
           }
       }
   }
}

我不理解預設功能的三件事:

function () payable public {
   require(!crowdsaleClosed);
   uint amount = msg.value;
   balanceOf[msg.sender] += amount;
   amountRaised += amount;
   tokenReward.transfer(msg.sender, amount / price);
  emit FundTransfer(msg.sender, amount, true);
}

tokenReward.transfer(msg.sender, 金額/價格); 據我了解,應該立即轉移購買的代幣數量。檢查 MEW(本地)仍然顯示<acc1>擁有令牌創建的所有令牌。<acc2>擁有更少ETH和購買的數量,但不是代幣地址而不是眾籌地址。

問題 1) 眾籌合約如何獲得 的代幣acc1,這是在 ERC20 標準中並由tokenReward = token(addressOfTokenUsedAsReward);

問題 2)為什麼acc2不擁有來自原始代幣地址(地址 1)的任何代幣,而是擁有來自眾籌地址(地址 2)的代幣?

問題 3)_transferERC20 範例中的函式檢查發送者是否有足夠的令牌:

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

這是否意味著最後一個人必須購買確切的剩餘代幣數量,否則眾籌會引發錯誤?

問題 1) 眾籌合約如何獲取 acc1 的代幣,這是在 ERC20 標準中,由 tokenReward = token(addressOfTokenUsedAsReward);

**A1:**如果acc1批准眾籌合約,可以委託它通過approve()ERC20代幣中的功能轉移一定數量的代幣。

此程式碼tokenReward = token(addressOfTokenUsedAsReward);與其他程式語言一樣基本,您可以在眾籌合約中將 ERC20 代幣轉換為代幣介面,因為 ERC20 代幣實現了transfer代幣介面的必要功能。

問題 2)為什麼 acc2 不擁有來自原始代幣地址(地址 1)的任何代幣,而是擁有來自眾籌地址(地址 2)的代幣?

A2: : 當你呼叫時tokenReward.transfer(msg.sender, amount / price);msg.sender裡面的tokenReward.transfer()將是智能合約地址,而不是<address1>. 這意味著我們將代幣從眾籌合約轉移到msg.sender呼叫回退/預設函式。

問題 3)ERC20 範例中的 _transfer 函式檢查發送者是否有足夠的令牌:

**A3:**這是我們需要在每個編碼/業務邏輯中進行的基本驗證,不僅在 ERC20 中。如果未通過驗證,則 tnx 將被還原,ETH 金額將退還給呼叫者。

引用自:https://ethereum.stackexchange.com/questions/57669