Ergo'da Dağıtık Borsa Sözleşmeleri
31 Temmuz 2020

Ergo, alıcılar ve satıcılar tarafından bağımsız olarak blok zincirine konulabilen imzalı alım ve satım emirlerinin uygulamasına olanak tanıyan, ifade gücü yüksek akıllı sözleşmelere ve işlem modeline sahiptir. Bir off-chain eşleştirme servisi, Ergo blok zincirini gözlemleyebilir, eşleşen emirleri bulabilir ve herhangi bir sır bilmeden takas işlemini gönderebilir. Eşleştirme, bir takas işleminin parçası olarak ödenen DEX ödülü ile teşvik edilebilir. İki emrin eşleşmesini ilk keşfeden herkes, takas işlemini oluşturabilir ve ERG cinsinden bir ödül alabilir. Kısmi eşleştirme desteklenmektedir, bu da hedef (alım/satım) emrinin kısmen yerine getirilebileceği anlamına gelir; bu durumda aynı takas işleminde yeni bir "artık" emir (kutu) oluşturulması gerekir. Herhangi bir emir, "sahibi" tarafından herhangi bir zamanda iptal edilebilir.
Satış emri sözleşmesi kaynak.
Alım emri sözleşmesi kaynak.
Kısmi eşleştirme
Her iki sözleşmede de derleme sırasında kodlanmış token fiyatı ve DEX ücreti parametreleri bulunmaktadır. Bu, "artık" emir varlıklarını, alım emri için ERG'leri ve satış emri için token'ları kontrol etmemizi sağlar.
Alım emri sözleşmesinde, doğru parametreler ve varlıklar içerip içermediğini kontrol ederek bir artık kutu arıyoruz.
// kısmi eşleştirme durumunda, bu tx'de eşleşmeyen fonlarla yeni bir alım emri kutusu oluşturulmalıdır
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
}
Sonra, aşağıdaki özelliklerin geçerli olduğunu kontrol ediyoruz:
- "Artık" emir kutusunun değeri, mevcut kutunun (emir) değeri ile bu takas işleminde aldığımız token'ların ERG değeri ve bu takas işlemi için DEX ücreti çıkarıldığında elde edilen değerdir.
- Bu takas işleminde yalnızca bir "artık" emir kutusu oluşturulmuştur.
// satın alınan token'lar için ödenen ERG'ler
val returnTokenValue = returnTokenAmount * tokenPrice
// toplam eşleştirme durumu (tüm ERG'ler harcanır ve doğru miktarda token satın alınır)
val totalMatching = (SELF.value - expectedDexFee) == returnTokenValue &&
returnBox.value >= fullSpread
// kısmi eşleştirme durumu, örneğin satın alınan token'ların yanı sıra, bu emrin eşleşmeyen kısmı için ERG'ler ile yeni bir alım emri talep ediyoruz
val partialMatching = {
val correctResidualOrderBoxValue = (SELF.value - returnTokenValue - expectedDexFee)
foundResidualOrderBoxes.size == 1 &&
foundResidualOrderBoxes(0).value == correctResidualOrderBoxValue &&
returnBox.value >= fullSpread
}
Satış emri sözleşmesinde, doğru parametreler ve varlıklar içerip içermediğini kontrol ederek bir artık kutu arıyoruz.
// kısmi eşleştirme durumunda, bu tx'de eşleşmeyen token'larla yeni bir satış emri kutusu oluşturulmalıdır
// kodun ilerleyen kısımlarında yalnızca bir tane böyle kutu oluşturulduğunu kontrol edin
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
}
Sonra, aşağıdaki özelliklerin geçerli olduğunu kontrol ediyoruz:
- Mevcut kutudaki (emir) token miktarı ile "artık" emir kutusu arasındaki fark, satıcının bu takas işleminde "satılan" token'lar için aldığı ERG miktarını belirler (
soldTokenAmount * tokenPrice). - "Artık" emir kutusunun değeri, mevcut kutunun (emir) değeri ile bu takas işlemi için DEX ücreti çıkarıldığında elde edilen değerdir.
- Bu takas işleminde yalnızca bir "artık" emir kutusu oluşturulmuştur.
// kısmi eşleştirme durumu, örneğin alınan ERG'lerin yanı sıra, bu emrin eşleşmeyen kısmı için token'larla yeni bir satış emri talep ediyoruz
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
}
}
Toplam eşleştirme
Hem satış hem de alım emirleri, takas işleminde tamamen yerine getirilebilir. Bu durumda, "artık" emir kutusu için bir gereklilik yoktur.
Bu yol için, aşağıdaki özelliklerin geçerli olduğunu kontrol ediyoruz.
Satış emri için:
- Bu takas işleminde satıcının aldığı ERG miktarı, mevcut emirdeki token miktarı ile token fiyatının çarpımına eşit olmalıdır.
val totalMatching = (returnBox.value == selfTokenAmount * tokenPrice + fullSpread(selfTokenAmount))
source
Alım emri için:
- Bu takas işleminde alıcının aldığı token değeri (token miktarı * token fiyatı, ERG cinsinden), mevcut kutunun (emir) değeri ile DEX ücreti çıkarıldığında elde edilen değere eşit olmalıdır.
val totalMatching = (SELF.value - expectedDexFee) == (returnTokenAmount * tokenPrice) && returnBox.value >= fullSpread
source
Teklif-Satış farkı
Karşı emirlerin sıralama kontrolü
Fark, alım (teklif) emri fiyatı ile satış (satış) emri fiyatı arasındaki farktır. Eğer bir fark varsa, "daha eski" emrin bunu almasını sağlamak istiyoruz.
Bu sözleşme, karşı emirlerin (harcama emirleri) fark miktarına göre sıralanmasını gerektirir. Böylece daha büyük fark olanlar "ilk" olarak "tüketilecektir".
Alım emri sözleşmesinde:
// bu emrin belirli bir karşı emir (yükseklik) için farkı alıp almayacağını kontrol et
val spreadIsMine = { (counterOrderBoxHeight: Int) =>
// yalnızca sıkı bir şekilde daha büyük olduğu için, eşitlik yalnızca satış emri sözleşmesinde kazanır
// Denys: yükseklik eşit olduğunda kimin farkı alacağına karar vermemiz gerekiyor, herhangi bir neden olmaksızın alım emrini seçtim
counterOrderBoxHeight >= SELF.creationInfo._1
}
// INPUTS'te karşı (satış) emirlerin farklara göre sıralandığını kontrol et
// böylece daha büyük (üst) fark "ilk" olarak "tüketilecektir"
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
}
}
Ayrıca, karşı satış emirlerinin R5 kaydında belirtilen token fiyatının doğru aralıkta olduğunu kontrol ediyoruz, böylece aritmetik taşma ve diğer benzer saldırıların istismarını önleyebiliriz.
Satış emri sözleşmesinde:
// bu emrin belirli bir karşı emir (yükseklik) için farkı alıp almayacağını kontrol et
val spreadIsMine = { (counterOrderBoxHeight: Int) =>
// eşitlik yalnızca alım emri sözleşmesinde kazanır
// Denys: yükseklik eşit olduğunda kimin farkı alacağına karar vermemiz gerekiyor, herhangi bir neden olmaksızın alım emrini seçtim
counterOrderBoxHeight > SELF.creationInfo._1
}
// INPUTS'te karşı (alım) emirlerin farklara göre sıralandığını kontrol et
// böylece daha büyük (üst) fark "ilk" olarak "tüketilecektir"
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)
// alım emrinin DEX ücreti burada kullanılmasa da, pozitif olup olmadığını kontrol ediyoruz
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
}
}
Ayrıca, karşı alım emirlerinin R5 kaydında belirtilen token fiyatının ve R6 kaydındaki DEX ücretinin doğru aralıkta olduğunu kontrol ediyoruz.
Fark hesaplama
Mevcut emrin farkını alıp almadığını kontrol etmek için, önce onu hesaplamamız gerekiyor. Karşı emirler fark miktarına göre sıralandığında, onları bu sırayla "tüketmeye" başlıyoruz ve bu eşleşmede kalan token sayısını azaltıyoruz.
Alım emri sözleşmesinde:
// tüm karşı (satış) emirlerden aldığımız toplam fark
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)) {
// fark bizim
val spreadPerToken = tokenPrice - sellOrderTokenPrice
val sellOrderSpread = spreadPerToken * tokenAmountFromThisOrder
(returnTokensLeft - tokenAmountFromThisOrder, accumulatedFullSpread + sellOrderSpread)
}
else {
// fark bizim değil
(returnTokensLeft - tokenAmountFromThisOrder, accumulatedFullSpread)
}
})._2
}
Satış emri sözleşmesinde, o alım emrinde kaç token olduğunu hesaplamak için hem token fiyatına hem de DEX ücreti miktarına güvenmemiz gerekiyor. Bunun yanı sıra, bu takas işleminde "satılan" token miktarını geri kutu değerinden çıkaramadığımız için, fark hesaplamasını ileride kodda bileceğimiz belirli bir token miktarı ile parametreli hale getiriyoruz:
// tüm karşı (alım) emirlerden aldığımız toplam fark
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)) {
// fark bizim
val spreadPerToken = buyOrderTokenPrice - tokenPrice
val buyOrderSpread = spreadPerToken * tokenAmountInThisOrder
(returnTokensLeft - tokenAmountInThisOrder, accumulatedFullSpread + buyOrderSpread)
}
else {
// fark bizim değil
(returnTokensLeft - tokenAmountInThisOrder, accumulatedFullSpread)
}
})._2
}
Alınan farkı kontrol et
Fark miktarı belirlendikten sonra, mevcut emrin gerçekten farkı alıp almadığını kontrol etmemiz gerekiyor.
Alım emri sözleşmesinde, bunun geri kutu değerine dahil olduğunu kontrol ediyoruz:
// toplam eşleştirme durumu (tüm ERG'ler harcanır ve doğru miktarda token satın alınır)
val totalMatching = (SELF.value - expectedDexFee) == returnTokenValue &&
returnBox.value >= fullSpread
// kısmi eşleştirme durumu, örneğin satın alınan token'ların yanı sıra, bu emrin eşleşmeyen kısmı için ERG'ler ile yeni bir alım emri talep ediyoruz
val partialMatching = {
val correctResidualOrderBoxValue = (SELF.value - returnTokenValue - expectedDexFee)
foundResidualOrderBoxes.size == 1 &&
foundResidualOrderBoxes(0).value == correctResidualOrderBoxValue &&
returnBox.value >= fullSpread
}
Satış emri sözleşmesinde, bu takas işleminde "satılan" token miktarını bildiğimizde, geri kutu değerinin farka dahil olduğunu kontrol ediyoruz.
Toplam eşleştirme durumunda mevcut emirdeki toplam token miktarını kullanıyoruz:
// toplam eşleştirme durumu (tüm token'lar satılır ve tam miktarda ERG alınır)
val totalMatching = (returnBox.value == selfTokenAmount * tokenPrice + fullSpread(selfTokenAmount))
Kısmi eşleştirme durumunda, "satılan" token miktarını artık emrinden biliyoruz (val soldTokenAmount = selfTokenAmount - residualOrderTokenAmount) ve farkın geri kutu değerine dahil olduğunu kontrol ediyoruz:
val returnBoxValueIsCorrect = returnBox.value == soldTokenErgValue + fullSpread(soldTokenAmount)
Share post
13 Ağustos 2025
9 Temmuz 2025
12 Mayıs 2025






