基于以太坊的交易所BANCOR算法实现-转换算法框架
V刘晨曦
发表于 2022-11-26 14:54:17
2406
1
0
6 ?+ M& C8 K& U8 {" E& f) j2 `( z6 c
若利用智能合约的强大而灵活的“资金流转控制”能力,在通证合约中控制着一定量的储备金,让通证与储备金之间拥有一定的兑换能力,那么Token的价值就可以储备金为锚定物,而不完全依赖于项目方。通证持有者也就不用承担项目失败或者项目方可能诈骗跑路的风险。
$ q7 P" `6 a6 H4 m( R
若通证与锚定物之间的兑换算法采用了Bancor算法,又符合ERC20标准,则被称为智能通证(Smart-Token) 。为了简单起见,以下的论述以ETH作为锚定物举例说明。购买与售卖Token的过程如下:# k. a" K5 l# G: w$ N3 w9 F% x
“购买者”发送一定量的ETH到Token合约地址,触发了合约代码自动执行"购买功能代码",获得对应数量的Token;“售卖者”发送一定量的Token到Token合约地址,触发了合约代码自动执行“售卖功能代码”,获得对应数量的ETH。: J# Y/ B4 Y$ _
0 o$ D0 n# y0 }
若AToken与BToken都是以ETH为锚定物的智能通证,那么Token持有者无需通过交易所,仅仅凭借智能合约提供的买卖与兑换功能,就能实现AToken与BToken的自由兑换,比如AToken–>ETH–>BToken,多种智能通证之间通过共同的锚定物串接起来,就形成了一个价值网络(Bancor Network)。
【核心智能合约简单描述】% \; T; K) d# J6 G) b9 t. P
1,contract BancorConverter
功能说明:代币转换器,允许一个智能代币和其他代币之间的转换,ERC20连接器的余额可以是虚拟的,从而不需要依赖于真实的余额,这有助于避免在一个协约中有大量金额的风险。转换器可以升级。, `, e/ e$ j( Q; c l" \5 w
2,ITokenConverter
功能说明:BancorConverter的父类接口之一,EIP228 Token Converter接口,用于智能代币的买卖和数量计算接口。
3,SmartTokenController R& d8 E5 k% V& J) }( [
功能说明:BancorConverter的父类接口之一,智能代币管理器。智能代币管理器是一个可以升级的模块,从而允许更多功能和问题修复。当它接受了代币的所有权,它会成为代币的唯一管理器,执行各个功能。# g' ?+ f0 l) l% O' _7 U
4,Managed
功能说明: BancorConverter的父类之一,提供协议管理的支持。
5,IBancorConverterExtensions) G; C( N) q: l3 V! E
功能说明:BancorConverter的公开变量类,bancor converter extensions 协议。能返回formula,gasPriceLimit,quickConverter等3类接口合约。 ~% T N9 W8 X; _' E! {/ p2 r$ D
#4,核心函数分析
##4.1 convert(…)函数' N3 [$ d% o# ~' g
convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256)功能: 将一定数量的_fromToken 转换为 _toToken;源码:, R# e; `: _. b' q. Y x6 A4 Z
/**
@dev 将一定数量的_fromToken 转换为 _toToken
@param _fromToken 用来转换ERC20代币
@param _toToken 被转换到的ERC20代币2 W: E& R5 l1 d/ U) s
@param _amount 转换的数量,基于fromToken5 \9 I0 r- W$ U; c6 c# S8 W4 A
@param _minReturn 限制转换的结果需要高于minReturn,否则取消8 N) O0 d: }( S' ^, p
@return conversion 返回数量 y( q4 {! {6 E4 `+ D1 q
*/- a, e8 }% F+ @
function convert(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256) {
convertPath = [_fromToken, token, _toToken];
return quickConvert(convertPath, _amount, _minReturn);
}
/**/ t* V( M% h- m8 ~8 J7 \# e S
@dev 通过之前定义的转换路径来转换代币# p7 j; M P" o q, B0 C; k, o
注意:当从ERC20代币进行转换,需要提前设置补贴% N& \: u* A- m
@param _path 转换路径" A2 h% K7 g/ i5 w6 n2 f- ?
@param _amount 转换的数量' q% R# K4 w* E. a1 `$ d
@param _minReturn 限制转换的结果需要高于minReturn,否则取消
@return 返回数量
*/
quickConvertfunction quickConvert(IERC20Token[] _path, uint256 _amount, uint256 _minReturn)
public: D% t. x; A. x; A
payable# `: `: R v ]
validConversionPath(_path)
returns (uint256)$ `4 s' N6 z9 K S' z
{
return quickConvertPrioritized(_path, _amount, _minReturn, 0x0, 0x0, 0x0, 0x0, 0x0);
}
/**' q5 n% C. v* B
@dev 通过之前定义的转换路径来转换代币 G% Z- M3 P1 u3 f5 z$ s
注意:当从ERC20代币进行转换,需要提前设置补贴
@param _path 转换路径
@param _amount 转换的数量) g% T4 J% @0 H4 E! T
@param _minReturn 限制转换的结果需要高于minReturn,否则取消
@param _block 如果当前的区块超过了参数,则取消
@param _nonce 发送者地址的nonce
@param _v 通过交易签名提取
@param _r 通过交易签名提取
@param _s 通过交易签名提取4 t: K" Z, M X% B
@return 返回数量1 D& d/ W+ p3 |/ }& m8 ], D
*/! ^5 u5 A2 W! E* k/ c; K$ }/ F
function quickConvertPrioritized(IERC20Token[] _path, uint256 _amount, uint256 _minReturn, uint256 _block, uint256 _nonce, uint8 _v, bytes32 _r, bytes32 _s)
public' T" [% c! X! Z; D8 M* F- \
payable
validConversionPath(_path)2 K2 K' O( A- D R: O
returns (uint256)
{! m) Z6 N& p$ W5 O5 |1 k
IERC20Token fromToken = _path[0];: P" y& Y. G; N# s
IBancorQuickConverter quickConverter = extensions.quickConverter();8 J7 Z, T E3 P6 a5 c
// 我们需要从调用者向快速转换着把源代币转化
// 因此他能基于调用者进行转换% c2 E2 u4 p \% n" F0 D7 ^
if (msg.value == 0) {
// 如果不是ETH,把源代币发给快速调用者
// 如果是智能代币,不需要补贴 —— 销毁代币,然后发给快速转换者
if (fromToken == token) {
token.destroy(msg.sender, _amount); // 销毁调用者的_amount代币, c8 j0 m/ C7 z" J( D: u2 X5 a
token.issue(quickConverter, _amount); // 把_amount的新代币发给快速转换者$ L4 b# ^* F9 h1 v* {" D! x
} else {
// 否则,我们假设有了补贴,发给快速转换者
assert(fromToken.transferFrom(msg.sender, quickConverter, _amount));
}) \' _- {, t& U2 i( I* x0 w3 D
}' j. A/ E+ o0 t( Z* H" c
// 执行转换,把ETH转回
return quickConverter.convertForPrioritized.value(msg.value)(_path, _amount, _minReturn, msg.sender, _block, _nonce, _v, _r, _s);2 L% w" }5 B7 e6 H2 Y K6 ^
}# I6 ^# T7 Y8 ?; G! e
##4.2 change(…)函数7 V2 I( M3 w# T" W! D
** function change**(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount, uint256 _minReturn) public returns (uint256)功能: 将一定数量的_fromToken 转换为 _toToken。弃用了,向后兼容。设计思路和源码结构挺好的代码:/ g, l, b; c6 L6 c1 A! @! U
既然是过期代码,源代码就不放了。
##4.3 getReturn(…)函数 R3 O: H3 B; v7 D2 h p
function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256)4 K: I! C+ p% l$ F' C% W- n
3 O7 ^! L! R$ F, X8 W) J
功能:
返回从一个代币转换为另一个代币的预期数源码:. H, T0 E" Y+ g. H6 ]9 y& u
/**
@dev 返回从一个代币转换为另一个代币的预期数量) S ]* m! ?; |: ^: g! g
@param _fromToken ERC20 被转换的代币
@param _toToken ERC20 转换成的代币
@param _amount 转换的数量( h$ f v* @, i- V
@return 与其转换的数量) L- I/ C' P3 U6 a [
*/
function getReturn(IERC20Token _fromToken, IERC20Token _toToken, uint256 _amount) public view returns (uint256) {
require(_fromToken != _toToken); // 验证输入+ \* o; [7 Q/ A- q/ S
// 基于当前代币转换
if (_toToken == token)" f1 B( c# k9 V
return getPurchaseReturn(_fromToken, _amount);8 d" l( }9 B5 V' o/ z) E
else if (_fromToken == token)
return getSaleReturn(_toToken, _amount);
// 在两个连接器之间转换
uint256 purchaseReturnAmount = getPurchaseReturn(_fromToken, _amount);, I" t+ H' |3 X1 h7 {% G
return getSaleReturn(_toToken, purchaseReturnAmount, safeAdd(token.totalSupply(), purchaseReturnAmount));4 Y: n4 x; R; D7 s
}
/**- t5 y3 J2 o9 f. A5 \7 U
@dev 返回通过一个连接器代币购买代币的预期结果, F R L, D, O" q
@param _connectorToken 连接器代币协约地址0 i/ v4 w0 U. [$ J% |4 W8 `/ I( R# }
@param _depositAmount 买入的数量
@return 预期的数量
*/
function getPurchaseReturn(IERC20Token _connectorToken, uint256 _depositAmount)
public
view- Z9 f$ v& P- s# a. g
active
validConnector(_connectorToken)' K4 q8 G8 ~; G# t0 b( I
returns (uint256). ?' V$ L5 T; ]+ y/ S: n
{5 B9 t2 M0 G0 U i
Connector storage connector = connectors[_connectorToken];
require(connector.isPurchaseEnabled); // validate input2 A" V! t* ^2 c5 i$ A; H. ?% M
uint256 tokenSupply = token.totalSupply();
uint256 connectorBalance = getConnectorBalance(_connectorToken);. ?% ~% F3 b# ~* y9 e0 W u5 }. n
uint256 amount = extensions.formula().calculatePurchaseReturn(tokenSupply, connectorBalance, connector.weight, _depositAmount);
// 扣除费用2 z3 w& z- m- u6 ? V' u1 E
uint256 feeAmount = getConversionFeeAmount(amount);
return safeSub(amount, feeAmount);
}
/**
@dev 返回通过一个连接器代币卖出代币的预期结果! E& T. M- L- [# A- I7 G
@param _connectorToken 连接器代币协约地址0 A' f! w* L! @+ H) K- e( A
@param _sellAmount 卖出的数量
@return 预期得到的数量! i4 P. y6 V0 K+ E2 U
*/. O# A7 t+ U4 b: \4 B, ^5 _& t$ h9 _
function getSaleReturn(IERC20Token _connectorToken, uint256 _sellAmount) public view returns (uint256) {5 F- M& ]0 d6 ?: K I9 u
return getSaleReturn(_connectorToken, _sellAmount, token.totalSupply());: n, t K, p- v! M3 y7 B
}
/**
@dev 工具,基于一个总供应量,返回基于一个连接器代币来卖掉代币的期待返回
@param _connectorToken 连接器代币协议地址
@param _sellAmount 销售的数量
@param _totalSupply 设置总供应量
@return 返回的数量) {8 S1 W9 M7 K! X/ j
*/2 `- p! u/ [5 ~6 i0 n1 u( s
function getSaleReturn(IERC20Token _connectorToken, uint256 _sellAmount, uint256 _totalSupply)
private
view
active! T2 U* W# b+ F+ M% F+ u" i
validConnector(_connectorToken)1 m0 n; _' }9 I. M- P/ E* b' u
greaterThanZero(_totalSupply)
returns (uint256)
{
Connector storage connector = connectors[_connectorToken];
uint256 connectorBalance = getConnectorBalance(_connectorToken);, O3 r( N2 c+ ?& j
uint256 amount = extensions.formula().calculateSaleReturn(_totalSupply, connectorBalance, connector.weight, _sellAmount);$ D `4 V, ?8 F+ m1 }3 K
// 从返回的数量中剪掉费用
uint256 feeAmount = getConversionFeeAmount(amount);! ?2 r; S$ p+ d9 w
return safeSub(amount, feeAmount);
}
BitMere.com 比特池塘系信息发布平台,比特池塘仅提供信息存储空间服务。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
声明:该文观点仅代表作者本人,本文不代表比特池塘立场,且不构成建议,请谨慎对待。
成为第一个吐槽的人