基于以太坊的交易所BANCOR算法实现-转换算法框架
V刘晨曦
发表于 2022-11-26 14:54:17
2404
1
0
) W- m. `# G7 A) ~2 L% j. \
若利用智能合约的强大而灵活的“资金流转控制”能力,在通证合约中控制着一定量的储备金,让通证与储备金之间拥有一定的兑换能力,那么Token的价值就可以储备金为锚定物,而不完全依赖于项目方。通证持有者也就不用承担项目失败或者项目方可能诈骗跑路的风险。
若通证与锚定物之间的兑换算法采用了Bancor算法,又符合ERC20标准,则被称为智能通证(Smart-Token) 。为了简单起见,以下的论述以ETH作为锚定物举例说明。购买与售卖Token的过程如下:
“购买者”发送一定量的ETH到Token合约地址,触发了合约代码自动执行"购买功能代码",获得对应数量的Token;“售卖者”发送一定量的Token到Token合约地址,触发了合约代码自动执行“售卖功能代码”,获得对应数量的ETH。
若AToken与BToken都是以ETH为锚定物的智能通证,那么Token持有者无需通过交易所,仅仅凭借智能合约提供的买卖与兑换功能,就能实现AToken与BToken的自由兑换,比如AToken–>ETH–>BToken,多种智能通证之间通过共同的锚定物串接起来,就形成了一个价值网络(Bancor Network)。
- N1 K% K. q3 q1 L
【核心智能合约简单描述】# B3 I% L6 Q% n! E. C* Y
1,contract BancorConverter) J! T, ]/ [5 I, D- E3 B' Y* U
功能说明:代币转换器,允许一个智能代币和其他代币之间的转换,ERC20连接器的余额可以是虚拟的,从而不需要依赖于真实的余额,这有助于避免在一个协约中有大量金额的风险。转换器可以升级。
2,ITokenConverter7 D } k2 n1 T) b, M
功能说明:BancorConverter的父类接口之一,EIP228 Token Converter接口,用于智能代币的买卖和数量计算接口。
3,SmartTokenController: P: U1 ] r+ j- T( [
功能说明:BancorConverter的父类接口之一,智能代币管理器。智能代币管理器是一个可以升级的模块,从而允许更多功能和问题修复。当它接受了代币的所有权,它会成为代币的唯一管理器,执行各个功能。9 I: C% u8 @4 S- I& q3 r
4,Managed: `. V. U4 j7 O+ V1 V) S
功能说明: BancorConverter的父类之一,提供协议管理的支持。
5,IBancorConverterExtensions! B/ \7 o; O' C5 l; P; h% y
功能说明:BancorConverter的公开变量类,bancor converter extensions 协议。能返回formula,gasPriceLimit,quickConverter等3类接口合约。
#4,核心函数分析# n+ Q9 P" X8 Y! U( G0 i, {9 X
##4.1 convert(…)函数6 `9 {) q& C( Z6 D8 o
convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256)功能: 将一定数量的_fromToken 转换为 _toToken;源码:4 l4 C( d, {/ S# m
. O; Z6 a2 ^9 k6 A6 Z6 j( s
/**( O9 B; E# p4 C# H$ n% s# N- m1 m
@dev 将一定数量的_fromToken 转换为 _toToken
@param _fromToken 用来转换ERC20代币' u8 ~' W6 ^: u* Z- _
@param _toToken 被转换到的ERC20代币
@param _amount 转换的数量,基于fromToken/ L9 P! Q) w& B, H1 s2 }% ~. D8 w
@param _minReturn 限制转换的结果需要高于minReturn,否则取消/ b* I3 C+ Y, M9 S
@return conversion 返回数量
*/
function convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256) {& L" ^% M. z* G) S. s" g
convertPath = [_fromToken, token, _toToken];
return quickConvert(convertPath, _amount, _minReturn);0 ^' X" S! ]; y5 s5 L
}
/**
@dev 通过之前定义的转换路径来转换代币8 K0 w4 E4 H4 I" o9 u; f4 o& ~ o
注意:当从ERC20代币进行转换,需要提前设置补贴
@param _path 转换路径
@param _amount 转换的数量
@param _minReturn 限制转换的结果需要高于minReturn,否则取消
@return 返回数量+ M! [+ s7 m$ H# V4 B m3 D8 y4 B. J
*/( }. E# T$ ]; c5 H) p% ]/ n
quickConvertfunction quickConvert(IERC20Token[] _path, uint256 _amount, uint256 _minReturn)
public" d2 n" ~2 { z$ f9 j, v# ?/ R: _
payable. Q( S, `+ k- M( t, R$ a# a A
validConversionPath(_path)
returns (uint256)
{; V9 ?1 @ \8 _/ u3 C& l
return quickConvertPrioritized(_path, _amount, _minReturn, 0x0, 0x0, 0x0, 0x0, 0x0);
}$ ]6 \1 d0 o5 r& _/ Z& |) i$ w
/**3 X( y+ _& F6 v! Y/ C% O& I* }
@dev 通过之前定义的转换路径来转换代币6 t: R) m' H* X; [4 l y9 i, e0 | L
注意:当从ERC20代币进行转换,需要提前设置补贴
@param _path 转换路径
@param _amount 转换的数量$ }8 F# U/ b" y8 w1 S
@param _minReturn 限制转换的结果需要高于minReturn,否则取消
@param _block 如果当前的区块超过了参数,则取消$ s; w; N4 R7 [1 l
@param _nonce 发送者地址的nonce
@param _v 通过交易签名提取* u7 f/ {/ o% h& l
@param _r 通过交易签名提取3 Z% \( G; g' p
@param _s 通过交易签名提取
@return 返回数量
*/
function quickConvertPrioritized(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, uint256 _block, uint256 _nonce, uint8 _v, bytes32 _r, bytes32 _s)
public+ D8 r& r" K) L8 Z) p
payable# c: b8 s, G! c: t; F
validConversionPath(_path)
returns (uint256)1 M$ V0 |* }: g/ L: Y* ^7 a
{
IERC20Token fromToken = _path[0];" c. P8 b9 W: ^0 `3 N
IBancorQuickConverter quickConverter = extensions.quickConverter();
// 我们需要从调用者向快速转换着把源代币转化- O* r3 _1 A* Q A4 m5 e/ z
// 因此他能基于调用者进行转换
if (msg.value == 0) {; h! r* L$ ?" `5 V4 T) C
// 如果不是ETH,把源代币发给快速调用者
// 如果是智能代币,不需要补贴 —— 销毁代币,然后发给快速转换者! G7 v& @; D6 e" I. k1 n
if (fromToken == token) {+ S1 q' M2 E6 M/ y- f$ s
token.destroy(msg.sender, _amount); // 销毁调用者的_amount代币
token.issue(quickConverter, _amount); // 把_amount的新代币发给快速转换者8 `2 q1 _% Z8 k1 \" Z6 Z
} else {
// 否则,我们假设有了补贴,发给快速转换者
assert(fromToken.transferFrom(msg.sender, quickConverter, _amount));
}" A8 X: W2 Y) B6 `
}
// 执行转换,把ETH转回
return quickConverter.convertForPrioritized.value(msg.value)(_path, _amount, _minReturn, msg.sender, _block, _nonce, _v, _r, _s);; o( A5 h1 `( I( z H, {
}
p) \; H' a# }6 m
##4.2 change(…)函数& e( d/ f) n1 H. {7 Q$ ]
** function change**(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256)功能: 将一定数量的_fromToken 转换为 _toToken。弃用了,向后兼容。设计思路和源码结构挺好的代码:
1 n& c- M3 E: c! m5 B: E
既然是过期代码,源代码就不放了。
##4.3 getReturn(…)函数' ]$ G5 N5 L3 g5 z4 P! f
5 H4 _: K6 }! F# C
function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256) I& V+ ]6 P7 ]* e) ^
. n% V% s& A4 E( }. l+ A
功能:
返回从一个代币转换为另一个代币的预期数源码:
/**: w6 ^3 t6 [" V: F
@dev 返回从一个代币转换为另一个代币的预期数量
@param _fromToken ERC20 被转换的代币
@param _toToken ERC20 转换成的代币1 M0 n' Q8 a- g" ] h: ^" F. w+ x3 C5 n" F
@param _amount 转换的数量
@return 与其转换的数量3 w& \& ~8 N& ], B% q! g8 D
*/4 u. j3 F9 |: a& T+ s: q
function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256) {" o; ]5 M+ V# E" ]
require(_fromToken != _toToken); // 验证输入
// 基于当前代币转换% V/ t/ \- ?# e% `! y8 q) h
if (_toToken == token)
return getPurchaseReturn(_fromToken, _amount);; Q* O" ?: e( z
else if (_fromToken == token)
return getSaleReturn(_toToken, _amount);
// 在两个连接器之间转换
uint256 purchaseReturnAmount = getPurchaseReturn(_fromToken, _amount);
return getSaleReturn(_toToken, purchaseReturnAmount, safeAdd(token.totalSupply(), purchaseReturnAmount));6 U" F2 j% o+ l1 k: W
}
/**
@dev 返回通过一个连接器代币购买代币的预期结果
@param _connectorToken 连接器代币协约地址
@param _depositAmount 买入的数量
@return 预期的数量; ]: D0 n5 g% h$ z2 G
*// o8 ]2 p0 V, N) N* H U9 ]) T' P
function getPurchaseReturn(IERC20Token _connectorToken, uint256 _depositAmount)% L6 x& Z1 K+ o( m
public
view: p! y# {: r" m+ j8 q
active
validConnector(_connectorToken)
returns (uint256)* q* N& C8 C% X/ D V
{
Connector storage connector = connectors[_connectorToken];/ ]% k+ h2 {' n4 G* F- E! ~
require(connector.isPurchaseEnabled); // validate input
uint256 tokenSupply = token.totalSupply();$ ^; Q# v; p0 e9 W; z3 m
uint256 connectorBalance = getConnectorBalance(_connectorToken);
uint256 amount = extensions.formula().calculatePurchaseReturn(tokenSupply, connectorBalance, connector.weight, _depositAmount);
// 扣除费用) q7 ^3 `; I3 R. C G% T9 U
uint256 feeAmount = getConversionFeeAmount(amount);) j2 u5 ]# H0 N+ B* Q" D
return safeSub(amount, feeAmount);$ p0 Q" k. ^* _5 D
}/ c$ n) W# k3 J# e1 Y' z; q! O$ h
/**
@dev 返回通过一个连接器代币卖出代币的预期结果
@param _connectorToken 连接器代币协约地址6 r" W+ _& W& E3 j$ v! O+ L O
@param _sellAmount 卖出的数量% h5 u" q& L9 H8 K! |2 Q
@return 预期得到的数量3 t7 p' o" e1 y" m$ @
*/
function getSaleReturn(IERC20Token _connectorToken, uint256 _sellAmount) public view returns (uint256) {
return getSaleReturn(_connectorToken, _sellAmount, token.totalSupply());+ h; [$ {& W- l" X8 k
}6 B4 ]& M! z& Y9 o. ~
/**
@dev 工具,基于一个总供应量,返回基于一个连接器代币来卖掉代币的期待返回( i, d- N/ v+ j1 i
@param _connectorToken 连接器代币协议地址
@param _sellAmount 销售的数量
@param _totalSupply 设置总供应量
@return 返回的数量: d+ }: y8 U6 j8 C% ~5 o, I( V. H3 ^
*/: m" k2 }. X( X9 i$ |9 ?
function getSaleReturn(IERC20Token _connectorToken, uint256 _sellAmount, uint256 _totalSupply)* e; m4 j- W7 S5 j E3 r
private( K g$ r {" M2 @: `/ ^) u
view
active$ P$ a: f" f$ l4 {5 w9 s3 K
validConnector(_connectorToken)+ X7 a- C( P- ?
greaterThanZero(_totalSupply)* O J: x% M9 _, ~+ g, p9 K. z
returns (uint256)3 O+ I; K0 S( }5 S
{
Connector storage connector = connectors[_connectorToken];! y Z! M9 O: u7 [! O# r
uint256 connectorBalance = getConnectorBalance(_connectorToken);$ A5 T* y- z6 b8 W, t( [
uint256 amount = extensions.formula().calculateSaleReturn(_totalSupply, connectorBalance, connector.weight, _sellAmount);
// 从返回的数量中剪掉费用3 F1 C8 C0 A3 d- E2 x
uint256 feeAmount = getConversionFeeAmount(amount);3 I# u% @# B# D( d
return safeSub(amount, feeAmount);$ [; ]& h. M4 Q% z9 u
}
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
成为第一个吐槽的人