Contratti di Scambio Decentralizzati su Ergo

This page is machine-translated.
Denys Zadorozhnyi

31 luglio 2020

Ergo ha contratti intelligenti espressivi e un modello transazionale che consente l'implementazione di un protocollo DEX senza fiducia, in cui ordini di acquisto e vendita firmati possono essere inseriti nella blockchain in modo indipendente da acquirenti e venditori. Un servizio di abbinamento off-chain può osservare la blockchain di Ergo, trovare ordini corrispondenti e inviare la transazione di scambio senza conoscere alcun segreto. L'abbinamento può essere incentivato da una ricompensa DEX pagata come parte di una transazione di scambio. Chiunque scopra per primo l'abbinamento dei due ordini può creare la transazione di scambio e ricevere una ricompensa in ERG. L'abbinamento parziale è supportato, il che significa che l'ordine target (acquisto/vendita) può essere eseguito parzialmente, nel qual caso un nuovo ordine "residuo" (box) deve essere creato nella stessa transazione di scambio. Qualsiasi ordine può essere annullato in qualsiasi momento dal "proprietario".

Contratto di ordine di vendita source.

Contratto di ordine di acquisto source.

Abbinamento parziale

Entrambi i contratti hanno parametri di prezzo del token e commissione DEX codificati in una compilazione. Questo ci consente di controllare gli asset dell'ordine "residuo", ERG per un ordine di acquisto e token per un ordine di vendita.

Nel contratto di ordine di acquisto, cerchiamo un box residuo, controllando che abbia parametri e asset corretti.

// in caso di abbinamento parziale, un nuovo box di ordine di acquisto dovrebbe essere creato con fondi che non sono stati abbinati in questa tx
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
}

source

Poi, controlliamo che le seguenti proprietà siano valide:

  • Il valore (ERG) del box di ordine "residuo" è il valore del box corrente (ordine) meno il valore in ERG dei token che stiamo ricevendo in questa transazione di scambio e meno la commissione DEX per questa transazione di scambio.
  • Solo un box di ordine "residuo" è creato in questa transazione di scambio.
// ERG pagati per i token acquistati
val returnTokenValue = returnTokenAmount * tokenPrice
// ramo per abbinamento totale (tutti gli ERG sono spesi e la corretta quantità di token è acquistata)
val totalMatching = (SELF.value - expectedDexFee) == returnTokenValue && 
  returnBox.value >= fullSpread
// ramo per abbinamento parziale, ad esempio, oltre ai token acquistati, richiediamo un nuovo ordine di acquisto con ERG per 
// la parte non abbinata di questo ordine
val partialMatching = {
  val correctResidualOrderBoxValue = (SELF.value - returnTokenValue - expectedDexFee)
  foundResidualOrderBoxes.size == 1 && 
    foundResidualOrderBoxes(0).value == correctResidualOrderBoxValue && 
    returnBox.value >= fullSpread
}

source

Nel contratto di ordine di vendita, cerchiamo un box residuo, controllando che abbia parametri e asset corretti.

// in caso di abbinamento parziale, un nuovo box di ordine di vendita dovrebbe essere creato con token che non sono stati abbinati in questa tx
// controlla che ci sia solo un box di questo tipo creato più avanti nel codice
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
}

source

Poi, controlliamo che le seguenti proprietà siano valide:

  • La differenza tra la quantità di token nel box corrente (ordine) e il box di ordine "residuo" determina la quantità di ERG che il venditore riceve per i token "venduti" in questa transazione di scambio (soldTokenAmount * tokenPrice).
  • Il valore (ERG) del box di ordine "residuo" è il valore del box corrente (ordine) meno la commissione DEX per questa transazione di scambio.
  • Solo un box di ordine "residuo" è creato in questa transazione di scambio.
// ramo per abbinamento parziale, ad esempio, oltre agli ERG ricevuti, richiediamo un nuovo ordine di vendita con token per 
// la parte non abbinata di questo ordine
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
  }
}

source

Abbinamento totale

Sia gli ordini di vendita che quelli di acquisto possono essere eseguiti nella transazione di scambio interamente. In questo caso, non c'è alcun requisito per il box di ordine "residuo".
Per questo percorso, controlliamo che le seguenti proprietà siano valide.
Per l'ordine di vendita:

  • La quantità di ERG che il venditore riceve in questa transazione di scambio deve essere uguale alla quantità di token nell'ordine corrente moltiplicata per il prezzo del token.
    val totalMatching = (returnBox.value == selfTokenAmount * tokenPrice + fullSpread(selfTokenAmount))
    source

