Contratos de Troca Descentralizada na Ergo

This page is machine-translated.
Denys Zadorozhnyi

31 de julho de 2020

A Ergo possui contratos inteligentes expressivos e um modelo transacional que permite a implementação de um protocolo DEX sem confiança, no qual ordens de compra e venda assinadas podem ser inseridas na blockchain de forma independente por compradores e vendedores. Um serviço de correspondência off-chain pode observar a blockchain da Ergo, encontrar ordens correspondentes e submeter a transação de troca sem conhecer nenhum segredo. A correspondência pode ser incentivada por recompensas DEX pagas como parte de uma transação de troca. Qualquer um que descobrir primeiro a correspondência das duas ordens pode criar a transação de troca e receber uma recompensa em ERGs. A correspondência parcial é suportada, o que significa que a ordem alvo (compra/venda) pode ser executada parcialmente, caso em que uma nova ordem "residual" (caixa) deve ser criada na mesma transação de troca. Qualquer ordem pode ser cancelada a qualquer momento pelo "proprietário".

Contrato de ordem de venda fonte.

Contrato de ordem de compra fonte.

Correspondência parcial

Ambos os contratos têm parâmetros de preço de token e taxa DEX codificados em uma compilação. Isso nos permite verificar os ativos da ordem "residual", ERGs para uma ordem de compra e tokens para uma ordem de venda.

No contrato de ordem de compra, procuramos uma caixa residual, verificando se ela possui os parâmetros e ativos corretos.

// em caso de correspondência parcial, uma nova caixa de ordem de compra deve ser criada com os fundos que não estão correspondidos nesta transação
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
}

fonte

Em seguida, verificamos se as seguintes propriedades se mantêm:

  • O valor (ERGs) da caixa de ordem "residual" é o valor da caixa atual (ordem) menos o valor em ERGs dos tokens que estamos recebendo nesta transação de troca e menos a taxa DEX para esta transação de troca.
  • Apenas uma caixa de ordem "residual" é criada nesta transação de troca.
// ERGs pagos pelos tokens comprados
val returnTokenValue = returnTokenAmount * tokenPrice
// ramificação para correspondência total (todos os ERGs são gastos e a quantidade correta de tokens é comprada)
val totalMatching = (SELF.value - expectedDexFee) == returnTokenValue && 
  returnBox.value >= fullSpread
// ramificação para correspondência parcial, por exemplo, além dos tokens comprados, exigimos uma nova ordem de compra com ERGs para 
// a parte não correspondida desta ordem
val partialMatching = {
  val correctResidualOrderBoxValue = (SELF.value - returnTokenValue - expectedDexFee)
  foundResidualOrderBoxes.size == 1 && 
    foundResidualOrderBoxes(0).value == correctResidualOrderBoxValue && 
    returnBox.value >= fullSpread
}

fonte

No contrato de ordem de venda, procuramos uma caixa residual, verificando se ela possui os parâmetros e ativos corretos.

// em caso de correspondência parcial, uma nova caixa de ordem de venda deve ser criada com tokens que não estão correspondidos nesta transação
// verifique que há apenas uma caixa desse tipo feita mais tarde no código
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
}

fonte

Em seguida, verificamos se as seguintes propriedades se mantêm:

  • A diferença entre a quantidade de tokens na caixa atual (ordem) e a caixa de ordem "residual" determina a quantidade de ERGs que o vendedor recebe pelos tokens "vendidos" nesta transação de troca (soldTokenAmount * tokenPrice).
  • O valor (ERGs) da caixa de ordem "residual" é o valor da caixa atual (ordem) menos a taxa DEX para esta transação de troca.
  • Apenas uma caixa de ordem "residual" é criada nesta transação de troca.
// ramificação para correspondência parcial, por exemplo, além dos ERGs recebidos, exigimos uma nova ordem de venda com tokens para 
// a parte não correspondida desta ordem
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
  }
}

fonte

Correspondência total

Tanto as ordens de venda quanto as de compra podem ser executadas na transação de troca na íntegra. Nesse caso, não há exigência para a caixa de ordem "residual".
Para esse caminho, verificamos se as seguintes propriedades se mantêm.
Para a ordem de venda:

  • A quantidade de ERGs que o vendedor recebe nesta transação de troca deve ser igual à quantidade de tokens na ordem atual vezes o preço do token.
    val totalMatching = (returnBox.value == selfTokenAmount * tokenPrice + fullSpread(selfTokenAmount))
    fonte

