1. 程式人生 > >第二十五課 如何開發自己的BANCOR去中心化交易平臺?

第二十五課 如何開發自己的BANCOR去中心化交易平臺?

#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

####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 3) 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 << 0; uint256 public constant FEATURE2 = 1 << 1; uint256 public constant FEATURE3 = 1 << 2; 2,關鍵函式: function enableFeatures(uint256 _features, bool _enable) public

####13,ContractRegistry.sol 1,功能描述: 合約註冊器可以根據名字保持合約地址。owner能更新合約地址,以便合約名稱總是指向最新版本的給定合約。 其他合約可以查詢合約註冊器獲得更新的地址,而不是依賴於固定的地址。注意:合約名稱現在限制在32個位元組,以便優化GAS費用。 2,核心函式: registerAddress(bytes32 _contractName, address _contractAddress) - 註冊合約 function unregisterAddress(bytes32 _contractName) public ownerOnly - 登出合約

#3,2個聯結器通證兌換測試場景 ##3.1 場景:2個聯結器通證ERC1和ERC2的兌換測試 假設有2個聯結器代幣存在BANCOR轉換器中,其中

  • ERC1 5000個,25% CW權重;
  • ERC2 8000個, 15% CW權重;
  • 初始發行智慧代幣 TKN1 20000個; 請問,500個ERC1可以兌換多少個ERC2呢? 我們先來手工拆解計算下,然後用程式執行來驗證正確性。

500個ERC1可以兌換多少TKN1呢?

  • SmartTokenAmount = SmartTokenTokenSupply *((1 + ConnectorToken / ConnectorTokenBalance)^ CW - 1)
  • SmartTokenAmount = 20000 * (( 1 + 500 / 5000 )^ 0.25 - 1 ) = 482(TKN1)

前一步所得的TKN1可以兌換多少個ERC2呢?

  • connectorTokenAmount = ConnectorTokenBalance *(1 - (1 - SmartTokenAmount / SmartTokenTokenSupply)^ (1 / CW) )
  • connectorTokenAmount = 8000 * (1 - (1 - 482 / (20000+482))^ (1 / 0.15)) = 8000 * (1 - 0.9765 ^ 6.6667) = 8000 * (1 - 0.853390) = 1173 ####兌換結論:500個ERC1 可以兌換1173個ERC2

##3.2 部署測試流程圖 輝哥帶領大家用truffle的框架進行測試驗證。所以,需要寫js測試函式。先整理下工程思路,完成流程圖的寫作。 ###3.2.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)

#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,同手工計算結果。

如有需要的同學下載測試工程的可加入知識星球下載。