Per l'ordine di acquisto:

  • Il valore del token (quantità di token * prezzo del token, in ERG) che l'acquirente riceve in questa transazione di scambio deve essere uguale al valore del box corrente (ordine) meno la commissione DEX.
    val totalMatching = (SELF.value - expectedDexFee) == (returnTokenAmount * tokenPrice) && returnBox.value >= fullSpread
    source

Spread Bid-Ask

Controllo di ordinamento degli ordini contro

Lo spread è la differenza tra il prezzo dell'ordine di acquisto (bid) e il prezzo dell'ordine di vendita (ask). Vogliamo assicurarci che, se c'è uno spread, l'ordine "più vecchio" lo ottenga.
Per questo contratto, è richiesto che gli ordini contro (ordini di spesa) siano ordinati per importo di spread. In modo che quelli con uno spread maggiore vengano "consumati" per primi.
Nel contratto di ordine di acquisto:

// controlla se questo ordine dovrebbe ottenere lo spread per un dato ordine contro (altezza)
val spreadIsMine = { (counterOrderBoxHeight: Int) => 
// maggiore o uguale poiché solo un maggiore rigoroso dà vittoria nel contratto di ordine di vendita
// Denys: dobbiamo decidere chi ottiene lo spread se l'altezza è uguale, senza alcun motivo ho scelto l'ordine di acquisto
counterOrderBoxHeight >= SELF.creationInfo._1 
}

// controlla che gli ordini contro (vendita) siano ordinati per spread in INPUTS
// in modo che lo spread maggiore (superiore) venga "consumato" per primo
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 
}
}

source

Controlliamo anche che il prezzo del token dichiarato nel registro R5 degli ordini di vendita contro sia nell'intervallo corretto per prevenire l'esploitazione di overflow aritmetico e altri attacchi simili.

Nel contratto di ordine di vendita:

// controlla se questo ordine dovrebbe ottenere lo spread per un dato ordine contro (altezza)
val spreadIsMine = { (counterOrderBoxHeight: Int) => 
// maggiore rigorosamente poiché l'uguaglianza dà vittoria nel contratto di ordine di acquisto
// Denys: dobbiamo decidere chi ottiene lo spread se l'altezza è uguale, senza alcun motivo ho scelto l'ordine di acquisto
counterOrderBoxHeight > SELF.creationInfo._1 
}

// controlla che gli ordini contro (acquisto) siano ordinati per spread in INPUTS
// in modo che lo spread maggiore (superiore) venga "consumato" per primo
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)
    // anche se la commissione DEX dell'ordine di acquisto non è utilizzata qui, controlliamo se è positiva come parte del controllo di sanità
    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 
}
}

source

Controlliamo anche che il prezzo del token dichiarato nel registro R5 e la commissione DEX per token in R6 degli ordini di acquisto contro siano nell'intervallo corretto.

Calcolo dello spread

Per controllare che l'ordine corrente ottenga il proprio spread, dobbiamo calcolarlo prima. Con gli ordini contro ordinati per importo di spread, iniziamo a "consumare" in quell'ordine, diminuendo il numero di token rimasti in questo abbinamento.
Nel contratto di ordine di acquisto:

// spread aggregato che otteniamo da tutti gli ordini contro (vendita)
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)) {
      // lo spread è nostro
      val spreadPerToken = tokenPrice - sellOrderTokenPrice
      val sellOrderSpread = spreadPerToken * tokenAmountFromThisOrder
      (returnTokensLeft - tokenAmountFromThisOrder, accumulatedFullSpread + sellOrderSpread)
    }
    else {
      // lo spread non è nostro
      (returnTokensLeft - tokenAmountFromThisOrder, accumulatedFullSpread)
    }
  })._2
}

source

Nel contratto di ordine di vendita, dobbiamo fare affidamento sia sul prezzo del token che sull'importo della commissione DEX per calcolare quanti token ci sono in quell'ordine di acquisto. Oltre a ciò, poiché non possiamo dedurre la quantità di token "venduti" in questa transazione di scambio dal valore del box di ritorno, rendiamo il calcolo dello spread parametrizzato con la quantità concreta di token che conosceremo più avanti nel codice:

