Sivo 悉见

设计和发行你自己的加密货币

匿名 · 更新于 2018/5/21

硬币

我们将创建一个数字令牌。以太坊生态系统中的代币可以代表任何可替代的可交易商品:硬币,忠诚点,金币,白条,游戏内物品等。由于所有代币都以标准方式实现了一些基本功能,这也意味着您的代币将立即与以太坊钱包和任何其他使用相同标准的客户或合同兼容。

最小可用令牌

标准令牌合约可能相当复杂。但实质上,一个非常基本的令牌归结为:

pragma solidity ^0.4.20;

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
        ) public {
        balanceOf[msg.sender] = initialSupply;              // Give the creator all initial tokens(为创建者提供所有初始令牌)
    }

    /* Send coins(发币) */
    function transfer(address _to, uint256 _value) public {
        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(将其添加到收件人)
    }
}

代码

但是如果你只想复制粘贴更完整的代码,那么使用下面的代码:

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(18位小数是强烈建议的默认值,避免改变它)
    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);

    /**
     * Constructor 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(防止转移到0x0地址。使用burn()代替)
        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(从你的账户发送余额到_to)
     *
     * @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(允许_spender代表您使用_value代币)
     *
     * @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
        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;
    }
}

了解代码

部署新合同

所以让我们从基础开始。打开电子钱包应用程序,转到合同选项卡,然后部署新合同Solidity合同源代码文本字段中,输入以下代码:

    contract MyToken {
        /* This creates an array with all balances */
        mapping (address => uint256) public balanceOf;
    }

映射意味着一个关联数组,您可以将地址与余额相关联。地址是基本的十六进制以太坊格式,余额是整数,范围从0到115 quattuorvigintillion。如果你不知道quutuorvigintillion的数量是多少,那么比你计划使用你的代币的任何东西都要多。公共关键字,意味着这个变量将可以访问通过在blockchain任何人,这意味着所有余额公共(因为他们需要的是,为了让客户能够显示它们)。

编辑新合约

如果你马上公布你的合同,它会起作用,但不会很有用:它可能是一个合同,可以查询你的硬币的余额是否有任何地址 - 但是因为你从未创建过一枚硬币,所以每一枚硬币都会返回0.所以我们将在启动时创建几个令牌。在最后一个右括号之前添加此代码,就在mapping .. line下面。

    function MyToken() {
        balanceOf[msg.sender] = 21000000;
    }

请注意,函数MyToken与合同MyToken具有相同的名称。 这是非常重要的,如果你重命名一个,你必须重命名另一个:这是一个特殊的启动函数,只有在合同首次上传到网络时才会运行一次。 此功能将设置msg.sender的余额,即部署合同的用户,余额为2100万。

2,100万的选择是相当随意的,你可以在代码中将它改变为任何你想要的东西,但有一个更好的方法:相反,将它作为函数的参数提供,就像这样:

    function MyToken(uint256 initialSupply) public {
        balanceOf[msg.sender] = initialSupply;
    }

看看合同旁边的右栏,你会看到一个下拉列表,写下合同。 选择“MyToken”合同,你会看到现在它显示了一个名为Constructor参数的部分。 这些是令牌的可变参数,因此您可以重复使用相同的代码,并且将来只能更改这些变量。

编辑新合约

现在你有一个创建令牌余额的功能合同,但由于没有任何功能来移动它,它所做的只是保留在同一个帐户上。 所以我们现在要实施。 在最后一个括号之前写下以下代码。

    /* Send coins */
    function transfer(address _to, uint256 _value) {
        /* Add and subtract new balances */
        balanceOf[msg.sender] -= _value;
        balanceOf[_to] += _value;
    }

这是一个非常简单的函数:它有一个接收者和一个值作为参数,每当有人调用它时,它将从他们的余额中减去_value并将其添加到_to余额中。 马上就有一个明显的问题:如果这个人想要发送比它拥有更多的东西会发生什么? 由于我们不想在这份特定合同中处理债务,因此我们只需进行快速检查,如果发件人没有足够的资金,合同执行就会停止。 这也是检查溢出,以避免有一个数字这么大,再次变成零。

