区块链智能合约:去中心化交易所Bancor业务合约分析(一)

Bancor的github里公布的代码算是我见过的项目里比较规范的了,而且还提供了很多test实例,确实花了很大的功夫。最近这段时间会关注分析一下这个项目的代码,跟大家分享一下Solidity这种语言是如何实现项目规范化的。

Github地址:https://github.com/bancorprotocol/contracts

如何入手?

正好这个项目用的是truffle框架,我们先解释一下truffle项目部署(仅涉及合约部分)的作业流程:

  1. truffle框架下一般有contracts, migration, test三个文件夹,另外还有一个truffle-config.js文件。contracts放的是智能合约文件*.sol;migration放的是合约部署的设定js文件;test放的是测试事件。
  2. 一般来说,合约写好之后会在项目目录下运行truffle compile,目录下会产生一个build文件夹,里面存放了编译后的bytecode和abi。
  3. 接着需要运行truffle migrate让合约上链(一般来说都会是本地的Ganache/旧版叫testrpc)
  4. 运行truffle test执行测试事件,terminal会实时输出结果。

Bancor的项目写得比较标准,我们可以直接去migration/2_deploy_contract.js看看Bancor一整套合约的部署流程,然后再逐一进行分析。

/* global artifacts */
/* eslint-disable prefer-reflect */

const Utils = artifacts.require('Utils.sol');
const Owned = artifacts.require('Owned.sol');
const Managed = artifacts.require('Managed.sol');
const TokenHolder = artifacts.require('TokenHolder.sol');
const ERC20Token = artifacts.require('ERC20Token.sol');
const EtherToken = artifacts.require('EtherToken.sol');
const SmartToken = artifacts.require('SmartToken.sol');
const SmartTokenController = artifacts.require('SmartTokenController.sol');
const BancorFormula = artifacts.require('BancorFormula.sol');
const BancorGasPriceLimit = artifacts.require('BancorGasPriceLimit.sol');
const BancorQuickConverter = artifacts.require('BancorQuickConverter.sol');
const BancorConverterExtensions = artifacts.require('BancorConverterExtensions.sol');
const BancorConverter = artifacts.require('BancorConverter.sol');
const CrowdsaleController = artifacts.require('CrowdsaleController.sol');

module.exports = async (deployer) => {
    deployer.deploy(Utils);
    deployer.deploy(Owned);
    deployer.deploy(Managed);
    deployer.deploy(TokenHolder);
    deployer.deploy(ERC20Token, 'DummyToken', 'DUM', 0);
    deployer.deploy(EtherToken);
    await deployer.deploy(SmartToken, 'Token1', 'TKN1', 2);
    deployer.deploy(SmartTokenController, SmartToken.address);
    deployer.deploy(BancorFormula);
    deployer.deploy(BancorGasPriceLimit, '22000000000');
    deployer.deploy(BancorQuickConverter);
    deployer.deploy(BancorConverterExtensions, '0x125463', '0x145463', '0x125763');
    deployer.deploy(BancorConverter, SmartToken.address, '0x124', 0, '0x0', 0);
    deployer.deploy(CrowdsaleController, SmartToken.address, 4102444800, '0x125', '0x126', 1);
};

从名称就可以看出来Bancor的合约分三类:

类别 名称
基础支持 Utils.sol, Owned.sol, Managed.sol
代币实现 TokenHolder.sol, ERC20Token.sol, EtherToken.sol, SmartToken.sol
交易逻辑 SmartTokenController.sol, BancorFormula.sol, BancorGasPriceLimit.sol, BancorQuickConverter.sol,
BancorConverterExtensions.sol, BancorConverter.sol, CrowdsaleController.sol

这次的第一节会快速过完第一类和第二类的一半XD

基础支持合约

Utils.sol

pragma solidity ^0.4.18;
/* Utilities & Common Modifiers */
contract Utils {
    /** constructor  **/
    function Utils() public {
    }
    // verifies that an amount is greater than zero
    modifier greaterThanZero(uint256 _amount) {
        require(_amount > 0);
        _;
    }
    // validates an address - currently only checks that it isn't null
    modifier validAddress(address _address) {
        require(_address != address(0));
        _;
    }
    // verifies that the address is different than this contract address
    modifier notThis(address _address) {
        require(_address != address(this));
        _;
    }
    // Overflow protected math functions
    /** @dev returns the sum of _x and _y, asserts if the calculation overflows
        @param _x   value 1
        @param _y   value 2
        @return sum
    */
    function safeAdd(uint256 _x, uint256 _y) internal pure returns (uint256) {
        uint256 z = _x + _y;
        assert(z >= _x);
        return z;
    }
    /** @dev returns the difference of _x minus _y, asserts if the subtraction results in a negative number
        @param _x   minuend
        @param _y   subtrahend
        @return difference
    */
    function safeSub(uint256 _x, uint256 _y) internal pure returns (uint256) {
        assert(_x >= _y);
        return _x - _y;
    }
    /** @dev returns the product of multiplying _x by _y, asserts if the calculation overflows
        @param _x   factor 1
        @param _y   factor 2
        @return product
    */
    function safeMul(uint256 _x, uint256 _y) internal pure returns (uint256) {
        uint256 z = _x * _y;
        assert(_x == 0 || z / _x == _y);
        return z;
    }
}

