第二十五課 如何開發(fā)自己的BANCOR去中心化交易平臺(tái)匹厘?

1,摘要

《第二十四課 基于以太坊的交易所BANCOR算法實(shí)現(xiàn)-轉(zhuǎn)換算法框架》 講解了以太坊solidity實(shí)現(xiàn)的BancorConverter轉(zhuǎn)換主合約的邏輯和代碼脐区,但是沒有涉及核心互換及計(jì)算代碼愈诚,而是通過interface類的方式進(jìn)行隔離。
本文詳細(xì)描述一下內(nèi)容牛隅,能跑的程序才是真分享:
(1)BancorNetwork網(wǎng)絡(luò)的文件框架和功能炕柔;
(2)BancorConverter合約測試執(zhí)行流程;
(3)2個(gè)連接器通證ERC1-ERC22的轉(zhuǎn)換驗(yàn)證結(jié)果媒佣;
(4)ETH-CLB(彩貝)的轉(zhuǎn)化驗(yàn)證結(jié)果

2匕累,BancorNetwork網(wǎng)絡(luò)的文件框架和功能

BancorProtocol工程是一個(gè)帶有TRUFFLE框架的solidity智能合約Bancor算法實(shí)現(xiàn)框架。文件列表如下:

|   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

其中的智能合約代碼量實(shí)在繁多默伍,限于篇幅限制欢嘿,就不一一介紹了,輝哥介紹其中主要的幾個(gè)文件也糊。

1炼蹦,BancorNetwork.sol

(1),功能描述:
BancorNetwork合約是bancor通證轉(zhuǎn)換的主入口显设。
通過提供轉(zhuǎn)換路徑框弛,它允許通過唯一的方法在bancor network中的任何通證轉(zhuǎn)換為其他的通證。
轉(zhuǎn)換路徑注意點(diǎn):
1>捕捂,轉(zhuǎn)換路徑是一種數(shù)據(jù)結(jié)構(gòu)瑟枫,用于在bancor network中把一種通證轉(zhuǎn)換為另一種通知斗搞。有時(shí)一次轉(zhuǎn)換可能不夠,需要多個(gè)跳轉(zhuǎn)慷妙。
這個(gè)路徑定義了哪些轉(zhuǎn)換會(huì)被使用僻焚,每個(gè)步驟會(huì)做哪一種轉(zhuǎn)換。
2>膝擂,這個(gè)路徑格式不包含復(fù)雜的結(jié)構(gòu)虑啤。相反,它已單一數(shù)組的方式呈現(xiàn)架馋,其中每一個(gè)跳轉(zhuǎn)(hop)包含2個(gè)數(shù)組元素 -智能通證和目標(biāo)通證狞山。
另外,第一個(gè)元素總是源通證叉寂。智能通證只作為指向轉(zhuǎn)換器的指針使用(因?yàn)檗D(zhuǎn)換器地址很可能會(huì)變)萍启。
(2),關(guān)鍵函數(shù):
1) function registerEtherToken(IEtherToken _token, bool _register)
注冊(cè)EtherToken屏鳍。如果轉(zhuǎn)換路徑包括ETH勘纯,則需要定義EtherToken來替換。
2)function verifyTrustedSender(IERC20Token[] _path, uint256 _amount, uint256 _block, address _addr, uint8 _v, bytes32 _r, bytes32 _s) private
通過從橢圓簽名算法還原關(guān)聯(lián)的公鑰钓瞭,驗(yàn)證簽名地址是可信的驳遵。如果有錯(cuò)誤則返回0值。
注意:簽名只在一次轉(zhuǎn)換有效山涡,這個(gè)給定的區(qū)塊后會(huì)失效堤结。
3) function convertFor(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, address _for) public payable
在bancor網(wǎng)絡(luò)中,根據(jù)預(yù)定義的轉(zhuǎn)換路徑佳鳖,把通證轉(zhuǎn)換為其他通證霍殴,并把轉(zhuǎn)換得到的通證打給目標(biāo)賬戶。
注意:轉(zhuǎn)換器必須已經(jīng)有源通證的管理權(quán)限系吩。
4) function getReturnByPath(IERC20Token[] _path, uint256 _amount) public view returns (uint256)
返回給定轉(zhuǎn)換路徑的目標(biāo)通證的數(shù)量
5)function convertForMultiple(IERC20Token[] _paths, uint256[] _pathStartIndex, uint256[] _amounts, uint256[] _minReturns, address _for) public payable
通過預(yù)定義的轉(zhuǎn)換路徑,把計(jì)算出來的結(jié)果通證給目標(biāo)賬號(hào)妒蔚。該函數(shù)在一個(gè)單一的原子轉(zhuǎn)換中也允許多次轉(zhuǎn)換穿挨。
注意:轉(zhuǎn)換器應(yīng)該已經(jīng)控制源通證。
6) function claimAndConvert(IERC20Token[] _path, uint256 _amount, uint256 _minReturn) public returns (uint256)
聲明調(diào)用者的通知肴盏,通過預(yù)定義的轉(zhuǎn)換路徑把通證轉(zhuǎn)換為其他通證 科盛。