要在合同执行中停止执行,您可以返回抛出前者将花费更少的天然气,但可能会更加令人头疼,因为迄今为止您对合同所做的任何更改都将被保留。 另一方面,'抛出'将取消所有合约的执行,恢复交易可能做出的任何改变,并且发送者将失去他所发送的所有乙醚。 但是由于电子钱包可以检测到合同会抛出,它总是显示警报,因此阻止任何Ether被花费。

    function transfer(address _to, uint256 _value) {
        /* Check if sender has balance and for overflows */
        require(balanceOf[msg.sender] >= _value && balanceOf[_to] + _value >= balanceOf[_to]);

        /* Add and subtract new balances */
        balanceOf[msg.sender] -= _value;
        balanceOf[_to] += _value;
    }

现在缺少的是有关合同的一些基本信息。 在不久的将来,这可以通过令牌注册表来处理,但现在我们将它们直接添加到合同中:

string public name;
string public symbol;
uint8 public decimals;

现在我们更新构造函数以允许在开始时设置所有这些变量:

    /* 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(用于显示的小数位数)
    }

最后,我们现在需要一些名为Events的东西。 这些是特殊的空白功能,您可以致电以帮助像以太坊钱包这样的客户跟踪合同中发生的活动。 活动应以大写字母开头。 在合同开始处添加此行以声明事件:

    event Transfer(address indexed from, address indexed to, uint256 value);

然后你只需要在“传输”功能中添加这两行:

        /* Notify anyone listening that this transfer took place */
        Transfer(msg.sender, _to, _value);

现在你的代币已经准备好了!

注意到评论?

那些@notice和@param评论,你可能会问什么? 这就是Natspec自然语言规范的一个新兴标准,它允许钱包向用户显示合同即将做的事情的自然语言描述。 虽然目前还没有很多钱包的支持,但这种情况在未来会发生变化,所以很有必要做好准备。

如何部署

如果您不在那里,请打开以太坊钱包,转到合同选项卡,然后点击“部署新合同”。

现在从上方获取令牌源并将其粘贴到“Solidity source field”中。 如果代码编译没有任何错误,您应该在右侧看到一个“选择合同”下拉列表。 获取并选择“MyToken”合同。 在右栏中,您会看到您需要个性化您自己的令牌的所有参数。 您可以根据自己的喜好调整它们,但为了本教程的目的,我们建议您选择以下参数:10,000个供应商,您想要的任何名称,符号的“%”和2个小数位。 你的应用应该是这样的:

Ethereum Wallet截图2015-12-03 at 3.50.36 PM 10

滚动到页面的末尾,您会看到该合同计算成本的估计值,您可以选择一个费用来确定您愿意为此支付多少乙醚。 如果您愿意,您可以将任何多余的乙醚返还给您,以便您可以保留默认设置。 按下“部署”,输入您的账户密码,然后等待几秒钟,以完成交易。

Ethereum Wallet截图2015-12-03 at 3.50.36 PM 11

您将被重定向到首页,在那里您可以看到您的交易正在等待确认。 点击名为“Etherbase”(您的主要帐户)的帐户,不超过一分钟后,您应该看到您的帐户将显示您拥有100%的刚创建的股份。 将一些朋友发送给几个朋友:选择“发送”,然后选择要发送的货币(以太网或您新创建的分享),将您的朋友的地址粘贴到“至”字段并按“发送”。

截图2015-12-03上午9时8分15秒

如果你将它发送给朋友,他们将不会在他们的钱包中看到任何东西。这是因为钱包只追踪它知道的令牌,并且您必须手动添加这些令牌。现在转到“合同”选项卡,您应该看到一个指向您新创建合同的链接。点击它进入其页面。由于这是一个非常简单的合同页面,因此在这里没有太多要做的事情,只需点击“复制地址”并将合同地址粘贴到文本编辑器中,您很快就会需要它。

要添加令牌观看,请转至合同页面,然后单击“观看令牌”。弹出窗口会出现,您只需要粘贴合同地址。令牌名称,符号和十进制数字应自动填充,但如果不是,您可以放入任何您想要的东西(它只会影响它在钱包上的显示方式)。一旦你这样做了,你会自动显示出你拥有该令牌的任何平衡,并且你可以将它发送给其他任何人。

以太坊钱包Beta 4屏幕截图2015-12-03上午9时44分44秒

现在你有你自己的加密标记!令牌本身可用作当地社区的价值交换跟踪工作时间或其他忠诚计划的方式。但是,通过使货币具有实用价值,我们能够使货币具有内在价值吗?

