目前除了BTC,ETH,EOS三大公链外,其余的虚拟币均是基于公链上发行的代币。其实发行代币很简单,只需要不到100行代码就能发行属于自己的虚拟货币了。

智能合约

所谓的智能合约,其实就是运行在ETH主网上的代码。Code is Low。在区块链中,代码即是法律,称其为合约,代表这是大家都遵守的一个东西。

ERC20

这次发行的是基于ETH的ERC20标准的代币。何为ERC20?简单点讲,ERC20可以理解为编程语言的一个接口,我们必须实现这些接口。比如这个接口定义了变量totalSupply,这个变量代表着发行总量。还有诸如transferfreeze等等,只有按照这些规范写出来的智能合约,才能被认可为代币。

合约代码

合约使用的V神自己写的语言Solidity,如果不想再配置麻烦的环境的话,可以使用在线的IDE,Remix

先贴出代码

pragma solidity ^0.4.25;

/**
 * Math operations with safety checks
 */
library Safe {
  function safeMul(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a * b;
    assert(a == 0 || c / a == b);
    return c;
  }

  function safeDiv(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b > 0);
    uint256 c = a / b;
    assert(a == b * c + a % b);
    return c;
  }

  function safeSub(uint256 a, uint256 b) internal pure returns (uint256) {
    assert(b <= a);
    return a - b;
  }

  function safeAdd(uint256 a, uint256 b) internal pure returns (uint256) {
    uint256 c = a + b;
    assert(c>=a && c>=b);
    return c;
  }


}
contract MyCoin{
    uint public totalSupply = 500004322.443245*10**18;  //Total amount of distribution
    uint8 constant public decimals = 18;
    string constant public name = "MyCoin";
    string constant public symbol = "MC";
    
	address public owner;


    mapping (address => uint256) public balanceOf;
	mapping (address => uint256) public freezeOf;
    mapping (address => mapping (address => uint256)) public allowance;

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

    /* Initializes contract with initial supply tokens to the creator of the contract */
    constructor() public{
        balanceOf[msg.sender] = totalSupply;
		owner = msg.sender;
    }

    /// @param _to The address of the recipient
    /// @param _value The amount of token to be transferred
    function transfer(address _to, uint256 _value) public {
        require(_to != 0x0);
		require(_value > 0);
        require(balanceOf[msg.sender] >= _value);
        require(balanceOf[_to] + _value >= balanceOf[_to]);
        balanceOf[msg.sender] = Safe.safeSub(balanceOf[msg.sender], _value);
        balanceOf[_to] = Safe.safeAdd(balanceOf[_to], _value);
        emit Transfer(msg.sender, _to, _value);
    }

    /// @param _spender The address of the account able to transfer the tokens
    /// @param _value The amount of wei to be approved for transfer
    /// @return Is it successfully approved
    function approve(address _spender, uint256 _value) public
        returns (bool success) {
		require(_value > 0);
        allowance[msg.sender][_spender] = _value;
        return true;
    }
       

    /// @param _from The address of the sender
    /// @param _to The address of the recipient
    /// @param _value The amount of token to be transferred
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool) {
        require(_to != 0x0);
        require(_value > 0);
        require(balanceOf[_from] > _value);
        require(balanceOf[_to] + _value >= balanceOf[_to]);
        require(_value <= allowance[_from][msg.sender]);
        balanceOf[_from] = Safe.safeSub(balanceOf[_from], _value);
        balanceOf[_to] = Safe.safeAdd(balanceOf[_to], _value);
        allowance[_from][msg.sender] = Safe.safeSub(allowance[_from][msg.sender], _value);
        emit Transfer(_from, _to, _value);
        return true;
    }

    /// @param _value The amount of token to be burned
    /// @return Is it successfully burned
    function burn(uint256 _value) public returns (bool) {
        require(balanceOf[msg.sender] >= _value);
        require(_value > 0);
        balanceOf[msg.sender] = Safe.safeSub(balanceOf[msg.sender], _value);
        totalSupply = Safe.safeSub(totalSupply,_value);
        emit Burn(msg.sender, _value);
        return true;
    }
    
    /// @param _value The amount of token to be freeze
    /// @return Is it successfully froze
	function freeze(uint256 _value) public returns (bool) {
        require(balanceOf[msg.sender] >= _value);
		require(_value > 0);
        balanceOf[msg.sender] = Safe.safeSub(balanceOf[msg.sender], _value);
        freezeOf[msg.sender] = Safe.safeAdd(freezeOf[msg.sender], _value);
        emit Freeze(msg.sender, _value);
        return true;
    }
	
    /// @param _value The amount of token to be unfreeze
    /// @return Is it successfully unfroze
	function unfreeze(uint256 _value) public returns (bool) {
        require(freezeOf[msg.sender] >= _value);
        require(_value > 0);
        freezeOf[msg.sender] = Safe.safeSub(freezeOf[msg.sender], _value);
		balanceOf[msg.sender] = Safe.safeAdd(balanceOf[msg.sender], _value);
        emit Unfreeze(msg.sender, _value);
        return true;
    }
	
    function() payable public {
        revert();
    }
}