总的来说也没什么特别的,modifier中确认的都是数字格式和地址问题,加减乘方法都用internal pure封装,也没有外来攻击可言。

Owned.sol和Managed.sol
这两个文件内容基本一样,具体负责的功能不同所以分开了,我这里只贴一份。

pragma solidity ^0.4.18;
/*    Provides support and utilities for contract management   */
contract Managed {
    address public manager;
    address public newManager;
    event ManagerUpdate(address indexed _prevManager, address indexed _newManager);

    /**        @dev constructor    */
    function Managed() public {
        manager = msg.sender;
    }
    // allows execution by the manager only
    modifier managerOnly {
        assert(msg.sender == manager);
        _;
    }
    /** @dev allows transferring the contract management
        the new manager still needs to accept the transfer
        can only be called by the contract manager
        @param _newManager    new contract manager
    */
    function transferManagement(address _newManager) public managerOnly {
        require(_newManager != manager);
        newManager = _newManager;
    }
    /** @dev used by a new manager to accept a management transfer    */
    function acceptManagement() public {
        require(msg.sender == newManager);
        ManagerUpdate(manager, newManager);
        manager = newManager;
        newManager = address(0);
    }
}

这个合约实际上跟OpenZeppelin (https://github.com/OpenZeppelin/zeppelin-solidity) 项目中的Owner.sol很像,基本功能就是在创建的时候把创建人自动设置成合约主人,增加ownerOnly modifier在后续函数中提供交易提出方的身份校验。唯一的区别就是转移ownership的方式不同。

  • OpenZeppelin:直接设定newOwner address后转移,一步到位。
  • Bancor:current owner更新newOwner地址,newOwner再通过acceptOwnership去接收,保证了newOwner的合法性,在acceptOwnership发生前current owner都可以再更新地址,有更高的容错性。但消耗的gas肯定更高了。

代币实现合约:接口(interface)篇

到这里事情就开始有点复杂了,我们需要引入一个新的概念:interface。
interface在编译中其实并不产生任何字节码,但是在coding过程中会帮忙起到一个代码规范的效果。Bancor的interface文件夹中一共有10个interface,跟代币实现相关的interface有4个(或者说5个,因为IOwned.sol也算在里面):

/*    Owned contract interface    */
contract IOwned {
    // this function isn't abstract since the compiler emits automatically generated getter functions as external
    function owner() public view returns (address) {}
    function transferOwnership(address _newOwner) public;
    function acceptOwnership() public;
}

/*   Token Holder interface    */
contract ITokenHolder is IOwned {
    function withdrawTokens(IERC20Token _token, address _to, uint256 _amount) public;
}

/*    ERC20 Standard Token interface    */
contract IERC20Token {
    // these functions aren't abstract since the compiler emits automatically generated getter functions as external
    function name() public view returns (string) {}
    function symbol() public view returns (string) {}
    function decimals() public view returns (uint8) {}
    function totalSupply() public view returns (uint256) {}
    function balanceOf(address _owner) public view returns (uint256) { _owner; }
    function allowance(address _owner, address _spender) public view returns (uint256) { _owner; _spender; 
    function transfer(address _to, uint256 _value) public returns (bool success);
    function transferFrom(address _from, address _to, uint256 _value) public returns (bool success);
    function approve(address _spender, uint256 _value) public returns (bool success);
}

/*    Ether Token interface    */
contract IEtherToken is ITokenHolder, IERC20Token {
    function deposit() public payable;
    function withdraw(uint256 _amount) public;
    function withdrawTo(address _to, uint256 _amount) public;
}

/*    Smart Token interface    */
contract ISmartToken is IOwned, IERC20Token {
    function disableTransfers(bool _disable) public;
    function issue(address _to, uint256 _amount) public;
    function destroy(address _from, uint256 _amount) public;
}

根据这几个interface就可以画出一个简单的结构图,图中可以看出,SmartToken是ERC20Token的一类,加入了Owned功能封装;而EtherToken中附加的功能包括:提取token,eth充值,一看即知,它承担了token与外部eth转接的功能,可以在后面的众筹合约中直接应用。


区块链智能合约:去中心化交易所Bancor业务合约分析(一)_第1张图片
TokenStruc.png

下一篇将开始实际的代币合约分析,敬请期待!

你可能感兴趣的:(区块链智能合约:去中心化交易所Bancor业务合约分析(一))