改善你的令牌

你可以部署你的整个密码令牌,而不需要触及一行代码,但是当你开始自定义它时,真正的魔法就会发生。 以下部分将提供有关可以添加到令牌的功能的建议,以使其更适合您的需求。

更多的基本功能

您会注意到基本令牌合约中还有一些功能,比如批准,发送等等。这些功能用于令牌与其他合同交互:如果您想要将代币销售给分散交易所,只需将它们发送到地址就足够了,因为交换所不会意识到新代币或发送的人他们,因为合同不能订阅事件只是函数调用因此,对于合同,您应该首先批准他们可以从您的账户转移的令牌数量,然后通过ping让他们知道他们应该做的事情 - 或者通过approveAndCall完成两项操作

由于许多这些功能都必须重新实现令牌的转移,所以将它们改为内部函数是有道理的,只能由合约本身调用它们:

    /* 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);
    }

现在,所有的功能导致硬币的转移,可以自己做检查,然后调用传输使用正确的参数。注意这个函数会将硬币从任何账户转移到任何其他账户,而不需要任何人的许可:这就是为什么它是一个内部函数,只能由合约调用:如果你添加任何调用它的函数,确保它正确地验证调用者应该有权移动这些。

集中管理员

所有的dapps默认都是完全分散的,但这并不意味着他们不能拥有某种中央管理器,如果你想要的话。也许你想要能够铸造更多的硬币,也许你想禁止一些人使用你的货币。您可以添加任何这些功能,但问题在于您只能在开始时添加这些功能,因此所有令牌持有者在决定拥有游戏前都会始终准确地知道游戏规则。

为了实现这一点,你需要一个货币中央控制器。这可能是一个简单的账户,但也可能是一个合同,因此创建更多令牌的决定将取决于合同:如果它是一个可以投票的民主组织,或者它可能只是一种限制令牌所有者的权力。

为了做到这一点,我们将学习一个非常有用的契约属性:继承继承允许合同获得父合同的属性,而不必重新定义所有这些属性。这使得代码更清晰,更易于重用。合约MyToken {之前,将此代码添加到代码的第一行

    contract owned {
        address public owner;

        function owned() {
            owner = msg.sender;
        }

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

        function transferOwnership(address newOwner) onlyOwner {
            owner = newOwner;
        }
    }

这创建了一个非常基本的合同,除了定义一些关于可以“拥有”的合同的通用函数之外,它什么也不做。现在,下一步就是要添加文本是拥有你的合同:

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

This means that all the functions inside MyToken now can access the variable owner and the modifier onlyOwner. The contract also gets a function to transfer ownership. Since it might be interesting to set the owner of the contract at startup, you can also add this to the constructor function:

    function MyToken(
        uint256 initialSupply,
        string tokenName,
        uint8 decimalUnits,
        string tokenSymbol,
        address centralMinter
        ) {
        if(centralMinter != 0 ) owner = centralMinter;
    }

CENTRAL MINT

假设你想要改变流通中的硬币数量。 当您的代币实际上代表区块链资产(如黄金证书或政府货币)并且您希望虚拟资源库反映真实资产时,就是这种情况。 当货币持有者希望对令牌的价格进行某种控制,并希望发行或移除令牌时,也可能出现这种情况。

首先,我们需要添加一个变量来存储totalSupply并将其分配给我们的构造函数。

    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。 这意味着该函数将在编译时被重写,以继承我们之前定义的修饰符onlyOwner中的代码。 此函数的代码将插入修改器函数的下划线处,这意味着此特定函数只能由设置为所有者的帐户调用。 只需将此添加到拥有所有者修饰符的合同中,您就可以创建更多硬币。

资产冻结

根据您的使用情况,您可能需要对谁可以或不可以使用您的令牌有一些监管障碍。 为此,您可以添加一个参数,使合同所有者能够冻结或解冻资产。

将此变量和函数添加到合同中的任何位置。 你可以把它们放在任何地方,但为了好的做法,我们建议你将映射与其他事件的映射和事件放在一起。

    mapping (address => bool) public frozenAccount;
    event FrozenFunds(address target, bool frozen);

    function freezeAccount(address target, bool freeze) onlyOwner {
        frozenAccount[target] = freeze;
        FrozenFunds(target, freeze);
    }

使用此代码,默认情况下所有帐户都会解冻,但所有者可以通过调用冻结帐户将其中的任何帐户设置为冻结状态。 不幸的是,冻结没有实际效果,因为我们没有在转移函数中添加任何内容。 我们正在改变这一点:

    function transfer(address _to, uint256 _value) {
        require(!frozenAccount[msg.sender]);

现在任何被冻结的账户都会保持其资金不变,但无法移动。 在冻结所有帐户之前,所有帐户都会解冻,但您可以轻松地将该行为恢复为需要手动批准每个帐户的白名单。 只需将frozenAccount重命名为approvedAccount并将最后一行更改为:

        require(approvedAccount[msg.sender]);

AUTOMATIC SELLING AND BUYING

So far, you've relied on utility and trust to value your token. But if you want you can make the token's value be backed by Ether (or other tokens) by creating a fund that automatically sells and buys them at market value.

First, let's set the price for buying and selling:

    uint256 public sellPrice;
    uint256 public buyPrice;

    function setPrices(uint256 newSellPrice, uint256 newBuyPrice) onlyOwner {
        sellPrice = newSellPrice;
        buyPrice = newBuyPrice;
    }

This is acceptable for a price that doesn't change very often, as every new price change will require you to execute a transaction and spend a bit of Ether. If you want to have a constant floating price we recommend investigating standard data feeds

The next step is making the buy and sell functions:

    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
    }

Notice that this will not create new tokens but change the balance the contract owns. The contract can hold both its own tokens and Ether and the owner of the contract, while it can set prices or in some cases create new tokens (if applicable) it cannot touch the bank's tokens or Ether. The only way this contract can move funds is by selling and buying them.

Note Buy and sell "prices" are not set in Ether, but in wei the minimum currency of the system (equivalent to the cent in the Euro and Dollar, or the Satoshi in Bitcoin). One Ether is 1000000000000000000 wei. So when setting prices for your token in Ether, add 18 zeros at the end.

When creating the contract, send enough Ether to it so that it can buy back all the tokens on the market otherwise your contract will be insolvent and your users won't be able to sell their tokens.

The previous examples, of course, describe a contract with a single central buyer and seller, a much more interesting contract would allow a market where anyone can bid different prices, or maybe it would load the prices directly from an external source.

AUTOREFILL

Everytime, you make a transaction on Ethereum you need to pay a fee to the miner of the block that will calculate the result of your smart contract. While this might change in the future, for the moment fees can only be paid in Ether and therefore all users of your tokens need it. Tokens in accounts with a balance smaller than the fee are stuck until the owner can pay for the necessary fee. But in some use cases, you might not want your users to think about Ethereum, blockchain or how to obtain Ether, so one possible approach would have your coin automatically refill the user balance as soon as it detects the balance is dangerously low.

In order to do that, first you need to create a variable that will hold the threshold amount and a function to change it. If you don't know any value, set it to 5 finney (0.005 Ether).

    uint public minBalanceForAccounts;

    function setMinBalance(uint minimumBalanceInFinney) onlyOwner {
         minBalanceForAccounts = minimumBalanceInFinney * 1 finney;
    }

Then, add this line to the transfer function so that the sender is refunded:

    /* Send coins */
    function transfer(address _to, uint256 _value) {
        ...
        if(msg.sender.balance < minBalanceForAccounts)
            sell((minBalanceForAccounts - msg.sender.balance) / sellPrice);
    }