2,ContractIds.sol

(1)菜皂,功能描述:
定義一些常量
(2)贞绵,關(guān)鍵函數(shù):
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個(gè)空函數(shù)定義
(2)谴垫,關(guān)鍵函數(shù):
function convert(…) public payable
function convertFor(…) public payable
function convertForPrioritized2(…) public payable

3, BancorConverter.sol

《第二十四課 基于以太坊的交易所BANCOR算法實(shí)現(xiàn)-轉(zhuǎn)換算法框架》文件描述母蛛。

4翩剪,BancorConverterFactory.sol

(1),功能描述:
根據(jù)參數(shù)增加一個(gè)新的轉(zhuǎn)換器彩郊,把所有權(quán)和管理權(quán)限轉(zhuǎn)給發(fā)送者前弯。
(2),關(guān)鍵函數(shù):
function createConverter(_token,_registry,_maxConversionFee,
_connectorToken,_connectorWeight) public returns(converterAddress)

5秫逝,BancorConverterUpgrader.sol

1恕出,功能描述:
Bancor轉(zhuǎn)換升級(jí)器允許把舊的Bancor轉(zhuǎn)換器(0.4 或者更高)升級(jí)到最新的版本。
為了開始升級(jí)流程违帆,首先把轉(zhuǎn)換器的所有權(quán)轉(zhuǎn)讓給升級(jí)合約剃根,然后調(diào)用升級(jí)函數(shù)。
在升級(jí)流程的最后前方,最新升級(jí)的轉(zhuǎn)換器的所有權(quán)要轉(zhuǎn)讓歸還給原始所有者狈醉。新轉(zhuǎn)換器的地址在ConverterUpgrade事件中是有效的。
2惠险,核心函數(shù):
1) function upgrade(IBancorConverterExtended _oldConverter, bytes32 _version)
把舊的轉(zhuǎn)換器升級(jí)到新的版本苗傅。
在調(diào)用本函數(shù)前,如果所有權(quán)沒有被轉(zhuǎn)移給升級(jí)器合約將會(huì)拋出異常班巩。新轉(zhuǎn)換器的所有權(quán)將會(huì)被轉(zhuǎn)移給原始的所有者渣慕。
2) function readConnector(IBancorConverterExtended _converter, _address, _isLegacyVersion)
讀取連接器的配置

6,BancorFormula.sol