// spread aggregato che otteniamo da tutti gli ordini contro (acquisto)
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)) {
      // lo spread è nostro
      val spreadPerToken = buyOrderTokenPrice - tokenPrice
      val buyOrderSpread = spreadPerToken * tokenAmountInThisOrder
      (returnTokensLeft - tokenAmountInThisOrder, accumulatedFullSpread + buyOrderSpread)
    }
    else {
      // lo spread non è nostro
      (returnTokensLeft - tokenAmountInThisOrder, accumulatedFullSpread)
    }
  })._2
}

source

Controlla lo spread ricevuto

Con l'importo dello spread determinato, dobbiamo controllare se l'ordine corrente ha effettivamente ricevuto lo spread.
Nel contratto di ordine di acquisto, controlliamo che sia incluso nel valore del box di ritorno:

// ramo per abbinamento totale (tutti gli ERG sono spesi e la corretta quantità di token è acquistata)
val totalMatching = (SELF.value - expectedDexFee) == returnTokenValue && 
  returnBox.value >= fullSpread
// ramo per abbinamento parziale, ad esempio, oltre ai token acquistati, richiediamo un nuovo ordine di acquisto con ERG per 
// la parte non abbinata di questo ordine
val partialMatching = {
  val correctResidualOrderBoxValue = (SELF.value - returnTokenValue - expectedDexFee)
  foundResidualOrderBoxes.size == 1 && 
    foundResidualOrderBoxes(0).value == correctResidualOrderBoxValue && 
    returnBox.value >= fullSpread
}

source

Nel contratto di ordine di vendita, non appena sappiamo la quantità di token "venduti" in questa transazione di scambio, controlliamo che il valore del box di ritorno abbia incluso lo spread.
Nel caso di abbinamento totale, utilizziamo la quantità totale di token nell'ordine corrente:

// ramo per abbinamento totale (tutti i token sono venduti e l'importo totale di ERG ricevuto)
val totalMatching = (returnBox.value == selfTokenAmount * tokenPrice + fullSpread(selfTokenAmount))

source

Nel caso di abbinamento parziale, sappiamo l'importo del token "venduto" dall'ordine residuo (val soldTokenAmount = selfTokenAmount - residualOrderTokenAmount) e controlliamo che lo spread sia incluso nel valore del box di ritorno:

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

source

Share post

Ergo Infrastructure DAO: Decentralizzare la Spina Dorsale dell'Ecosistema Ergo

Ergo Infrastructure DAO: Decentralizzare la Spina Dorsale dell'Ecosistema Ergo

La missione di Ergo è sempre stata radicata nella decentralizzazione, non solo a livello di consenso, ma in tutto lo stack.

Ergo Platform

13 agosto 2025

Mew Finance: Un Toolkit DeFi Giocoso per l'Ecosistema Ergo

Mew Finance: Un Toolkit DeFi Giocoso per l'Ecosistema Ergo

Mew Finance è una suite di applicazioni decentralizzate sulla Blockchain Ergo.

Ergo Platform

12 agosto 2025

Lithos: Decentralizzare il Mining con Pool On-Chain

Lithos: Decentralizzare il Mining con Pool On-Chain

Lithos è un nuovo protocollo progettato per ristrutturare il funzionamento delle pool di mining spostandole on-chain, dando ai min.

Ergo Platform

24 luglio 2025

Sigma 6.0: Un Ergo più Intelligente e Flessibile

Sigma 6.0: Un Ergo più Intelligente e Flessibile

Sigma 6.0 è un importante aggiornamento proposto per la blockchain Ergo.

Ergo Platform

23 luglio 2025

Plasmare il Futuro di Rosen: Una Chiamata della Comunità su Cinque Proposte Chiave del Tesoro

Plasmare il Futuro di Rosen: Una Chiamata della Comunità su Cinque Proposte Chiave del Tesoro

Il co-fondatore di Rosen, Armeanio, ha presentato cinque nuove proposte al Tesoro di Rosen.

Ergo Platform

9 luglio 2025

L'Extended UTXO di Ergo e l'Ascesa dell'Intelligenza Economica Artificiale

L'Extended UTXO di Ergo e l'Ascesa dell'Intelligenza Economica Artificiale

