Skip to main content

Submit Bid

This guide will provide steps for submitting a bid to a batch auction in the Axis system 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);

The quote token is also required:

// Define the tokens used in the auction
MockERC20 quoteToken = _getQuoteToken();

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.

Then we define the amount in, amount out and recipient as variables:

// Prepare inputs
uint96 amount = 1e18;
uint96 amountOut = 2e18;
address recipient = address(0x10);

There are a number of additional steps to prepare the input parameters for bids, which are outlined in the subsequent sections.

Auction Public Key

The public key of the auction is required for bid encryption and for calculation of a public key for the bid. This is retrieved from the EncryptedMarginalPrice module for the chosen lot.

// Fetch the public key from the EncryptedMarginalPrice contract
Point memory auctionPublicKey;
{
IEncryptedMarginalPrice empModule =
IEncryptedMarginalPrice(address(auctionHouse.getModuleForId(lotId)));
IEncryptedMarginalPrice.AuctionData memory empAuctionData =
empModule.getAuctionData(lotId);

auctionPublicKey = empAuctionData.publicKey;
}

Bid Private Key and Seed

The bid private key (which should be kept secret) and seed (a randomly-generated number) are supplied as environment variables (BID_PRIVATE_KEY and BID_SEED) and used during bid encryption.

// Obtain the bid private key from the environment variable
uint256 bidPrivateKey = vm.envUint("BID_PRIVATE_KEY");
uint256 bidSeedRaw = vm.envUint("BID_SEED");
if (bidSeedRaw > type(uint128).max) {
revert("BID_SEED must be less than uint128 max");
}
uint128 bidSeed = uint128(bidSeedRaw);

Encrypt Amount Out

With the auction public key, bid private key and bid seed assembled, we can encrypt the bid amount out. The axis-utils repository contains a library to simplify bid encryption, which in turn uses the included ECIES library.

note

The bid private key should only be handled in an off-chain script, otherwise third-parties would be able to decrypt the submitted bid.

// Encrypt the bid amount out
uint256 encryptedAmountOut = EncryptedMarginalPriceBid.encryptAmountOut(
lotId, recipient, amount, amountOut, auctionPublicKey, bidSeed, bidPrivateKey
);

Auction Module Parameters

The parameters to the auction module, EncryptedMarginalPrice, are assembled into the required struct format.

// Prepare parameters for the EncryptedMarginalPrice module
IEncryptedMarginalPrice.BidParams memory empBidParams;
{
// Generate a public key for the bid
Point memory bidPublicKey = ECIES.calcPubKey(auctionPublicKey, bidPrivateKey);

empBidParams = IEncryptedMarginalPrice.BidParams({
encryptedAmountOut: encryptedAmountOut,
bidPublicKey: bidPublicKey
});
}

Combining Bid Parameters

Permit2 approval can be optionally granted. For simplicity, this example will not use Permit2 approval. Instead, the buyer must approve the spending of tokens by the AtomicAuctionHouse.

// Prepare Permit2 approval (unused)
bytes memory permit2Data = abi.encode("");

Data can be passed to the onBid callback, which is unused in this example.

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

The bid parameters are then assembled.

// Prepare parameters
IBatchAuctionHouse.BidParams memory bidParams = IBatchAuctionHouse.BidParams({
lotId: lotId,
bidder: recipient, // Who should receive the purchased tokens
referrer: _REFERRER, // The referrer (e.g. frontend) for the buyer
amount: amount, // The amount of quote tokens in
auctionData: abi.encode(empBidParams), // Auction module-specific data
permit2Data: permit2Data // Permit 2 approval (optional)
});

This would result in a bid with the following characteristics:

  • 1 quote token would be provided by the buyer
  • If the bid is successful, the payout (in base tokens) would be a minimum of 2.
  • The recipient would receive the payout instead of the _BUYER

Permit2

A purchase can optionally be performed using Permit2 approvals.

TODO

Token Funding

The BatchAuctionHouse will attempt to transfer amount quantity of quote tokens from the buyer to itself. For this reason, we need to ensure that the buyer has the required balance.

// Mint the required quote tokens
quoteToken.mint(_BUYER, amount);

If Permit2 approval has not been given, the BatchAuctionHouse needs to have spending approved:

// Approve spending of quote tokens
if (usePermit2_ == false) {
vm.prank(_BUYER);
quoteToken.approve(_batchAuctionHouse, amount);
}

Contract Call

Lastly, we call the function to bid. This must be performed as the buyer who possesses the quote tokens.

// Conduct the purchase
// The buyer must have enough quote tokens to complete the purchase
vm.prank(_BUYER);
uint64 bidId = auctionHouse.bid(bidParams, callbackData);
console2.log("Purchase completed. Bid ID:", bidId);

Unlike in atomic auctions, a bid on a batch auction will not provide an immediate payout. Payouts must be claimed manually through the claimBids() function (see guide) after the auction lot has been settled.

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

  • If the caller does not have amount quantity of quote tokens
  • If the caller has not approved the BatchAuctionHouse to spend amount quantity of quote tokens
    • This can be provided through the traditional ERC20.approve() function
    • Alternatively, Permit2 Approval can be provided
  • The auction module determines the bid to be invalid

Source Code

The source code for the guide is located in the bid-submit.s.sol file.