Decentralizált Tőzsdei Szerződések az Ergo-n
2020. július 31.

Az Ergo kifejező okos szerződésekkel és tranzakciós modellel rendelkezik, amely lehetővé teszi a bizalom nélküli DEX protokoll megvalósítását, amelyben az aláírt vételi és eladási megbízásokat függetlenül helyezhetik el a blokkláncon a vásárlók és az eladók. Egy off-chain egyeztető szolgáltatás figyelheti az Ergo blokkláncot, megtalálhatja a megfelelő megbízásokat, és benyújthatja a csere tranzakciót anélkül, hogy bármilyen titkot ismerne. Az egyeztetést DEX jutalommal lehet ösztönözni, amelyet a csere tranzakció részeként fizetnek. Bárki, aki először felfedezi a két megbízás egyezését, létrehozhatja a csere tranzakciót, és jutalmat kaphat ERG-ben. A részleges egyeztetés támogatott, ami azt jelenti, hogy a cél (vételi/eladási) megbízás részben végrehajtható, ilyenkor egy új "maradvány" megbízást (doboz) kell létrehozni ugyanabban a csere tranzakcióban. Bármely megbízás bármikor törölhető az "tulajdonos" által.
Eladási megbízás szerződés forrás.
Vételi megbízás szerződés forrás.
Részleges egyeztetés
Mindkét szerződés tartalmazza a token árát és a DEX díj paramétereit, amelyek a fordítás során kódolva vannak. Ez lehetővé teszi számunkra, hogy ellenőrizzük a "maradvány" megbízás eszközeit, ERG-t egy vételi megbízás esetén, és tokeneket egy eladási megbízás esetén.
A vételi megbízás szerződésben keresünk egy maradvány dobozt, ellenőrizve, hogy helyes paraméterekkel és eszközökkel rendelkezik.
// részleges egyeztetés esetén új vételi megbízás dobozt kell létrehozni a tranzakcióban nem egyeztetett alapokkal
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
}
Ezután ellenőrizzük, hogy a következő tulajdonságok érvényesek-e:
- A "maradvány" megbízás dobozának értéke (ERG) a jelenlegi doboz (megbízás) értéke mínusz az ERG értéke a tokenekért, amelyeket ebben a csere tranzakcióban kapunk, és mínusz a DEX díj a csere tranzakcióhoz.
- Csak egy "maradvány" megbízás doboz jön létre ebben a csere tranzakcióban.
// ERG, amelyet a vásárolt tokenekért fizettek
val returnTokenValue = returnTokenAmount * tokenPrice
// ág a teljes egyeztetéshez (minden ERG elköltve és a helyes mennyiségű token megvásárolva)
val totalMatching = (SELF.value - expectedDexFee) == returnTokenValue &&
returnBox.value >= fullSpread
// ág a részleges egyeztetéshez, pl. a vásárolt tokenek mellett új vételi megbízást követelünk ERG-ért a
// megbízás nem egyeztetett részéért
val partialMatching = {
val correctResidualOrderBoxValue = (SELF.value - returnTokenValue - expectedDexFee)
foundResidualOrderBoxes.size == 1 &&
foundResidualOrderBoxes(0).value == correctResidualOrderBoxValue &&
returnBox.value >= fullSpread
}
Az eladási megbízás szerződésben keresünk egy maradvány dobozt, ellenőrizve, hogy helyes paraméterekkel és eszközökkel rendelkezik.
// részleges egyeztetés esetén új eladási megbízás dobozt kell létrehozni a tranzakcióban nem egyeztetett tokenekkel
// ellenőrizni kell, hogy csak egy ilyen doboz készült-e később a kódban
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
}
Ezután ellenőrizzük, hogy a következő tulajdonságok érvényesek-e:
- A különbség a jelenlegi doboz (megbízás) token mennyisége és a "maradvány" megbízás doboz között meghatározza az ERG mennyiségét, amelyet az eladó kap a tokenek "eladásáért" ebben a csere tranzakcióban (
soldTokenAmount * tokenPrice). - A "maradvány" megbízás dobozának értéke (ERG) a jelenlegi doboz (megbízás) értéke mínusz a DEX díj a csere tranzakcióhoz.
- Csak egy "maradvány" megbízás doboz jön létre ebben a csere tranzakcióban.
// ág a részleges egyeztetéshez, pl. a kapott ERG mellett új eladási megbízást követelünk tokenekért a
// megbízás nem egyeztetett részéért
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
}
}
Teljes egyeztetés
Mind az eladási, mind a vételi megbízások teljes mértékben végrehajthatók a csere tranzakcióban. Ebben az esetben nincs szükség a "maradvány" megbízás dobozra.
Ehhez az úthoz ellenőrizzük, hogy a következő tulajdonságok érvényesek-e.
Eladási megbízás esetén:
- Az ERG mennyiség, amelyet az eladó kap ebben a csere tranzakcióban, meg kell, hogy egyezzen a jelenlegi megbízásban lévő tokenek mennyiségével, szorozva a token árával.
val totalMatching = (returnBox.value == selfTokenAmount * tokenPrice + fullSpread(selfTokenAmount))
forrás
Vételi megbízás esetén:
- A token értéke (token mennyiség * token ár, ERG-ben), amelyet a vásárló kap ebben a csere tranzakcióban, meg kell, hogy egyezzen a jelenlegi doboz (megbízás) értékével mínusz a DEX díj.
val totalMatching = (SELF.value - expectedDexFee) == (returnTokenAmount * tokenPrice) && returnBox.value >= fullSpread
forrás
Kereslet-Kínálat különbség
Ellenkező megbízások rendezési ellenőrzése
A különbség a vételi (ajánlati) megbízás ára és az eladási (kínálati) megbízás ára között. Biztosítani szeretnénk, hogy ha van különbség, az "idősebb" megbízás kapja meg azt.
Ehhez a szerződés megköveteli, hogy az ellenkező megbízások (kiadási megbízások) a különbség mértéke szerint legyenek rendezve. Így azok, amelyek nagyobb különbséggel rendelkeznek, "fogyasztódnak" először.
A vételi megbízás szerződésében:
// ellenőrizni, hogy ez a megbízás megkapja-e a különbséget egy adott ellenkező megbízás (magasság) esetén
val spreadIsMine = { (counterOrderBoxHeight: Int) =>
// nagyobb vagy egyenlő, mivel csak a szigorúan nagyobb ad győzelmet az eladási megbízás szerződésben
// Denys: el kell döntenünk, ki kapja a különbséget, ha a magasság egyenlő, ok nélkül a vételi megbízást választottam
counterOrderBoxHeight >= SELF.creationInfo._1
}
// ellenőrizni, hogy az ellenkező (eladási) megbízások a különbség szerint rendezettek az INPUTS-ben
// így a nagyobb (felső) különbség "fogyasztódik" először
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
}
}
Ellenőrizzük azt is, hogy a deklarált token ár a R5 regiszterben az ellenkező eladási megbízások esetén a helyes tartományban van, hogy megakadályozzuk a matematikai túlcsordulás és más hasonló támadások kihasználását.
Az eladási megbízás szerződésében:
// ellenőrizni, hogy ez a megbízás megkapja-e a különbséget egy adott ellenkező megbízás (magasság) esetén
val spreadIsMine = { (counterOrderBoxHeight: Int) =>
// szigorúan nagyobb, mivel az egyenlőség győzelmet ad a vételi megbízás szerződésben
// Denys: el kell döntenünk, ki kapja a különbséget, ha a magasság egyenlő, ok nélkül a vételi megbízást választottam
counterOrderBoxHeight > SELF.creationInfo._1
}
// ellenőrizni, hogy az ellenkező (vételi) megbízások a különbség szerint rendezettek az INPUTS-ben
// így a nagyobb (felső) különbség "fogyasztódik" először
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)
// bár a vételi megbízás DEX díját itt nem használják, ellenőrizzük, hogy pozitív-e, mint a szanálási ellenőrzés része
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
}
}
Ellenőrizzük a deklarált token árat a R5 regiszterben, és a DEX díjat tokenenként az R6 regiszterben az ellenkező vételi megbízások esetén, hogy a helyes tartományban legyenek.
Különbség kiszámítása
Azt, hogy a jelenlegi megbízás megkapja a különbségét, először ki kell számítanunk. Az ellenkező megbízások rendezésével a különbség mértéke szerint kezdjük "fogyasztani" őket abban a sorrendben, csökkentve a megbízásban maradt tokenek számát.
A vételi megbízás szerződésében:
// az összes ellenkező (eladási) megbízásból származó összesített különbség
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)) {
// a különbség a miénk
val spreadPerToken = tokenPrice - sellOrderTokenPrice
val sellOrderSpread = spreadPerToken * tokenAmountFromThisOrder
(returnTokensLeft - tokenAmountFromThisOrder, accumulatedFullSpread + sellOrderSpread)
}
else {
// a különbség nem a miénk
(returnTokensLeft - tokenAmountFromThisOrder, accumulatedFullSpread)
}
})._2
}
Az eladási megbízás szerződésében támaszkodnunk kell mind a token árra, mind a DEX díj összegére, hogy kiszámítsuk, hány token van abban a vételi megbízásban. Ezen kívül, mivel nem tudjuk levonni a csere tranzakcióban "eladott" token mennyiséget a visszatérő doboz értékéből, a különbség kiszámítását parametrizáljuk a konkrét token mennyiséggel, amelyet később tudni fogunk a kódban:
// az összesített különbség, amelyet az összes ellenkező (vételi) megbízásból kapunk
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)) {
// a különbség a miénk
val spreadPerToken = buyOrderTokenPrice - tokenPrice
val buyOrderSpread = spreadPerToken * tokenAmountInThisOrder
(returnTokensLeft - tokenAmountInThisOrder, accumulatedFullSpread + buyOrderSpread)
}
else {
// a különbség nem a miénk
(returnTokensLeft - tokenAmountInThisOrder, accumulatedFullSpread)
}
})._2
}
Kaptuk-e a különbséget
A különbség mértékének meghatározásával ellenőriznünk kell, hogy a jelenlegi megbízás valóban megkapta-e a különbséget.
A vételi megbízás szerződésében ellenőrizzük, hogy ez benne van a visszatérő doboz értékében:
// ág a teljes egyeztetéshez (minden ERG elköltve és a helyes mennyiségű token megvásárolva)
val totalMatching = (SELF.value - expectedDexFee) == returnTokenValue &&
returnBox.value >= fullSpread
// ág a részleges egyeztetéshez, pl. a vásárolt tokenek mellett új vételi megbízást követelünk ERG-ért a
// megbízás nem egyeztetett részéért
val partialMatching = {
val correctResidualOrderBoxValue = (SELF.value - returnTokenValue - expectedDexFee)
foundResidualOrderBoxes.size == 1 &&
foundResidualOrderBoxes(0).value == correctResidualOrderBoxValue &&
returnBox.value >= fullSpread
}
Az eladási megbízás szerződésében, amint tudjuk a token mennyiséget, amely "eladásra került" ebben a csere tranzakcióban, ellenőrizzük, hogy a visszatérő doboz értéke tartalmazza-e a különbséget.
Teljes egyeztetés esetén a jelenlegi megbízásban lévő teljes token mennyiséget használjuk:
// ág a teljes egyeztetéshez (minden token eladva és teljes mennyiségű ERG-t kapva)
val totalMatching = (returnBox.value == selfTokenAmount * tokenPrice + fullSpread(selfTokenAmount))
Részleges egyeztetés esetén tudjuk a "eladott" token mennyiségét a maradvány megbízásból (val soldTokenAmount = selfTokenAmount - residualOrderTokenAmount), és ellenőrizzük, hogy a különbség benne van a visszatérő doboz értékében:
val returnBoxValueIsCorrect = returnBox.value == soldTokenErgValue + fullSpread(soldTokenAmount)
Share post
2025. augusztus 13.
2025. augusztus 12.
2025. július 9.
2025. május 12.






