Skip to content

0xSoftBoi/lock-mint-bridge-lab

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

lock-mint-bridge-lab

A small, annotated lock-and-mint bridge built for auditors — the one accounting invariant, the attestation-gate fix that holds it, and runnable reproductions of the bridge hacks that didn't have it.

It's the public companion to two write-ups:

This is a clean-room teaching artifact — minimal, self-contained, no external dependencies beyond forge-std. It is not audited and not for production.

The whole thing in one line

A lock-and-mint bridge is one accounting invariant:

wrapped tokens minted on the destination must never exceed the collateral locked on the source.

function invariant_supply_le_collateral() public view {
    assertLe(wrapped.totalSupply(), vault.totalLocked());
}

Break it and the bridge prints money. The classic way to break it: trust a relayer to say "a lock happened" with no sound on-chain proof that it did. That's the Ronin / Wormhole family — the most expensive bug class in the space.

The fix: an attestation gate

Every value-moving call (mint, unlock, refund) passes through a pluggable verifier over a digest that binds all the parameters plus the chain id and the contract address:

bytes32 digest = keccak256(abi.encode(
    DOMAIN, block.chainid, address(this),
    commitId, recipient, amount, sourceChainId
));
require(verifier.verify(digest, attestation), "unauthorized");
  • Binding address(this) stops a signature replaying across deployments; block.chainid stops it crossing chains; commitId stops a second mint; the domain tag stops an unlock signature being reused as a refund.
  • The verifier is pluggable (IAttestationVerifier): ECDSA on EVM destinations (EcdsaAttestationVerifier), or a post-quantum lattice verifier on a chain you control — without touching the vault/adapter logic. (Why a lattice signature, and why not wrapped in a SNARK, is the subject of the second post.)
  • A commitId can reach at most one terminal outcome {UNLOCKED, REFUNDED}, enforced on-chain — that's the cross-domain "minted and refunded" double-spend closed.

What's here

Contract Role
SourceVault Locks collateral; authority for totalLocked. Unlock/refund are gated and mutually exclusive.
MintAdapter Mints the wrapped token against an attestation; replay-protected by commitId; burns to start the return leg.
WrappedToken Destination ERC-20. Admin ≠ minter (an admin key must not be a silent parallel minter).
EcdsaAttestationVerifier Operator-whitelist ECDSA gate (EIP-2 low-s, no zero signer).

Tests (forge test)

  • test/invariant/Supply.invariant.t.sol — drives the bridge with a handler that models an honest operator and an adversarial relayer, over 512 runs × depth 100:
    • invariant_supply_le_collateral — supply never exceeds locked collateral.
    • invariant_adversary_minted_nothing — an adversary armed only with forged attestations mints exactly zero.
  • test/Bridge.t.sol — unit tests for the gate (forged / junk / tampered-amount / replay all rejected), the unlock-XOR-refund outcome, and the admin≠minter rule. Plus test_mutation_gateOff_breaksSupplyInvariant: disable the gate (revert the fix) and the same forged mint succeeds and breaks the invariant in one call — a test that can't be made to fail by removing the fix isn't testing the fix.
  • test/historical/Historical.t.sol — minimal reproductions showing the gate rejects each move: Ronin (rotated-out / non-operator key), Wormhole (no unverified mint path), Nomad (zero/default proof authorizes nothing).
forge install   # pulls forge-std
forge test      # 17 tests; invariants at 512 × 100

Scope / honesty

The supply≤collateral guarantee is, end-to-end, an operator-coordination property that the gate makes enforceable on-chain — a rogue relayer can't unlock or refund without an operator signature — not a property the contracts can prove about events on another chain by themselves. The historical reproductions are deliberately minimal stand-ins for the real exploits, not faithful forks. See SECURITY.md.

License

MIT.

About

Annotated lock-and-mint bridge for auditors — the supply<=collateral invariant, an attestation-gate fix, a 512x100 Foundry invariant suite, and Ronin/Wormhole/Nomad exploit reproductions

Topics

Resources

License

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors