美文网首页HiBlock区块链社区
基于以太坊的交易所BANCOR算法实现-转换算法框架

基于以太坊的交易所BANCOR算法实现-转换算法框架

作者: 宇宙永恒 | 来源:发表于2018-10-10 22:22 被阅读7次
    image

    1

    摘要

    EOS带火了Bancor算法。其实BANCOR算法是解决长尾通证流通的最好方式之一。鉴于目前通证大部分是基于ERC20的,辉哥正在尝试实现一个使用SOLIDITY语言实现的去中心化交易所,把项目信息整理分享出来。

    【本文目标】
    (1)学习BANCOR通证转换合约的类图
    (2)学习BANCOR通证核心函数
    (3)获取源码和注释

    2

    BANCOR算法功能描述

    在以太坊上发行的大量ERC20-Token是没有价值锚定的,其价值完全依赖于项目方的技术与运营能力,若项目失败了,则通证(TOKEN)价值就很可能归零。

    若利用智能合约的强大而灵活的“资金流转控制”能力,在通证合约中控制着一定量的储备金,让通证与储备金之间拥有一定的兑换能力,那么Token的价值就可以储备金为锚定物,而不完全依赖于项目方。通证持有者也就不用承担项目失败或者项目方可能诈骗跑路的风险。

    若通证与锚定物之间的兑换算法采用了Bancor算法,又符合ERC20标准,则被称为智能通证(Smart-Token) 。为了简单起见,以下的论述以ETH作为锚定物举例说明。购买与售卖Token的过程如下:

    • “购买者”发送一定量的ETH到Token合约地址,触发了合约代码自动执行"购买功能代码",获得对应数量的Token;

    • “售卖者”发送一定量的Token到Token合约地址,触发了合约代码自动执行“售卖功能代码”,获得对应数量的ETH。

    若AToken与BToken都是以ETH为锚定物的智能通证,那么Token持有者无需通过交易所,仅仅凭借智能合约提供的买卖与兑换功能,就能实现AToken与BToken的自由兑换,比如AToken-->ETH-->BToken,多种智能通证之间通过共同的锚定物串接起来,就形成了一个价值网络(Bancor Network)。

    鉴于篇幅限制,本文不准备上传完整源码文件,有需要的同学可加入知识星球下载。

    image

    【备注】 加入辉哥知识星球可申请加入区块链交流专项微信群,及时交流各种区块链技术问题。

    3

    类图分析

    image

    BANCOR转换算法框架类图

    【核心智能合约简单描述】

    1,contract BancorConverter

    功能说明:代币转换器,允许一个智能代币和其他代币之间的转换,ERC20连接器的余额可以是虚拟的,从而不需要依赖于真实的余额,这有助于避免在一个协约中有大量金额的风险。转换器可以升级。

    2,ITokenConverter

    功能说明:BancorConverter的父类接口之一,EIP228 Token Converter接口,用于智能代币的买卖和数量计算接口。

    3,SmartTokenController

    功能说明:BancorConverter的父类接口之一,智能代币管理器。智能代币管理器是一个可以升级的模块,从而允许更多功能和问题修复。当它接受了代币的所有权,它会成为代币的唯一管理器,执行各个功能。

    4,Managed

    功能说明: BancorConverter的父类之一,提供协议管理的支持。

    5,IBancorConverterExtensions

    功能说明:BancorConverter的公开变量类,bancor converter extensions 协议。能返回formula,gasPriceLimit,quickConverter等3类接口合约。

    4

    核心函数分析

    4.1 convert(...)函数

    convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256)

    功能: 将一定数量的_fromToken 转换为 _toToken;

    流程图:

    image

    convert函数

    源码:

    /**
    
            @dev 将一定数量的_fromToken 转换为 _toToken
    
            @param _fromToken  用来转换ERC20代币
    
            @param _toToken    被转换到的ERC20代币
    
            @param _amount     转换的数量,基于fromToken
    
            @param _minReturn  限制转换的结果需要高于minReturn,否则取消
    
            @return conversion 返回数量
    
        */
    
        function convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256) {
    
                convertPath = [_fromToken, token, _toToken];
    
                return quickConvert(convertPath, _amount, _minReturn);
    
        }
    
    /**
    
            @dev 通过之前定义的转换路径来转换代币
    
            注意:当从ERC20代币进行转换,需要提前设置补贴
    
            @param _path        转换路径
    
            @param _amount      转换的数量
    
            @param _minReturn   限制转换的结果需要高于minReturn,否则取消
    
            @return 返回数量
    
        */
    
        quickConvertfunction quickConvert(IERC20Token[] _path, uint256 _amount, uint256 _minReturn)
    
            public
    
            payable
    
            validConversionPath(_path)
    
            returns (uint256)
    
        {
    
            return quickConvertPrioritized(_path, _amount, _minReturn, 0x0, 0x0, 0x0, 0x0, 0x0);
    
        }
    
    /**
    
            @dev 通过之前定义的转换路径来转换代币
    
            注意:当从ERC20代币进行转换,需要提前设置补贴
    
            @param _path        转换路径
    
            @param _amount      转换的数量
    
            @param _minReturn   限制转换的结果需要高于minReturn,否则取消
    
            @param _block       如果当前的区块超过了参数,则取消
    
            @param _nonce       发送者地址的nonce
    
            @param _v           通过交易签名提取
    
            @param _r           通过交易签名提取
    
            @param _s           通过交易签名提取
    
            @return 返回数量
    
        */
    
        function quickConvertPrioritized(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, uint256 _block, uint256 _nonce, uint8 _v, bytes32 _r, bytes32 _s)
    
            public
    
            payable
    
            validConversionPath(_path)
    
            returns (uint256)
    
        {
    
            IERC20Token fromToken = _path[0];
    
            IBancorQuickConverter quickConverter = extensions.quickConverter();
    
            // 我们需要从调用者向快速转换着把源代币转化
    
            // 因此他能基于调用者进行转换
    
            if (msg.value == 0) {
    
                // 如果不是ETH,把源代币发给快速调用者
    
                // 如果是智能代币,不需要补贴 —— 销毁代币,然后发给快速转换者
    
                if (fromToken == token) {
    
                    token.destroy(msg.sender, _amount); // 销毁调用者的_amount代币
    
                    token.issue(quickConverter, _amount); // 把_amount的新代币发给快速转换者
    
                } else {
    
                    // 否则,我们假设有了补贴,发给快速转换者
    
                    assert(fromToken.transferFrom(msg.sender, quickConverter, _amount));
    
                }
    
            }
    
            // 执行转换,把ETH转回
    
            return quickConverter.convertForPrioritized.value(msg.value)(_path, _amount, _minReturn, msg.sender, _block, _nonce, _v, _r, _s);
    
        }
    

    4.2 change(...)函数

    ** function change**(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256)

    功能: 将一定数量的_fromToken 转换为 _toToken。弃用了,向后兼容。设计思路和源码结构挺好的,我画了流程图。

    流程图:

    image

    BANCOR算法-change函数1

    image

    BANCOR算法-change函数2

    4.3 getReturn(...)函数

    function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256)
    

    功能:
    返回从一个代币转换为另一个代币的预期数量

    流程图:

    image
    getReturn
    
    image
    getPurchaseReturn
    
    image
    getSaleReturn
    

    源码:

    /**
    
            @dev 返回从一个代币转换为另一个代币的预期数量
    
            @param _fromToken  ERC20 被转换的代币
    
            @param _toToken    ERC20 转换成的代币
    
            @param _amount     转换的数量
    
            @return 与其转换的数量
    
        */
    
        function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256) {
    
            require(_fromToken != _toToken); // 验证输入
    
            // 基于当前代币转换
    
            if (_toToken == token)
    
                return getPurchaseReturn(_fromToken, _amount);
    
            else if (_fromToken == token)
    
                return getSaleReturn(_toToken, _amount); 
    
           // 在两个连接器之间转换
    
            uint256 purchaseReturnAmount = getPurchaseReturn(_fromToken, _amount);
    
            return getSaleReturn(_toToken, purchaseReturnAmount, safeAdd(token.totalSupply(), purchaseReturnAmount));
    
        }
    
    /**
    
            @dev 返回通过一个连接器代币购买代币的预期结果
    
            @param _connectorToken  连接器代币协约地址
    
            @param _depositAmount   买入的数量
    
            @return 预期的数量
    
        */
    
        function getPurchaseReturn(IERC20Token _connectorToken, uint256 _depositAmount)
    
            public
    
            view
    
            active
    
            validConnector(_connectorToken)
    
            returns (uint256)
    
        {
    
            Connector storage connector = connectors[_connectorToken];
    
            require(connector.isPurchaseEnabled); // validate input
    
            uint256 tokenSupply = token.totalSupply();
    
            uint256 connectorBalance = getConnectorBalance(_connectorToken);
    
            uint256 amount = extensions.formula().calculatePurchaseReturn(tokenSupply, connectorBalance, connector.weight, _depositAmount);
    
            // 扣除费用
    
            uint256 feeAmount = getConversionFeeAmount(amount);
    
            return safeSub(amount, feeAmount);
    
        }
    
    /**
    
            @dev 返回通过一个连接器代币卖出代币的预期结果
    
            @param _connectorToken  连接器代币协约地址
    
            @param _sellAmount      卖出的数量
    
            @return 预期得到的数量
    
        */
    
        function getSaleReturn(IERC20Token _connectorToken, uint256 _sellAmount) public view returns (uint256) { 
    
           return getSaleReturn(_connectorToken, _sellAmount, token.totalSupply());
    
        }
    
    /**
    
            @dev 工具,基于一个总供应量,返回基于一个连接器代币来卖掉代币的期待返回
    
            @param _connectorToken  连接器代币协议地址
    
            @param _sellAmount      销售的数量
    
            @param _totalSupply     设置总供应量
    
            @return 返回的数量
    
        */
    
        function getSaleReturn(IERC20Token _connectorToken, uint256 _sellAmount, uint256 _totalSupply)
    
            private
    
            view
    
            active
    
            validConnector(_connectorToken)
    
            greaterThanZero(_totalSupply)
    
            returns (uint256)
    
        {
    
            Connector storage connector = connectors[_connectorToken]; 
    
           uint256 connectorBalance = getConnectorBalance(_connectorToken);
    
            uint256 amount = extensions.formula().calculateSaleReturn(_totalSupply, connectorBalance, connector.weight, _sellAmount);
    
            // 从返回的数量中剪掉费用
    
            uint256 feeAmount = getConversionFeeAmount(amount);
    
            return safeSub(amount, feeAmount);
    
        }
    

    未完待续:上述代码完成了智能代币和连接代币转化的关系,但是没有涉及核心互换及计算代码,而是通过interface类的方式进行隔离。这个在另外一个课程进行讲解。

    本文作者:HiBlock区块链技术布道群-辉哥

    原文发布于简书

    加微信baobaotalk_com,加入技术布道群

    Blockathon|48小时极客竞赛,区块链马拉松等你挑战(上海)

    时间:2018年10月19-21日

    地点:(上海黄浦)露香园路1号(近淮海东路)P2

    • 招募50名开发者(识别下图二维码或点击“阅读原文”即可了解详情并报名)
    image

    北京blockathon回顾:

    Blockathon(北京):48小时极客开发,区块松11个现场交付项目创意公开

    成都blockathon回顾:

    Blockathon2018(成都站)比赛落幕,留给我们这些区块链应用思考

    相关文章

      网友评论

        本文标题:基于以太坊的交易所BANCOR算法实现-转换算法框架

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