基于以太坊的交易所BANCOR算法实现-转换算法框架
V刘晨曦
发表于 2022-11-26 14:54:17
2405
1
0
+ k: E0 \2 ]& p7 L0 X) t" k; f5 L8 |
若利用智能合约的强大而灵活的“资金流转控制”能力,在通证合约中控制着一定量的储备金,让通证与储备金之间拥有一定的兑换能力,那么Token的价值就可以储备金为锚定物,而不完全依赖于项目方。通证持有者也就不用承担项目失败或者项目方可能诈骗跑路的风险。
) h k+ N* R, h3 P
若通证与锚定物之间的兑换算法采用了Bancor算法,又符合ERC20标准,则被称为智能通证(Smart-Token) 。为了简单起见,以下的论述以ETH作为锚定物举例说明。购买与售卖Token的过程如下:
“购买者”发送一定量的ETH到Token合约地址,触发了合约代码自动执行"购买功能代码",获得对应数量的Token;“售卖者”发送一定量的Token到Token合约地址,触发了合约代码自动执行“售卖功能代码”,获得对应数量的ETH。* Q& `" m- r `5 _0 a6 X3 y5 x
, N. n' I O; }4 h
若AToken与BToken都是以ETH为锚定物的智能通证,那么Token持有者无需通过交易所,仅仅凭借智能合约提供的买卖与兑换功能,就能实现AToken与BToken的自由兑换,比如AToken–>ETH–>BToken,多种智能通证之间通过共同的锚定物串接起来,就形成了一个价值网络(Bancor Network)。; P Q: B: C0 u* L
* h" [/ ]) T5 @1 x8 d) D5 E, ~
【核心智能合约简单描述】1 q0 |1 X- q* \. A* A1 O7 q
1,contract BancorConverter9 ?' v E+ N5 z' E
功能说明:代币转换器,允许一个智能代币和其他代币之间的转换,ERC20连接器的余额可以是虚拟的,从而不需要依赖于真实的余额,这有助于避免在一个协约中有大量金额的风险。转换器可以升级。% _. O& R1 s( g" L2 f, `
2,ITokenConverter
功能说明:BancorConverter的父类接口之一,EIP228 Token Converter接口,用于智能代币的买卖和数量计算接口。1 Q8 {9 z8 \- d
3,SmartTokenController
功能说明:BancorConverter的父类接口之一,智能代币管理器。智能代币管理器是一个可以升级的模块,从而允许更多功能和问题修复。当它接受了代币的所有权,它会成为代币的唯一管理器,执行各个功能。
4,Managed
功能说明: BancorConverter的父类之一,提供协议管理的支持。
5,IBancorConverterExtensions
功能说明:BancorConverter的公开变量类,bancor converter extensions 协议。能返回formula,gasPriceLimit,quickConverter等3类接口合约。
#4,核心函数分析+ h' e7 M+ o& Q1 @7 {+ \# _
##4.1 convert(…)函数# h! [: `/ w2 ]/ \7 X) R6 M8 N
convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256)功能: 将一定数量的_fromToken 转换为 _toToken;源码:
/**
@dev 将一定数量的_fromToken 转换为 _toToken( E9 v7 u) v( n% P( l
@param _fromToken 用来转换ERC20代币/ s ^* i% ?5 S: }+ S5 c
@param _toToken 被转换到的ERC20代币
@param _amount 转换的数量,基于fromToken4 N+ W/ }+ _9 b; N* i0 v& W7 n
@param _minReturn 限制转换的结果需要高于minReturn,否则取消
@return conversion 返回数量9 p8 c& t6 M5 M7 J, b4 N
*/0 m, q- [. j+ i1 d9 A9 @& E
function convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256) {8 H8 p$ x* x5 P3 f$ m
convertPath = [_fromToken, token, _toToken];
return quickConvert(convertPath, _amount, _minReturn);
}, o! V, \; V- _
/**0 B9 q% C2 ~/ o
@dev 通过之前定义的转换路径来转换代币! _9 o7 T2 {# ] p. O3 G/ D! w
注意:当从ERC20代币进行转换,需要提前设置补贴
@param _path 转换路径! g9 ^) o' M" }$ B; Y0 B
@param _amount 转换的数量
@param _minReturn 限制转换的结果需要高于minReturn,否则取消
@return 返回数量
*/: W6 c; B. V( Q
quickConvertfunction quickConvert(IERC20Token[] _path, uint256 _amount, uint256 _minReturn)
public
payable0 j- v. s, v: q& G4 R: l3 i
validConversionPath(_path)1 X1 Z6 d4 H1 {+ T- t$ R
returns (uint256)
{
return quickConvertPrioritized(_path, _amount, _minReturn, 0x0, 0x0, 0x0, 0x0, 0x0); l4 k0 ] p+ r% ^
}& |( q. T# h5 J* J; Q2 v
/**& X7 ?( q; k& r/ w+ K# y
@dev 通过之前定义的转换路径来转换代币
注意:当从ERC20代币进行转换,需要提前设置补贴5 i$ Y, b7 r6 L; P7 h% t1 m9 [; s
@param _path 转换路径
@param _amount 转换的数量% H D# y) z6 F; b+ P, k
@param _minReturn 限制转换的结果需要高于minReturn,否则取消- \* {; @: e" f0 B B5 M
@param _block 如果当前的区块超过了参数,则取消
@param _nonce 发送者地址的nonce3 X0 l, f& Q& M6 [
@param _v 通过交易签名提取
@param _r 通过交易签名提取+ ]) l( d" j) W3 f, U
@param _s 通过交易签名提取4 n( [) Q2 U i, D8 k9 ?$ H, P
@return 返回数量! Y- S& k0 s* {5 N& l
*/0 f }5 E' B3 Z& m9 s- f
function quickConvertPrioritized(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, uint256 _block, uint256 _nonce, uint8 _v, bytes32 _r, bytes32 _s)! K1 s) u3 _+ a, A5 o* O: D
public' S- b, b1 y# N. F
payable3 `+ J& g# o' w% S
validConversionPath(_path)
returns (uint256)! s: y Q' r; Y& \3 f+ \4 W- D
{: j6 e5 [6 e4 e: F8 e( Q
IERC20Token fromToken = _path[0];5 a! U8 V0 o. c- Q& G# u
IBancorQuickConverter quickConverter = extensions.quickConverter();
// 我们需要从调用者向快速转换着把源代币转化
// 因此他能基于调用者进行转换1 D) b7 ^- m' h& H1 u7 h. o
if (msg.value == 0) {/ w( u/ e8 m4 I9 b
// 如果不是ETH,把源代币发给快速调用者' h. o, e- A/ i$ b) V
// 如果是智能代币,不需要补贴 —— 销毁代币,然后发给快速转换者
if (fromToken == token) {4 E% M3 b/ t( h
token.destroy(msg.sender, _amount); // 销毁调用者的_amount代币
token.issue(quickConverter, _amount); // 把_amount的新代币发给快速转换者
} else {( h7 }3 H0 }# x# ?3 r3 a, q- F
// 否则,我们假设有了补贴,发给快速转换者* u, e' h- }8 H7 g7 [
assert(fromToken.transferFrom(msg.sender, quickConverter, _amount));6 A' @& x$ p" O8 w6 ?8 q. \
}4 \2 V: G' ]* y2 ]8 p0 `( J
}. N% G9 O% p8 V# S9 L" \3 _
// 执行转换,把ETH转回
return quickConverter.convertForPrioritized.value(msg.value)(_path, _amount, _minReturn, msg.sender, _block, _nonce, _v, _r, _s);
}
1 E' G6 h$ e" [
##4.2 change(…)函数
** function change**(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256)功能: 将一定数量的_fromToken 转换为 _toToken。弃用了,向后兼容。设计思路和源码结构挺好的代码:
( a3 w3 S. n' a& V: j! `9 l" Z
既然是过期代码,源代码就不放了。
##4.3 getReturn(…)函数
/ F% G/ S2 [+ `* c: t# I
function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256)
功能:9 D. u: S. x( o9 f
返回从一个代币转换为另一个代币的预期数源码:
/**
@dev 返回从一个代币转换为另一个代币的预期数量
@param _fromToken ERC20 被转换的代币% B& @2 }1 k- B2 |
@param _toToken ERC20 转换成的代币
@param _amount 转换的数量
@return 与其转换的数量, F1 M) s) E: D9 ~' M0 F. E/ ?
*/
function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256) {
require(_fromToken != _toToken); // 验证输入! u. X" b0 V" `# G( u) t/ Q* ]6 O
// 基于当前代币转换
if (_toToken == token)6 F/ `) \" U9 M" @, @
return getPurchaseReturn(_fromToken, _amount);
else if (_fromToken == token)
return getSaleReturn(_toToken, _amount);
// 在两个连接器之间转换1 z. B! ^0 G3 B$ C/ {6 h8 P
uint256 purchaseReturnAmount = getPurchaseReturn(_fromToken, _amount); D2 i, y/ Q; n+ Q6 A
return getSaleReturn(_toToken, purchaseReturnAmount, safeAdd(token.totalSupply(), purchaseReturnAmount));* T" {) W# E: R3 T" M. N0 S
}
/**
@dev 返回通过一个连接器代币购买代币的预期结果
@param _connectorToken 连接器代币协约地址
@param _depositAmount 买入的数量0 `5 O* }' [3 o; Y( u
@return 预期的数量0 m& _8 R+ Z4 g5 D5 Q/ t; y9 G2 I2 r, B& h
*/3 }0 j$ Z+ } }" F& J
function getPurchaseReturn(IERC20Token _connectorToken, uint256 _depositAmount)
public
view
active4 ^; T; ?# ^' x8 n! c# D8 {
validConnector(_connectorToken)" {& Y% g5 j$ W# K9 x3 g& z( G
returns (uint256)
{1 ^; K- c) C0 x& y
Connector storage connector = connectors[_connectorToken];4 D# ^& X8 ?* z: W8 c9 p7 ^
require(connector.isPurchaseEnabled); // validate input* A5 y4 ~. h, m' d* E* v7 s
uint256 tokenSupply = token.totalSupply();
uint256 connectorBalance = getConnectorBalance(_connectorToken);
uint256 amount = extensions.formula().calculatePurchaseReturn(tokenSupply, connectorBalance, connector.weight, _depositAmount);
// 扣除费用
uint256 feeAmount = getConversionFeeAmount(amount);
return safeSub(amount, feeAmount);2 _) V I/ Z- |, u/ Y$ s
}
/**: U3 | d5 P* O) }7 O; c
@dev 返回通过一个连接器代币卖出代币的预期结果' C( ^% H* N* {9 J( K- \
@param _connectorToken 连接器代币协约地址
@param _sellAmount 卖出的数量
@return 预期得到的数量
*/
function getSaleReturn(IERC20Token _connectorToken, uint256 _sellAmount) public view returns (uint256) {$ M* B% b) _+ G; N- o
return getSaleReturn(_connectorToken, _sellAmount, token.totalSupply());
}
/**
@dev 工具,基于一个总供应量,返回基于一个连接器代币来卖掉代币的期待返回
@param _connectorToken 连接器代币协议地址9 s3 H5 J! M8 {% ?/ `) @7 U
@param _sellAmount 销售的数量4 ~, C2 ?' H3 `9 ~) y# h
@param _totalSupply 设置总供应量
@return 返回的数量
*/
function getSaleReturn(IERC20Token _connectorToken, uint256 _sellAmount, uint256 _totalSupply)
private" `& X8 ^7 T- s. ~, Y; _! Z( y
view( E) w% |- Z* T$ s/ X+ D d G: w
active
validConnector(_connectorToken)
greaterThanZero(_totalSupply)
returns (uint256)+ H" Q [1 Z$ i* T2 Q" ?
{
Connector storage connector = connectors[_connectorToken];9 }9 G6 {' {! q# M
uint256 connectorBalance = getConnectorBalance(_connectorToken);! h9 E1 B# z- V9 Y3 a
uint256 amount = extensions.formula().calculateSaleReturn(_totalSupply, connectorBalance, connector.weight, _sellAmount);' z8 @4 L+ {8 i t
// 从返回的数量中剪掉费用
uint256 feeAmount = getConversionFeeAmount(amount);
return safeSub(amount, feeAmount);" y# t3 o/ _: y( v4 o
}
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
成为第一个吐槽的人