Some Details About Ergo Auction House

The following is the new auction contract that supports custom tokens (e.g., SigUSD), royalties, and timestamp as auction duration:

{
  val seller = SELF.R4[Coll[Byte]].get
  val endTime = SELF.R5[Long].get
  val minBidDelta = SELF.R6[Long].get
  val currBidder = SELF.R8[Coll[Byte]].get

  // auction currency can be any token like SigUSD
  val isCurrencyERG = SELF.tokens.size == 1
  val getAuctionCurrency = {(b: Box) => {
     if (isCurrencyERG) Coll[Byte]()
     else SELF.tokens(0)._1
  }}
  val getAuctionBid = {(b: Box) => {
     if (isCurrencyERG) b.value
     else SELF.tokens(0)._2
  }}
  val currBid = getAuctionBid(SELF)

  val extendThreshold = 30 * 60 * 1000L // 30 mins
  val extendNum = 40 * 60 * 1000L // 40 mins

  // this is a bid
  val auctionLogic = if (CONTEXT.preHeader.timestamp < endTime) {
     val newSelf = OUTPUTS(0) // new auction box
     val oldBidRefundBox = OUTPUTS(1) // refund box

     // we extend the auction end time if the bid is placed near the very end
     val newEndTime = if (endTime - HEIGHT <= extendThreshold) endTime + extendNum
                      else endTime

     // preserve auctioned tokens
     newSelf.tokens(0) == SELF.tokens(0) &&
     // correct value and contract for the new box
     getAuctionBid(newSelf) >= getAuctionBid(SELF) + minBidDelta &&
     newSelf.propositionBytes == SELF.propositionBytes &&
     // shouldn't be able to add tokens - will change the currency from ERG to a worthless token
     SELF.tokens.size == newSelf.tokens.size &&
     // currency must be the same
     getAuctionCurrency(SELF) == getAuctionCurrency(newSelf) &&
     // refund the previous bidder
     oldBidRefundBox.propositionBytes == currBidder &&
     getAuctionBid(oldBidRefundBox) >= currBid &&
     // preserve the auction config
     newSelf.R4[Coll[Byte]].get == seller &&
     newSelf.R5[Long].get == newEndTime &&
     newSelf.R6[Long].get == minBidDelta &&
     newSelf.R9[Coll[Byte]] == SELF.R9[Coll[Byte]] &&
     newSelf.R7[Coll[Byte]] == SELF.R7[Coll[Byte]]

  // auction has ended
  } else {
     val winnerBox = OUTPUTS(0)
     val sellerBox = OUTPUTS(1)
     val feeBox = OUTPUTS(2)

     val artistRoyalty = OUTPUTS(3)
     val originalIssuanceBox = artistRoyalty.R4[Box].get
     val dataInput = CONTEXT.dataInputs(0)
     val auctionFee = currBid / dataInput.R4[Int].get
     val feeTo = dataInput.R5[Coll[Byte]].get
     val artistShare = currBid / dataInput.R6[Int].get

     val artistGetsHisShare = {
       originalIssuanceBox.id == blake2b256(originalIssuanceBox.bytes) && // the box has integrity
       originalIssuanceBox.id == SELF.tokens(0)._1 && // the same ID as the NFT
       getAuctionBid(artistRoyalty) >= artistShare && // gets at least the percentage defined in the auction config box
       artistRoyalty.propositionBytes == originalIssuanceBox.propositionBytes // goes to the artist
     }


     dataInput.tokens(0)._1 == auctionTwinToken &&
     feeBox.value >= auctionFee &&
     feeBox.propositionBytes == feeTo &&
     winnerBox.tokens(0) == SELF.tokens(0) &&
     winnerBox.propositionBytes == currBidder &&
     sellerBox.value >= currBid - auctionFee - maxFee * 2 &&
     sellerBox.propositionBytes == seller &&
     artistGetsHisShare
  }
  sigmaProp(auctionLogic)
}

@kushti @scalahub and others, please audit when you find the time - at least the changed/added parts.

7 Likes