美文网首页Solidity智能合约专案
区块链智能合约:去中心化交易所Bancor业务合约分析(一)

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

作者: 时令_辰分 | 来源:发表于2018-03-22 15:23 被阅读0次

    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转接的功能,可以在后面的众筹合约中直接应用。


    TokenStruc.png

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

    相关文章

      网友评论

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

        本文链接:https://www.haomeiwen.com/subject/ikraqftx.html