14.1 系统概述
本章笔者将带领读者一起动手实践,综合运用前面各章节的知识实现一个简化版本并且带有众筹功能的链上去中心化交易所智能合约。需要提醒读者牢记和注意的是在我国不能进行非法ICO和虚拟货币相关的交易和兑换业务。因此本简化版本实例阉割了很多实际交易所的功能并且只是部署在本地私有网络中,同时不提供上层配套的和用户实际交互的UI模块。不要运用本章的知识进行法律范围外的商业应用和推广,而只是将本章介绍的编程知识作为快速掌握solidity编写复杂的链上智能合约的有效途径。现实中智能合约应用比较多的场景还是金融、保险和游戏类应用,而金融类应用中关于众筹和去中心化交易所有比较多的落地应用,特别是后者而且涉及大量的solidity编程知识,是综合演示solidity编程知识比较好的教学范例应用。同时如果哪一天法律许可了,进行一定的改造和升级并配套上层的用户交互界面就是一个独立的应用,可以减少从头开发的工作量。再次提醒读者在具体法律许可之前本章所有实例都是在本地私有测试链上,不能部署于主网并开放给第三方用户使用。
本章计划实现的带众筹功能的交易模块,本质上就是先进行虚拟交易所项目的众筹,项目众筹参与用户可以按照投入的虚拟eth得到对应比例的内部代币,在众筹期间该代币都锁定在众筹合约中直到众筹时间结束或者提前完成。众筹结束后会进行募集的虚拟eth资金的有效性检查,如果满足预期众筹目标则此次众筹成功,参与用户可以提现其分配的内部代币数量,同时众筹受益人得到募集到的虚拟eth,并且开启交易模块的交易功能;反之如果众筹失败则原路返回用户的参与资金整个项目结束。
项目众筹参与用户在后期可以通过两种途径得到收益:一种是将众筹得到的代币加入交易模块的资金池通过交易所的运行过程中得到的交易佣金来按照比例得到分红;另外一种是将得到的代币转入交易模块进行交易,通过代币升值或赚钱代币挂单差价来得到收益。
系统整个功能通过一系列的智能合约来配合完成,从逻辑上主要分为分红合约、众筹合约以及ERC20代币交易合约三大核心模块,各模块底层实现时都借助于内部发行的标准ERC20代币。其中分红合约相对比较简单底层实现以标准的ERC20内部代币为核心,主要完成后期交易模块运行过程中交易佣金的累计以及佣金的按比例分红,并支持用户动态的追加和撤回投资。众筹合约主要完成项目的众筹,并根据最终是否众筹成功来执行不同的后处理流程。交易合约是所有模块中最核心和最复杂的,主要实现以虚拟eth为计价和交易货币单位,完成不同ERC20代币的交易和兑换并收取交易佣金,在遇到外部法律或其他紧急问题时可以冻结所有的交易和提现操作。其中实现的重点和难点是买卖挂单的处理以及成交指定历史挂单的处理。所有模块实现时涉及算术运算的地方都利用SafeMath库来完成以提高运算安全性。整个系统示意图如下:
从系统结构图可知首先以内部发行的ERC20代币为基础和核心;然后进行模拟众筹如果众筹结束且成功后就开启交易模块的交易功能;在交易模块正常运行过程中通过撮合成交ERC20代币与eth之间的买卖交易单得到交易佣金;通过分红模块进行交易佣金的累计,并按照用户投入的内部代币比例进行相应分红;如果遇到内部或外部紧急情况可以冻结交易模块和分红模块所有的操作,直到再次运行的外部条件得到满足再恢复交易和分红模块的执行。
在实际实现时整个项目主要基于zeppelin框架提供的各种基础合约进行扩展和定制,以提高整体编码效率以及项目运行时的安全性保证。接下来我们对系统中涉及到的每个功能模块的实现细节进行探讨和剖析,希望读者和笔者一起一步步动手实践,用solidity从零构建一个复杂的链上智能合约系统,从而达到快速而全面掌握solidity编程知识。
14.2 ERC20分红代币MTT
从前面介绍可知不管是进行前期的项目众筹,还是后期交易模块正常运转后对交易佣金的分红都需要涉及内部发行的ERC20代币。因此首先我们来实现内部的ERC20代币MTT。内部代币MTT是符合ERC20协议的标准代币,但增加了额外功能以支持紧急情况下对所有交易请求的冻结。当由于外部法律法规政策变化或者发现内部合约实现bug时冻结所有的转账和授权功能,直到发现的bug修复或者外部的运行条件得到许可。
其中基础代币功能是通过继承合约StandardToken来实现的,因此通过继承现有的标准代币合约,我们只需要定制个性化的代币信息如发行总量、代币精度以及代币符号等,就可以实现了一个符合ERC20协议的标准代币,并自动支持各种数字钱包,示意代码如下:
//github import
import "https://github.com/dayangxi/soliditynew/contracts/token/ERC20/ERC20Pausable.sol";
import "https://github.com/dayangxi/soliditynew/contracts/token/ERC20/ERC20Detailed.sol";
contract MyTestToken is ERC20Pausable, ERC20Detailed {
constructor () public ERC20Detailed("MyTestToken", "MTT", 18) {
_mint(msg.sender, 10000 * (10 ** uint256(decimals())));
}
}
对代币合约核心的转账业务以及额度授权管理功能的冻结是通过继承继承合约Pausable来实现的。具体实现时通过继承来的函数修改器检查当前核心业务类操作是否被冻结,如果被管理员冻结则回滚状态修改抛出运行异常;如果没有被管理员冻结则调用父类的相应操作完成实际业务请求。比如针对原始的转账操作首先通过函数修改器进行前置条件检查,检查通过后再调用父类转账函数进行实际的转账请求,核心代码如下:
function transfer(address _to, uint256 _value)
public whenNotPaused returns (bool) {
return super.transfer(_to, _value);
}
同理对额度授权管理功能的改造也是类型,核心代码如下:
function approve(address _spender, uint256 _value)
public whenNotPaused returns (bool) {
return super.approve(_spender, _value);
}
对MTT实现的其他业务类操作的函数如decreaseApproval、increaseApproval和transferFrom也是遵循同样的原则进行改造此处不再进行重复。从范例实现代码可知,只要管理员不对MTT执行紧急情冻结操作其表现形式和普通的ERC20代币一样;但如果执行了交易冻结操作,所有的业务类操作都不被许可,但不影响查询业务类操作。