如何开发自己的BANCOR去中心化交易平台?
如何开发自己的BANCOR去中心化交易平台?
## 1,摘要 [基于以太坊的交易所BANCOR算法实现-转换算法框架](https://learnblockchain.cn/2018/09/21/875ba83002dc) 讲解了以太坊solidity实现的BancorConverter转换主合约的逻辑和代码,但是没有涉及核心互换及计算代码,而是通过interface类的方式进行隔离。 本文详细描述一下内容,能跑的程序才是真分享: (1)BancorNetwork网络的文件框架和功能; (2)BancorConverter合约测试执行流程; (3)2个连接器通证ERC1-ERC22的转换验证结果; (4)ETH-CLB(彩贝)的转化验证结果 ## 2,BancorNetwork网络的文件框架和功能 BancorProtocol工程是一个带有TRUFFLE框架的solidity智能合约Bancor算法实现框架。文件列表如下: ``` | package.json | README.md | trace.log | | | BancorProtocol.iml | | encodings.xml | | misc.xml | | modules.xml | | workspace.xml | | | \---copyright | profiles_settings.xml | \---solidity | truffle-config.js | +---contracts | | BancorNetwork.sol | | ContractIds.sol | | FeatureIds.sol | | IBancorNetwork.sol | | | +---build | | BancorConverter.abi | | BancorConverter.bin | | BancorConverterFactory.abi | | BancorConverterFactory.bin | | BancorConverterUpgrader.abi | | BancorConverterUpgrader.bin | | BancorFormula.abi | | BancorFormula.bin | | BancorGasPriceLimit.abi | | BancorGasPriceLimit.bin | | BancorNetwork.abi | | BancorNetwork.bin | | BancorPriceFloor.abi | | BancorPriceFloor.bin | | ContractFeatures.abi | | ContractFeatures.bin | | ContractIds.abi | | ContractIds.bin | | ContractRegistry.abi | | ContractRegistry.bin | | CrowdsaleController.abi | | CrowdsaleController.bin | | ERC20Token.abi | | ERC20Token.bin | | EtherToken.abi | | EtherToken.bin | | FeatureIds.abi | | FeatureIds.bin | | IBancorConverter.abi | | IBancorConverter.bin | | IBancorConverterExtended.abi | | IBancorConverterExtended.bin | | IBancorConverterFactory.abi | | IBancorConverterFactory.bin | | IBancorFormula.abi | | IBancorFormula.bin | | IBancorGasPriceLimit.abi | | IBancorGasPriceLimit.bin | | IBancorNetwork.abi | | IBancorNetwork.bin | | IContractFeatures.abi | | IContractFeatures.bin | | IContractRegistry.abi | | IContractRegistry.bin | | IERC20Token.abi | | IERC20Token.bin | | IEtherToken.abi | | IEtherToken.bin | | IOwned.abi | | IOwned.bin | | ISmartToken.abi | | ISmartToken.bin | | ITokenHolder.abi | | ITokenHolder.bin | | IWhitelist.abi | | IWhitelist.bin | | Managed.abi | | Managed.bin | | Owned.abi | | Owned.bin | | SmartToken.abi | | SmartToken.bin | | SmartTokenController.abi | | SmartTokenController.bin | | TokenHolder.abi | | TokenHolder.bin | | Utils.abi | | Utils.bin | | Whitelist.abi | | Whitelist.bin | | | +---converter | | | BancorConverter.sol | | | BancorConverterFactory.sol | | | BancorConverterUpgrader.sol | | | BancorFormula.sol | | | BancorGasPriceLimit.sol | | | | | \---interfaces | | IBancorConverter.sol | | IBancorConverterFactory.sol | | IBancorFormula.sol | | IBancorGasPriceLimit.sol | | | +---crowdsale | | CrowdsaleController.sol | | | +---helpers | | Migrations.sol | | TestBancorFormula.sol | | TestCrowdsaleController.sol | | TestERC20Token.sol | | TestFeatures.sol | | TestUtils.sol | | | +---legacy | | BancorPriceFloor.sol | | | +---token | | | ERC20Token.sol | | | EtherToken.sol | | | SmartToken.sol | | | SmartTokenController.sol | | | | | \---interfaces | | IERC20Token.sol | | IEtherToken.sol | | ISmartToken.sol | | | \---utility | | ContractFeatures.sol | | ContractRegistry.sol | | Managed.sol | | Owned.sol | | TokenHolder.sol | | Utils.sol | | Whitelist.sol | | | \---interfaces | IContractFeatures.sol | IContractRegistry.sol | IOwned.sol | ITokenHolder.sol | IWhitelist.sol | +---migrations | 1_initial_migration.js | 2_deploy_contracts.js | +---python | | BenchmarkTestPurchase.py | | BenchmarkTestSale.py | | CoverageExpTestPurchase.py | | CoverageExpTestSale.py | | CoverageUniTestPurchase.py | | CoverageUniTestSale.py | | EmulationExpTestCrossConnector.py | | EmulationExpTestPurchase.py | | EmulationExpTestSale.py | | EmulationUniTestCrossConnector.py | | EmulationUniTestPurchase.py | | EmulationUniTestSale.py | | MongoExpTestPurchase.py | | MongoExpTestSale.py | | MongoUniTestPurchase.py | | MongoUniTestSale.py | | PerformanceExpTestCrossConnector.py | | PerformanceExpTestPurchase.py | | PerformanceExpTestSale.py | | PerformanceUniTestCrossConnector.py | | PerformanceUniTestPurchase.py | | PerformanceUniTestSale.py | | RandomTestCrossConnector.py | | RandomTestPower.py | | RandomTestPurchase.py | | RandomTestSale.py | | RandomTestTrade.py | | | +---AutoGenerate | | | PrintFileFormulaConstants.py | | | PrintFunctionBancorFormula.py | | | PrintFunctionGeneralExp.py | | | PrintFunctionOptimalExp.py | | | PrintFunctionOptimalLog.py | | | PrintIntScalingFactors.py | | | PrintLn2ScalingFactors.py | | | PrintMaxExpPerPrecision.py | | | | | \---common | | constants.py | | functions.py | | __init__.py | | | +---FormulaNativePython | | __init__.py | | | +---FormulaSolidityPort | | __init__.py | | | +---InputGenerator | | __init__.py | | | +---Utilities | | +---Changer | | | Decrease.py | | | Increase.py | | | | | \---Converter | | | example_commands.json | | | example_model.json | | | run.py | | | | | \---engine | | __init__.py | | | \---Web3Wrapper | __init__.py | \---test | BancorConverter.js | BancorConverterUpgrader.js | BancorFormula.js | BancorNetwork.js | ContractFeatures.js | ContractRegistry.js | CrowdsaleController.js | ERC20Token.js | EtherToken.js | Managed.js | Owned.js | SmartToken.js | SmartTokenController.js | TokenHolder.js | Utils.js | Whitelist.js | \---helpers FormulaConstants.js Utils.js ``` 其中的智能合约代码量实在繁多,限于篇幅限制,就不一一介绍了,辉哥介绍其中主要的几个文件。 #### 1,BancorNetwork.sol **(1),功能描述:** BancorNetwork合约是bancor通证转换的主入口。 通过提供转换路径,它允许通过唯一的方法在bancor network中的任何通证转换为其他的通证。 转换路径注意点: 1>,转换路径是一种数据结构,用于在bancor network中把一种通证转换为另一种通知。有时一次转换可能不够,需要多个跳转。 这个路径定义了哪些转换会被使用,每个步骤会做哪一种转换。 2>,这个路径格式不包含复杂的结构。相反,它已单一数组的方式呈现,其中每一个跳转(hop)包含2个数组元素 -智能通证和目标通证。 另外,第一个元素总是源通证。智能通证只作为指向转换器的指针使用(因为转换器地址很可能会变)。 **(2),关键函数:** 1) function **registerEtherToken** (IEtherToken _token, bool _register) 注册EtherToken。如果转换路径包括ETH,则需要定义EtherToken来替换。 2)function **verifyTrustedSender** (IERC20Token[] _path, uint256 _amount, uint256 _block, address _addr, uint8 _v, bytes32 _r, bytes32 _s) private 通过从椭圆签名算法还原关联的公钥,验证签名地址是可信的。如果有错误则返回0值。 注意:签名只在一次转换有效,这个给定的区块后会失效。 3) function **convertFor** (IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _for) public payable 在bancor网络中,根据预定义的转换路径,把通证转换为其他通证,并把转换得到的通证打给目标账户。 注意:转换器必须已经有源通证的管理权限。 4) function **getReturnByPath** (IERC20Token[] _path, uint256 _amount) public view returns (uint256) 返回给定转换路径的目标通证的数量 5)function **convertForMultiple** (IERC20Token[] _paths, uint256[] _pathStartIndex, uint256[] _amounts, uint256[] _minReturns, address _for) public payable 通过预定义的转换路径,把计算出来的结果通证给目标账号。该函数在一个单一的原子转换中也允许多次转换。 注意:转换器应该已经控制源通证。 6) function **claimAndConvert** (IERC20Token[] _path, uint256 _amount, uint256 _minReturn) public returns (uint256) 声明调用者的通知,通过预定义的转换路径把通证转换为其他通证 。 #### 2,ContractIds.sol **(1),功能描述:** 定义一些常量 **(2),关键函数:** bytes32 public constant CONTRACT_FEATURES = "ContractFeatures"; bytes32 public constant BANCOR_NETWORK = "BancorNetwork"; bytes32 public constant BANCOR_FORMULA = "BancorFormula"; bytes32 public constant BANCOR_GAS_PRICE_LIMIT = "BancorGasPriceLimit"; bytes32 public constant BANCOR_CONVERTER_FACTORY = "BancorConverterFactory"; #### 3,BancorNetwork.sol **(1),功能描述:** Bancor Network interface,3个空函数定义 **(2),关键函数:** function convert(…) public payable function convertFor(…) public payable function convertForPrioritized2(…) public payable #### 3, BancorConverter.sol 见 [《第二十四课 基于以太坊的交易所BANCOR算法实现-转换算法框架》](https://learnblockchain.cn/2018/09/21/875ba83002dc) 文件描述。 #### 4,BancorConverterFactory.sol **(1),功能描述:** 根据参数增加一个新的转换器,把所有权和管理权限转给发送者。 **(2),关键函数:** function **createConverter** (_token,_registry,_maxConversionFee, _connectorToken,_connectorWeight) public returns(converterAddress) #### 5,BancorConverterUpgrader.sol **1,功能描述:** Bancor转换升级器允许把旧的Bancor转换器(0.4 或者更高)升级到最新的版本。 为了开始升级流程,首先把转换器的所有权转让给升级合约,然后调用升级函数。 在升级流程的最后,最新升级的转换器的所有权要转让归还给原始所有者。新转换器的地址在ConverterUpgrade事件中是有效的。 **2,核心函数:** 1) function **upgrade** (IBancorConverterExtended _oldConverter, bytes32 _version) 把旧的转换器升级到新的版本。 在调用本函数前,如果所有权没有被转移给升级器合约将会抛出异常。新转换器的所有权将会被转移给原始的所有者。 2) function **readConnector** (IBancorConverterExtended _converter, _address, _isLegacyVersion) 读取连接器的配置 #### 6,BancorFormula.sol **(1),功能描述:** 转换器的算法。 **(2),核心函数:** 1) function **calculatePurchaseReturn** (uint256 _supply, uint256 _connectorBalance, uint32 _connectorWeight, uint256 _depositAmount) public view returns (uint256) 根据给定的一个token供应量,连接器余额,权重和存入的数量(连接器代币),计算出一个给定转换的反馈。 公式: Return = _supply * ((1 + _depositAmount / _connectorBalance) ^ (_connectorWeight / 1000000) - 1) 2) function **calculateSaleReturn** (uint256 _supply, uint256 _connectorBalance, uint32 _connectorWeight, uint256 _sellAmount) public view returns (uint256) 根据给定的一个token供应量,连接器余额,权重和卖出的数量(目标通证),计算出一个给定转换的连接器通证的结果。公式: Return = _connectorBalance * (1 - (1 - _sellAmount / _supply) ^ (1 / (_connectorWeight / 1000000))) 3)function **calculateCrossConnectorReturn** (uint256 _fromConnectorBalance, uint32 _fromConnectorWeight, uint256 _toConnectorBalance, uint32 _toConnectorWeight, uint256 _amount) public view returns (uint256) 根据给定的2个连接器通证的余额/权重,第一个连接器通证的售卖数量,计算出把第一个连接器通证转换为第二个连接器通证的转换数量。 公式: Return = _toConnectorBalance * (1 - (_fromConnectorBalance / (_fromConnectorBalance + _amount)) ^ (_fromConnectorWeight / _toConnectorWeight)) #### 7,BancorGasPriceLimit.sol **1,功能描述:** BancorGasPriceLimit合约充当一个额外的前台运行的攻击减缓机制。它在所有的bancor转换器上设置了一个最大的gas费用, 以便阻止想抢先交易的用户插队。gas费用上限对所有的转换器都适用。并且它可以被所有者更新,以便能跟当前网络的gas费用相符合。 **2,核心函数:** 1) function **setGasPrice** (uint256 _gasPrice) public ownerOnly 更新gas上限; 2)function **validateGasPrice** (uint256 _gasPrice) 判断输入gas是否不超过gas上限。 #### 8, CrowdsaleController.sol **1,功能描述:** 智能通证控制器的众筹版本,允许捐赠者用ether交换Bancor通证。众筹期间价格恒定。 注意:20%的捐赠者是用ETH连接器通证余额的BNT通证。 **2,核心函数:** 1) function() payable public -> function processContribution() private 转移ETH给收益账户;发行智能代币给捐赠者;1个ETH反回100个智能代币。 #### 9, BancorPriceFloor.sol **1,功能描述** bancor底价合约是一个简单的合约,以一个恒定的ETH价格售卖智能代币。 **2,核心函数** 1. function **sell()** public returns (uint256 amount) 把发送者的所有智能代币转给底价合约,发送(智能代币/100)给发送者 2) function **withdraw** (uint256 _amount) public ownerOnly 管理员提取一定数量的ETH #### 10,SmartToken.sol **1,功能描述:** 智能代币 **2,核心函数:** 1) function **issue** (address _to, uint256 _amount) 对某个账户地址增加智能代币余额 _amount,增加发行总量_amount 2)function **destroy** (address _from, uint256 _amount) 对某个账户地址销毁智能代币余额_amount,减少发行总量_amount 1. function **transfer** (address _to, uint256 _value) public transfersAllowed 智能代币转账函数 4)function **transferFrom** (address _from, address _to, uint256 _value) public transfersAllowed 授权后的转移 #### 11,SmartTokenController.sol **1,功能描述:** 智能合约控制器是智能合约可升级的一部分,它允许修改BUG,增加功能。 一旦接受了TOKEN的管理权限,它就变成了该TOKEN的唯一控制器,可以执行任何TOKEN的相关函数。 为了升级控制器,管理权限包括任何相关数据,必须转移给新的控制器。 智能代币必须在构建函数设置,而且之后不可修改。 **2,核心函数:** 1) function **transferTokenOwnership** (address _newOwner) public ownerOnly: 管理账号操作,更新控制权 2) function **acceptTokenOwnership** () public ownerOnly 管理账号操作,接受控制权 3)function **withdrawFromToken** (IERC20Token _token, address _to, uint256 _amount ) public ownerOnly 管理账号操作,收回代币到特定账号 #### 12,ContractFeatures.sol **1,功能描述:** 一般合约允许区块链上的每个合约定义它支持的功能。 其他合约可以查询这个合约,找出一个区块链上的特定合约是否支持特定的功能。 每个合约类型可以定义它自己的功能列表标志。合约定义的功能只能够被标识为可以/不可以。 可以使用bit位标识函数功能,例如: uint256 public constant FEATURE1 = 1 ![](https://img.learnblockchain.cn/2020/02/01_/700681883.png) 1\. 创建转换器及合约注册工作 对应的代码: ``` before(async () => { /* 新建合约注册器ContractRegistry,合约IDcontractIds, */ contractRegistry = await ContractRegistry.new(); contractIds = await ContractIds.new(); /* 新建合约特征contractFeatures,把合约名称"ContractFeatures" 和合约地址注册到合约注册器 */ contractFeatures = await ContractFeatures.new(); let contractFeaturesId = await contractIds.CONTRACT_FEATURES.call(); await contractRegistry.registerAddress(contractFeaturesId, contractFeatures.address); /* 新建gas费用限制合约BancorGasPriceLimit,上限价格为22Gwei,把合约名称"BancorGasPriceLimit" 和合约地址注册到合约注册器 */ let gasPriceLimit = await BancorGasPriceLimit.new(gasPrice); let gasPriceLimitId = await contractIds.BANCOR_GAS_PRICE_LIMIT.call(); await contractRegistry.registerAddress(gasPriceLimitId, gasPriceLimit.address); /* 新建公式合约formula,把合约名称"BancorFormula" 和合约地址注册到合约注册器 */ let formula = await BancorFormula.new(); let formulaId = await contractIds.BANCOR_FORMULA.call(); await contractRegistry.registerAddress(formulaId, formula.address); /* 新建Bancor网络合约BancorNetwork,参数为合约注册器地址,把合约名称"BANCOR_NETWORK" 和合约地址注册到合约注册器 */ /* bancorNetwork设置签名者地址为accounts[3] */ let bancorNetwork = await BancorNetwork.new(contractRegistry.address); let bancorNetworkId = await contractIds.BANCOR_NETWORK.call(); await contractRegistry.registerAddress(bancorNetworkId, bancorNetwork.address); await bancorNetwork.setSignerAddress(accounts[3]); /* 创建智能通证,名称为Token1,符号为TKN1,数量为2个 */ //let token = await SmartToken.new('Token1', 'TKN1', 2); /* 创建一个连接器通证,名称为ERC Token 1,符号ERC1,数量为10万个 */ //let connectorToken = await TestERC20Token.new('ERC Token 1', 'ERC1', 100000); //tokenAddress = token.address; //connectorTokenAddress = connectorToken.address; }); ``` ### 3.2.2 智能通证和连接器通证初始化 ![](https://img.learnblockchain.cn/2020/02/01_/529347108.png) 2\. 转换器初始化,供第三步骤函数调用 ![](https://img.learnblockchain.cn/2020/02/01_/45659026.png) 2.1 初始化结束后的智能通证和ERC1/ERC2的分布情况 对应的实现代码如下: ``` async function initConverter(accounts, activate, maxConversionFee = 0) { /* 创建智能通证,名称为TKN1,符号为Token1 */ token = await SmartToken.new('Token1', 'TKN1', 2); tokenAddress = token.address; /* 发行2种ERC20连接器通证 */ connectorToken = await TestERC20Token.new('ERC Token 1', 'ERC1', 100000); connectorTokenAddress = connectorToken.address; connectorToken2 = await TestERC20Token.new('ERC Token 2', 'ERC2', 200000); connectorTokenAddress2 = connectorToken2.address; /* 创建Bancor转换器, */ let converter = await BancorConverter.new( tokenAddress, contractRegistry.address, maxConversionFee, connectorTokenAddress, 250000 ); /* 设置ERC2通证为连接器通证,15%的权重 ,不允许虚拟余额*/ let converterAddress = converter.address; await converter.addConnector(connectorTokenAddress2, 150000, false); /* 给账号accounts[0]发行2万个TKN1智能代币 ,给转换器地址转账5000个ERC1,给转换器地址转账8000个ERC2,*/ await token.issue(accounts[0], 20000); await connectorToken.transfer(converterAddress, 5000); await connectorToken2.transfer(converterAddress, 8000); if (activate) { await token.transferOwnership(converterAddress); await converter.acceptTokenOwnership(); } return converter; } ``` ### 3.2.3 ERC1和ERC2的兑换函数实现 ![](https://img.learnblockchain.cn/2020/02/01_/174110522.png) 3.ERC1和ERC2的兑换函数 对应代码: ``` it('verifies that getReturn returns the same amount as buy -> sell when converting between 2 connectors', async () => { let converter = await initConverter(accounts, true); let returnAmount = await converter.getReturn.call(connectorTokenAddress, connectorTokenAddress2, 500); console.log("The returnAmount is : %d",returnAmount.toNumber()); await connectorToken.approve(converter.address, 500); let purchaseRes = await converter.convert(connectorTokenAddress, tokenAddress, 500, 1); let purchaseAmount = getConversionAmount(purchaseRes); console.log("The purchaseAmount is : %d", purchaseAmount); let saleRes = await converter.convert(tokenAddress, connectorTokenAddress2, purchaseAmount, 1); let saleAmount = getConversionAmount(saleRes); console.log("The saleAmount is : %d",saleAmount); // converting directly between 2 tokens is more efficient than buying and then selling // which might result in a very small rounding difference assert(returnAmount.minus(saleAmount).absoluteValue().toNumber() < 2); }); ``` ### 3.2.4 运行结果 在TRUFFLE下运行测试文件BanncorConverter2.js,可以验证算法代码实现结果等同于上面的算法结果。 ``` The returnAmount is : 1175 The purchaseAmount is : 482 The saleAmount is : 1174 verifies that getReturn returns the same amount as buy -> sell when converting between 2 connectors (1899ms) ``` 如果对truffle命令和测试运行不了解的,可参考文档 [《第四课 以太坊开发框架Truffle从入门到实战》](https://www.jianshu.com/p/2e2b3b12eb0e) 。 ## 4,CLB(一种ERC20)和ETH兑换测试场景 ### 4.1 场景:1种连接器通证CLOB和ETH的兑换测试 对于自己搭建的交易所,更常见的场景为ERC20通证兑换为ETH,解决长尾代币的流通性问题。 假设有一种ERC20和ETH在BANCOR转换器中,其中 * CLB 90000个,90% CW权重,市价1元/个; * ETH 10个, 10% CW权重,市价1000元/个; * 初始发行智能代币 TKN1 1000个,PRICE 0.1 (个/ETH); PRICE 100 (个/CLOB); 请问,1000个CLOB可以兑换多少个ETH呢? 我们先来手工拆解计算下,然后用程序运行来验证正确性。 #### 1000个CLOB可以兑换多少个TKN1? * SmartTokenAmount = SmartTokenTokenSupply *((1 + ConnectorToken / ConnectorTokenBalance)^ CW - 1) = 1000 * (( 1 + 1000 / 90000 )^ 0.9 - 1 ) = **9.99446694706181297191051400502** (个TNK1) #### 9.994466947个TKN1可以兑换多少个ETH呢? * connectorTokenAmount = ConnectorTokenBalance *(1 - (1 - SmartTokenAmount / SmartTokenTokenSupply)^ (1 / CW) ) * connectorTokenAmount = 10 * (1 - (1 - (9.994466947 / (1000 + 9.994466947)))^ (1 / 0.1) ) = 10 * (1 - (1 - (9.994466947 / (1000 + 9.994466947)))^ (1 / 0.1) ) = 10 * (1 - (1 - (0.00989556603929837667128805564395))^ (1 / 0.1) ) = 10 * (1 - (1 - (0.00989556603929837667128805564395))^ (1 / 0.1) ) = 10 * (1 - 0.99010443396070162332871194435605 ^ 10 ) = 10 * (1 - 0.90533655025365121589722721359431) = **0.94663449746348784102772786405694** (个ETH) #### 兑换结论:1000个CLB可以兑换0.946个ETH 按照假设的市价,两者的价值均为1000元左右,符合期望。 ## 4.2 测试代码 针对上面兑换算法的公式描述如下: ``` /* 创建智能通证,名称为TKN1,符号为Token1 */ token = await SmartToken.new('Token1', 'TKN1', 2); tokenAddress = token.address; /* 发行ERC20连接器通证CLOB */ connectorToken = await TestERC20Token.new('ColorBay Token', 'CLB', '1000000000000000000000000000'); connectorTokenAddress = connectorToken.address; /* 从accounts[0]给etherToken存入10个ETH */ etherToken = await EtherToken.new(); /* 获取以太坊的余额 */ let EtherBalance = await web3.eth.getBalance(accounts[0]); console.log("The EtherBalance of accounts[0] is : %d",EtherBalance); //etherToken.address.transfer(1000); /* 往etherToken存10个ETH */ await etherToken.deposit({ value: '10000000000000000000' }); /* 创建Bancor转换器,添加CLOB作为连接器代币,权重为90% */ let converter = await BancorConverter.new( tokenAddress, contractRegistry.address, maxConversionFee, connectorTokenAddress, 900000 ); /* 设置ETH作为连接器代币,权重为10%*/ let converterAddress = converter.address; await converter.addConnector(etherToken.address, 100000, false); /* 给账号accounts[0]发行1000个TKN1智能代币 ,给转换器地址转账90000个CLOB*/ await token.issue(accounts[0], '1000000000000000000000'); await connectorToken.transfer(converterAddress, '90000000000000000000000'); /* 给转换器地址转账10个ETH */ await etherToken.transfer(converter.address, '10000000000000000000'); if (activate) { await token.transferOwnership(converterAddress); await converter.acceptTokenOwnership(); } /* 设置跟ETH的转换路径 */ clobQuickBuyPath = [connectorTokenAddress, token.address, etherToken.address]; /* 授权连接器通证CLOB 1000个 */ await connectorToken.approve(converter.address, '1000000000000000000000'); let purchaseRes = await converter.convert(connectorTokenAddress, tokenAddress, '1000000000000000000000', 1); let purchaseAmount = getConversionAmount(purchaseRes); console.log("The purchaseAmount is : %d", purchaseAmount); let saleRes = await converter.convert(tokenAddress, etherToken.address, purchaseAmount, 1); let saleAmount = getConversionAmount(saleRes); console.log("The saleAmount is : %d",saleAmount); ``` ### 4.3 运行结果 ``` The EtherBalance of accounts[0] is : 82637441999919870000 The purchaseAmount is : 9994466947061813000 The saleAmount is : 946634497469028600 verifies that user can get ETH through sell CLB (2408ms) ``` 如上所示,此处代码以wei为单位,处于10^18,得到的结果为0.9466个ETH,同手工计算结果。 ## 4,总结 从白皮书,算法公式验证到代码实现,辉哥从技术穿刺的角度讲透了BANCOR算法在以太坊环境的实现。 * * * 辉哥整理了BANCOR的系列知识分享,列表如下: (1) [【白皮书】Bancor协议:通过智能合约为加密货币提供持续流动性(附PDF下载)](https://www.jianshu.com/p/cbfb9abf8e07) (2) [【易错概念】以实例形式深入浅出讲透BANCOR算法](https://learnblockchain.cn/2018/09/17/fe48ed1f38cd) (3) [第二十四课基于以太坊的交易所BANCOR算法实现-转换算法框架](https://learnblockchain.cn/2018/09/21/875ba83002dc) (4) [第二十五课如何开发自己的BANCOR去中心化交易平台?](https://learnblockchain.cn/2018/09/30/9fc78f8f5773) (5) [第二十七课如何从BANCOR.NETWORK去中心化交易所兑换ENJIN通证](https://learnblockchain.cn/2018/10/11/617103393dc6) 如有需要的同学下载测试工程的可加入知识星球下载。 ![](https://img.learnblockchain.cn/2020/02/01_/672295474.png)
1,摘要
基于以太坊的交易所BANCOR算法实现-转换算法框架 讲解了以太坊solidity实现的BancorConverter转换主合约的逻辑和代码,但是没有涉及核心互换及计算代码,而是通过interface类的方式进行隔离。 本文详细描述一下内容,能跑的程序才是真分享: (1)BancorNetwork网络的文件框架和功能; (2)BancorConverter合约测试执行流程; (3)2个连接器通证ERC1-ERC22的转换验证结果; (4)ETH-CLB(彩贝)的转化验证结果
2,BancorNetwork网络的文件框架和功能
BancorProtocol工程是一个带有TRUFFLE框架的solidity智能合约Bancor算法实现框架。文件列表如下:
| package.json
| README.md
| trace.log
|
| | BancorProtocol.iml
| | encodings.xml
| | misc.xml
| | modules.xml
| | workspace.xml
| |
| \---copyright
| profiles_settings.xml
|
\---solidity
| truffle-config.js
|
+---contracts
| | BancorNetwork.sol
| | ContractIds.sol
| | FeatureIds.sol
| | IBancorNetwork.sol
| |
| +---build
| | BancorConverter.abi
| | BancorConverter.bin
| | BancorConverterFactory.abi
| | BancorConverterFactory.bin
| | BancorConverterUpgrader.abi
| | BancorConverterUpgrader.bin
| | BancorFormula.abi
| | BancorFormula.bin
| | BancorGasPriceLimit.abi
| | BancorGasPriceLimit.bin
| | BancorNetwork.abi
| | BancorNetwork.bin
| | BancorPriceFloor.abi
| | BancorPriceFloor.bin
| | ContractFeatures.abi
| | ContractFeatures.bin
| | ContractIds.abi
| | ContractIds.bin
| | ContractRegistry.abi
| | ContractRegistry.bin
| | CrowdsaleController.abi
| | CrowdsaleController.bin
| | ERC20Token.abi
| | ERC20Token.bin
| | EtherToken.abi
| | EtherToken.bin
| | FeatureIds.abi
| | FeatureIds.bin
| | IBancorConverter.abi
| | IBancorConverter.bin
| | IBancorConverterExtended.abi
| | IBancorConverterExtended.bin
| | IBancorConverterFactory.abi
| | IBancorConverterFactory.bin
| | IBancorFormula.abi
| | IBancorFormula.bin
| | IBancorGasPriceLimit.abi
| | IBancorGasPriceLimit.bin
| | IBancorNetwork.abi
| | IBancorNetwork.bin
| | IContractFeatures.abi
| | IContractFeatures.bin
| | IContractRegistry.abi
| | IContractRegistry.bin
| | IERC20Token.abi
| | IERC20Token.bin
| | IEtherToken.abi
| | IEtherToken.bin
| | IOwned.abi
| | IOwned.bin
| | ISmartToken.abi
| | ISmartToken.bin
| | ITokenHolder.abi
| | ITokenHolder.bin
| | IWhitelist.abi
| | IWhitelist.bin
| | Managed.abi
| | Managed.bin
| | Owned.abi
| | Owned.bin
| | SmartToken.abi
| | SmartToken.bin
| | SmartTokenController.abi
| | SmartTokenController.bin
| | TokenHolder.abi
| | TokenHolder.bin
| | Utils.abi
| | Utils.bin
| | Whitelist.abi
| | Whitelist.bin
| |
| +---converter
| | | BancorConverter.sol
| | | BancorConverterFactory.sol
| | | BancorConverterUpgrader.sol
| | | BancorFormula.sol
| | | BancorGasPriceLimit.sol
| | |
| | \---interfaces
| | IBancorConverter.sol
| | IBancorConverterFactory.sol
| | IBancorFormula.sol
| | IBancorGasPriceLimit.sol
| |
| +---crowdsale
| | CrowdsaleController.sol
| |
| +---helpers
| | Migrations.sol
| | TestBancorFormula.sol
| | TestCrowdsaleController.sol
| | TestERC20Token.sol
| | TestFeatures.sol
| | TestUtils.sol
| |
| +---legacy
| | BancorPriceFloor.sol
| |
| +---token
| | | ERC20Token.sol
| | | EtherToken.sol
| | | SmartToken.sol
| | | SmartTokenController.sol
| | |
| | \---interfaces
| | IERC20Token.sol
| | IEtherToken.sol
| | ISmartToken.sol
| |
| \---utility
| | ContractFeatures.sol
| | ContractRegistry.sol
| | Managed.sol
| | Owned.sol
| | TokenHolder.sol
| | Utils.sol
| | Whitelist.sol
| |
| \---interfaces
| IContractFeatures.sol
| IContractRegistry.sol
| IOwned.sol
| ITokenHolder.sol
| IWhitelist.sol
|
+---migrations
| 1_initial_migration.js
| 2_deploy_contracts.js
|
+---python
| | BenchmarkTestPurchase.py
| | BenchmarkTestSale.py
| | CoverageExpTestPurchase.py
| | CoverageExpTestSale.py
| | CoverageUniTestPurchase.py
| | CoverageUniTestSale.py
| | EmulationExpTestCrossConnector.py
| | EmulationExpTestPurchase.py
| | EmulationExpTestSale.py
| | EmulationUniTestCrossConnector.py
| | EmulationUniTestPurchase.py
| | EmulationUniTestSale.py
| | MongoExpTestPurchase.py
| | MongoExpTestSale.py
| | MongoUniTestPurchase.py
| | MongoUniTestSale.py
| | PerformanceExpTestCrossConnector.py
| | PerformanceExpTestPurchase.py
| | PerformanceExpTestSale.py
| | PerformanceUniTestCrossConnector.py
| | PerformanceUniTestPurchase.py
| | PerformanceUniTestSale.py
| | RandomTestCrossConnector.py
| | RandomTestPower.py
| | RandomTestPurchase.py
| | RandomTestSale.py
| | RandomTestTrade.py
| |
| +---AutoGenerate
| | | PrintFileFormulaConstants.py
| | | PrintFunctionBancorFormula.py
| | | PrintFunctionGeneralExp.py
| | | PrintFunctionOptimalExp.py
| | | PrintFunctionOptimalLog.py
| | | PrintIntScalingFactors.py
| | | PrintLn2ScalingFactors.py
| | | PrintMaxExpPerPrecision.py
| | |
| | \---common
| | constants.py
| | functions.py
| | __init__.py
| |
| +---FormulaNativePython
| | __init__.py
| |
| +---FormulaSolidityPort
| | __init__.py
| |
| +---InputGenerator
| | __init__.py
| |
| +---Utilities
| | +---Changer
| | | Decrease.py
| | | Increase.py
| | |
| | \---Converter
| | | example_commands.json
| | | example_model.json
| | | run.py
| | |
| | \---engine
| | __init__.py
| |
| \---Web3Wrapper
| __init__.py
|
\---test
| BancorConverter.js
| BancorConverterUpgrader.js
| BancorFormula.js
| BancorNetwork.js
| ContractFeatures.js
| ContractRegistry.js
| CrowdsaleController.js
| ERC20Token.js
| EtherToken.js
| Managed.js
| Owned.js
| SmartToken.js
| SmartTokenController.js
| TokenHolder.js
| Utils.js
| Whitelist.js
|
\---helpers
FormulaConstants.js
Utils.js
其中的智能合约代码量实在繁多,限于篇幅限制,就不一一介绍了,辉哥介绍其中主要的几个文件。
1,BancorNetwork.sol
(1),功能描述: BancorNetwork合约是bancor通证转换的主入口。 通过提供转换路径,它允许通过唯一的方法在bancor network中的任何通证转换为其他的通证。 转换路径注意点: 1>,转换路径是一种数据结构,用于在bancor network中把一种通证转换为另一种通知。有时一次转换可能不够,需要多个跳转。 这个路径定义了哪些转换会被使用,每个步骤会做哪一种转换。 2>,这个路径格式不包含复杂的结构。相反,它已单一数组的方式呈现,其中每一个跳转(hop)包含2个数组元素 -智能通证和目标通证。 另外,第一个元素总是源通证。智能通证只作为指向转换器的指针使用(因为转换器地址很可能会变)。 (2),关键函数: 1) function registerEtherToken (IEtherToken _token, bool _register) 注册EtherToken。如果转换路径包括ETH,则需要定义EtherToken来替换。 2)function verifyTrustedSender (IERC20Token[] _path, uint256 _amount, uint256 _block, address _addr, uint8 _v, bytes32 _r, bytes32 _s) private 通过从椭圆签名算法还原关联的公钥,验证签名地址是可信的。如果有错误则返回0值。 注意:签名只在一次转换有效,这个给定的区块后会失效。 3) function convertFor (IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _for) public payable 在bancor网络中,根据预定义的转换路径,把通证转换为其他通证,并把转换得到的通证打给目标账户。 注意:转换器必须已经有源通证的管理权限。 4) function getReturnByPath (IERC20Token[] _path, uint256 _amount) public view returns (uint256) 返回给定转换路径的目标通证的数量 5)function convertForMultiple (IERC20Token[] _paths, uint256[] _pathStartIndex, uint256[] _amounts, uint256[] _minReturns, address _for) public payable 通过预定义的转换路径,把计算出来的结果通证给目标账号。该函数在一个单一的原子转换中也允许多次转换。 注意:转换器应该已经控制源通证。 6) function claimAndConvert (IERC20Token[] _path, uint256 _amount, uint256 _minReturn) public returns (uint256) 声明调用者的通知,通过预定义的转换路径把通证转换为其他通证 。
2,ContractIds.sol
(1),功能描述: 定义一些常量 (2),关键函数: bytes32 public constant CONTRACT_FEATURES = "ContractFeatures"; bytes32 public constant BANCOR_NETWORK = "BancorNetwork"; bytes32 public constant BANCOR_FORMULA = "BancorFormula"; bytes32 public constant BANCOR_GAS_PRICE_LIMIT = "BancorGasPriceLimit"; bytes32 public constant BANCOR_CONVERTER_FACTORY = "BancorConverterFactory";
3,BancorNetwork.sol
(1),功能描述: Bancor Network interface,3个空函数定义 (2),关键函数: function convert(…) public payable function convertFor(…) public payable function convertForPrioritized2(…) public payable
3, BancorConverter.sol
见 《第二十四课 基于以太坊的交易所BANCOR算法实现-转换算法框架》 文件描述。
4,BancorConverterFactory.sol
(1),功能描述: 根据参数增加一个新的转换器,把所有权和管理权限转给发送者。 (2),关键函数: function createConverter (_token,_registry,_maxConversionFee, _connectorToken,_connectorWeight) public returns(converterAddress)
5,BancorConverterUpgrader.sol
1,功能描述: Bancor转换升级器允许把旧的Bancor转换器(0.4 或者更高)升级到最新的版本。 为了开始升级流程,首先把转换器的所有权转让给升级合约,然后调用升级函数。 在升级流程的最后,最新升级的转换器的所有权要转让归还给原始所有者。新转换器的地址在ConverterUpgrade事件中是有效的。 2,核心函数: 1) function upgrade (IBancorConverterExtended _oldConverter, bytes32 _version) 把旧的转换器升级到新的版本。 在调用本函数前,如果所有权没有被转移给升级器合约将会抛出异常。新转换器的所有权将会被转移给原始的所有者。 2) function readConnector (IBancorConverterExtended _converter, _address, _isLegacyVersion) 读取连接器的配置
6,BancorFormula.sol
(1),功能描述: 转换器的算法。 (2),核心函数: 1) function calculatePurchaseReturn (uint256 _supply, uint256 _connectorBalance, uint32 _connectorWeight, uint256 _depositAmount) public view returns (uint256) 根据给定的一个token供应量,连接器余额,权重和存入的数量(连接器代币),计算出一个给定转换的反馈。 公式: Return = _supply ((1 + _depositAmount / _connectorBalance) ^ (_connectorWeight / 1000000) - 1) 2) function calculateSaleReturn (uint256 _supply, uint256 _connectorBalance, uint32 _connectorWeight, uint256 _sellAmount) public view returns (uint256) 根据给定的一个token供应量,连接器余额,权重和卖出的数量(目标通证),计算出一个给定转换的连接器通证的结果。公式: Return = _connectorBalance (1 - (1 - _sellAmount / _supply) ^ (1 / (_connectorWeight / 1000000))) 3)function calculateCrossConnectorReturn (uint256 _fromConnectorBalance, uint32 _fromConnectorWeight, uint256 _toConnectorBalance, uint32 _toConnectorWeight, uint256 _amount) public view returns (uint256) 根据给定的2个连接器通证的余额/权重,第一个连接器通证的售卖数量,计算出把第一个连接器通证转换为第二个连接器通证的转换数量。 公式: Return = _toConnectorBalance * (1 - (_fromConnectorBalance / (_fromConnectorBalance + _amount)) ^ (_fromConnectorWeight / _toConnectorWeight))
7,BancorGasPriceLimit.sol
1,功能描述: BancorGasPriceLimit合约充当一个额外的前台运行的攻击减缓机制。它在所有的bancor转换器上设置了一个最大的gas费用, 以便阻止想抢先交易的用户插队。gas费用上限对所有的转换器都适用。并且它可以被所有者更新,以便能跟当前网络的gas费用相符合。 2,核心函数: 1) function setGasPrice (uint256 _gasPrice) public ownerOnly 更新gas上限; 2)function validateGasPrice (uint256 _gasPrice) 判断输入gas是否不超过gas上限。
8, CrowdsaleController.sol
1,功能描述: 智能通证控制器的众筹版本,允许捐赠者用ether交换Bancor通证。众筹期间价格恒定。 注意:20%的捐赠者是用ETH连接器通证余额的BNT通证。 2,核心函数: 1) function() payable public -> function processContribution() private 转移ETH给收益账户;发行智能代币给捐赠者;1个ETH反回100个智能代币。
9, BancorPriceFloor.sol
1,功能描述 bancor底价合约是一个简单的合约,以一个恒定的ETH价格售卖智能代币。 2,核心函数
- function sell() public returns (uint256 amount) 把发送者的所有智能代币转给底价合约,发送(智能代币/100)给发送者 2) function withdraw (uint256 _amount) public ownerOnly 管理员提取一定数量的ETH
10,SmartToken.sol
1,功能描述: 智能代币 2,核心函数: 1) function issue (address _to, uint256 _amount) 对某个账户地址增加智能代币余额 _amount,增加发行总量_amount 2)function destroy (address _from, uint256 _amount) 对某个账户地址销毁智能代币余额_amount,减少发行总量_amount
- function transfer (address _to, uint256 _value) public transfersAllowed 智能代币转账函数 4)function transferFrom (address _from, address _to, uint256 _value) public transfersAllowed 授权后的转移
11,SmartTokenController.sol
1,功能描述: 智能合约控制器是智能合约可升级的一部分,它允许修改BUG,增加功能。 一旦接受了TOKEN的管理权限,它就变成了该TOKEN的唯一控制器,可以执行任何TOKEN的相关函数。 为了升级控制器,管理权限包括任何相关数据,必须转移给新的控制器。 智能代币必须在构建函数设置,而且之后不可修改。 2,核心函数: 1) function transferTokenOwnership (address _newOwner) public ownerOnly: 管理账号操作,更新控制权 2) function acceptTokenOwnership () public ownerOnly 管理账号操作,接受控制权 3)function withdrawFromToken (IERC20Token _token, address _to, uint256 _amount ) public ownerOnly 管理账号操作,收回代币到特定账号
12,ContractFeatures.sol
1,功能描述: 一般合约允许区块链上的每个合约定义它支持的功能。 其他合约可以查询这个合约,找出一个区块链上的特定合约是否支持特定的功能。 每个合约类型可以定义它自己的功能列表标志。合约定义的功能只能够被标识为可以/不可以。 可以使用bit位标识函数功能,例如: uint256 public constant FEATURE1 = 1
1. 创建转换器及合约注册工作
对应的代码:
before(async () => {
/* 新建合约注册器ContractRegistry,合约IDcontractIds, */
contractRegistry = await ContractRegistry.new();
contractIds = await ContractIds.new();
/* 新建合约特征contractFeatures,把合约名称"ContractFeatures" 和合约地址注册到合约注册器 */
contractFeatures = await ContractFeatures.new();
let contractFeaturesId = await contractIds.CONTRACT_FEATURES.call();
await contractRegistry.registerAddress(contractFeaturesId, contractFeatures.address);
/* 新建gas费用限制合约BancorGasPriceLimit,上限价格为22Gwei,把合约名称"BancorGasPriceLimit" 和合约地址注册到合约注册器 */
let gasPriceLimit = await BancorGasPriceLimit.new(gasPrice);
let gasPriceLimitId = await contractIds.BANCOR_GAS_PRICE_LIMIT.call();
await contractRegistry.registerAddress(gasPriceLimitId, gasPriceLimit.address);
/* 新建公式合约formula,把合约名称"BancorFormula" 和合约地址注册到合约注册器 */
let formula = await BancorFormula.new();
let formulaId = await contractIds.BANCOR_FORMULA.call();
await contractRegistry.registerAddress(formulaId, formula.address);
/* 新建Bancor网络合约BancorNetwork,参数为合约注册器地址,把合约名称"BANCOR_NETWORK" 和合约地址注册到合约注册器 */
/* bancorNetwork设置签名者地址为accounts[3] */
let bancorNetwork = await BancorNetwork.new(contractRegistry.address);
let bancorNetworkId = await contractIds.BANCOR_NETWORK.call();
await contractRegistry.registerAddress(bancorNetworkId, bancorNetwork.address);
await bancorNetwork.setSignerAddress(accounts[3]);
/* 创建智能通证,名称为Token1,符号为TKN1,数量为2个 */
//let token = await SmartToken.new('Token1', 'TKN1', 2);
/* 创建一个连接器通证,名称为ERC Token 1,符号ERC1,数量为10万个 */
//let connectorToken = await TestERC20Token.new('ERC Token 1', 'ERC1', 100000);
//tokenAddress = token.address;
//connectorTokenAddress = connectorToken.address;
});
3.2.2 智能通证和连接器通证初始化
2. 转换器初始化,供第三步骤函数调用
2.1 初始化结束后的智能通证和ERC1/ERC2的分布情况
对应的实现代码如下:
async function initConverter(accounts, activate, maxConversionFee = 0) {
/* 创建智能通证,名称为TKN1,符号为Token1 */
token = await SmartToken.new('Token1', 'TKN1', 2);
tokenAddress = token.address;
/* 发行2种ERC20连接器通证 */
connectorToken = await TestERC20Token.new('ERC Token 1', 'ERC1', 100000);
connectorTokenAddress = connectorToken.address;
connectorToken2 = await TestERC20Token.new('ERC Token 2', 'ERC2', 200000);
connectorTokenAddress2 = connectorToken2.address;
/* 创建Bancor转换器, */
let converter = await BancorConverter.new(
tokenAddress,
contractRegistry.address,
maxConversionFee,
connectorTokenAddress,
250000
);
/* 设置ERC2通证为连接器通证,15%的权重 ,不允许虚拟余额*/
let converterAddress = converter.address;
await converter.addConnector(connectorTokenAddress2, 150000, false);
/* 给账号accounts[0]发行2万个TKN1智能代币 ,给转换器地址转账5000个ERC1,给转换器地址转账8000个ERC2,*/
await token.issue(accounts[0], 20000);
await connectorToken.transfer(converterAddress, 5000);
await connectorToken2.transfer(converterAddress, 8000);
if (activate) {
await token.transferOwnership(converterAddress);
await converter.acceptTokenOwnership();
}
return converter;
}
3.2.3 ERC1和ERC2的兑换函数实现
3.ERC1和ERC2的兑换函数
对应代码:
it('verifies that getReturn returns the same amount as buy -> sell when converting between 2 connectors', async () => {
let converter = await initConverter(accounts, true);
let returnAmount = await converter.getReturn.call(connectorTokenAddress, connectorTokenAddress2, 500);
console.log("The returnAmount is : %d",returnAmount.toNumber());
await connectorToken.approve(converter.address, 500);
let purchaseRes = await converter.convert(connectorTokenAddress, tokenAddress, 500, 1);
let purchaseAmount = getConversionAmount(purchaseRes);
console.log("The purchaseAmount is : %d", purchaseAmount);
let saleRes = await converter.convert(tokenAddress, connectorTokenAddress2, purchaseAmount, 1);
let saleAmount = getConversionAmount(saleRes);
console.log("The saleAmount is : %d",saleAmount);
// converting directly between 2 tokens is more efficient than buying and then selling
// which might result in a very small rounding difference
assert(returnAmount.minus(saleAmount).absoluteValue().toNumber() < 2);
});
3.2.4 运行结果
在TRUFFLE下运行测试文件BanncorConverter2.js,可以验证算法代码实现结果等同于上面的算法结果。
The returnAmount is : 1175
The purchaseAmount is : 482
The saleAmount is : 1174
verifies that getReturn returns the same amount as buy -> sell when converting between 2 connectors (1899ms)
如果对truffle命令和测试运行不了解的,可参考文档 《第四课 以太坊开发框架Truffle从入门到实战》 。
4,CLB(一种ERC20)和ETH兑换测试场景
4.1 场景:1种连接器通证CLOB和ETH的兑换测试
对于自己搭建的交易所,更常见的场景为ERC20通证兑换为ETH,解决长尾代币的流通性问题。 假设有一种ERC20和ETH在BANCOR转换器中,其中
- CLB 90000个,90% CW权重,市价1元/个;
- ETH 10个, 10% CW权重,市价1000元/个;
- 初始发行智能代币 TKN1 1000个,PRICE 0.1 (个/ETH); PRICE 100 (个/CLOB);
请问,1000个CLOB可以兑换多少个ETH呢? 我们先来手工拆解计算下,然后用程序运行来验证正确性。
1000个CLOB可以兑换多少个TKN1?
- SmartTokenAmount = SmartTokenTokenSupply ((1 + ConnectorToken / ConnectorTokenBalance)^ CW - 1) = 1000 (( 1 + 1000 / 90000 )^ 0.9 - 1 ) = 9.99446694706181297191051400502 (个TNK1)
9.994466947个TKN1可以兑换多少个ETH呢?
- connectorTokenAmount = ConnectorTokenBalance *(1 - (1 - SmartTokenAmount / SmartTokenTokenSupply)^ (1 / CW) )
- connectorTokenAmount = 10 (1 - (1 - (9.994466947 / (1000 + 9.994466947)))^ (1 / 0.1) ) = 10 (1 - (1 - (9.994466947 / (1000 + 9.994466947)))^ (1 / 0.1) ) = 10 (1 - (1 - (0.00989556603929837667128805564395))^ (1 / 0.1) ) = 10 (1 - (1 - (0.00989556603929837667128805564395))^ (1 / 0.1) ) = 10 (1 - 0.99010443396070162332871194435605 ^ 10 ) = 10 (1 - 0.90533655025365121589722721359431) = 0.94663449746348784102772786405694 (个ETH)
兑换结论:1000个CLB可以兑换0.946个ETH
按照假设的市价,两者的价值均为1000元左右,符合期望。
4.2 测试代码
针对上面兑换算法的公式描述如下:
/* 创建智能通证,名称为TKN1,符号为Token1 */
token = await SmartToken.new('Token1', 'TKN1', 2);
tokenAddress = token.address;
/* 发行ERC20连接器通证CLOB */
connectorToken = await TestERC20Token.new('ColorBay Token', 'CLB', '1000000000000000000000000000');
connectorTokenAddress = connectorToken.address;
/* 从accounts[0]给etherToken存入10个ETH */
etherToken = await EtherToken.new();
/* 获取以太坊的余额 */
let EtherBalance = await web3.eth.getBalance(accounts[0]);
console.log("The EtherBalance of accounts[0] is : %d",EtherBalance);
//etherToken.address.transfer(1000);
/* 往etherToken存10个ETH */
await etherToken.deposit({ value: '10000000000000000000' });
/* 创建Bancor转换器,添加CLOB作为连接器代币,权重为90% */
let converter = await BancorConverter.new(
tokenAddress,
contractRegistry.address,
maxConversionFee,
connectorTokenAddress,
900000
);
/* 设置ETH作为连接器代币,权重为10%*/
let converterAddress = converter.address;
await converter.addConnector(etherToken.address, 100000, false);
/* 给账号accounts[0]发行1000个TKN1智能代币 ,给转换器地址转账90000个CLOB*/
await token.issue(accounts[0], '1000000000000000000000');
await connectorToken.transfer(converterAddress, '90000000000000000000000');
/* 给转换器地址转账10个ETH */
await etherToken.transfer(converter.address, '10000000000000000000');
if (activate) {
await token.transferOwnership(converterAddress);
await converter.acceptTokenOwnership();
}
/* 设置跟ETH的转换路径 */
clobQuickBuyPath = [connectorTokenAddress, token.address, etherToken.address];
/* 授权连接器通证CLOB 1000个 */
await connectorToken.approve(converter.address, '1000000000000000000000');
let purchaseRes = await converter.convert(connectorTokenAddress, tokenAddress, '1000000000000000000000', 1);
let purchaseAmount = getConversionAmount(purchaseRes);
console.log("The purchaseAmount is : %d", purchaseAmount);
let saleRes = await converter.convert(tokenAddress, etherToken.address, purchaseAmount, 1);
let saleAmount = getConversionAmount(saleRes);
console.log("The saleAmount is : %d",saleAmount);
4.3 运行结果
The EtherBalance of accounts[0] is : 82637441999919870000
The purchaseAmount is : 9994466947061813000
The saleAmount is : 946634497469028600
verifies that user can get ETH through sell CLB (2408ms)
如上所示,此处代码以wei为单位,处于10^18,得到的结果为0.9466个ETH,同手工计算结果。
4,总结
从白皮书,算法公式验证到代码实现,辉哥从技术穿刺的角度讲透了BANCOR算法在以太坊环境的实现。
辉哥整理了BANCOR的系列知识分享,列表如下: (1) 【白皮书】Bancor协议:通过智能合约为加密货币提供持续流动性(附PDF下载) (2) 【易错概念】以实例形式深入浅出讲透BANCOR算法 (3) 第二十四课基于以太坊的交易所BANCOR算法实现-转换算法框架 (4) 第二十五课如何开发自己的BANCOR去中心化交易平台? (5) 第二十七课如何从BANCOR.NETWORK去中心化交易所兑换ENJIN通证
如有需要的同学下载测试工程的可加入知识星球下载。
- 发表于 2018-09-30 14:09
- 阅读 ( 5277 )
- 学分 ( 5 )
- 分类:DeFi
评论