Table of contents
- How it works
- Hiding bids via “splitting”
- Aztec Connect for anonymous bidding
- Communicating a commitment
- inputValue
- How it compares
- Gas costs
- User experience
- Privacy
Editor’s note: This piece is part of our ongoing series on all things auctions for web3. Part 1 was an overview of technical challenges (and opportunities) specific to designing on-chain auctions. Part 2 was a piece on clearing the market and avoiding gas wars. Part 3 and Part 4 explore how auction theory translates into practice by introducing two implementations of sealed-bid auctions.
Over the course of this series, we’ve explored different strategies for bridging the gap between auction theory and what can be built on-chain, each with its unique nuances. In particular, we’ve focused on implementing sealed-bid, second-price (Vickrey) auctions, which have been used for decades in sales of art, timber, and ad space. But we rarely see them implemented trustlessly using smart contracts, in part due to the difficulty of implementing private bids. The transparent nature of public blockchains can prevent dishonest intermediaries from censoring bids or manipulating auction results; but they also impose challenges on developers, who must find novel solutions for protecting their users’ privacy.
In our previous auction implementations, we’ve used two approaches to keeping on-chain bids private: The first (OverCollateralizedAuction) conceals bid values using overcollateralization (where bidders lock up more collateral than required by the bid), protecting privacy at the expense of capital efficiency. This led us to our second design (SneakyAuction), which uses the CREATE2
opcode to camouflage bids among other transfers on the blockchain. But empirical analysis showed that this approach would not be effective at hiding very large bids that deviate from amounts typically transacted on Ethereum.
In this post, we return with a novel cross-chain approach (called AztecConnectAuction) that can provide privacy to bids of any size, without requiring extra collateral. Relying on multiple blockchains achieves functionality that wouldn’t be possible on any single chain alone, and provides a different set of benefits and tradeoffs for developers to consider when implementing their own protocols. In our case, we use Aztec Connect to leverage the anonymity of Aztec’s ZK rollup, while retaining the benefits of Ethereum L1 settlement.
We’ve added the implementation to our Auction Zoo repository on GitHub, alongside our previous auction implementations. We hope you’ll build on these, share your ideas, and send us your feedback.
First a quick refresher on the Vickrey auction: Bidders submit private bids (traditionally for a single item) to the auctioneer in sealed envelopes. The highest bidder wins, but pays the second-highest bid. To translate these characteristics on-chain, our new auction follows the same blueprint as before: Bids are collateralized and committed to during the “bidding phase”, in such a way that doesn’t reveal their precise values until they are revealed in the subsequent “reveal phase”. Once the reveal phase is over, the auction can be ended and the winning bidder pays the seller in the amount of the second-highest bid (the Vickrey payment rule). Our auction implementations differ in how they keep bids hidden during the bidding phase — this time, we will use a mechanism that is capital efficient even for large bids.
In OverCollateralizedAuction, privacy relied on overcollateralization to hide the exact bid value. In SneakyAuction, we were able to achieve bid privacy by using the CREATE2 opcode to hide the intent of a bid transaction — the value of the transaction is publicly visible, but the transaction is indistinguishable from a normal ETH transfer.
An alternative approach is to obfuscate the value of a bid by splitting it into multiple, unlinkable transactions. Imagine an auctioneer receives three checks for $100, $200, and $300, each signed with invisible ink. All three could have been signed by the same bidder, tendering a bid for $600. Or, they could be three separate bids, signed by three respective bidders. Or, there might be two bidders, one of whom has split their bid into two checks. Even if the check amounts are public, the underlying bid values are hidden until the signatures are revealed.
Translating this on-chain, bidders can send their bid collateral in multiple payment transactions from unlinkable wallet addresses. With each transaction, they provide a hash commitment that can later be opened to link the pieces back together — for example, the commitment might be computed as keccak256(totalBidValue, bidderId, nonce)
, where payments with the same bidderId
and totalBidValue
are part of the same bid. The random nonce
value prevents bids from being recovered by brute-force.
The underlying concept makes sense as a way to preserve bidder privacy, but a downside of this approach is that it requires the bidder to have multiple unlinked wallet addresses, funded with the amounts they intend to use for their bid. This can be difficult to achieve in practice — even evading an amateur Etherscan sleuth requires care, let alone institutional forensic tools like Chainalysis or TRM.
Worse yet, revealing a bid publicly and permanently links all the addresses that were used. So to bid in a subsequent auction, you would need a fresh set of unlinked addresses. To solve this problem, we turn to another tool: Aztec Connect.
Aztec Connect is a framework that enables users to access smart contracts on Ethereum L1 from the privacy of Aztec’s ZK rollup. The rollup processor contract on Ethereum can make calls to compatible bridge contracts that adhere to a specific interface, allowing Aztec users to interact with those contracts with their escrowed assets. A bridge contract typically plugs into an existing DeFi protocol on L1; for example, bridges have been implemented for Uniswap, Lido, and Element Finance.
The key property that Aztec Connect provides our auction is anonymity: the Aztec account that originated the Aztec Connect transaction is hidden from observers. Anonymity allows us to emulate the scheme using unlinked addresses described in the previous section. Revealing a bid won’t reveal the Aztec account used to place the bid, so the same account can be used across multiple auctions without compromising privacy.
Anonymity alone is not sufficient to place a bid –– we also need to associate a hash commitment with each payment which can later be opened during the reveal phase. Recall that in OverCollateralizedAuction, the commitment is provided as a parameter to the commitBid</code< function, which records the commitment and collateral amount in storage. In SneakyAuction, collateral is sent to an undeployed
CREATE2
contract, the address of which serves as the hash commitment.
In an Aztec Connect bridge contract, all cross-chain calls are routed through the bridge’s convert
function (see below). The asset parameters usually disambiguate the intent of the call –– in the Lido bridge, for example, if the input asset is ETH the convert
function will convert it to wrapped stETH; if the input asset is wrapped stETH the convert
function will convert it to ETH. To place a bid in our auction contract, bidders would invoke the convert
function with ETH as the input asset (once per payment).
Interface of the convert
function (from the Aztec docs)
Sometimes, the assets alone are not enough to determine the intended behavior of the call. The Uniswap bridge, for example, lets users privately swap tokens on Uniswap v3. To do so, the user must specify the path of the trade, i.e. which asset pools to swap against –– to obtain the best rate selling ETH for DAI, you might want to use the 30 bps pool to trade ETH into USDC, then the 1 bps pool to trade USDC into DAI. To specify this trade path, the user must provide the intermediate token (USDC) and fee tiers (30 bps and 1 bps) for the two pools used.
The convert
function’s auxData
parameter is how this “auxiliary data” is passed to the bridge. Note, however, that auxData
is only 64 bits! (This is an artifact of the tradeoff between calldata size and proof costs –– more auxData
would require longer proving times and higher gas costs. 64 bits strikes a reasonable balance for most use cases) This means that bridges need to be frugal in how they use their auxiliary data: the Uniswap bridge encodes paths by representing tokens and fee tiers using just a couple of bits each (the encoding scheme is described here).
Our first instinct might be to use auxData
for our hash commitment. But auxData
is only 64 bits, which is not enough to be cryptographically secure. Clever encoding tricks won’t work here –– any attempt to compress the commitment would compromise its security. In order to circumvent the limitations of auxData
, we will need to get creative.
Other than auxData
, the only other parameter of convert
that can be used to encode arbitrary data is inputValue
–– the amount of input asset being used in the Aztec Connect transaction. But can we use inputValue
to encode our hash commitment? For the parameter to encode a cryptographically secure hash commitment (around 256 bits), the bidder must have a lot of whatever input asset is being used –– so much so that any asset with real monetary value is out of the question.
Fortunately, Aztec Connect has the notion of virtual assets, which are used to represent assets that the Aztec network doesn’t support. Most importantly for our use case, a bridge can mint an arbitrary amount of a virtual asset in a convert
call. This leads us to the following two-transaction process for communicating a hash commitment via Aztec Connect:
inputValue
is interpreted as the hash commitment and stored, along with the current timestamp. The life cycle of an Aztec Connect auction
The bidder deposits ETH and receives the virtual asset.
The bidder records a hash commitment by burning some amount of the virtual asset received in the previous step.
With this two-step procedure, we circumvent the size constraint of auxData
and associate each payment with a secure hash commitment. The downside of this approach is that it requires two transactions per payment.
Now that we have a feasible implementation for our Aztec Connect auction, we can compare it to our two previous implementations (OverCollateralizedAuction and SneakyAuction) –– looking at gas costs, user experience, and privacy in particular.
AztecConnectAuction’s createAuction
, endAuction
, and withdrawCollateral
functions have comparable gas costs to our previous implementations. The costs of committing and revealing a bid scales with the number of pieces the collateral is split into. The following table shows the estimated gas costs for a bid that has been split into two pieces.
OverCollateralizedAuction | SneakyAuction | AztecConnectAuction | |
createAuction | 132,625 | 112,296 | 113,507 |
commitBid | 46,120 | 21,000 (cost of an ETH transfer) | 179,656 (bid split into two payments) |
revealBid | 33,728 | 135,741 | 47,939 (bid split into two payments) |
endAuction | 57,652 | 87,340 | 53,513 |
withdrawCollateral | 30,426 | 65,825 | 50,019 |
Approximate gas costs of different operations, based on Foundry unit tests and Aztec Connect gas estimates
Although AztecConnectAuction follows a similar flow to our previous implementations (bidding phase, reveal phase, auction ends), there are some UX quirks that come with operating cross-chain.
The seller doesn’t need to bridge any assets or make any Aztec transactions, and is paid out on L1 when the auction ends. The bidders, on the other hand, perform some actions on Aztec and others on Ethereum:
inputValue
trick isn’t enough), so it must be done directly on Ethereum. withdrawCollateral
function on the auction contract. If they want to keep their ETH on L2 (e.g. to bid in future auctions), they can withdraw to their Aztec account with an Aztec Connect transaction. Finally, two additional practical complexities: First, commiting to a bid requires two Aztec Connect transactions per piece of collateral. We could abstract this away on the frontend into a single user interaction for a more seamless experience. Second, these transactions should be distributed throughout the bidding period to maximize privacy. We could delegate this task to a bot, which would submit the transactions on the bidders’ behalf.
Overall, the user experience for sellers is nearly identical to that of our previous implementations. The experience for bidders is more complicated –– they need to make multiple transactions to place a bid and they must interact with both Ethereum and Aztec over the course of the auction.
One notable advantage AztecConnectAuction has over our previous two implementations is that it can plausibly provide privacy for high-value auctions without compromising capital efficiency. In OverCollateralizedAuction, the cost of overcollateralization is exacerbated when the bids themselves require significant collateral. In SneakyAuction, privacy relies on exogenous ETH transfers, which usually aren’t large enough to hide high-value bids.
Like SneakyAuction, AztecConnectAuction also allows bidders to overcollateralize their bids for an additional layer of privacy. Bid privacy also scales with the number of concurrent auctions — if two auctions are in their bidding phases at the same time, one auction’s bids serve as noise for the other. Bidders remain anonymous during the bidding period, and the total number of bids is hidden.
That said, AztecConnectAuction has its own caveats. Bidders need to be mindful of the on-chain footprints they leave, which may compromise their privacy:
These considerations are not unique to our auction; bridging assets between a private and public blockchain inherently introduces potential for privacy leakage (Aztec’s documentation provides more details on the nuances of cross-chain privacy). The good news is that the proliferation of Aztec-compatible applications like this auction creates more reason to keep assets on Aztec long-term, rather than bridging back and forth. More activity on Aztec grows its privacy sets, which strengthens the value proposition of applications built on Aztec, which generates more activity –– a virtuous cycle.
***
This foray into on-chain sealed-bid auctions took us outside the walls of Ethereum L1, leveraging Aztec’s private rollup to anonymize bidding activity. Our third recipe for sealed-bid auctions –– anonymity plus collateral-splitting –– has its own privacy characteristics and nuances, which we compared to our previous designs, and that others may want to compare to their own approaches.
Our auction implementation is one of a handful of natively cross-chain smart contract protocols, which rely on operating across multiple blockchains to achieve functionality that wouldn’t be possible on any single chain alone. As cross-chain infrastructure continues to mature, we expect to see the emergence of natively cross-chain protocols which utilize the unique capabilities of different blockchains. We hope our sealed-bid auction provides some inspiration for what is possible here.
Acknowledgements: Thank you to the Aztec team (Joe Andrews, Josh Crites) for answering my many questions about Aztec Connect; Matt Gleason and Noah Citron for reviewing the code; and to Daejun Park, Joseph Bonneau, Scott Kominers, Sonal Chokshi, and Tim Roughgarden for valuable feedback on this post. Special thanks to Stephanie Zinn for editing.
***
The views expressed here are those of the individual AH Capital Management, L.L.C. (“a16z”) personnel quoted and are not the views of a16z or its affiliates. Certain information contained in here has been obtained from third-party sources, including from portfolio companies of funds managed by a16z. While taken from sources believed to be reliable, a16z has not independently verified such information and makes no representations about the current or enduring accuracy of the information or its appropriateness for a given situation. In addition, this content may include third-party advertisements; a16z has not reviewed such advertisements and does not endorse any advertising content contained therein.
This content is provided for informational purposes only, and should not be relied upon as legal, business, investment, or tax advice. You should consult your own advisers as to those matters. References to any securities or digital assets are for illustrative purposes only, and do not constitute an investment recommendation or offer to provide investment advisory services. Furthermore, this content is not directed at nor intended for use by any investors or prospective investors, and may not under any circumstances be relied upon when making a decision to invest in any fund managed by a16z. (An offering to invest in an a16z fund will be made only by the private placement memorandum, subscription agreement, and other relevant documentation of any such fund and should be read in their entirety.) Any investments or portfolio companies mentioned, referred to, or described are not representative of all investments in vehicles managed by a16z, and there can be no assurance that the investments will be profitable or that other investments made in the future will have similar characteristics or results. A list of investments made by funds managed by Andreessen Horowitz (excluding investments for which the issuer has not provided permission for a16z to disclose publicly as well as unannounced investments in publicly traded digital assets) is available at https://a16z.com/investments/.
Charts and graphs provided within are for informational purposes solely and should not be relied upon when making any investment decision. Past performance is not indicative of future results. The content speaks only as of the date indicated. Any projections, estimates, forecasts, targets, prospects, and/or opinions expressed in these materials are subject to change without notice and may differ or be contrary to opinions expressed by others. Please see https://a16z.com/disclosures for additional important information.