Dezentralisierte Austauschverträge auf Ergo

This page is machine-translated.
Denys Zadorozhnyi

31. Juli 2020

Ergo verfügt über ausdrucksstarke Smart Contracts und ein Transaktionsmodell, das die Implementierung eines vertrauenslosen DEX-Protokolls ermöglicht, bei dem unterzeichnete Kauf- und Verkaufsaufträge unabhängig von Käufern und Verkäufern in die Blockchain eingegeben werden können. Ein Off-Chain-Matching-Service kann die Ergo-Blockchain beobachten, passende Aufträge finden und die Swap-Transaktion einreichen, ohne irgendwelche Geheimnisse zu kennen. Das Matching kann durch DEX-Belohnungen incentiviert werden, die als Teil einer Swap-Transaktion gezahlt werden. Jeder, der zuerst das Matching der beiden Aufträge entdeckt, kann die Swap-Transaktion erstellen und eine Belohnung in ERGs erhalten. Teilweises Matching wird unterstützt, was bedeutet, dass der Ziel-(Kauf-/Verkaufs-)Auftrag teilweise ausgeführt werden kann, in diesem Fall muss ein neuer "Rest"-Auftrag (Box) in derselben Swap-Transaktion erstellt werden. Jeder Auftrag kann jederzeit vom "Besitzer" storniert werden.

Verkaufauftragvertrag Quelle.

Kaufauftragvertrag Quelle.

Teilweises Matching

Beide Verträge haben Tokenpreis- und DEX-Gebührenparameter, die bei der Kompilierung kodiert sind. Dies ermöglicht es uns, die "Rest"-Auftragswerte, ERGs für einen Kaufauftrag und Tokens für einen Verkaufsauftrag zu überprüfen.

Im Kaufauftragvertrag suchen wir nach einer Restbox und überprüfen, ob sie die richtigen Parameter und Vermögenswerte hat.

// im Falle eines teilweisen Matchings sollte eine neue Kaufauftragbox mit Mitteln erstellt werden, die in dieser Transaktion nicht übereinstimmen
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
}

Quelle

Dann überprüfen wir, ob die folgenden Eigenschaften gelten:

  • Der Wert (ERGs) der "Rest"-Auftragsbox ist der Wert der aktuellen Box (Auftrag) minus dem ERGs-Wert der Tokens, die wir in dieser Swap-Transaktion erhalten, und minus der DEX-Gebühr für diese Swap-Transaktion.
  • Es wird nur eine "Rest"-Auftragsbox in dieser Swap-Transaktion erstellt.
// ERGs, die für die gekauften Tokens bezahlt wurden
val returnTokenValue = returnTokenAmount * tokenPrice
// Zweig für vollständiges Matching (alle ERGs werden ausgegeben und die korrekte Anzahl an Tokens wird gekauft)
val totalMatching = (SELF.value - expectedDexFee) == returnTokenValue && 
  returnBox.value >= fullSpread
// Zweig für teilweises Matching, z.B. neben gekauften Tokens verlangen wir einen neuen Kaufauftrag mit ERGs für 
// den nicht übereinstimmenden Teil dieses Auftrags
val partialMatching = {
  val correctResidualOrderBoxValue = (SELF.value - returnTokenValue - expectedDexFee)
  foundResidualOrderBoxes.size == 1 && 
    foundResidualOrderBoxes(0).value == correctResidualOrderBoxValue && 
    returnBox.value >= fullSpread
}

Quelle

Im Verkaufsauftragvertrag suchen wir nach einer Restbox und überprüfen, ob sie die richtigen Parameter und Vermögenswerte hat.

// im Falle eines teilweisen Matchings sollte eine neue Verkaufsauftragbox mit Tokens erstellt werden, die in dieser Transaktion nicht übereinstimmen
// überprüfen, dass später im Code nur eine solche Box erstellt wird
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
}

Quelle

Dann überprüfen wir, ob die folgenden Eigenschaften gelten:

  • Der Unterschied zwischen der Tokenanzahl in der aktuellen Box (Auftrag) und der "Rest"-Auftragsbox bestimmt die Menge an ERGs, die der Verkäufer für die in dieser Swap-Transaktion "verkauften" Tokens erhält (soldTokenAmount * tokenPrice).
  • Der Wert (ERGs) der "Rest"-Auftragsbox ist der Wert der aktuellen Box (Auftrag) minus der DEX-Gebühr für diese Swap-Transaktion.
  • Es wird nur eine "Rest"-Auftragsbox in dieser Swap-Transaktion erstellt.