(1)抱慌,功能描述:
轉(zhuǎn)換器的算法逊桦。
(2),核心函數(shù):
1) function calculatePurchaseReturn(uint256 _supply, uint256 _connectorBalance, uint32 _connectorWeight, uint256 _depositAmount) public view returns (uint256)
根據(jù)給定的一個(gè)token供應(yīng)量抑进,連接器余額强经,權(quán)重和存入的數(shù)量(連接器代幣),計(jì)算出一個(gè)給定轉(zhuǎn)換的反饋寺渗。 公式:
Return = _supply * ((1 + _depositAmount / _connectorBalance) ^ (_connectorWeight / 1000000) - 1)
2) function calculateSaleReturn(uint256 _supply, uint256 _connectorBalance, uint32 _connectorWeight, uint256 _sellAmount) public view returns (uint256)
根據(jù)給定的一個(gè)token供應(yīng)量匿情,連接器余額,權(quán)重和賣出的數(shù)量(目標(biāo)通證)信殊,計(jì)算出一個(gè)給定轉(zhuǎn)換的連接器通證的結(jié)果炬称。公式:
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)
根據(jù)給定的2個(gè)連接器通證的余額/權(quán)重,第一個(gè)連接器通證的售賣數(shù)量涡拘,計(jì)算出把第一個(gè)連接器通證轉(zhuǎn)換為第二個(gè)連接器通證的轉(zhuǎn)換數(shù)量玲躯。
公式:
Return = _toConnectorBalance * (1 - (_fromConnectorBalance / (_fromConnectorBalance + _amount)) ^ (_fromConnectorWeight / _toConnectorWeight))

7,BancorGasPriceLimit.sol

1,功能描述:
BancorGasPriceLimit合約充當(dāng)一個(gè)額外的前臺(tái)運(yùn)行的攻擊減緩機(jī)制跷车。它在所有的bancor轉(zhuǎn)換器上設(shè)置了一個(gè)最大的gas費(fèi)用棘利,
以便阻止想搶先交易的用戶插隊(duì)。gas費(fèi)用上限對(duì)所有的轉(zhuǎn)換器都適用姓赤。并且它可以被所有者更新赡译,以便能跟當(dāng)前網(wǎng)絡(luò)的gas費(fèi)用相符合。
2不铆,核心函數(shù):
1) function setGasPrice(uint256 _gasPrice) public ownerOnly
更新gas上限蝌焚;
2)function validateGasPrice(uint256 _gasPrice)
判斷輸入gas是否不超過gas上限。

8誓斥, CrowdsaleController.sol

1只洒,功能描述:
智能通證控制器的眾籌版本,允許捐贈(zèng)者用ether交換Bancor通證劳坑。眾籌期間價(jià)格恒定毕谴。
注意:20%的捐贈(zèng)者是用ETH連接器通證余額的BNT通證。
2距芬,核心函數(shù):
1) function() payable public -> function processContribution() private
轉(zhuǎn)移ETH給收益賬戶涝开;發(fā)行智能代幣給捐贈(zèng)者;1個(gè)ETH反回100個(gè)智能代幣框仔。

9舀武, BancorPriceFloor.sol

1,功能描述
bancor底價(jià)合約是一個(gè)簡單的合約离斩,以一個(gè)恒定的ETH價(jià)格售賣智能代幣银舱。
2,核心函數(shù)

  1. function sell() public returns (uint256 amount)
    把發(fā)送者的所有智能代幣轉(zhuǎn)給底價(jià)合約跛梗,發(fā)送(智能代幣/100)給發(fā)送者
    2) function withdraw(uint256 _amount) public ownerOnly
    管理員提取一定數(shù)量的ETH

10寻馏,SmartToken.sol

1,功能描述:
智能代幣
2核偿,核心函數(shù):
1) function issue(address _to, uint256 _amount)
對(duì)某個(gè)賬戶地址增加智能代幣余額 _amount诚欠,增加發(fā)行總量_amount
2)function destroy(address _from, uint256 _amount)
對(duì)某個(gè)賬戶地址銷毀智能代幣余額_amount,減少發(fā)行總量_amount

  1. function transfer(address _to, uint256 _value) public transfersAllowed
    智能代幣轉(zhuǎn)賬函數(shù)
    4)function transferFrom(address _from, address _to, uint256 _value) public transfersAllowed
    授權(quán)后的轉(zhuǎn)移

11宪祥,SmartTokenController.sol

