
Ergo具有表达能力强的智能合约和交易模型,允许实现无信任的DEX协议,其中买卖双方可以独立地将签名的买入和卖出订单放入区块链。一个链下匹配服务可以观察Ergo区块链,找到匹配的订单,并在不知道任何秘密的情况下提交交换交易。匹配可以通过作为交换交易一部分支付的DEX奖励来激励。任何首先发现两个订单匹配的人可以创建交换交易并获得ERGs奖励。支持部分匹配,这意味着目标(买入/卖出)订单可以部分执行,在这种情况下,必须在同一交换交易中创建一个新的“剩余”订单(箱)。任何订单都可以随时由“所有者”取消。
卖出订单合约 source。
买入订单合约 source。
部分匹配
两个合约在编译时编码了代币价格和DEX费用参数。这使我们能够检查“剩余”订单资产,买入订单的ERGs和卖出订单的代币。
在买入订单合约中,我们搜索一个剩余箱,检查它是否具有正确的参数和资产。
// 在部分匹配的情况下,新的买入订单箱应创建,包含在此交易中未匹配的资金
val foundResidualOrderBoxes = OUTPUTS.filter { (b: Box) =>
val tokenIdParamIsCorrect = b.R4[Coll[Byte]].isDefined && b.R4[Coll[Byte]].get == tokenId
val tokenPriceParamIsCorrect = b.R5[Long].isDefined && b.R5[Long].get == tokenPrice
val dexFeePerTokenParamIsCorrect = b.R6[Long].isDefined && b.R6[Long].get == dexFeePerToken
val contractParamsAreCorrect = tokenIdParamIsCorrect &&
tokenPriceParamIsCorrect && dexFeePerTokenParamIsCorrect
val referenceMe = b.R7[Coll[Byte]].isDefined && b.R7[Coll[Byte]].get == SELF.id
val guardedByTheSameContract = b.propositionBytes == SELF.propositionBytes
contractParamsAreCorrect && referenceMe && guardedByTheSameContract
}
然后,我们检查以下属性是否成立:
- “剩余”订单箱的值(ERGs)是当前箱(订单)的值减去我们在此交换交易中收到的代币的ERGs值,并减去此交换交易的DEX费用。
- 在此交换交易中仅创建一个“剩余”订单箱。
// 为购买的代币支付的ERGs
val returnTokenValue = returnTokenAmount * tokenPrice
// 完全匹配的分支(所有ERGs都已花费并购买了正确数量的代币)
val totalMatching = (SELF.value - expectedDexFee) == returnTokenValue &&
returnBox.value >= fullSpread
// 部分匹配的分支,例如,除了购买的代币外,我们要求一个新的买入订单,包含
// 此订单未匹配部分的ERGs
val partialMatching = {
val correctResidualOrderBoxValue = (SELF.value - returnTokenValue - expectedDexFee)
foundResidualOrderBoxes.size == 1 &&
foundResidualOrderBoxes(0).value == correctResidualOrderBoxValue &&
returnBox.value >= fullSpread
}
在卖出订单合约中,我们搜索一个剩余箱,检查它是否具有正确的参数和资产。
// 在部分匹配的情况下,新的卖出订单箱应创建,包含在此交易中未匹配的代币
// 检查代码后面是否仅创建了一个这样的箱
val foundResidualOrderBoxes = OUTPUTS.filter { (b: Box) =>
val tokenIdParamIsCorrect = b.R4[Coll[Byte]].isDefined && b.R4[Coll[Byte]].get == tokenId
val tokenPriceParamIsCorrect = b.R5[Long].isDefined && b.R5[Long].get == tokenPrice
val dexFeePerTokenParamIsCorrect = b.R6[Long].isDefined && b.R6[Long].get == dexFeePerToken
val contractParamsAreCorrect = tokenIdParamIsCorrect &&
tokenPriceParamIsCorrect &&
dexFeePerTokenParamIsCorrect
val referenceMe = b.R7[Coll[Byte]].isDefined && b.R7[Coll[Byte]].get == SELF.id
val guardedByTheSameContract = b.propositionBytes == SELF.propositionBytes
contractParamsAreCorrect && referenceMe && guardedByTheSameContract
}
然后,我们检查以下属性是否成立:
- 当前箱(订单)中的代币数量与“剩余”订单箱之间的差额决定了卖方在此交换交易中收到的ERGs数量(
soldTokenAmount * tokenPrice)。 - “剩余”订单箱的值(ERGs)是当前箱(订单)的值减去此交换交易的DEX费用。
- 在此交换交易中仅创建一个“剩余”订单箱。
// 部分匹配的分支,例如,除了收到的ERGs外,我们要求一个新的卖出订单,包含
// 此订单未匹配部分的代币
val partialMatching = {
foundResidualOrderBoxes.size == 1 && {
val residualOrderBox = foundResidualOrderBoxes(0)
val residualOrderTokenData = residualOrderBox.tokens(0)
val residualOrderTokenAmount = residualOrderTokenData._2
val soldTokenAmount = selfTokenAmount - residualOrderTokenAmount
val soldTokenErgValue = soldTokenAmount * tokenPrice
val expectedDexFee = dexFeePerToken * soldTokenAmount
val residualOrderTokenId = residualOrderTokenData._1
val tokenIdIsCorrect = residualOrderTokenId == tokenId
val residualOrderValueIsCorrect = residualOrderBox.value == (SELF.value - expectedDexFee)
val returnBoxValueIsCorrect = returnBox.value == soldTokenErgValue + fullSpread(soldTokenAmount)
tokenIdIsCorrect &&
soldTokenAmount >= 1 &&
residualOrderValueIsCorrect &&
returnBoxValueIsCorrect
}
}
完全匹配
卖出和买入订单可以在交换交易中完全执行。在这种情况下,不需要“剩余”订单箱。
对于此路径,我们检查以下属性是否成立。
对于卖出订单:
- 卖方在此交换交易中收到的ERGs数量必须等于当前订单中代币的数量乘以代币价格。
val totalMatching = (returnBox.value == selfTokenAmount * tokenPrice + fullSpread(selfTokenAmount))
source
对于买入订单:
- 买方在此交换交易中收到的代币值(代币数量 * 代币价格,单位为ERGs)必须等于当前箱(订单)的值减去DEX费用。
val totalMatching = (SELF.value - expectedDexFee) == (returnTokenAmount * tokenPrice) && returnBox.value >= fullSpread
source
买卖差价
反向订单排序检查
差价是买入(出价)订单价格与卖出(要价)订单价格之间的差异。我们希望确保如果存在差价,“较旧”的订单会获得它。
对于此合约,要求反向订单(支出订单)按差价金额排序。这样,差价更大的订单将“被消耗”在前。
在买入订单合约中:
// 检查此订单是否应获得给定反向订单(高度)的差价
val spreadIsMine = { (counterOrderBoxHeight: Int) =>
// 大于或等于,因为只有严格大于才能在卖出订单合约中获胜
// Denys:我们必须决定如果高度相等,谁获得差价,毫无理由我选择了买入订单
counterOrderBoxHeight >= SELF.creationInfo._1
}
// 检查反向(卖出)订单在输入中是否按差价排序
// 这样,较大的(顶部)差价将“被消耗”在前
val sellOrderBoxesAreSortedBySpread = { (boxes: Coll[Box]) =>
boxes.size > 0 && {
val alledgedlyTopSpread = if (spreadIsMine(boxes(0).creationInfo._1)) {
tokenPrice - boxes(0).R5[Long].getOrElse(0L)
} else { 0L }
boxes.fold((alledgedlyTopSpread, true), { (t: (Long, Boolean), box: Box) =>
val prevSpread = t._1
val isSorted = t._2
val boxTokenPrice = box.R5[Long].getOrElse(0L)
val boxTokenPriceIsCorrect = boxTokenPrice > 0 && boxTokenPrice <= tokenPrice
val spread = if (spreadIsMine(box.creationInfo._1)) {
tokenPrice - boxTokenPrice
} else { 0L }
(spread, isSorted && boxTokenPriceIsCorrect && spread <= prevSpread)
})._2
}
}
我们还检查反向卖出订单的R5寄存器中声明的代币价格是否在正确范围内,以防止利用算术溢出和其他类似攻击。
在卖出订单合约中:
// 检查此订单是否应获得给定反向订单(高度)的差价
val spreadIsMine = { (counterOrderBoxHeight: Int) =>
// 严格大于,因为相等在买入订单合约中获胜
// Denys:我们必须决定如果高度相等,谁获得差价,毫无理由我选择了买入订单
counterOrderBoxHeight > SELF.creationInfo._1
}
// 检查反向(买入)订单在输入中是否按差价排序
// 这样,较大的(顶部)差价将“被消耗”在前
val buyOrderBoxesAreSortedBySpread = { (boxes: Coll[Box]) =>
boxes.size > 0 && {
val alledgedlyTopSpread = if (spreadIsMine(boxes(0).creationInfo._1)) {
boxes(0).R5[Long].getOrElse(0L) - tokenPrice
} else { 0L }
boxes.fold((alledgedlyTopSpread, true), { (t: (Long, Boolean), box: Box) =>
val prevSpread = t._1
val isSorted = t._2
val boxTokenPrice = box.R5[Long].getOrElse(0L)
// 尽管买入订单的DEX费用在这里未使用,但我们检查其是否为正数作为合理性检查的一部分
val boxDexFeePerToken = box.R6[Long].getOrElse(0L)
val spread = if (spreadIsMine(box.creationInfo._1)) { boxTokenPrice - tokenPrice } else { 0L }
(spread, isSorted && boxTokenPrice >= tokenPrice && boxDexFeePerToken > 0L && spread <= prevSpread)
})._2
}
}
我们还检查反向买入订单的R5寄存器中声明的代币价格和R6中的每个代币的DEX费用是否在正确范围内。
差价计算
为了检查当前订单是否获得其差价,我们需要先计算它。通过按差价金额排序的反向订单,我们开始按该顺序“消耗”它们,减少此匹配中剩余的代币数量。
在买入订单合约中:
// 从所有反向(卖出)订单中获得的总差价
val fullSpread = {
spendingSellOrders.fold((returnTokenAmount, 0L), { (t: (Long, Long), sellOrder: Box) =>
val returnTokensLeft = t._1
val accumulatedFullSpread = t._2
val sellOrderTokenPrice = sellOrder.R5[Long].get
val sellOrderTokenAmount = sellOrder.tokens(0)._2
val tokenAmountFromThisOrder = min(returnTokensLeft, sellOrderTokenAmount)
if (spreadIsMine(sellOrder.creationInfo._1)) {
// 差价是我们的
val spreadPerToken = tokenPrice - sellOrderTokenPrice
val sellOrderSpread = spreadPerToken * tokenAmountFromThisOrder
(returnTokensLeft - tokenAmountFromThisOrder, accumulatedFullSpread + sellOrderSpread)
}
else {
// 差价不是我们的
(returnTokensLeft - tokenAmountFromThisOrder, accumulatedFullSpread)
}
})._2
}
在卖出订单合约中,我们需要依赖代币价格和DEX费用金额来计算该买入订单中有多少代币。此外,由于我们无法从返回箱值中推导出在此交换交易中“出售”的代币数量,因此我们使差价计算参数化为我们稍后将在代码中知道的具体代币数量:
// 从所有反向(买入)订单中获得的总差价
val fullSpread = { (tokenAmount: Long) =>
spendingBuyOrders.fold((tokenAmount, 0L), { (t: (Long, Long), buyOrder: Box) =>
val returnTokensLeft = t._1
val accumulatedFullSpread = t._2
val buyOrderTokenPrice = buyOrder.R5[Long].get
val buyOrderDexFeePerToken = buyOrder.R6[Long].get
val buyOrderTokenAmountCapacity = buyOrder.value / (buyOrderTokenPrice + buyOrderDexFeePerToken)
val tokenAmountInThisOrder = min(returnTokensLeft, buyOrderTokenAmountCapacity)
if (spreadIsMine(buyOrder.creationInfo._1)) {
// 差价是我们的
val spreadPerToken = buyOrderTokenPrice - tokenPrice
val buyOrderSpread = spreadPerToken * tokenAmountInThisOrder
(returnTokensLeft - tokenAmountInThisOrder, accumulatedFullSpread + buyOrderSpread)
}
else {
// 差价不是我们的
(returnTokensLeft - tokenAmountInThisOrder, accumulatedFullSpread)
}
})._2
}
检查收到的差价
确定差价金额后,我们需要检查当前订单是否确实收到了差价。
在买入订单合约中,我们检查它是否包含在返回箱值中:
// 完全匹配的分支(所有ERGs都已花费并购买了正确数量的代币)
val totalMatching = (SELF.value - expectedDexFee) == returnTokenValue &&
returnBox.value >= fullSpread
// 部分匹配的分支,例如,除了购买的代币外,我们要求一个新的买入订单,包含
// 此订单未匹配部分的ERGs
val partialMatching = {
val correctResidualOrderBoxValue = (SELF.value - returnTokenValue - expectedDexFee)
foundResidualOrderBoxes.size == 1 &&
foundResidualOrderBoxes(0).value == correctResidualOrderBoxValue &&
returnBox.value >= fullSpread
}
在卖出订单合约中,一旦我们知道在此交换交易中“出售”的代币数量,我们检查返回箱值是否包含差价。
在完全匹配的情况下,我们使用当前订单中的总代币数量:
// 完全匹配的分支(所有代币都已出售并收到全部ERGs)
val totalMatching = (returnBox.value == selfTokenAmount * tokenPrice + fullSpread(selfTokenAmount))
在部分匹配的情况下,我们从剩余订单中知道“出售”的代币数量(val soldTokenAmount = selfTokenAmount - residualOrderTokenAmount),并检查差价是否包含在返回箱值中:
val returnBoxValueIsCorrect = returnBox.value == soldTokenErgValue + fullSpread(soldTokenAmount)
Share post




















