Skip to main content

Settle Auction

This guide will provide steps for settling a batch auction through direct integration with the contracts. This particular guide will use the EncryptedMarginalPrice auction module, which keeps the amount out of each bid a secret until after the conclusion of the auction lot.

Setup

First, define the BatchAuctionHouse that will be used. This should correspond to the same BatchAuctionHouse in which the auction lot was created.

// Define the deployed BatchAuctionHouse
IBatchAuctionHouse auctionHouse = IBatchAuctionHouse(_batchAuctionHouse);

Inputs

We obtain from the LOT_ID environment variable the lot ID that identifies each auction lot. The lot ID is a uint96 number, so should be checked before being downcast.

// Obtain the lot from the environment variable
uint256 lotIdRaw = vm.envUint("LOT_ID");
if (lotIdRaw > type(uint96).max) {
revert("LOT_ID must be less than uint96 max");
}
uint96 lotId = uint96(lotIdRaw);

The lot ID is returned from the auction creation function. See the Create Auction Guide for more details.

Private Key Submission

The auction module (in this case, EncryptedMarginalPrice) will be used a few times, so cache the module address.

// Load the EMP module
address moduleAddress = address(auctionHouse.getModuleForId(lotId));

EncryptedMarginalPrice auctions require the auction's private key to be submitted after conclusion of the auction. This enables bid decryption and eventual settlement. Axis provides a key management service that custodies the key and exposes it at the appropriate time.

Assuming that the key has been released, we submit the key to the auction module:

// Submit the private key
// The call can be performed by anyone
{
uint256 privateKey = vm.envUint("AUCTION_PRIVATE_KEY");

// Submit private key can decrypt the bids, but we will skip this
uint64 submitNumBids;
bytes32[] memory submitSortHints;

IEncryptedMarginalPrice(moduleAddress).submitPrivateKey(
lotId, privateKey, submitNumBids, submitSortHints
);
}

Bid Decryption

The next step is to decrypt the bids.

We first determine the number of bids to decrypt, as the result will be used a few times.

// Get the number of bids
uint256 numBids = IBatchAuction(moduleAddress).getNumBids(lotId);

In case there are a large number of bids, we divide the bids into batches of 100:

uint64 bidsPerBatch = 100;

After each bid is decrypted, it is inserted into a queue and sorted. Sort hints are used to reduce the gas consumption. We need to prepare an array of sort hints for each batch, set to the start (highest priority) in the queue.

// Prepare the sort hints
// This will not result in optimal gas usage
bytes32 queueStart =
0x0000000000000000ffffffffffffffffffffffff000000000000000000000001;
bytes32[] memory sortHints = new bytes32[](bidsPerBatch);
for (uint64 i = 0; i < bidsPerBatch; i++) {
sortHints[i] = queueStart;
}
note

In order to obtain the optimal hints, you can decrypt locally, iteratively build a copy of the bid queue and then find where the bid will be slotted in.

Alternatively, the Cloak API handles this.

We then decrypt and sort the bids:

// Decrypt the bids in 100-bid batches
// If the number of bids is less than 100,
// numBatches will be 0 due to integer division and rounding down
IEncryptedMarginalPrice empModule = IEncryptedMarginalPrice(moduleAddress);
uint256 numBatches = numBids / bidsPerBatch;
for (uint64 i = 0; i <= numBatches; i++) {
// The call can be performed by anyone
empModule.decryptAndSortBids(lotId, bidsPerBatch, sortHints);
}

Contract Call

The settle() function calls the onSettle callback, which may take input data. As the callback is unused in this example, it can remain empty.

// Define callback data (unused)
bytes memory callbackData = abi.encode("");

Lastly, we call the settle() function:

// Perform the settlement
// The call can be performed by anyone
auctionHouse.settle(lotId, numBids, callbackData);
console2.log("Settlement completed. Lot ID:", lotId);

It is worthwhile noting that the settle call may fail for the following reasons:

  • The lot ID is invalid
  • The lot is in a state that does not support settlement

Source Code

The source code for the guide is located in the settle.s.sol file.