基于以太坊的交易所BANCOR算法实现-转换算法框架
V刘晨曦
发表于 2022-11-26 14:54:17
2326
1
0
若利用智能合约的强大而灵活的“资金流转控制”能力,在通证合约中控制着一定量的储备金,让通证与储备金之间拥有一定的兑换能力,那么Token的价值就可以储备金为锚定物,而不完全依赖于项目方。通证持有者也就不用承担项目失败或者项目方可能诈骗跑路的风险。, D4 ~; R; l$ ?+ R9 i
6 r8 g7 m" C r% }5 d) b
若通证与锚定物之间的兑换算法采用了Bancor算法,又符合ERC20标准,则被称为智能通证(Smart-Token) 。为了简单起见,以下的论述以ETH作为锚定物举例说明。购买与售卖Token的过程如下:
“购买者”发送一定量的ETH到Token合约地址,触发了合约代码自动执行"购买功能代码",获得对应数量的Token;“售卖者”发送一定量的Token到Token合约地址,触发了合约代码自动执行“售卖功能代码”,获得对应数量的ETH。1 G9 E4 A0 A5 S" s5 l
2 H$ h2 |+ K; k) `0 K
若AToken与BToken都是以ETH为锚定物的智能通证,那么Token持有者无需通过交易所,仅仅凭借智能合约提供的买卖与兑换功能,就能实现AToken与BToken的自由兑换,比如AToken–>ETH–>BToken,多种智能通证之间通过共同的锚定物串接起来,就形成了一个价值网络(Bancor Network)。
. b, L* X! f5 ^9 C8 b6 `
【核心智能合约简单描述】6 G! n! q. q& z
1,contract BancorConverter! J9 u- ~. [# R- u" n7 o- W9 a
功能说明:代币转换器,允许一个智能代币和其他代币之间的转换,ERC20连接器的余额可以是虚拟的,从而不需要依赖于真实的余额,这有助于避免在一个协约中有大量金额的风险。转换器可以升级。
2,ITokenConverter
功能说明:BancorConverter的父类接口之一,EIP228 Token Converter接口,用于智能代币的买卖和数量计算接口。
3,SmartTokenController
功能说明:BancorConverter的父类接口之一,智能代币管理器。智能代币管理器是一个可以升级的模块,从而允许更多功能和问题修复。当它接受了代币的所有权,它会成为代币的唯一管理器,执行各个功能。
4,Managed
功能说明: BancorConverter的父类之一,提供协议管理的支持。
5,IBancorConverterExtensions
功能说明:BancorConverter的公开变量类,bancor converter extensions 协议。能返回formula,gasPriceLimit,quickConverter等3类接口合约。1 J' M, G) l1 ~& c8 G! T
#4,核心函数分析0 Q" V& W7 g* l! l# H; K- G. s7 g
##4.1 convert(…)函数
convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256)功能: 将一定数量的_fromToken 转换为 _toToken;源码:1 Y; ~2 R' _; y r+ T- M3 d
/**$ K0 A/ J7 a8 P2 @4 v8 D
@dev 将一定数量的_fromToken 转换为 _toToken
@param _fromToken 用来转换ERC20代币
@param _toToken 被转换到的ERC20代币1 ^# L% c& s6 x$ w: }1 w
@param _amount 转换的数量,基于fromToken" l( p/ I% Q5 L- K
@param _minReturn 限制转换的结果需要高于minReturn,否则取消
@return conversion 返回数量' z6 _$ Q# i2 m2 _
*/
function convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256) {
convertPath = [_fromToken, token, _toToken];
return quickConvert(convertPath, _amount, _minReturn);
}# R- { E4 O. S" u6 P( l
/**$ _7 j+ r/ W% t$ u& n
@dev 通过之前定义的转换路径来转换代币8 |6 e: A7 K1 T
注意:当从ERC20代币进行转换,需要提前设置补贴% S9 K( b! C, w$ U
@param _path 转换路径) D! i( C2 o- U7 B( y3 ]
@param _amount 转换的数量
@param _minReturn 限制转换的结果需要高于minReturn,否则取消
@return 返回数量; o! s/ l0 c$ o
*/9 M7 F' `4 d `( `9 H. A+ N
quickConvertfunction quickConvert(IERC20Token[] _path, uint256 _amount, uint256 _minReturn)
public
payable3 c1 x! K4 z6 K$ O d
validConversionPath(_path)) T/ s* a6 l. h- D W- z% B
returns (uint256)7 C3 z) \* I3 \
{
return quickConvertPrioritized(_path, _amount, _minReturn, 0x0, 0x0, 0x0, 0x0, 0x0);" e* [ o6 i: {+ e4 H0 y" k
}
/**# H$ W' ^: E& s1 b( ^8 ]
@dev 通过之前定义的转换路径来转换代币
注意:当从ERC20代币进行转换,需要提前设置补贴
@param _path 转换路径
@param _amount 转换的数量
@param _minReturn 限制转换的结果需要高于minReturn,否则取消1 k- K& x9 K8 ?$ }3 @
@param _block 如果当前的区块超过了参数,则取消0 V! e& _$ i7 A1 ]0 t
@param _nonce 发送者地址的nonce
@param _v 通过交易签名提取
@param _r 通过交易签名提取
@param _s 通过交易签名提取/ ?1 B- f" a! W% a' j
@return 返回数量, }% d! ^% L+ x& P: P
*/
function quickConvertPrioritized(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, uint256 _block, uint256 _nonce, uint8 _v, bytes32 _r, bytes32 _s)% k6 ]! Z. C, R/ {# _
public9 O5 `& B% }- n, m
payable7 j4 h7 }) L) P2 V4 T4 p# r
validConversionPath(_path)
returns (uint256)
{6 C5 i8 X) g! T1 D2 ?9 Q8 N7 z6 v
IERC20Token fromToken = _path[0];
IBancorQuickConverter quickConverter = extensions.quickConverter();! t5 {- }6 j/ a* W2 X$ I
// 我们需要从调用者向快速转换着把源代币转化
// 因此他能基于调用者进行转换6 b$ y; f0 f& y& E& F
if (msg.value == 0) {% e/ f0 }9 X0 y+ L$ Z
// 如果不是ETH,把源代币发给快速调用者5 O. M8 }3 z5 N0 |1 J: _
// 如果是智能代币,不需要补贴 —— 销毁代币,然后发给快速转换者
if (fromToken == token) {' J3 [2 L9 k4 h; d" r
token.destroy(msg.sender, _amount); // 销毁调用者的_amount代币
token.issue(quickConverter, _amount); // 把_amount的新代币发给快速转换者) p2 Z" Q0 ` H# U) B
} else {
// 否则,我们假设有了补贴,发给快速转换者& C& h7 `% g0 T0 k6 U* N9 S0 B
assert(fromToken.transferFrom(msg.sender, quickConverter, _amount));
}
}
// 执行转换,把ETH转回
return quickConverter.convertForPrioritized.value(msg.value)(_path, _amount, _minReturn, msg.sender, _block, _nonce, _v, _r, _s);
}
##4.2 change(…)函数, p, W [6 ]6 g* {9 D
** function change**(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256)功能: 将一定数量的_fromToken 转换为 _toToken。弃用了,向后兼容。设计思路和源码结构挺好的代码:
( \2 t# E; k: M# Y& E! p
既然是过期代码,源代码就不放了。
##4.3 getReturn(…)函数8 @; i, C* o2 r" Z; \
7 F* v2 k9 y. D& E( H( X& | ^1 {
function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256)0 v( H# W: y; \" k) M& W: r
功能:
返回从一个代币转换为另一个代币的预期数源码:! P o3 n, \. Z f5 E, b* D' p% k
9 e i, C; j* u; y5 s& L2 k
/**
@dev 返回从一个代币转换为另一个代币的预期数量: i! b3 A7 U! n: l5 Q
@param _fromToken ERC20 被转换的代币
@param _toToken ERC20 转换成的代币8 \4 @, P7 Y+ p w- G! _) X
@param _amount 转换的数量( S7 x8 O* c. B* Y! J
@return 与其转换的数量
*/ |% j* x S8 l
function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256) {
require(_fromToken != _toToken); // 验证输入
// 基于当前代币转换
if (_toToken == token)7 {) i. A1 Y5 ~" u
return getPurchaseReturn(_fromToken, _amount); U, Q2 n9 p q, Y. m
else if (_fromToken == token)
return getSaleReturn(_toToken, _amount);
// 在两个连接器之间转换
uint256 purchaseReturnAmount = getPurchaseReturn(_fromToken, _amount);
return getSaleReturn(_toToken, purchaseReturnAmount, safeAdd(token.totalSupply(), purchaseReturnAmount));$ e: T7 c! K- G( v ]' a, ]) x
}
/**
@dev 返回通过一个连接器代币购买代币的预期结果
@param _connectorToken 连接器代币协约地址, ]& s$ o/ [4 J8 d0 Q
@param _depositAmount 买入的数量
@return 预期的数量% L7 Z7 T& n. j5 X4 F) ^
*/
function getPurchaseReturn(IERC20Token _connectorToken, uint256 _depositAmount)
public. y' g8 P6 u# _
view
active
validConnector(_connectorToken)4 ~+ `3 ~# ]. {; w; Q3 ^7 N
returns (uint256)
{5 A4 D" P% s# |+ c3 i
Connector storage connector = connectors[_connectorToken];
require(connector.isPurchaseEnabled); // validate input
uint256 tokenSupply = token.totalSupply();
uint256 connectorBalance = getConnectorBalance(_connectorToken);" X; L( L- \* D
uint256 amount = extensions.formula().calculatePurchaseReturn(tokenSupply, connectorBalance, connector.weight, _depositAmount);( S( n9 Q3 K. J+ u+ f5 M1 ^) p1 Z# G
// 扣除费用
uint256 feeAmount = getConversionFeeAmount(amount);' T* \# N, J0 L+ U# y/ e J/ c
return safeSub(amount, feeAmount);
}$ [- i" M; A0 O' `2 U
/**
@dev 返回通过一个连接器代币卖出代币的预期结果
@param _connectorToken 连接器代币协约地址
@param _sellAmount 卖出的数量: ~2 \6 G: J8 t9 d( \3 l
@return 预期得到的数量
*/
function getSaleReturn(IERC20Token _connectorToken, uint256 _sellAmount) public view returns (uint256) {
return getSaleReturn(_connectorToken, _sellAmount, token.totalSupply());+ R& r% W. W& l1 d) y* @+ }! F
}
/**
@dev 工具,基于一个总供应量,返回基于一个连接器代币来卖掉代币的期待返回
@param _connectorToken 连接器代币协议地址0 b/ c, t2 y' @" S7 a" K# d
@param _sellAmount 销售的数量
@param _totalSupply 设置总供应量
@return 返回的数量
*/
function getSaleReturn(IERC20Token _connectorToken, uint256 _sellAmount, uint256 _totalSupply)& ^' w: k+ k) O2 Y' z/ B
private/ X" I( i( S5 A& E
view
active
validConnector(_connectorToken)
greaterThanZero(_totalSupply)
returns (uint256)
{
Connector storage connector = connectors[_connectorToken]; S8 A7 `+ r4 Q/ z( G& O% b/ V
uint256 connectorBalance = getConnectorBalance(_connectorToken);
uint256 amount = extensions.formula().calculateSaleReturn(_totalSupply, connectorBalance, connector.weight, _sellAmount);' G8 g( j5 J c# o1 w. o1 p3 i" n' }
// 从返回的数量中剪掉费用
uint256 feeAmount = getConversionFeeAmount(amount);3 F! N& ]7 L& o$ a# `
return safeSub(amount, feeAmount);9 F1 j8 ?0 G6 R* m' Q
}
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
成为第一个吐槽的人