4 → 2
steps to run it, after one-time setup
1 tx
token-standard dvp settlement
17 + 9
foundry + daml tests, green
0
lines of privacy plumbing in daml
built on the latest stack: daml 3.4 on canton · canton network token standard (cip-0056) · solidity 0.8.35
Watch the privacy hold
Place a sealed bid. See exactly what each party is, and isn't, allowed to read.
Four parties, four ledgers. The auctioneer co-signs every bid, so it sees all of them. Each bidder signs only their own. Flip to EVM and every redaction bar drops at once: the difference between a private ledger and a public one, in a click. Then settle: the winner's funds pay the seller and losers are refunded atomically, and even then you only ever see your own money move.
Live ledger · 0 sealed bids
Each Bid is shared only with the auctioneer and its bidder. Watch a bid appear in full for them, and as a redacted bar for everyone else.
Alice
Bidder
- no bids yet
Bob
Bidder
- no bids yet
Carol
Bidder
- no bids yet
Auctioneer
Auctioneer
- no bids yet
Redaction bars are a teaching aid. On a live Canton deployment, a sealed bid never reaches a non-stakeholder's participant node at all. They wouldn't see even a placeholder.
But can the auctioneer cheat?
If only the auctioneer sees the bids, what stops it crowning the wrong winner?
Privacy puts every bid in the auctioneer's hands, which raises the obvious objection. The answer is on the ledger: settlement has to prove it counted every bid, so the winner is the true high bid, not just a trusted one.
✓ enforced
It can't drop a higher bid
Every sealed bid bumps a registry-signed counter the moment it is placed. To settle, the auctioneer must hand back exactly that many bids, so the set is provably all of them, never a chosen few.
↩ reclaimable
Worst case is a stall, not a stolen win
A misbehaving auctioneer can refuse to settle, but it can never crown a loser. Once the deadline passes, every bidder reclaims their own locked funds without it.
the tradeoff
Trust sits on the neutral registry
The count is vouched for by the token issuer, not the auctioneer, because a fully private ledger cannot prove completeness on its own. The EVM side turns the same dial the other way: it reveals every bid so anyone can check.
the same privacy, in code
on the evm, privacy is machinery. on canton, it's the type.
The Solidity contract spends commit, reveal, deposits, and a timeline to fake a secret. The Daml template just names who is allowed to see the bid, and the rest disappears.
// commit phase: hide the bid behind a hash + a deposit
function commit(bytes32 blindedBid) external payable onlyBefore(biddingEnd) {
if (blindedBid == bytes32(0)) revert EmptyCommitment();
if (bids[msg.sender].blindedBid != bytes32(0)) revert AlreadyCommitted();
bids[msg.sender] = Bid({blindedBid: blindedBid, deposit: msg.value, revealed: false});
}
// reveal phase: open it later, and now it is public forever
function reveal(uint256 value, bytes32 secret) external onlyAfter(biddingEnd) onlyBefore(revealEnd) {
Bid storage bid = bids[msg.sender];
if (bid.blindedBid == bytes32(0)) revert NothingCommitted();
bool valid = bid.blindedBid == _commitmentOf(value, secret)
&& bid.deposit >= value && value > 0;
if (valid && value > highestBid) {
highestBid = value; highestBidder = msg.sender;
}
// ... refunds, pendingReturns, withdraw, forfeiture
}-- a single sealed bid, co-signed by the auctioneer and the
-- bidder and nobody else. it carries the bidder's locked
-- funds as a token-standard Allocation.
template Bid
with
auctioneer : Party
auctionId : Text
item : Text
bidder : Party
beneficiary : Party
amount : Decimal
allocation : ContractId Allocation
where
signatory auctioneer, bidder
ensure amount > 0.0
-- the signatory set is the entire privacy modelThe translation
Everything you'd reach for in Solidity, and what it becomes.
✕ deleted
The reveal phase
No hash to commit to, so no second window to reveal it. A bid is born private.
✕ deleted
Forfeiture
Losing bidders aren't punished. Their locked funds are returned atomically once the auctioneer settles, so there's no 'reveal or lose your deposit' threat.
✕ deleted
The pull-payment dance
Settlement is one atomic transaction. The winner pays the seller and losers are refunded together, so there's no escrow-then-withdraw step and no reentrancy surface.
the lifecycle
Two timed windows and a forfeiture rule. Or two transactions.
Privacy is the headline, but the cost shows up in the lifecycle. The EVM auction runs across two timed phases and a string of transactions, and punishes anyone who forgets to reveal. On Canton the same auction is a bid and an atomic settlement.
Solidity / EVM
public- deploy the contractsetup
the commit and reveal deadlines are fixed up front
- commit windowtimed
each bidder sends a transaction carrying a hashed bid and a deposit
- reveal windowforfeit risk
each bidder sends a reveal transaction; miss it and the deposit is forfeited
- auctionEnd()tx
a transaction tallies the revealed bids and picks the winner
- withdraw()tx
every loser pulls their own refund in a separate transaction
two timed windows, up to 2N + 2 transactions
deposit forfeited if a bidder never reveals
Daml / Canton
private- create the auctionsetup
the auctioneer invites bidders; a single settle deadline
- place bid1 tx
seals the bid and locks the funds as a token-standard holding, in one transaction
- settleatomic
atomic delivery-versus-payment once the auctioneer settles: the winner pays the seller and losers are refunded, recorded together
two steps, one atomic settlement
no reveal window, no forfeiture, no pull-payment
run it yourself
From reading about it to running it, in one command.
Everything on this page is reproducible from the repo. No Docker: a real Canton node boots in-process, the token-standard packages are vendored, and the scripts find your toolchain for you.
$ git clone github.com/Giri-Aayush/solidity-to-daml-confidential-auction$ cd solidity-to-daml-confidential-auction$ make test # 17 solidity + 9 daml tests, green$ make canton # the auction on a real canton node$ make web # this site, locally
make test
Runs both suites, the same thing CI runs on every push. The token-standard packages are vendored, so it builds offline.
make canton
Boots a real Canton node in-process (no Docker), deploys the auction, and runs the privacy + DvP proof on the live ledger, then cleans up.
make web
Serves this explainer locally, so you can read the code and poke the demo side by side.