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ù)
- 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
- 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è)
對(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 智能通證和連接器通證初始化
對(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)
對(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通證