Para a ordem de compra:

  • O valor do token (quantidade de token * preço do token, em ERGs) que o comprador recebe nesta transação de troca deve ser igual ao valor da caixa atual (ordem) menos a taxa DEX.
    val totalMatching = (SELF.value - expectedDexFee) == (returnTokenAmount * tokenPrice) && returnBox.value >= fullSpread
    fonte

Spread de Bid-Ask

Verificação de ordenação de ordens contrárias

O spread é a diferença entre o preço da ordem de compra (bid) e o preço da ordem de venda (ask). Queremos garantir que, se houver um spread, a ordem "mais antiga" o receba.
Para isso, o contrato exige que as ordens contrárias (ordens de gasto) sejam ordenadas pela quantidade de spread. Assim, aquelas com um spread maior serão "consumidas" primeiro.
No contrato de ordem de compra:

// verifique se esta ordem deve receber o spread para uma determinada ordem contrária (altura)
val spreadIsMine = { (counterOrderBoxHeight: Int) => 
// maior ou igual, uma vez que apenas um estritamente maior dá vitória no contrato de ordem de venda
// Denys: temos que decidir quem recebe o spread se a altura for igual, sem qualquer razão eu escolhi a ordem de compra
counterOrderBoxHeight >= SELF.creationInfo._1 
}

// verifique se as ordens contrárias (venda) estão ordenadas por spread em INPUTS
// para que o spread maior (topo) seja "consumido" primeiro
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 
}
}

fonte

Também verificamos se o preço do token declarado no registro R5 das ordens de venda contrárias está na faixa correta para evitar a exploração de estouro aritmético e outros ataques semelhantes.

No contrato de ordem de venda:

// verifique se esta ordem deve receber o spread para uma determinada ordem contrária (altura)
val spreadIsMine = { (counterOrderBoxHeight: Int) => 
// estritamente maior, uma vez que a igualdade dá vitória no contrato de ordem de compra
// Denys: temos que decidir quem recebe o spread se a altura for igual, sem qualquer razão eu escolhi a ordem de compra
counterOrderBoxHeight > SELF.creationInfo._1 
}

// verifique se as ordens contrárias (compra) estão ordenadas por spread em INPUTS
// para que o spread maior (topo) seja "consumido" primeiro
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)
    // embora a taxa DEX da ordem de compra não seja usada aqui, verificamos se é positiva como parte da verificação de sanidade
    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 
}
}

fonte

Também verificamos se o preço do token declarado no registro R5 e a taxa DEX por token em R6 das ordens de compra contrárias estão na faixa correta.

Cálculo do spread

Para verificar se a ordem atual recebe seu spread, precisamos calculá-lo primeiro. Com as ordens contrárias ordenadas pela quantidade de spread, começamos a "consumi-las" nessa ordem, diminuindo o número de tokens restantes nesta correspondência.
No contrato de ordem de compra:

// spread agregado que obtemos de todas as ordens contrárias (venda)
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)) {
      // o spread é nosso
      val spreadPerToken = tokenPrice - sellOrderTokenPrice
      val sellOrderSpread = spreadPerToken * tokenAmountFromThisOrder
      (returnTokensLeft - tokenAmountFromThisOrder, accumulatedFullSpread + sellOrderSpread)
    }
    else {
      // o spread não é nosso
      (returnTokensLeft - tokenAmountFromThisOrder, accumulatedFullSpread)
    }
  })._2
}

fonte

No contrato de ordem de venda, precisamos nos basear tanto no preço do token quanto na quantidade da taxa DEX para calcular quantos tokens estão naquela ordem de compra. Além disso, como não podemos deduzir a quantidade de token "vendido" nesta transação de troca a partir do valor da caixa de retorno, fazemos o cálculo do spread parametrizado com a quantidade concreta de token que saberemos mais tarde no código:

// spread agregado que obtemos de todas as ordens contrárias (compra)
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)) {
      // o spread é nosso
      val spreadPerToken = buyOrderTokenPrice - tokenPrice
      val buyOrderSpread = spreadPerToken * tokenAmountInThisOrder
      (returnTokensLeft - tokenAmountInThisOrder, accumulatedFullSpread + buyOrderSpread)
    }
    else {
      // o spread não é nosso
      (returnTokensLeft - tokenAmountInThisOrder, accumulatedFullSpread)
    }
  })._2
}

fonte

Verificação do spread recebido

Com a quantidade de spread determinada, precisamos verificar se a ordem atual realmente recebeu o spread.
No contrato de ordem de compra, verificamos se está incluído no valor da caixa de retorno:

// ramificação para correspondência total (todos os ERGs são gastos e a quantidade correta de tokens é comprada)
val totalMatching = (SELF.value - expectedDexFee) == returnTokenValue && 
  returnBox.value >= fullSpread
// ramificação para correspondência parcial, por exemplo, além dos tokens comprados, exigimos uma nova ordem de compra com ERGs para 
// a parte não correspondida desta ordem
val partialMatching = {
  val correctResidualOrderBoxValue = (SELF.value - returnTokenValue - expectedDexFee)
  foundResidualOrderBoxes.size == 1 && 
    foundResidualOrderBoxes(0).value == correctResidualOrderBoxValue && 
    returnBox.value >= fullSpread
}

fonte

No contrato de ordem de venda, assim que soubermos a quantidade de token "vendido" nesta transação de troca, verificamos se o valor da caixa de retorno inclui o spread.
No caso de correspondência total, usamos a quantidade total de token na ordem atual:

// ramificação para correspondência total (todos os tokens são vendidos e a quantidade total de ERGs recebida)
val totalMatching = (returnBox.value == selfTokenAmount * tokenPrice + fullSpread(selfTokenAmount))

fonte

No caso de correspondência parcial, sabemos a quantidade de token "vendido" da ordem residual (val soldTokenAmount = selfTokenAmount - residualOrderTokenAmount) e verificamos se o spread está incluído no valor da caixa de retorno:

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

fonte

Share post

Ergo Infrastructure DAO: Descentralizando a Espinha Dorsal do Ecossistema Ergo

Ergo Infrastructure DAO: Descentralizando a Espinha Dorsal do Ecossistema Ergo

A missão da Ergo sempre foi enraizada na descentralização, não apenas na camada de consenso, mas em toda a pilha.

Ergo Platform

13 de agosto de 2025

Mew Finance: Um Conjunto de Ferramentas DeFi Divertido para o Ecossistema Ergo

Mew Finance: Um Conjunto de Ferramentas DeFi Divertido para o Ecossistema Ergo

Mew Finance é um conjunto de aplicativos descentralizados na Blockchain Ergo.

Ergo Platform

12 de agosto de 2025

Lithos: Descentralizando a Mineração com Pools On-Chain

Lithos: Descentralizando a Mineração com Pools On-Chain

Lithos é um novo protocolo projetado para reformular como os pools de mineração funcionam, movendo-os para on-chain, dando aos min.

Ergo Platform

24 de julho de 2025

Sigma 6.0: Um Ergo Mais Inteligente e Flexível

Sigma 6.0: Um Ergo Mais Inteligente e Flexível

Sigma 6.0 é uma grande atualização proposta para a blockchain Ergo.

Ergo Platform

23 de julho de 2025

Moldando o Futuro de Rosen: Uma Chamada da Comunidade sobre Cinco Propostas Chave do Tesouro

Moldando o Futuro de Rosen: Uma Chamada da Comunidade sobre Cinco Propostas Chave do Tesouro

O co-fundador da Rosen, Armeanio, apresentou cinco novas propostas ao Tesouro de Rosen.

Ergo Platform

9 de julho de 2025

UTXO Estendido da Ergo e a Ascensão da Inteligência Econômica Artificial

UTXO Estendido da Ergo e a Ascensão da Inteligência Econômica Artificial

Uma Visão Prática para Agentes Econômicos Autônomos Agentes econômicos autônomos na blockchain da Ergo realizam trabalho útil em .

Ergo Platform

12 de maio de 2025

ErgoHACK X: Inteligência Artificial na Blockchain Ergo