You can also instead change it so that the fee is paid forward to the receiver by the sender:

    /* Send coins */
    function transfer(address _to, uint256 _value) {
        ...
        if(_to.balance<minBalanceForAccounts)
            _to.send(sell((minBalanceForAccounts - _to.balance) / sellPrice));
    }

This will ensure that no account receiving the token has less than the necessary Ether to pay the fees.

PROOF OF WORK

There are some ways to tie your coin supply to a mathematical formula. One of the simplest ways would be to make it a "merged mining" with Ether, meaning that anyone who finds a block on Ethereum would also get a reward from your coin, given that anyone calls the reward function on that block. You can do it using the special keyword coinbase that refers to the miner who finds the block.

    function giveBlockReward() {
        balanceOf[block.coinbase] += 1;
    }

It's also possible to add a mathematical formula, so that anyone who can do math can win a reward. On this next example you have to calculate the cubic root of the current challenge gets a point and the right to set the next challenge:

    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
    }

Of course, while calculating cubic roots can be hard for someone to do on their heads, they are very easy with a calculator, so this game could be easily broken by a computer. Also since the last winner can choose the next challenge, they could pick something they know and therefore would not be a very fair game to other players. There are tasks that are easy for humans but hard for computers but they are usually very hard to code in simple scripts like these. Instead, a fairer system should be one that is very hard for a computer to do, but isn't very hard for a computer to verify. A great candidate would be to create a hash challenge where the challenger has to generate hashes from multiple numbers until they find one that is lower than a given difficulty.