// Zweig für teilweises Matching, z.B. neben erhaltenen ERGs verlangen wir einen neuen Verkaufsauftrag mit Tokens für 
// den nicht übereinstimmenden Teil dieses Auftrags
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
  }
}

Quelle

Vollständiges Matching

Sowohl Verkaufs- als auch Kaufaufträge können in der Swap-Transaktion vollständig ausgeführt werden. In diesem Fall gibt es keine Anforderung für die "Rest"-Auftragsbox.
Für diesen Pfad überprüfen wir, ob die folgenden Eigenschaften gelten.
Für Verkaufsauftrag:

  • Die ERGs-Menge, die der Verkäufer in dieser Swap-Transaktion erhält, muss der Menge an Tokens im aktuellen Auftrag multipliziert mit dem Tokenpreis entsprechen.
    val totalMatching = (returnBox.value == selfTokenAmount * tokenPrice + fullSpread(selfTokenAmount))
    Quelle

Für Kaufauftrag:

  • Der Tokenwert (Tokenanzahl * Tokenpreis, in ERGs), den der Käufer in dieser Swap-Transaktion erhält, muss dem Wert der aktuellen Box (Auftrag) minus der DEX-Gebühr entsprechen.
    val totalMatching = (SELF.value - expectedDexFee) == (returnTokenAmount * tokenPrice) && returnBox.value >= fullSpread
    Quelle

Bid-Ask-Spanne

Überprüfung der Sortierung der Gegenaufträge

Die Spanne ist der Unterschied zwischen dem Kauf- (Bid) und dem Verkaufs- (Ask) Auftragskurs. Wir möchten sicherstellen, dass, wenn es eine Spanne gibt, der "ältere" Auftrag sie erhält.
Für diesen Vertrag müssen die Gegenaufträge (Ausgabeaufträge) nach der Spannenhöhe sortiert sein. Damit werden die mit einer größeren Spanne zuerst "verbraucht".
Im Kaufauftragvertrag:

// überprüfen, ob dieser Auftrag die Spanne für einen gegebenen Gegenauftrag (Höhe) erhalten sollte
val spreadIsMine = { (counterOrderBoxHeight: Int) => 
// größer oder gleich, da nur ein striktes Größer im Verkaufsauftragvertrag gewinnt
// Denys: wir müssen entscheiden, wer die Spanne erhält, wenn die Höhe gleich ist, ohne Grund habe ich den Kaufauftrag gewählt
counterOrderBoxHeight >= SELF.creationInfo._1 
}

// überprüfen, dass die Gegen-(Verkaufs-)Aufträge in INPUTS nach Spanne sortiert sind
// damit die größere (obere) Spanne zuerst "verbraucht" wird
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 
}
}

Quelle

Wir überprüfen auch, ob der erklärte Tokenpreis im R5-Register der Gegen-Verkaufsaufträge im richtigen Bereich liegt, um auszuschließen, dass arithmetische Überläufe und andere ähnliche Angriffe ausgenutzt werden.

Im Verkaufsauftragvertrag:

// überprüfen, ob dieser Auftrag die Spanne für einen gegebenen Gegenauftrag (Höhe) erhalten sollte
val spreadIsMine = { (counterOrderBoxHeight: Int) => 
// strikt größer, da Gleichheit im Kaufauftragvertrag gewinnt
// Denys: wir müssen entscheiden, wer die Spanne erhält, wenn die Höhe gleich ist, ohne Grund habe ich den Kaufauftrag gewählt
counterOrderBoxHeight > SELF.creationInfo._1 
}

// überprüfen, dass die Gegen-(Kauf-)Aufträge in INPUTS nach Spanne sortiert sind
// damit die größere (obere) Spanne zuerst "verbraucht" wird
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)
    // obwohl die DEX-Gebühr des Kaufauftrags hier nicht verwendet wird, überprüfen wir, ob sie positiv ist, als Teil der Plausibilitätsprüfung
    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 
}
}

Quelle

Wir überprüfen auch, ob der erklärte Tokenpreis im R5-Register und die DEX-Gebühr pro Token im R6 der Gegen-Kaufaufträge im richtigen Bereich liegen.

Spannenberechnung

Um zu überprüfen, dass der aktuelle Auftrag seine Spanne erhält, müssen wir sie zuerst berechnen. Mit den nach Spannen sortierten Gegenaufträgen beginnen wir, sie in dieser Reihenfolge zu "verbrauchen", wobei die Anzahl der verbleibenden Tokens in diesem Match verringert wird.
Im Kaufauftragvertrag:

// aggregierte Spanne, die wir aus allen Gegen-(Verkaufs-)Aufträgen erhalten
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)) {
      // die Spanne gehört uns
      val spreadPerToken = tokenPrice - sellOrderTokenPrice
      val sellOrderSpread = spreadPerToken * tokenAmountFromThisOrder
      (returnTokensLeft - tokenAmountFromThisOrder, accumulatedFullSpread + sellOrderSpread)
    }
    else {
      // die Spanne gehört uns nicht
      (returnTokensLeft - tokenAmountFromThisOrder, accumulatedFullSpread)
    }
  })._2
}

Quelle

Im Verkaufsauftragvertrag müssen wir sowohl den Tokenpreis als auch die DEX-Gebühr berücksichtigen, um zu berechnen, wie viele Tokens in diesem Kaufauftrag sind. Darüber hinaus, da wir die Anzahl der "verkauften" Tokens in dieser Swap-Transaktion nicht aus dem Rückgabewert der Box ableiten können, parametrisieren wir die Spannenberechnung mit der konkreten Tokenanzahl, die wir später im Code wissen werden:

// aggregierte Spanne, die wir aus allen Gegen-(Kauf-)Aufträgen erhalten
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)) {
      // die Spanne gehört uns
      val spreadPerToken = buyOrderTokenPrice - tokenPrice
      val buyOrderSpread = spreadPerToken * tokenAmountInThisOrder
      (returnTokensLeft - tokenAmountInThisOrder, accumulatedFullSpread + buyOrderSpread)
    }
    else {
      // die Spanne gehört uns nicht
      (returnTokensLeft - tokenAmountInThisOrder, accumulatedFullSpread)
    }
  })._2
}

Quelle

Überprüfung der erhaltenen Spanne

Mit der bestimmten Spannenhöhe müssen wir überprüfen, ob der aktuelle Auftrag tatsächlich die Spanne erhalten hat.
Im Kaufauftragvertrag überprüfen wir, dass sie im Rückgabewert der Box enthalten ist:

// Zweig für vollständiges Matching (alle ERGs werden ausgegeben und die korrekte Anzahl an Tokens wird gekauft)
val totalMatching = (SELF.value - expectedDexFee) == returnTokenValue && 
  returnBox.value >= fullSpread
// Zweig für teilweises Matching, z.B. neben gekauften Tokens verlangen wir einen neuen Kaufauftrag mit ERGs für 
// den nicht übereinstimmenden Teil dieses Auftrags
val partialMatching = {
  val correctResidualOrderBoxValue = (SELF.value - returnTokenValue - expectedDexFee)
  foundResidualOrderBoxes.size == 1 && 
    foundResidualOrderBoxes(0).value == correctResidualOrderBoxValue && 
    returnBox.value >= fullSpread
}

Quelle

Im Verkaufsauftragvertrag, sobald wir die Anzahl der "verkauften" Tokens in dieser Swap-Transaktion kennen, überprüfen wir, ob der Rückgabewert der Box die Spanne enthält.
Im Fall des vollständigen Matchings verwenden wir die gesamte Tokenanzahl im aktuellen Auftrag:

// Zweig für vollständiges Matching (alle Tokens werden verkauft und der volle Betrag an ERGs erhalten)
val totalMatching = (returnBox.value == selfTokenAmount * tokenPrice + fullSpread(selfTokenAmount))

Quelle

Im Fall des teilweisen Matchings kennen wir die Anzahl der "verkauften" Tokens aus dem Restauftrag (val soldTokenAmount = selfTokenAmount - residualOrderTokenAmount) und überprüfen, ob die Spanne im Rückgabewert der Box enthalten ist:

val returnBoxValueIsCorrect = returnBox.value == soldTokenErgValue + fullSpread(soldTokenAmount)

Quelle

Share post

Ergo Infrastructure DAO: Dezentralisierung des Rückgrats des Ergo-Ökosystems

Ergo Infrastructure DAO: Dezentralisierung des Rückgrats des Ergo-Ökosystems

Die Mission von Ergo war schon immer in der Dezentralisierung verwurzelt, nicht nur auf der Konsensschicht, sondern über den gesam.

Ergo Platform

13. August 2025

Mew Finance: Ein verspieltes DeFi-Toolkit für das Ergo-Ökosystem

Mew Finance: Ein verspieltes DeFi-Toolkit für das Ergo-Ökosystem

