基于以太坊的交易所BANCOR算法实现-转换算法框架
V刘晨曦
发表于 2022-11-26 14:54:17
2364
1
0
若利用智能合约的强大而灵活的“资金流转控制”能力,在通证合约中控制着一定量的储备金,让通证与储备金之间拥有一定的兑换能力,那么Token的价值就可以储备金为锚定物,而不完全依赖于项目方。通证持有者也就不用承担项目失败或者项目方可能诈骗跑路的风险。" J* A X: @! F) c
5 h$ M; T+ [ K& b$ l
若通证与锚定物之间的兑换算法采用了Bancor算法,又符合ERC20标准,则被称为智能通证(Smart-Token) 。为了简单起见,以下的论述以ETH作为锚定物举例说明。购买与售卖Token的过程如下:
“购买者”发送一定量的ETH到Token合约地址,触发了合约代码自动执行"购买功能代码",获得对应数量的Token;“售卖者”发送一定量的Token到Token合约地址,触发了合约代码自动执行“售卖功能代码”,获得对应数量的ETH。
9 }/ k- g* C' y" K
若AToken与BToken都是以ETH为锚定物的智能通证,那么Token持有者无需通过交易所,仅仅凭借智能合约提供的买卖与兑换功能,就能实现AToken与BToken的自由兑换,比如AToken–>ETH–>BToken,多种智能通证之间通过共同的锚定物串接起来,就形成了一个价值网络(Bancor Network)。+ E; B$ b6 X4 V: w7 K
# H" \! e# x. W" v! x* Y% A5 N: w
【核心智能合约简单描述】
1,contract BancorConverter
功能说明:代币转换器,允许一个智能代币和其他代币之间的转换,ERC20连接器的余额可以是虚拟的,从而不需要依赖于真实的余额,这有助于避免在一个协约中有大量金额的风险。转换器可以升级。/ W' i" `, j2 f, X. F7 `% d
2,ITokenConverter) }' G$ ?8 d3 R/ w: O& _
功能说明:BancorConverter的父类接口之一,EIP228 Token Converter接口,用于智能代币的买卖和数量计算接口。6 c4 l0 H# G; g/ z B
3,SmartTokenController) [- P! r+ |) C$ L
功能说明:BancorConverter的父类接口之一,智能代币管理器。智能代币管理器是一个可以升级的模块,从而允许更多功能和问题修复。当它接受了代币的所有权,它会成为代币的唯一管理器,执行各个功能。/ `) r3 @6 H4 ^3 a0 n) ]- x
4,Managed$ V# T- \1 g7 S* p, S! n+ C
功能说明: BancorConverter的父类之一,提供协议管理的支持。 m, H @7 J& E, f* T0 c
5,IBancorConverterExtensions
功能说明:BancorConverter的公开变量类,bancor converter extensions 协议。能返回formula,gasPriceLimit,quickConverter等3类接口合约。, V' F7 O6 ^6 c. ~1 g* H6 Q
#4,核心函数分析0 r( W3 w+ `4 P K# R
##4.1 convert(…)函数8 S( [; [' _8 w% g8 w
convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256)功能: 将一定数量的_fromToken 转换为 _toToken;源码:' r/ c/ M9 b3 X
; G" |6 k& I* D, _/ f: K% f& Q8 O/ L
/**
@dev 将一定数量的_fromToken 转换为 _toToken
@param _fromToken 用来转换ERC20代币( k! ~! ~- j- d. ?0 H
@param _toToken 被转换到的ERC20代币8 h M4 @* k# F. |- \/ `( D/ S t8 x
@param _amount 转换的数量,基于fromToken
@param _minReturn 限制转换的结果需要高于minReturn,否则取消
@return conversion 返回数量
*/% d' t" t m5 V; A0 w8 }7 j! y
function convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256) { s4 J; ^- y: i" j
convertPath = [_fromToken, token, _toToken];
return quickConvert(convertPath, _amount, _minReturn);
}
/**7 `' D9 X: l5 H9 S( t$ ~+ M5 U x
@dev 通过之前定义的转换路径来转换代币
注意:当从ERC20代币进行转换,需要提前设置补贴
@param _path 转换路径, l! J: N7 x+ F, M, d; K# {. Q3 h. W
@param _amount 转换的数量
@param _minReturn 限制转换的结果需要高于minReturn,否则取消, t8 w1 g$ \. q; Q9 x _2 ]+ T
@return 返回数量- }& H5 Z; Z$ v0 m
*/1 p4 Y9 s, A1 {3 r
quickConvertfunction quickConvert(IERC20Token[] _path, uint256 _amount, uint256 _minReturn)
public
payable; \1 L0 \; B& L0 M, Q
validConversionPath(_path)
returns (uint256): ]# S! @% O F3 S- X+ y
{+ j! M' c/ f6 \# g) S% i
return quickConvertPrioritized(_path, _amount, _minReturn, 0x0, 0x0, 0x0, 0x0, 0x0);1 F! ^: O/ F) f& o+ e' }
}
/**
@dev 通过之前定义的转换路径来转换代币
注意:当从ERC20代币进行转换,需要提前设置补贴
@param _path 转换路径
@param _amount 转换的数量
@param _minReturn 限制转换的结果需要高于minReturn,否则取消) s" X# ~, }! T5 Y! c( j( v
@param _block 如果当前的区块超过了参数,则取消/ Y/ l! O) d1 P2 g J
@param _nonce 发送者地址的nonce
@param _v 通过交易签名提取2 q7 P- m3 \! E1 J. `" t
@param _r 通过交易签名提取) k0 _4 p: d$ X1 Q5 A0 q I% r) u, m
@param _s 通过交易签名提取% u( f" F6 n( Q5 i
@return 返回数量
*/
function quickConvertPrioritized(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, uint256 _block, uint256 _nonce, uint8 _v, bytes32 _r, bytes32 _s)
public
payable+ h S/ e+ D2 H; N1 @3 a9 z# b
validConversionPath(_path)
returns (uint256)2 S. r& P' A6 I' j, o" ^
{
IERC20Token fromToken = _path[0];: b/ }( B4 Y( l) ~, ]6 ~8 B
IBancorQuickConverter quickConverter = extensions.quickConverter();
// 我们需要从调用者向快速转换着把源代币转化: _$ M' n* l; f+ X5 w7 K, P
// 因此他能基于调用者进行转换& u. P8 b8 @6 K. Z2 J& f
if (msg.value == 0) {
// 如果不是ETH,把源代币发给快速调用者# [( Y1 N6 K& f4 \5 w! {; @
// 如果是智能代币,不需要补贴 —— 销毁代币,然后发给快速转换者
if (fromToken == token) {
token.destroy(msg.sender, _amount); // 销毁调用者的_amount代币
token.issue(quickConverter, _amount); // 把_amount的新代币发给快速转换者
} else {
// 否则,我们假设有了补贴,发给快速转换者9 A7 M( D0 Z: {
assert(fromToken.transferFrom(msg.sender, quickConverter, _amount));4 X) M9 m. p: W; `
}" n* S3 P0 |$ k
}
// 执行转换,把ETH转回& q1 M5 a+ H$ q) w' C
return quickConverter.convertForPrioritized.value(msg.value)(_path, _amount, _minReturn, msg.sender, _block, _nonce, _v, _r, _s);
}
##4.2 change(…)函数
** function change**(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256)功能: 将一定数量的_fromToken 转换为 _toToken。弃用了,向后兼容。设计思路和源码结构挺好的代码:/ Y4 Z( @# d3 C. J) r
既然是过期代码,源代码就不放了。
##4.3 getReturn(…)函数9 n/ W- y4 V. X) \
function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256)
. F0 l3 [( ?+ z2 V" ^4 ?7 G2 Q# g
功能:' q4 l3 S0 q& V# p3 G' `: i) N6 R
返回从一个代币转换为另一个代币的预期数源码:
/**
@dev 返回从一个代币转换为另一个代币的预期数量5 I( b( z- E6 @
@param _fromToken ERC20 被转换的代币
@param _toToken ERC20 转换成的代币 D7 u0 L( r& f% I- _
@param _amount 转换的数量
@return 与其转换的数量1 z( t3 H. H# C! W- a( w- `! H
*/
function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256) {
require(_fromToken != _toToken); // 验证输入
// 基于当前代币转换
if (_toToken == token)5 @# j3 q% q! X; ?, G( G; y7 i2 Z9 m: t
return getPurchaseReturn(_fromToken, _amount);* Q6 \1 Y& O, x& N3 ?
else if (_fromToken == token)( ~' g% \' m) j2 D$ l. R' E1 W
return getSaleReturn(_toToken, _amount);9 J9 i" |$ k- l3 h' f; D+ C1 b
// 在两个连接器之间转换' O0 Q2 k6 q0 ~; w
uint256 purchaseReturnAmount = getPurchaseReturn(_fromToken, _amount);
return getSaleReturn(_toToken, purchaseReturnAmount, safeAdd(token.totalSupply(), purchaseReturnAmount));+ g3 o, x. m( g7 Z
}$ k# Y5 y/ ~6 p: T6 A F# d
/**
@dev 返回通过一个连接器代币购买代币的预期结果
@param _connectorToken 连接器代币协约地址% ?( O7 G) q, w0 `, s
@param _depositAmount 买入的数量3 ~4 w: U1 f2 C0 _
@return 预期的数量) o5 q& z+ Y( L
*/
function getPurchaseReturn(IERC20Token _connectorToken, uint256 _depositAmount)4 {( j# @! {4 W3 o
public
view, W( x {; V/ v$ o
active. n& C) S0 ~6 i; v
validConnector(_connectorToken)" v: o' S! W8 l p
returns (uint256)
{/ ]$ w6 q3 A6 B/ e; S3 S- Y( O
Connector storage connector = connectors[_connectorToken];
require(connector.isPurchaseEnabled); // validate input
uint256 tokenSupply = token.totalSupply();3 w4 x7 m& _9 ?$ d$ U" } `3 T8 w
uint256 connectorBalance = getConnectorBalance(_connectorToken);
uint256 amount = extensions.formula().calculatePurchaseReturn(tokenSupply, connectorBalance, connector.weight, _depositAmount);
// 扣除费用
uint256 feeAmount = getConversionFeeAmount(amount);, f* N4 \) r" l( O$ m5 ?
return safeSub(amount, feeAmount);
}
/**
@dev 返回通过一个连接器代币卖出代币的预期结果! A6 _5 x/ K! g5 y9 Y( U
@param _connectorToken 连接器代币协约地址4 D% x4 {1 o* S H$ h. b8 Y7 Q
@param _sellAmount 卖出的数量2 k1 R& b* _3 T, }" C
@return 预期得到的数量
*/
function getSaleReturn(IERC20Token _connectorToken, uint256 _sellAmount) public view returns (uint256) {
return getSaleReturn(_connectorToken, _sellAmount, token.totalSupply());, ^6 _' z. S9 t+ R
}
/**
@dev 工具,基于一个总供应量,返回基于一个连接器代币来卖掉代币的期待返回
@param _connectorToken 连接器代币协议地址
@param _sellAmount 销售的数量
@param _totalSupply 设置总供应量
@return 返回的数量
*/$ k5 T* n) w3 Z; m4 [9 n, i
function getSaleReturn(IERC20Token _connectorToken, uint256 _sellAmount, uint256 _totalSupply)
private( M! d( z! n2 D$ r) f
view, p9 v4 n( Z& L! d, K1 K$ k
active# z% w2 h7 h0 U4 i
validConnector(_connectorToken)
greaterThanZero(_totalSupply)
returns (uint256) v: y& l# B1 n% V; a% E; C
{
Connector storage connector = connectors[_connectorToken];+ r5 ^' J7 T; \, J& S
uint256 connectorBalance = getConnectorBalance(_connectorToken);# _" ] A3 ]2 t( v& K% u& H
uint256 amount = extensions.formula().calculateSaleReturn(_totalSupply, connectorBalance, connector.weight, _sellAmount);
// 从返回的数量中剪掉费用
uint256 feeAmount = getConversionFeeAmount(amount);
return safeSub(amount, feeAmount);
}
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
成为第一个吐槽的人