Una Visione Pratica per Agenti Economici Autonomi Gli agenti economici autonomi sulla blockchain di Ergo svolgono un lavoro utile.

Ergo Platform

12 maggio 2025

ErgoHACK X: Intelligenza Artificiale sulla Blockchain di Ergo

ErgoHACK X: Intelligenza Artificiale sulla Blockchain di Ergo

Celebrare un Decennio di Innovazione Decentralizzata Unisciti al decimo anniversario di ErgoHACK e sii in prima linea nella rivolu.

Ergo Platform

10 aprile 2025

I partecipanti all'Hackaton V: mining and minting

I partecipanti all'Hackaton V: mining and minting

La registrazione per "ErgoHack V: Mining and Minting" è ufficialmente chiusa ed è tempo di esplorare ciò che i partecipanti hanno .

Ergo Platform

11 ottobre 2022

EIP37 Hardfork

EIP37 Hardfork

Dopo il merge di Ethereum, l'industria del mining di criptovalute ha assistito a un impressionante riorientamento del potere di ha.

Ergo Platform

3 ottobre 2022

ErgoHack V: incontra i nostri giudici

ErgoHack V: incontra i nostri giudici

Con le iscrizioni ora aperte, ErgoHack V si sta avvicinando rapidamente.

Ergo Platform

25 settembre 2022

Ergo: Dopo il merge di Ethereum

Ergo: Dopo il merge di Ethereum

Una discussione per la comunità mineraria Sono state un paio di settimane vorticose per l'industria del mining di criptovalute.

Ergo Platform

25 settembre 2022

La tabella di marcia di Ergo: cosa succederà. Parte 1

La tabella di marcia di Ergo: cosa succederà. Parte 1

Dal lancio sulla rete principale di Ergo il 1 luglio 2019, la blockchain ha raggiunto molti traguardi importanti.

Ergo Platform

24 settembre 2022

I premi di ErgoHack V

I premi di ErgoHack V

Con la fusione di Ethereum, stiamo assistendo a un cambiamento sismico nel panorama degli hashrate per le blockchain Proof of Work.

Ergo platform

18 settembre 2022

EIP-0028 di Ergo: ErgoAuth

EIP-0028 di Ergo: ErgoAuth

Quando si parla di blockchain, è importante ricordare che i wallet sono completamente anonimi.

Ergoplatform

4 settembre 2022

The Ergo Manifesto

The Ergo Manifesto

Il Manifesto Ergo desidera educare e offrire una panoramica di ciò che la tecnologia blockchain può raggiungere.

Ergo Foundation

3 settembre 2022

Ergo e il meccanismo di consenso di Autolykos: parte I

Ergo e il meccanismo di consenso di Autolykos: parte I

Quello che segue è un'analisi tecnica approfondita del meccanismo di consenso di Ergo, Autolykos.

Ergo Platform

28 agosto 2022

Ergo e il meccanismo di consenso di Autolykos: parte II

Ergo e il meccanismo di consenso di Autolykos: parte II

La scorsa settimana abbiamo introdotto un'analisi approfondita del meccanismo di consenso Autolykos di Ergo.

Ergo Platform

28 agosto 2022

Come acquistare Ergo da Kucoin

Come acquistare Ergo da Kucoin

Tutti gli aspetti di questo articolo non sono consigli finanziari. Ricontrolla tutte le informazioni fornite da altre fonti.

Ergoplatform

7 agosto 2022

Ethereum Mining Community e GPU Miners: il caso di Ergo dopo la fusione

Ethereum Mining Community e GPU Miners: il caso di Ergo dopo la fusione

Un cambiamento epocale in arrivo nel campo delle blockchain minabili Il panorama del mining di criptovalute Proof of Work sta per.

Ergoplatform

7 agosto 2022

Ergo: una risposta ai fallimenti della teoria monetaria moderna

Ergo: una risposta ai fallimenti della teoria monetaria moderna

Nel 2008, un gruppo o una persona sconosciuta ha rilasciato una riserva di valore peer-to-peer e l’ha chiamata Bitcoin.

Ergo platform

9 febbraio 2022

E' nata Ergoitaly.it

E' nata Ergoitaly.it

La prima community ufficiale Ergo in Italia Ergo è nato l'8 aprile 2019. Alle 20:41.

ErGonario

27 gennaio 2022