1聂薪,功能描述:
智能合約控制器是智能合約可升級(jí)的一部分,它允許修改BUG蝗羊,增加功能。 一旦接受了TOKEN的管理權(quán)限仁锯,它就變成了該TOKEN的唯一控制器耀找,可以執(zhí)行任何TOKEN的相關(guān)函數(shù)。
為了升級(jí)控制器,管理權(quán)限包括任何相關(guān)數(shù)據(jù)野芒,必須轉(zhuǎn)移給新的控制器蓄愁。 智能代幣必須在構(gòu)建函數(shù)設(shè)置,而且之后不可修改撮抓。
2,核心函數(shù):
1) function transferTokenOwnership(address _newOwner) public ownerOnly:
管理賬號(hào)操作摇锋,更新控制權(quán)
2) function acceptTokenOwnership() public ownerOnly
管理賬號(hào)操作丹拯,接受控制權(quán)
3)function withdrawFromToken(IERC20Token _token, address _to, uint256 _amount ) public ownerOnly
管理賬號(hào)操作,收回代幣到特定賬號(hào)

12荸恕,ContractFeatures.sol

1乖酬,功能描述:
一般合約允許區(qū)塊鏈上的每個(gè)合約定義它支持的功能。
其他合約可以查詢這個(gè)合約融求,找出一個(gè)區(qū)塊鏈上的特定合約是否支持特定的功能咬像。
每個(gè)合約類型可以定義它自己的功能列表標(biāo)志。合約定義的功能只能夠被標(biāo)識(shí)為可以/不可以生宛。
可以使用bit位標(biāo)識(shí)函數(shù)功能县昂,例如:
uint256 public constant FEATURE1 = 1 << 0;
uint256 public constant FEATURE2 = 1 << 1;
uint256 public constant FEATURE3 = 1 << 2;
2,關(guān)鍵函數(shù):
function enableFeatures(uint256 _features, bool _enable) public

13陷舅,ContractRegistry.sol

1倒彰,功能描述:
合約注冊(cè)器可以根據(jù)名字保持合約地址。owner能更新合約地址蔑赘,以便合約名稱總是指向最新版本的給定合約狸驳。
其他合約可以查詢合約注冊(cè)器獲得更新的地址,而不是依賴于固定的地址缩赛。注意:合約名稱現(xiàn)在限制在32個(gè)字節(jié)耙箍,以便優(yōu)化GAS費(fèi)用。
2酥馍,核心函數(shù):
registerAddress(bytes32 _contractName, address _contractAddress) - 注冊(cè)合約
function unregisterAddress(bytes32 _contractName) public ownerOnly - 注銷合約

3辩昆,2個(gè)連接器通證兌換測試場景

3.1 場景:2個(gè)連接器通證ERC1和ERC2的兌換測試

假設(shè)有2個(gè)連接器代幣存在BANCOR轉(zhuǎn)換器中,其中

  • ERC1 5000個(gè)旨袒,25% CW權(quán)重;
  • ERC2 8000個(gè)汁针, 15% CW權(quán)重;
  • 初始發(fā)行智能代幣 TKN1 20000個(gè)砚尽;
    請(qǐng)問施无,500個(gè)ERC1可以兌換多少個(gè)ERC2呢?
    我們先來手工拆解計(jì)算下必孤,然后用程序運(yùn)行來驗(yàn)證正確性猾骡。

500個(gè)ERC1可以兌換多少TKN1呢瑞躺?

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

前一步所得的TKN1可以兌換多少個(gè)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

兌換結(jié)論:500個(gè)ERC1 可以兌換1173個(gè)ERC2

3.2 部署測試流程圖

輝哥帶領(lǐng)大家用truffle的框架進(jìn)行測試驗(yàn)證兴想。所以幢哨,需要寫js測試函數(shù)。先整理下工程思路嫂便,完成流程圖的寫作逛裤。

3.2.1 創(chuàng)建轉(zhuǎn)換器及合約注冊(cè)

1. 創(chuàng)建轉(zhuǎn)換器及合約注冊(cè)工作

對(duì)應(yīng)的代碼:

before(async () => {
        /* 新建合約注冊(cè)器ContractRegistry运褪,合約IDcontractIds锥债, */
        contractRegistry = await ContractRegistry.new();
        contractIds = await ContractIds.new();

        /* 新建合約特征contractFeatures错敢,把合約名稱"ContractFeatures" 和合約地址注冊(cè)到合約注冊(cè)器 */
        contractFeatures = await ContractFeatures.new();
        let contractFeaturesId = await contractIds.CONTRACT_FEATURES.call();
        await contractRegistry.registerAddress(contractFeaturesId, contractFeatures.address);

        /* 新建gas費(fèi)用限制合約BancorGasPriceLimit,上限價(jià)格為22Gwei蔚龙,把合約名稱"BancorGasPriceLimit" 和合約地址注冊(cè)到合約注冊(cè)器 */
        let gasPriceLimit = await BancorGasPriceLimit.new(gasPrice);
        let gasPriceLimitId = await contractIds.BANCOR_GAS_PRICE_LIMIT.call();
        await contractRegistry.registerAddress(gasPriceLimitId, gasPriceLimit.address);

        /* 新建公式合約formula冰评,把合約名稱"BancorFormula" 和合約地址注冊(cè)到合約注冊(cè)器 */
        let formula = await BancorFormula.new();
        let formulaId = await contractIds.BANCOR_FORMULA.call();
        await contractRegistry.registerAddress(formulaId, formula.address);

        /* 新建Bancor網(wǎng)絡(luò)合約BancorNetwork,參數(shù)為合約注冊(cè)器地址木羹,把合約名稱"BANCOR_NETWORK" 和合約地址注冊(cè)到合約注冊(cè)器 */
        /* bancorNetwork設(shè)置簽名者地址為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]);

        /* 創(chuàng)建智能通證甲雅,名稱為Token1,符號(hào)為TKN1,數(shù)量為2個(gè) */
        //let token = await SmartToken.new('Token1', 'TKN1', 2);
        
        /* 創(chuàng)建一個(gè)連接器通證坑填,名稱為ERC Token 1抛人,符號(hào)ERC1,數(shù)量為10萬個(gè) */
        //let connectorToken = await TestERC20Token.new('ERC Token 1', 'ERC1', 100000);
        //tokenAddress = token.address;
        //connectorTokenAddress = connectorToken.address;
    });

3.2.2 智能通證和連接器通證初始化

2. 轉(zhuǎn)換器初始化脐瑰,供第三步驟函數(shù)調(diào)用
2.1 初始化結(jié)束后的智能通證和ERC1/ERC2的分布情況

對(duì)應(yīng)的實(shí)現(xiàn)代碼如下:

