流动性参数结构
通过上图我们可以了解到,要确定申请曲线的几个参数有:申请下价格、申请上价格、申请量、一个额外的参数kk去确定曲线的形状。关于kk的更详细信息,请参考闪兑算法的详细信息。
要确定请求曲线,我们需要四个参数:请求下价格、请求上价格、请求量和请求kk。因此,一种token所需的所有参数如下:
申请下价格、申请上价格、k申请、申请量
请求下价格、申请上价格、k请求、请求量
为了节约gas消耗,我们单独存储价格的小数部分。
类似地,数量的小数部分也可以单独存储。
在新版本中,我们引入了一个midPrice和一个名为swapFee的交易手续费参数。swapFee由交易发起人设定的lpFee和Dodo的mtFee组成。swapFee = lpFee + mtFee,类似于DODO V2。swapFee以百分比表示。 因此,申请上价格和请求下价格是基于midPrice和swapFee自动生成的。
计算公式如下:
swapSpread = midPrice * swapFee
申请上价格 = midPrice - swapSpread
请求下价格 = midPrice + swapSpread
为了优化存储,我们将请求上价格和midPrice之间的偏移量作为百分比进行参考,称为请求上比率。同样地,将申请下价格的偏移量作为百分比进行参考。因此,我们有请求上价格和申请下价格的转换公式。
申请上价格 = midPrice * (1 + 请求上比率)
申请下价格 = midPrice * ( 1 -请求下比例)
在这个结构中,mid价格的默认小数为18。例如,当前价格为1642,那么mid价格=1642,mid价格的小数=18;如果当前价格为0.0015,那么mid价格=15,mid价格的小数=14。
因此,曲线的两个参数都需要两个:
price 参数: mid价格,mid价格的小数,lp费率,请求比率,申请下比率
amount 参数: 请求量,申请量
curve 参数: 请求k,申请k
其中,lpFee费率、请求比率和申请下比率的单位为10000,表示如果lpFee费率=1000,则相当于lpFee费率=10%。
请求量和申请量的单位默认都设置为18。
setNSPriceSlot#
最高效的设置多个非稳定币代币价格的方法是将相应的价格集合打包到一个单独的插槽中。在合约中,只需覆盖插槽数据即可。插槽索引可以根据上述方法计算得出。在设置时,合约不会拆分和检查数据,因此SP需要仔细确定插槽数据(在交换期间恢复任何错误数据)。
例如,如果我们想要重置 WBTC 和 WETH 的价格,由于 WBTC 和 WETH 都属于价格存储中的非稳定代币,它们位于插槽 0 中。因此,我们需要调用 setNSPriceSlot() 方法,并将 [0] 作为第一个参数传入。第二个参数应通过使用 getNSTokenInfo() 检索插槽 0 中位置 0 处的当前价格数据,然后用新的 WBTC 和 WETH 价格进行覆盖。
至于第三个参数,它也应通过 getNSTokenInfo() 获取,首先检索 allFlag,然后将其二进制表示的第一个位(对应 WBTC)和第三个位(对应 WETH)设置为 0,因为它们的原始索引分别为 1 和 3。
/// @notice user set PriceListInfo.tokenPriceStable price info, only for stable coin
/// @param slotIndex tokenPriceStable index
/// @param priceSlots tokenPriceNS price info, every data has packed all 3 token price info
/// @param newAllFlag maker update token cumulative status,
/// for allFlag, tokenOriIndex represent bit index in allFlag. eg: tokenA has origin index 3, that means (allFlag >> 3) & 1 = token3's flag
/// flag = 0 means to reset cumulative. flag = 1 means not to reset cumulative.
/// @dev maker should be responsible for data availability
function setNSPriceSlot(
uint256[] calldata slotIndex,
uint256[] calldata priceSlots,
uint256 newAllFlag
) external onlyOwner {
setStablePriceSlot#
用于覆盖稳定币插槽的功能,类似于 setNSPriceSlot。
/// @notice user set PriceListInfo.tokenPriceStable price info, only for stable coin
/// @param slotIndex tokenPriceStable index
/// @param priceSlots tokenPriceStable price info, every data has packed all 3 token price info
/// @param newAllFlag maker update token cumulative status,
/// for allFlag, tokenOriIndex represent bit index in allFlag. eg: tokenA has origin index 3, that means (allFlag >> 3) & 1 = token3's flag
/// flag = 0 means to reset cumulative. flag = 1 means not to reset cumulative.
/// @dev maker should be responsible for data availability
function setStablePriceSlot(
uint256[] calldata slotIndex,
uint256[] calldata priceSlots,
uint256 newAllFlag
) external onlyOwner {
setTokensAmounts#
通过传入一个包含多个代币及其相应 amountSet 数据的数组,设置多个代币的数量信息。
例如:(正在更新)
/// @notice set token Amounts
/// @param tokens token address set
/// @param tokenAmounts token amounts set, each number pack one token all amounts.Each format is the same with amountSetAndK
/// [ask amounts(16) | ask amounts decimal(8) | bid amounts(16) | bid amounts decimal(8) ] = one slot could contains 4 token info
function setTokensAmounts(
address[] calldata tokens,
uint64[] calldata tokenAmounts
) public onlyOwner
setTokensKs#
设置多个代币的 k 数据,并将其写入 kAsk 和 kBid。将相同的 kAsk 和 kBid 结合为一个 32 位的数据结构:[kAsk (16) | kBid (16)]。
示例:(正在更新)
/// @notice set token Ks
/// @param tokens token address set
/// @param tokenKs token k_ask and k_bid, structure like [kAsk(16) | kBid(16)]
function setTokensKs(address[] calldata tokens, uint32[] calldata tokenKs) public onlyOwner
其他辅助功能介绍#
- getStableTokenInfo,获取稳定币的数量、稳定币的价格插槽数组以及当前的标志位。
/// @notice get all stable token Info
/// @param numberOfStable stable tokens' quantity
/// @param tokenPriceStable stable tokens' price slot array. each data contains up to 3 token prices
function getStableTokenInfo() public view returns (
uint256 numberOfStable,
uint256[] memory tokenPriceStable,
uint256 curFlag
)
- getNSTokenInfo,获取非稳定币的数量、非稳定币的价格插槽数组以及当前的标志位。
/// @notice get all non-stable token Info
/// @param number stable tokens' quantity
/// @param tokenPrices stable tokens' price slot array. each data contains up to 3 token prices
function getNSTokenInfo() public view returns (
uint256 number,
uint256[] memory tokenPrices,
uint256 curFlag
)
- stickPrice,用于连接价格。给定完整的价格插槽、要在插槽中设置的代币以及该代币的新价格集合,形成一个新的价格插槽。
/// @notice used for construct several price in one price slot
/// @param priceSlot origin price slot
/// @param slotInnerIndex token index in slot
/// @param priceSet the token info needed to update
function stickPrice(
uint256 priceSlot,
uint256 slotInnerIndex,
uint256 priceSet
) public pure returns (uint256 newPriceSlot)
- multicall,用于将多个设置打包到一个事务中。
/// @notice maker could use multicall to set different params in one tx.
function multicall(bytes[] calldata data) public returns (bytes[] memory results)
- getOneTokenOriginIndex(),获取代币的原始索引,如前文所述。
/// @notice get one token index. odd for none-stable, even for stable, true index = tokenIndex[address] / 2
function getOneTokenOriginIndex(address token) public view returns (uint256)
参数数据拼接#
以下是一些参数拼接的代码示例,供SP参考。当然,SP也可以实现自己的参数拼接方法,以实现更高的效率和节省 gas。
// 将四个数据拼接成一个插槽
// 一个插槽是 (16 | 8 | 16 | 8)
// 这种方法可以用于将 amountSet(ask 金额 | ask 金额小数 | bid 金额 | bid 金额小数)拼接成一个插槽
// 注意返回值为 256 位,并需要进行转换。
function stickOneSlot(
uint256 numberA,
uint256 numberADecimal,
uint256 numberB,
uint256 numberBDecimal
) public pure returns (uint256 numberSet) {
numberSet =
(numberA << 32) +
(numberADecimal << 24) +
(numberB << 8) +
numberBDecimal;
}
拼接金额#
// 生成一个标准的 64 位 amountInfo。
function stickAmount(
uint256 askAmount,
uint256 askAmountDecimal,
uint256 bidAmount,
uint256 bidAmountDecimal
) public pure returns (uint64 amountSet) {
amountSet = uint64(
stickOneSlot(
askAmount,
askAmountDecimal,
bidAmount,
bidAmountDecimal
)
);
}
拼接价格#
function stickPrice(
uint256 midPrice,
uint256 midPriceDecimal,
uint256 feeRate,
uint256 askUpRate,
uint256 bidDownRate
) public pure returns(uint80 priceInfo) {
priceInfo = uint80(
(midPrice << 56) + (midPriceDecimal << 48) + (feeRate << 32) + (askUpRate << 16) + bidDownRate
);
}
拼接 k#
function stickKs(uint256 kAsk, uint256 kBid) public pure returns (uint32 kSet) {
kSet = uint32((kAsk << 16) + kBid);
}
示例#
在这个例子中,我们将设置 WBTC 的价格。假设我们想要根据以下参数分配 WBTC 的流动性:
买入价格范围是 27940-27990。
卖出价格范围是 28010-28040。
买入金额为 1000 美元(购买代币的美元金额)。
卖出数量为 0.1 WBTC(卖出的代币数量)。
中间价格设置为 28000,mtFee = 0.02%。
对于代币 A 的两个价格曲线,买入价格为 vUSD / A,卖出价格为 A / vUSD。
swapFee = (28000 - 27990) / 28000 ≈ 0.035%
lpFee = swapFee - mtFee = 0.015%
舍入,lpFee = 0.02%,为 2
因此,实际
买入上限价格 = 28000 *(1 -(0.02% + 0.02%))= 27988.8
卖出下限价格 = 28000 *(1 +(0.02% + 0.02%))= 28011.2
类似地,按比例计算卖出上限和买入下限。
卖出上限 =(28040 - 28000)/ 28000 ≈ 0.14%,为 14
买入下限 =(27940 - 28000)/ 28000 ≈ 0.21%,为 21
买入金额为 1000,小数位数为 18(vUSD 的小数位数为 18)
卖出数量为 0.1,小数位数为 18(默认小数位数为 18)
去除小数点,并用于存储在合约中的数据。
中间价格 = 28000,中间价格小数位数 = 18
卖出上限比率 = 14,买入下限比率 = 21
买入金额 = 1000,小数位数 = 18
卖出数量 = 0.1,小数位数 = 17
一个价格集包含 72 位。
因此,PriceSet 为
[ 28000 | 18 | 14 | 21 ]
一个 amount set 包含 48 位。
因此,AmountSet 为
[ 1 | 17 | 1000 | 18 ]
其他设置#
当前的 maxInterval 也由 D3Maker 管理,因此有以下设置函数:
/// @notice set acceptable setting interval, if setting gap > maxInterval, swap will revert.
function setHeartbeat(uint256 newMaxInterval) public onlyOwner {
}