ErgoHACK X: Inteligência Artificial na Blockchain Ergo

Celebrando uma Década de Inovação Descentralizada Participe do 10º aniversário do ErgoHACK e esteja na vanguarda da revolução da I.

Ergo Platform

10 de abril de 2025

Ergo Pulse com Charles Hoskinson

Ergo Pulse com Charles Hoskinson

“Quando olhamos para as coisas que Alex tem feito, a equipe tem feito e a comunidade tem feito, eles são logicamente consistentes .

Plataforma Ergo

13 de abril de 2022

Mais novo mercado de NFTs em Ergo está ganhando a atenção dos Ergonautas

Mais novo mercado de NFTs em Ergo está ganhando a atenção dos Ergonautas

Em Outubro de 2020, NFTs na blockchain Ergo foram lançados.

Plataforma Ergo

7 de abril de 2022

NIPoPoWs em Ergo: Inovações em Blockchain

NIPoPoWs em Ergo: Inovações em Blockchain

A blockchain Ergo está rapidamente se tornando conhecida dentro do espaço cripto como uma líder em desenvolvimento e inovação em b.

Plataforma Ergo

1 de abril de 2022

Prova de Trabalho, Energia e Ergo

Prova de Trabalho, Energia e Ergo

Introdução Prova de Trabalho (PoW, em Inglês) foi descrita no artigo original de Bitcoin como sendo composta de duas partes.

Plataforma Ergo

29 de março de 2022

Armazenando Ergo: SAFEW

Armazenando Ergo: SAFEW

Neste artigo, continuamos nossa série de guias para configurar e usar uma das muitas novas carteiras Ergo.

Plataforma Ergo

25 de março de 2022

Minerando Ergo no Windows

Minerando Ergo no Windows

Minerar Ergo como parte de uma pool de mineração é fácil, particularmente no Windows.

Plataforma Ergo

17 de março de 2022

What is a DAO (Decentralized Autonomous Organization)?

What is a DAO (Decentralized Autonomous Organization)?

Introduction A decentralized autonomous organization (DAO) is a new type of cooperative.

Ergo Platform

15 de março de 2022

Instruções: Cunhando um Token Não-Fungível (NFT) na Blockchain Ergo

Instruções: Cunhando um Token Não-Fungível (NFT) na Blockchain Ergo

O que é um NFT? Qualquer unidade de uma moeda corrente, como por exemplo o dólar estadunidense, é indistinguível de qualquer outr.

Plataforma Ergo

8 de março de 2022

Blockchain e a Motivação para Ergo

Blockchain e a Motivação para Ergo

Desde o nascimento de Bitcoin, tecnologia blockchain tem sofrido enormes avanços.

Plataforma Ergo

3 de março de 2022

Resultados do ErgoHack III

Resultados do ErgoHack III

Após um bem-sucedido ErgoHack III, o júri tomou sua decisão final! Há tantos projetos maravilhosos que participaram neste último.

Fundação Ergo

1 de março de 2022

A Adoção de Moedas Estáveis está no Horizonte?

A Adoção de Moedas Estáveis está no Horizonte?

Hoje, moedas estáveis lastreadas ao dólar americano (USD) somam quase U$185 bilhões de valor de mercado - um aumento de 500% des.

Plataforma Ergo

24 de fevereiro de 2022

DeFi em Ergo: Taxas usando Tokens Personalizados

DeFi em Ergo: Taxas usando Tokens Personalizados

Moedas de infraestrutura são importantes pois precisamos delas para pagas taxas de transações quando usamos sistemas financeiros.

Plataforma Ergo

22 de fevereiro de 2022

Explicativo Ergo: Aluguel de Armazenamento

Explicativo Ergo: Aluguel de Armazenamento

Resumo do Artigo: Aluguel de Armazenamento é uma taxa por armazenamento de longo prazo na blockchain Ergo.

Plataforma Ergo

18 de fevereiro de 2022

Uma Introdução a Privacidade e Segurança em Blockchain

Uma Introdução a Privacidade e Segurança em Blockchain

Depois que o primeiro artigo científico emergiu na internet em 2008, a tecnologia blockchain evoluiu tremendamente.

Plataforma Ergo

17 de fevereiro de 2022