async function initConverter(accounts, activate, maxConversionFee = 0) {
    /* 創(chuàng)建智能通證妖枚,名稱為TKN1,符號(hào)為Token1  */
    token = await SmartToken.new('Token1', 'TKN1', 2);
    tokenAddress = token.address;

    /* 發(fā)行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;

    /* 創(chuàng)建Bancor轉(zhuǎn)換器苍在, */
    let converter = await BancorConverter.new(
        tokenAddress,
        contractRegistry.address,
        maxConversionFee,
        connectorTokenAddress,
        250000
    );

    /* 設(shè)置ERC2通證為連接器通證绝页,15%的權(quán)重 ,不允許虛擬余額*/
    let converterAddress = converter.address;
    await converter.addConnector(connectorTokenAddress2, 150000, false);

    /* 給賬號(hào)accounts[0]發(fā)行2萬個(gè)TKN1智能代幣 寂恬,給轉(zhuǎn)換器地址轉(zhuǎn)賬5000個(gè)ERC1续誉,給轉(zhuǎn)換器地址轉(zhuǎn)賬8000個(gè)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的兌換函數(shù)實(shí)現(xiàn)

3.ERC1和ERC2的兌換函數(shù)

對(duì)應(yīng)代碼:

 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 運(yùn)行結(jié)果

在TRUFFLE下運(yùn)行測試文件BanncorConverter2.js初肉,可以驗(yàn)證算法代碼實(shí)現(xiàn)結(jié)果等同于上面的算法結(jié)果酷鸦。

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)

如果對(duì)truffle命令和測試運(yùn)行不了解的,可參考文檔《第四課 以太坊開發(fā)框架Truffle從入門到實(shí)戰(zhàn)》牙咏。

4臼隔,CLB(一種ERC20)和ETH兌換測試場景

4.1 場景:1種連接器通證CLOB和ETH的兌換測試

對(duì)于自己搭建的交易所,更常見的場景為ERC20通證兌換為ETH妄壶,解決長尾代幣的流通性問題摔握。
假設(shè)有一種ERC20和ETH在BANCOR轉(zhuǎn)換器中,其中

  • CLB 90000個(gè)丁寄,90% CW權(quán)重盒发,市價(jià)1元/個(gè);
  • ETH 10個(gè)例嘱, 10% CW權(quán)重狡逢,市價(jià)1000元/個(gè)宁舰;
  • 初始發(fā)行智能代幣 TKN1 1000個(gè),PRICE 0.1 (個(gè)/ETH); PRICE 100 (個(gè)/CLOB)奢浑;

請(qǐng)問蛮艰,1000個(gè)CLOB可以兌換多少個(gè)ETH呢?
我們先來手工拆解計(jì)算下雀彼,然后用程序運(yùn)行來驗(yàn)證正確性壤蚜。

1000個(gè)CLOB可以兌換多少個(gè)TKN1?

  • SmartTokenAmount = SmartTokenTokenSupply *((1 + ConnectorToken / ConnectorTokenBalance)^ CW - 1)
    = 1000 * (( 1 + 1000 / 90000 )^ 0.9 - 1 )
    = 9.99446694706181297191051400502(個(gè)TNK1)

9.994466947個(gè)TKN1可以兌換多少個(gè)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(個(gè)ETH)

兌換結(jié)論:1000個(gè)CLB可以兌換0.946個(gè)ETH

按照假設(shè)的市價(jià)袜刷,兩者的價(jià)值均為1000元左右,符合期望莺丑。

4.2 測試代碼

針對(duì)上面兌換算法的公式描述如下:

    /* 創(chuàng)建智能通證著蟹,名稱為TKN1,符號(hào)為Token1  */
    token = await SmartToken.new('Token1', 'TKN1', 2);
    tokenAddress = token.address;

    /* 發(fā)行ERC20連接器通證CLOB */
    connectorToken = await TestERC20Token.new('ColorBay Token', 'CLB', '1000000000000000000000000000');
    connectorTokenAddress = connectorToken.address;

    /* 從accounts[0]給etherToken存入10個(gè)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個(gè)ETH */
    await etherToken.deposit({ value: '10000000000000000000' });

    /* 創(chuàng)建Bancor轉(zhuǎn)換器梢莽,添加CLOB作為連接器代幣萧豆,權(quán)重為90% */
    let converter = await BancorConverter.new(
        tokenAddress,
        contractRegistry.address,
        maxConversionFee,
        connectorTokenAddress,
        900000
    );

    /* 設(shè)置ETH作為連接器代幣,權(quán)重為10%*/
    let converterAddress = converter.address;
    await converter.addConnector(etherToken.address, 100000, false);

    /* 給賬號(hào)accounts[0]發(fā)行1000個(gè)TKN1智能代幣 昏名,給轉(zhuǎn)換器地址轉(zhuǎn)賬90000個(gè)CLOB*/
    await token.issue(accounts[0], '1000000000000000000000');
    await connectorToken.transfer(converterAddress, '90000000000000000000000');

    /* 給轉(zhuǎn)換器地址轉(zhuǎn)賬10個(gè)ETH */
    await etherToken.transfer(converter.address, '10000000000000000000');

    if (activate) {
        await token.transferOwnership(converterAddress);
        await converter.acceptTokenOwnership();
    }

    /* 設(shè)置跟ETH的轉(zhuǎn)換路徑 */
    clobQuickBuyPath = [connectorTokenAddress, token.address, etherToken.address];
    /* 授權(quán)連接器通證CLOB 1000個(gè) */
    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 運(yùn)行結(jié)果

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轻局,得到的結(jié)果為0.9466個(gè)ETH洪鸭,同手工計(jì)算結(jié)果。

4仑扑,總結(jié)

從白皮書览爵,算法公式驗(yàn)證到代碼實(shí)現(xiàn),輝哥從技術(shù)穿刺的角度講透了BANCOR算法在以太坊環(huán)境的實(shí)現(xiàn)夫壁。


輝哥整理了BANCOR的系列知識(shí)分享拾枣,列表如下:
(1)【白皮書】Bancor協(xié)議:通過智能合約為加密貨幣提供持續(xù)流動(dòng)性(附PDF下載)
(2)【易錯(cuò)概念】以實(shí)例形式深入淺出講透BANCOR算法
(3)第二十四課基于以太坊的交易所BANCOR算法實(shí)現(xiàn)-轉(zhuǎn)換算法框架
(4)第二十五課如何開發(fā)自己的BANCOR去中心化交易平臺(tái)?
(5)第二十七課如何從BANCOR.NETWORK去中心化交易所兌換ENJIN通證

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末盒让,一起剝皮案震驚了整個(gè)濱河市梅肤,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌邑茄,老刑警劉巖姨蝴,帶你破解...
    沈念sama閱讀 221,820評(píng)論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異肺缕,居然都是意外死亡左医,警方通過查閱死者的電腦和手機(jī)授帕,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,648評(píng)論 3 399
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來浮梢,“玉大人跛十,你說我怎么就攤上這事★跸酰” “怎么了芥映?”我有些...
    開封第一講書人閱讀 168,324評(píng)論 0 360
  • 文/不壞的土叔 我叫張陵,是天一觀的道長远豺。 經(jīng)常有香客問我奈偏,道長,這世上最難降的妖魔是什么躯护? 我笑而不...
    開封第一講書人閱讀 59,714評(píng)論 1 297
  • 正文 為了忘掉前任惊来,我火速辦了婚禮,結(jié)果婚禮上棺滞,老公的妹妹穿的比我還像新娘裁蚁。我一直安慰自己,他們只是感情好检眯,可當(dāng)我...
    茶點(diǎn)故事閱讀 68,724評(píng)論 6 397
  • 文/花漫 我一把揭開白布厘擂。 她就那樣靜靜地躺著,像睡著了一般锰瘸。 火紅的嫁衣襯著肌膚如雪刽严。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,328評(píng)論 1 310
  • 那天避凝,我揣著相機(jī)與錄音舞萄,去河邊找鬼。 笑死管削,一個(gè)胖子當(dāng)著我的面吹牛倒脓,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播含思,決...
    沈念sama閱讀 40,897評(píng)論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼崎弃,長吁一口氣:“原來是場噩夢(mèng)啊……” “哼!你這毒婦竟也來了含潘?” 一聲冷哼從身側(cè)響起饲做,我...
    開封第一講書人閱讀 39,804評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎遏弱,沒想到半個(gè)月后盆均,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,345評(píng)論 1 318
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡漱逸,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,431評(píng)論 3 340
  • 正文 我和宋清朗相戀三年泪姨,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了游沿。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,561評(píng)論 1 352
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡肮砾,死狀恐怖诀黍,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情唇敞,我是刑警寧澤蔗草,帶...
    沈念sama閱讀 36,238評(píng)論 5 350
  • 正文 年R本政府宣布,位于F島的核電站疆柔,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏镶柱。R本人自食惡果不足惜旷档,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,928評(píng)論 3 334
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望歇拆。 院中可真熱鬧鞋屈,春花似錦、人聲如沸故觅。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,417評(píng)論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽输吏。三九已至权旷,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間贯溅,已是汗流浹背拄氯。 一陣腳步聲響...
    開封第一講書人閱讀 33,528評(píng)論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留它浅,地道東北人译柏。 一個(gè)月前我還...
    沈念sama閱讀 48,983評(píng)論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像姐霍,于是被迫代替她去往敵國和親鄙麦。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,573評(píng)論 2 359

推薦閱讀更多精彩內(nèi)容