如何在QTUM上发起ICO代币众筹


  • qtum team

    如何在QTUM上发起ICO代币众筹

    1. 合约编写

    在上篇文章中,我们分析了几种经典的ICO众筹合约代码设计 https://forum.qtum.org/topic/164/ico%E4%BC%97%E7%AD%B9%E5%90%88%E7%BA%A6%E4%BB%A3%E7%A0%81%E5%88%86%E6%9E%90,

    本篇文章编写了一个简单的ICO众筹合约模版,可以在github中查看具体源码,
    https://github.com/icodeface/SimpleCrowdSale

    下面对代码做一些简单的分析。
    合约代码由 SimpleCrowdSale.sol、ERC20Token.sol、SafeMath.sol 三个文件构成,SafeMath中封装了安全的四则运算函数;ERC20Token 实现了基于ERC20标准的代币合约;SimpleCrowdSale 继承自ERC20Token,在其基础上新增了众筹部分的相关代码逻辑。

    合约的构造函数需要传入9个参数,分别代表 众筹开始时间(以秒为单位的时间戳)、持续时间(秒)、最小token目标数,最大token发放数、token兑换汇率、创始人预留比例、token名称、token符号、token小数位。

    // SimpleCrowdSale.sol
        function SimpleCrowdSale(uint256 _startTime,
                         uint256 _duration,
                         uint256 _tokenContributionMin,
                         uint256 _tokenContributionCap,
                         uint256 _tokenContributionRate,
                         uint8 _founderPercentOfTotal,
                         string _name,
                         string _symbol,
                         uint8 _decimals)
            ......
    

    contribute 函数是用于购买token的关键函数,在装饰器中限定了函数调用时间需要众筹区间内,同时所发送的QTUM数量必须大于0,函数主体部分判断了funding是否为真,计算兑换的token数量,更新了token总数totalSupply和balanceOf字典中该用户的token数量,最后返回此次兑换的token数目。

    // SimpleCrowdSale.sol
        function contribute()
            public
            payable
            between(startTime, endTime)
            validAmount(msg.value)
            returns (uint256 amount)
        {
            assert(funding);
            uint256 tokenAmount = safeMul(msg.value, tokenContributionRate) / (1 ether / 1 wei);
            assert(safeAdd(totalSupply, tokenAmount) <= tokenContributionCap);
            totalSupply = safeAdd(totalSupply, tokenAmount);
            balanceOf[msg.sender] = safeAdd(balanceOf[msg.sender], tokenAmount);
            Contribution(msg.sender, msg.value, tokenAmount);
            return tokenAmount;
        }
    

    finalize 函数在众筹达到预期目标的条件下可以调用执行,意味着token自由流通,创始人也将收到所得QTUM。

    // SimpleCrowdSale.sol
        function finalize()
            public
        {   
            // 判断是否达到成功条件
            assert(funding);
            assert(now >= endTime && totalSupply >= tokenContributionMin);
    
            funding = false; // 将funding设置为假,意味着众筹结束
            uint256 additionalTokens =
                totalSupply * founderPercentOfTotal / (100 - founderPercentOfTotal);
            totalSupply = safeAdd(totalSupply, additionalTokens); // 根据founderPercentOfTotal的值计算创始人应该分配的token数量
            // 更新totalSupply和balanceOf
            balanceOf[founder] = safeAdd(balanceOf[founder], additionalTokens);
            Transfer(0, founder, additionalTokens);
            transfersEnabled = true; //将transfersEnabled设置为真,意味着token可以自由流通了
            Finalized(now);
            founder.transfer(this.balance); // 众筹所得QTUM转入创始人账户中
        }
    

    refund 函数在众筹失败的条件下可以调用执行,调用者前期众筹时发送的QTUM将会被返还。

    // SimpleCrowdSale.sol
        function refund()
            public
        {
            // 若funding不为真,意味着众筹已经成功,不可以refund
            assert(funding);
            // 在超过众筹截至时间的前提下,判断当前众筹所得token是否达到最小目标数
            assert(now >= endTime && totalSupply <= tokenContributionMin);
    
            uint256 tokenAmount = balanceOf[msg.sender];
            assert(tokenAmount > 0);
            // 更新 balanceOf 和 totalSupply
            balanceOf[msg.sender] = 0;
            totalSupply = safeSub(totalSupply, tokenAmount);
            // 计算应返还的QTUM数
            uint256 refundValue = safeMul(tokenAmount, (1 ether / 1 wei)) / tokenContributionRate;
            Refund(msg.sender, refundValue);
            msg.sender.transfer(refundValue);
        }
    

    2. 合约部署

    如果没有安装 qtum ,需要先进行安装,https://github.com/qtumproject/qtum/blob/testnet-1/README.md

    合约调用和部署的基本方法可以参照 xiaolong 的文章 https://forum.qtum.org/topic/193/qtum%E6%98%9F%E7%81%AB%E7%BD%91%E7%BB%9C-sparknet-%E4%BD%BF%E7%94%A8%E6%96%B9%E6%B3%95%E5%8F%8A%E8%AF%B4%E6%98%8E

    2.1 编译合约

    在这里,我们借助solidity开发环境remix辅助我们更方便地编译合约,https://ethereum.github.io/browser-solidity/

    首先将代码添加到remix中,点击右侧“browser/SimpleCrowdSale.sol:SimpleCrowdSale” 下面的 “Contract details (bytecode, interface etc.)”,将会看到合约的详情信息,包括Bytecode。

    接着需要对合约的初始化参数进行编码,编码规则可参考 https://github.com/ethereum/wiki/wiki/Ethereum-Contract-ABI。在此,我们的参数为:
    1498831800, 259200, 100, 10000000000, 100, 10, "TestCoin", "T", 18,

    编码后的参数字节码为:

    0000000000000000000000000000000000000000000000000000000059565bb8000000000000000000000000000000000000000000000000000000000003f480000000000000000000000000000000000000000000000000000000000000006400000000000000000000000000000000000000000000000000000002540be4000000000000000000000000000000000000000000000000000000000000000064000000000000000000000000000000000000000000000000000000000000000a000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000001600000000000000000000000000000000000000000000000000000000000000012000000000000000000000000000000000000000000000000000000000000000854657374436f696e00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000015400000000000000000000000000000000000000000000000000000000000000
    

    2.2 部署合约

    在QTUM中部署合约的命令为createcontract "bytecode" (gaslimit gasprice "senderaddress" broadcast),
    将编码后的参数字节码添加到合约bytecode尾部,在命令行执行 ./qtum-cli createcontract "合约和参数的字节码" 49999999
    返回的结果如下:

    {
      "txid": "4dde46e384b7314aab3f4ca39e7129d9c9b4e08289e5d63305f0b3ebdb10e0dd",
      "sender": "QSnezVLjybKA5APBAwNSkUVUXes4Kh1uMQ",
      "hash160": "43d55f33546203079032cdce1105403af37638be",
      "address": "fde641f176917705578bbf69ec1c9ce62421ab28"
    }
    

    等待一两分钟完成当前区块之后,执行“./qtum-cli listcontracts”,可以看到我们的合约已经部署成功了。

    3. 合约调用

    在remix中,我们可以查看合约中每一个函数的ID。

    在QTUM中调用合约的命令为sendtocontract "contractaddress" "data" (amount gaslimit gasprice senderaddress broadcast), 其中data部分的规则为 函数ID+参数字节码。

    3.1 购买token

    执行./qtum-cli sendtocontract "fde641f176917705578bbf69ec1c9ce62421ab28" "d7bb99ba" 10.24 499999
    表示向当前合约的contribute函数发送10.24个QTUM,并支付0.00499999的手续费。
    回显如下:

    {
      "txid": "12ebc36dad35306745cc8d691236680166e57af4075d413d1137a5763bc924b2",
      "sender": "QTTVhWbBamVvThoPdxkjUupM7o5sbhfKa1",
      "hash160": "4b2dd6530a55f006091fb537041c50dc5aa222c6"
    }
    

    3.2 查看总token发行数

    执行./qtum-cli callcontract "fde641f176917705578bbf69ec1c9ce62421ab28" "18160ddd"

    回显如下:

    {
      "address": "fde641f176917705578bbf69ec1c9ce62421ab28",
      "executionResult": {
        "gasUsed": 21699,
        "excepted": "None",
        "newAddress": "fde641f176917705578bbf69ec1c9ce62421ab28",
        "output": "0000000000000000000000000000000000000000000000000000000000000400",
        "codeDeposit": 0,
        "gasRefunded": 0,
        "depositSize": 0,
        "gasForDeposit": 0
      },
      "transactionReceipt": {
        "stateRoot": "fb441f1a048182ea990bbc18fc1491c658b53906da2945ecc143182be35447e6",
        "gasUsed": 21699,
        "bloom": "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
        "log": [
        ]
      }
    }
    

    output 的值为16进制的400,转化为10进制即为1024。

    3.3 终止众筹

    finalize函数的地址ID为 4bb278f3
    执行 ./qtum-cli sendtocontract "fde641f176917705578bbf69ec1c9ce62421ab28" "4bb278f3" 0 499999
    执行成功后,合约创建者收到重筹所得QTUM,token也可以自由流通了。

    transferEnabled 为 真

    funding 为 假

    3.4 token转账与余额查看

    transfer函数的地址ID为 a9059cbb
    balanceOf函数的地址ID为 70a08231

    发送方的hash160地址为 4b2dd6530a55f006091fb537041c50dc5aa222c6,编码后为
    0000000000000000000000004b2dd6530a55f006091fb537041c50dc5aa222c6,
    token数为1024

    接收方的hash160地址为 7224c9892839b20c001036a0e313aac8dee42f7e,编码后为 0000000000000000000000007224c9892839b20c001036a0e313aac8dee42f7e, token数为0

    执行 ./qtum-cli sendtocontract "fde641f176917705578bbf69ec1c9ce62421ab28" "a9059cbb0000000000000000000000007224c9892839b20c001036a0e313aac8dee42f7e0000000000000000000000000000000000000000000000000000000000000040" 0 499999 0.00000001 "QTTVhWbBamVvThoPdxkjUupM7o5sbhfKa1"

    等待一个区块时间后,查看双方的token数量





  • 使用的solidity ide :
    https://ethereum.github.io/browser-solidity/#version=soljson-v0.4.13+commit.fb4cb1a.js&optimize=false

    但是出现这个问题:
    browser/SimpleCrowdSale.sol:82:9: DeclarationError: Modifier already used for this function.
    validAmount(100 - founderPercentOfTotal)
    ^---------------------------------------^
    请教楼主。。。。


  • qtum team

    @nisang 使用0.4.11版本的solidity看看。



  • 1.可否同时接收以太币和比特币,有无相关代码示例
    2.对于众筹开始前的锁定和第一,二,三周的额外奖例怎么在代码中体现出来


  • qtum team

    @zacharytaylor said in 如何在QTUM上发起ICO代币众筹:

    1.可否同时接收以太币和比特币,有无相关代码示例
    2.对于众筹开始前的锁定和第一,二,三周的额外奖例怎么在代码中体现出来

    1.合约本身无法做到跨链,如果你的ICO想同时接收多个币种,可以再ICO的时候要求使用非QTUM的用户附带上他的qtum地址,然后在合约中预留一个录入的接口,这个设计我记得bancor的合约代码中有看到过,你可以去看看。

    2.这份示例代码没有对不同时期的兑换比例进行处理,如果你需要这个功能,可以看看 the DAO的代码。

    function divisor() constant returns (uint divisor) {
            // The number of (base unit) tokens per wei is calculated
            // as `msg.value` * 20 / `divisor`
            // The fueling period starts with a 1:1 ratio
            if (closingTime - 2 weeks > now) {
                return 20;
            // Followed by 10 days with a daily creation rate increase of 5%
            } else if (closingTime - 4 days > now) {
                return (20 + (now - (closingTime - 2 weeks)) / (1 days));
            // The last 4 days there is a constant creation rate ratio of 1:1.5
            } else {
                return 30;
            }
       } 
    

Log in to reply
 

Looks like your connection to QTUM was lost, please wait while we try to reconnect.