Mew Finance ist eine dezentrale Anwendungs-Suite auf der Ergo-Blockchain.

Ergo Platform

12. August 2025

Lithos: Dezentralisierung des Minings mit On-Chain-Pools

Lithos: Dezentralisierung des Minings mit On-Chain-Pools

Lithos ist ein neues Protokoll, das darauf abzielt, die Funktionsweise von Mining-Pools zu revolutionieren, indem es sie on-chain .

Ergo Platform

24. Juli 2025

Sigma 6.0: Ein intelligenteres, flexibleres Ergo

Sigma 6.0: Ein intelligenteres, flexibleres Ergo

Sigma 6.0 ist ein bedeutendes vorgeschlagenes Upgrade für die Ergo-Blockchain.

Ergo Platform

23. Juli 2025

Die Zukunft von Rosen gestalten: Ein Gemeinschaftsaufruf zu fünf wichtigen Treasury-Vorschlägen

Die Zukunft von Rosen gestalten: Ein Gemeinschaftsaufruf zu fünf wichtigen Treasury-Vorschlägen

Der Mitbegründer von Rosen, Armeanio, hat fünf neue Vorschläge an die Rosen Treasury eingereicht.

Ergo Platform

9. Juli 2025

Ergos erweitertes UTXO und der Aufstieg der künstlichen wirtschaftlichen Intelligenz

Ergos erweitertes UTXO und der Aufstieg der künstlichen wirtschaftlichen Intelligenz

Eine praktische Vision für autonome wirtschaftliche Agenten Autonome wirtschaftliche Agenten auf der Ergo-Blockchain leisten nütz.

Ergo Platform

12. Mai 2025

ErgoHACK X: Künstliche Intelligenz auf der Ergo-Blockchain

ErgoHACK X: Künstliche Intelligenz auf der Ergo-Blockchain

Feier eines Jahrzehnts dezentraler Innovation Feiern Sie das 10-jährige Jubiläum von ErgoHACK und seien Sie an der Spitze der KI-R.

Ergo Platform

10. April 2025

Ergohack 9: Innovationen in UI/UX und Mining – Lernen Sie die visionären Gewinner kennen!

Ergohack 9: Innovationen in UI/UX und Mining – Lernen Sie die visionären Gewinner kennen!

Der letzte jährliche Ergo-Hackathon, ErgoHack IX, war eine sechstägige Veranstaltung, die Ende Oktober stattfand.

Ergo Platform

9. Dezember 2024

ErgoHack IX: Nächste Schritte zur erhöhten Akzeptanz

ErgoHack IX: Nächste Schritte zur erhöhten Akzeptanz

Wir haben einen langen Weg seit den frühen Tagen von Bitcoin zurückgelegt, und Kryptowährungen haben sich zu einer Branche mit Tau.

Ergo Platform

20. Oktober 2024

Ergo Vs Andere Blockchain-Plattformen: Was ist der Unterschied?

Ergo Vs Andere Blockchain-Plattformen: Was ist der Unterschied?

Ergo bietet eine Reihe einzigartiger Funktionen, die es von anderen Blockchain-Plattformen abheben.

Ergo Platform

19. August 2024

Verdienen Mit Der Rosen Brücke

Verdienen Mit Der Rosen Brücke

Bitcoin hat offiziell die Ergo-Ökosystem über die Rosen Brücke verbunden! Diese neu gebaute, dezentrale Infrastruktur ermöglicht v.

Ergo Platform

8. August 2024

Verdienen Mit Der Rosen Brücke

Verdienen Mit Der Rosen Brücke

Bitcoin hat offiziell die Ergo-Ökosystem über die Rosen Brücke verbunden! Diese neu gebaute, dezentrale Infrastruktur ermöglicht v.

Ergo Platform

8. August 2024

Wie Sigma Chains Bitcoin zu Ergo bringen werden

Wie Sigma Chains Bitcoin zu Ergo bringen werden

Ergos leistungsstarke, flexible und sichere Smart-Contract-Funktionalität öffnet die Tür zu einer ganzen Reihe neuer Anwendungsfäl.

Ergo Platform

15. Juli 2024

Wie Sigma Chains Bitcoin zu Ergo bringen werden

Wie Sigma Chains Bitcoin zu Ergo bringen werden

Die leistungsstarke, flexible und sichere Smart-Contract-Funktionalität von Ergo öffnet die Tür zu einer ganzen Reihe neuer Anwend.

Ergo Platform

15. Juli 2024