这个过程最早由Adam Back于1997年提出为Hashcash,之后由Satoshi Nakamoto在比特币中实现,作为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;

一旦合同在线,选择“工作证明”功能,将您最喜欢的号码添加到nonce字段并尝试执行。如果确认窗口发出红色警告,提示“无法执行数据”,请返回并选择另一个数字,直至找到允许交易前进的数字:此过程是随机的。如果您发现一个奖励,您将获得自上次奖励后每分钟获得的1个标记,然后将挑战难度向上或向下调整,以平均每个奖励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
    }
}

部署

向下滚动,您会看到部署的估计成本。如果您想要,您可以更改滑块设置较小的费用,但如果价格太低于平均市场价格,您的交易可能需要更长的时间才能完成。点击部署并输入您的密码。几秒钟后,您将被重定向到仪表板,在最新的交易中,您将看到一行说“创建合同”。等待几秒钟让某人选择你的交易,然后你会看到一个缓慢的蓝色矩形,表示有多少其他节点已经看到你的交易并确认了它们。您拥有的确认越多,您的代码已部署的可信度就越高。

创建令牌

点击管理页面上的链接,您将成为世界上最简单的中央银行仪表板,您可以用新创建的货币进行任何操作。

阅读合同的左侧,您可以免费获得所有可用于从合同中读取信息的选项和功能。如果您的令牌拥有所有者,它将在此处显示其地址。复制该地址并将其粘贴到余额中,它将向您显示任何帐户的余额(余额也会自动显示在具有令牌的任何帐户页面上)。

在右侧的“ 写入合同”您将看到可用于以任何方式更改或更改区块链的所有功能。这些将耗费天然气。如果您创建了允许您铸造新硬币的合同,则应该有一个名为“Mint Token”的功能。选择它。

管理中央美元

选择创建这些新货币的地址,然后选择金额(如果您将小数设置为2,则在金额后添加2个零,以创建正确的数量)。执行从中选择设置为所有者的帐户,将Ether数量保留为零,然后按执行。

经过几次确认后,收款人余额将会更新以反映新的金额。但是您的收件人钱包可能不会自动显示它:为了了解自定义令牌,钱包必须手动将其添加到监视列表中。复制您的令牌地址(在管理页面,按复制地址)并将其发送给您的收件人。如果他们还没有进入合约标签,请按Watch Token,然后在那里添加地址。最终用户可以自定义显示的名称,符号和小数量,特别是如果他们有其他类似(或相同)名称的令牌。主图标不可更改,用户在发送和接收令牌时应该注意它们,以确保它们处理的是实际交易,而不是一些模仿令牌。

添加令牌

使用你的硬币

部署令牌后,它们将被添加到您观看的令牌列表中,并且总余额将显示在您的帐户中。为了发送令牌,只需转到发送选项卡并选择一个包含令牌的帐户。账户中的令牌将在Ether下面列出选择它们,然后键入要发送的令牌数量。

如果您想添加其他人的令牌,只需转到合同标签并点击监视令牌例如,要将Unicorn(?)标记添加到观察列表中,只需添加地址0x89205A3A3b2A69De6Dbf7f01ED13B2108B2c43e7,其余信息将自动加载。点击确定,您的令牌将被添加。

隐形独角兽

独角兽标志专为那些捐赠给由以太坊基金会控制的地址0xfB6916095ca1df60bB79Ce92cE3Ea74c37c5d359创建的纪念品欲了解更多关于他们的信息在这里阅读

怎么办?

你刚刚学会了如何使用以太坊来发行令牌,这可以代表任何你想要的东西。但是你可以用令牌做什么?例如,您可以使用代币代表公司的股票,或者您可以使用中央委员会对何时发行新币以控制通胀进行投票。您也可以通过众包为筹集资金筹集资金接下来你会构建什么?

原文地址