Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,17 @@ Focused demos of individual capabilities — on-curve delegation, actions, ephem
<p><em>Up/down SPL-token prediction flow using session keys and an ER price oracle.</em></p>
</blockquote>
</td>
<td valign="top" width="33%"></td>
<td valign="top" width="33%">
<blockquote>
<p><strong><a href="./sealed-auction/">🔐 Sealed Auction / Anchor</a></strong></p>
<p>
<img src="https://img.shields.io/badge/Anchor-5243AA?style=flat-square" alt="Anchor"/>
<img src="https://img.shields.io/badge/Privacy-9333ea?style=flat-square" alt="Privacy"/>
<img src="https://img.shields.io/badge/SPL%20Token-9945FF?style=flat-square" alt="Tokens"/>
</p>
<p><em>Private sealed bids with SPL Token escrow.</em></p>
</blockquote>
</td>
<td valign="top" width="33%"></td>
</tr>
</table>
Expand Down
3 changes: 2 additions & 1 deletion scripts/projects.sh
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

REGULAR_PROJECTS=(anchor-counter binary-prediction crank-counter ephemeral-account-chats magic-actions oracle-priced-purchase pinocchio-counter rust-counter session-keys spl-tokens)
VRF_PROJECTS=(rewards-delegated-vrf roll-dice pinocchio-roll-dice)
TEE_PROJECTS=(private-counter pinocchio-private-counter rock-paper-scissor)
TEE_PROJECTS=(private-counter pinocchio-private-counter rock-paper-scissor sealed-auction)

ALL_PROJECTS=("${REGULAR_PROJECTS[@]}" "${VRF_PROJECTS[@]}" "${TEE_PROJECTS[@]}")

Expand All @@ -39,6 +39,7 @@ project_dir() {
private-counter) echo "private-counter/anchor" ;;
pinocchio-private-counter) echo "private-counter/pinocchio" ;;
rock-paper-scissor) echo "rock-paper-scissor/anchor" ;;
sealed-auction) echo "sealed-auction/anchor" ;;
oncurve-delegation) echo "oncurve-delegation/client" ;;
*) echo "" ;;
esac
Expand Down
69 changes: 69 additions & 0 deletions sealed-auction/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# Sealed Auction

Private first-price sealed-bid auction using a MagicBlock private Ephemeral Rollup and SPL Token
escrow.

The seller escrows a fixed Token A lot in an L1 SPL token account owned by the auction PDA. Bidders
pre-fund Token B however they want, can consolidate balances on the ER, and then submit hidden,
fully collateralized Token B bids on the private ER. After the deadline, the program reads the
private bid accounts on-chain, selects the highest bid, and releases the L1 Token A lot to the
winner. Token B proceeds and loser refunds are settled on the ER from a delegated auction-owned
Token B escrow, while auction-sponsored private bid accounts are closed before the auction PDA is
undelegated.

## What This Teaches

- Private ER permissions for hidden bid state.
- L1 SPL Token escrow for a public seller lot.
- Auction-sponsored ER-only bid PDAs plus an auction-owned delegated Token B escrow.
- Count-checked bid scanning and cleanup-gated auction undelegation.

## Flow

1. `initialize_auction` creates the auction, parks the seller's Token A in an auction PDA ATA,
creates the auction PDA's Token B ATA/eATA, delegates that Token B escrow to the ER, and preloads
sponsor lamports into the auction PDA.
2. Bidders can pre-fund and consolidate Token B outside the auction using normal delegated SPL token
transfers.
3. `delegate_auction` moves the count-only auction state to ER; auction state remains public because
it does not store bid amounts or bidder keys.
4. `place_bid` creates an auction-sponsored `Bid` PDA and moves Token B from the bidder's ER balance
into the auction-owned Token B escrow.
5. `init_bid_permission` immediately attaches private PER access to the created bid PDA.
6. `end_auction` requires exactly `bid_count` unique valid bid PDAs, scans them on ER, and records
the winner without undelegating the auction.
7. `settle_winning_bid` pays the winning Token B amount from the auction escrow to the seller on ER
and closes the winning bid account back to the auction PDA.
8. Losing bidders run `claim_refund` on ER; refunds are paid from the auction escrow and close losing
bid accounts back to the auction PDA.
9. `undelegate_auction` is allowed only after every accepted bid has been closed.
10. `finalize` runs on L1 after auction undelegation and transfers Token A to the winner.

No-bid auctions can be undelegated immediately after `end_auction`, then use `reclaim_unsold_lot`.

## Run

From this directory:

```bash
yarn build
yarn test:local
```

`yarn test:local` runs the deterministic account-graph checks without requiring a live local stack.
Those checks assert the auction-sponsored count-only instruction surface: no separate sponsor PDA,
no bidder key array in auction state, `end_auction` stays on ER, and `undelegate_auction` remains the
only commit/undelegate step. Under the repository harness, the localnet tests also verify Token A L1
custody and that sponsor lamports are preloaded into the auction PDA during initialization.
The full privacy and settlement walkthrough needs the standard MagicBlock local stack and QFS/TEE
endpoints from `../../scripts/local-env.sh`:

```bash
yarn setup
RUN_SEALED_AUCTION_LIVE=1 yarn test:local
```

## Out Of Scope

Reserve prices, Vickrey pricing, Dutch auctions, cranks, session keys, unbounded bidder sets, and a
frontend UI.
32 changes: 32 additions & 0 deletions sealed-auction/anchor/Anchor.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
[toolchain]
anchor_version = "1.0.2"

[features]
resolution = true
skip-lint = false

[programs.devnet]
sealed-auction = "F4vB5Ki7ZWnkht1shp2TCHG7GszLxRZ6pbizGNQecmor"

[programs.localnet]
sealed-auction = "F4vB5Ki7ZWnkht1shp2TCHG7GszLxRZ6pbizGNQecmor"

[registry]
url = "https://api.apr.dev"

[provider]
cluster = "devnet"
wallet = "~/.config/solana/id.json"

[[test.genesis]]
address = "DELeGGvXpWV2fqJUhqcF5ZSYMS4JTLjteaAMARRSaeSh"
program = "tests/fixtures/dlp.so"
upgradeable = false

[[test.genesis]]
address = "SPLxh1LVZzEkX99H6rqYizhytLWPZVV296zyYDPagv2"
program = "tests/fixtures/ephemeral_token_program.so"
upgradeable = false
Comment thread
coderabbitai[bot] marked this conversation as resolved.

[scripts]
test = "yarn run ts-mocha -p ./tsconfig.json -t 1000000 tests/sealed-auction.ts"
Loading
Loading