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.