第一行pragma solidity声明了编译的版本,不可去掉。

safe类用于安全的处理加减乘除运算。

contract MyCoin代表着我们的合约,名字为MyCoin。在合约开始就通过赋值的方式实现了ERC20标准中的totalSupplydecimalsnamesymbol,同时还定义了owner
除此之外声明了4个事件,TransferBurnFreezeUnfreeze。这是因为在调用完对应的方法之后,必须emit对应的事件。

constructor() public则是构造函数。在智能合约中,构造函数只能被执行一次,那就是部署合约的那一次。所以在构造函数中,我们将owner赋值为msg.sender,即为部署合约的地址。同时将该地址的余额改为发行总额。

其他方法为实现ERC20标准的一些方法,各个代币合约都大同小异,看看就行。

调试

在Remix右边Compile中,选好对应的编译器版本后,Start to compile。如果红色或黄色的报错框,那么代码就是通过编译了。

Run中可以调试自己的合约代码。Environment为调试环境,共有三种环境

  • JavaScript VM:JS虚拟机,通过JS模拟的钱包环境。
  • Injected Web3:使用MetaMask之类的Chrome插件钱包作为调试环境。
  • Web3 Provider:使用eth钱包作为测试环境,如geth。

这里我们先使用JavaScript VM,accounts中默认添加了一些测试账户,选择一个账户后,再选择我们的合约MyCoin,点击Deploy部署,此时这个合约就被部署在选中的虚拟钱包地址上了。在Deployed Contracts中能看到被部署的合约,点开后就会出现我们代码中实现的方法。

调试.png

在要测试的方法后面填入参数,多个参数用,隔开,再点击方法明就能执行,并且在中间的Debug区域会输出方法执行的情况。

部署合约

代码调试完毕后就可以部署了,部署很简单。
Compile中,点击details,可以看到ABI,BYTECODE等信息。找到WEB3DEPLOY,把里面的代码复制下来,在钱包中的JS命令行环境中粘贴并执行就OK了。一键部署,非常简单,我使用的geth进行部署。

etherscan中可以看到部署的情况。部署完成后还需要一次确认。
未确认的合约点开Code会提示你
Are you the Contract Creator? Verify And Publish Your Contract Source Code Today!
点击** Verify And Publish**开始确认。
填写必要的选项,Contract Name合约名,Compiler编译器版本,在大框中输入合约的代码,必须是和部署时的代码一模一样。

下一步后会提示你验证签名,这里我们使用钱包的web3.eth.sign()。根据提示会生成一段字符串让你的钱包验证,必须是使用部署合约的钱包验证。

var account = web3.eth.accounts[0];
var sha3Msg = web3.sha3("需要被验证的一串字符串");
var signedData = web3.eth.sign(account, sha3Msg);

将得到的signedData填入验证框中,通过验证就确认成功了。此时再查看合约的CodeCode旁会有绿色的小勾,并且你的代码也被公开出来了。

如果你想让你的代币拥有图标,网站邮箱链接等等,在Links中点击Update,这里同样会给你一串字符串Signed message,还是用web3.eth.sign()验证签名。通过验证后在Fill in form里按照规范填写需要的内容即可。