A coat-check ticket for your coins
Imagine you walk into a theatre with a heavy winter coat. You can't carry it into the auditorium, so you hand it to the coat-check desk and they give you a small numbered ticket. The ticket isn't your coat — it's a claim on your coat. You can put it in your pocket, even hand it to a friend, and whoever holds the ticket can walk up later and redeem the real coat. The coat never moved; only the claim did.
That is exactly what a bridge does. A bitcoin lives only on the Bitcoin chain — its existence is an entry in Bitcoin's ledger, and no message you send to Ethereum can teleport it there. So a bridge locks your real BTC on Bitcoin (the coat in the cloakroom) and mints a matching token on Ethereum (the ticket). That minted token is called a wrapped token, and it is an IOU: it has value only because you believe you can hand it back and redeem the original.
Lock-and-mint, step by step
The lock-and-mint cycle has four moves, and the round trip is symmetric: minting is gated by a lock, and unlocking is gated by a burn. Nothing is ever created out of thin air, and nothing the bridge controls is ever in two places at once.
- Lock. You send your real token into the bridge's vault on chain A. The vault doesn't destroy it — it holds it as collateral and emits a `Locked` event recording the amount and your address on chain B.
- Mint. Off-chain watchers (relayers / a validator set) observe that `Locked` event, and once they're convinced it's final, they instruct the wrapped-token contract on chain B to `mint` you the same amount of the IOU token.
- Burn. Later, to go home, you call `burn` on chain B. The wrapped token is destroyed — supply drops — and a `Redeem` event records how much and where you want the original sent back.
- Unlock. The bridge proves to the chain-A vault that the burn happened, and the vault releases your original token from collateral. The coat comes back out of the cloakroom; the ticket is shredded.
The single invariant that keeps the whole thing honest is conservation of supply: at every instant, the amount of wrapped token in circulation on chain B must equal the amount of real token locked in the vault on chain A. Start with 1,000 BTC locked and 1,000 wBTC minted. Someone deposits 1 more BTC → the bridge mints 1 wBTC → now 1,001 and 1,001. Someone burns 5 wBTC → the bridge releases 5 BTC → now 996 and 996. minted == locked, always. The moment minted exceeds locked, the IOU is under-collateralized — which is the on-chain signature of theft or a bug.
The wrapped token is only as good as its backing
The canonical real-world example is WBTC: real bitcoin is held in custody, and an ERC-20 wrapped asset of the same name is minted on Ethereum so that BTC's value can be used inside Ethereum's DeFi. Because the BTC sits in publicly known addresses, anyone can audit the reserves directly on the Bitcoin chain and check that they match the WBTC supply on Ethereum — a proof of reserves. The peg is not magic; it is this auditable 1:1 backing plus the right to redeem.
Here is the sharp edge of the IOU idea. Suppose 1,001 wBTC circulate but the vault is drained so only ~0 BTC remain behind them. Each wBTC is now a claim on nothing, and the market re-prices it toward zero — the wrapped token depegs. This is not hypothetical: when a major bridge lost 120,000 wrapped ETH to a hack, its backers had to inject 120,000 ETH to refill the reserves and hold the peg, precisely because a wrapped token with no collateral is just a worthless ticket for a coat that's gone. Holding a wrapped token means trusting that the lock on the other side is real and stays real.
The two halves of a bridge, in code
Let's make it concrete with a minimal EVM-to-EVM bridge (e.g. locking an ERC-20 on an L1 to use it on an L2 — a canonical bridge does essentially this). Chain A holds the vault; chain B holds the wrapped token. Read them as a pair: the vault never burns, the wrapper never custodies.
// ===== Chain A: lock real tokens; never destroy them =====
contract BridgeVault {
IERC20 public immutable token;
address public immutable operator; // who is allowed to authorize unlocks
mapping(bytes32 => bool) public processed; // replay guard for proven burns
constructor(IERC20 _token, address _operator) {
token = _token; operator = _operator;
}
// 1) Deposit -> tokens are LOCKED here as collateral.
function lock(uint256 amount, address recipientOnB) external {
token.transferFrom(msg.sender, address(this), amount);
emit Locked(msg.sender, recipientOnB, amount);
}
// 4) After a valid burn on chain B is proven, release the originals.
function unlock(bytes32 burnId, address to, uint256 amount) external {
require(msg.sender == operator, "not authorized");
require(!processed[burnId], "replay"); // each burn redeems exactly once
processed[burnId] = true;
token.transfer(to, amount);
emit Unlocked(burnId, to, amount);
}
event Locked(address indexed from, address recipientOnB, uint256 amount);
event Unlocked(bytes32 indexed burnId, address to, uint256 amount);
}// ===== Chain B: mint the IOU on deposit, burn it on exit =====
contract WrappedToken is ERC20 {
address public immutable bridge; // the only minter
constructor(address _bridge) ERC20("Wrapped TOKEN", "wTKN") {
bridge = _bridge;
}
modifier onlyBridge() { require(msg.sender == bridge, "not bridge"); _; }
// 2) Called after the relayer proves a Locked event on chain A.
function mint(address to, uint256 amount) external onlyBridge {
_mint(to, amount); // supply rises by exactly `amount`
}
// 3) Burn to go home; the relayer carries this proof back to chain A.
function burn(uint256 amount, address recipientOnA) external {
_burn(msg.sender, amount); // supply falls by exactly `amount`
emit Redeem(msg.sender, recipientOnA, amount);
}
event Redeem(address indexed from, address recipientOnA, uint256 amount);
}Stare at these two contracts and you'll notice the entire security of the bridge collapses to two privileged powers: who may call `mint` on chain B, and who may call `unlock` on chain A — both gated by the same `operator`/`bridge` authority. The cryptography of locking is the easy part; that `operator` is the soft underbelly. Exactly what that authority is — a multisig, a validator set, a light client, a zk proof — is the subject of the next guide, and it is where almost every real bridge has bled.
Who holds the keys? Custodial vs non-custodial
In our code the locked tokens live inside a contract, but something still has to be trusted to flip `mint` and `unlock` on. The first axis people use to classify bridges is where custody of the locked asset lives.
- Custodial (trusted). A named entity holds the real asset and the keys that authorize minting — for WBTC, a custodian holds the bitcoin and a merchant network mints/burns. You trust an off-chain organization not to abscond, get hacked, or be compelled by force. This is a trusted bridge: simple, legally accountable, but a single human-shaped point of failure.
- Non-custodial (contract-held). No single person holds the coins; they sit in a smart contract whose release rules are code. But read that carefully — 'non-custodial' is not the same as 'trustless'. If `mint`/`unlock` are authorized by an N-of-M multisig or a validator set, you've merely spread custody across a committee. Compromise the committee and the contract obeys.
- Trust-minimized. The strongest designs make `unlock` provable rather than authorized: a trustless bridge verifies a light-client proof or a validity proof that the burn really happened on the other chain, so security reduces to the chains' own consensus instead of an extra committee. Powerful, but heavy to build and gas-expensive to verify.
Three families of bridges (and one that isn't)
Lock-and-mint is the archetype, but it isn't the only plumbing. Knowing the family tells you what you actually receive on the far side — a wrapped IOU, a native token, or someone else's inventory.
- Lock-and-mint (what we built): lock on A, mint a wrapped IOU on B; burn on B to unlock on A. The destination token is not the canonical asset — it's a bridge-specific claim, and different bridges mint different, non-fungible-with-each-other versions.
- Burn-and-mint: when one issuer controls the token on every chain, you skip the IOU entirely — burn native USDC on A, mint native USDC on B (Circle's CCTP works this way). Both sides are the real thing, so there's no wrapped depeg risk; the trust shifts to the issuer's attestation that the burn occurred.
- Liquidity-network (lock-and-unlock): pools of the real asset sit on both chains. You deposit on A and withdraw the genuine token from a pool on B — fast, and you get the canonical asset, but it's capped by available liquidity and the pools must be funded and rebalanced.
And the one that isn't a bridge: an atomic swap. Two people who each already have a coin on opposite chains can trade without any bridge, vault, or wrapped token, using two hash-time-locked contracts. Alice locks her coin on chain A behind a hash; Bob locks his on chain B behind the same hash; whoever reveals the secret preimage to claim one side mathematically reveals it to the other, so either both legs complete or both refund after a timeout. It's beautifully trust-minimized — but it only swaps existing assets between willing counterparties; it can't manufacture a representation of BTC on a chain that has none. That limitation is exactly why lock-and-mint bridges exist.
What you're really carrying across
Step back and the picture is honest but uncomfortable. A lock-and-mint bridge doesn't move your asset; it freezes the original and issues a synthetic claim somewhere else. That synthetic is only money for as long as the lock is intact and the redemption right is honored. You've added a new dependency on top of both chains: the bridge itself.
So the mantra to carry into the next guide: a wrapped token is an IOU, an IOU is a promise, and a promise is only as strong as whoever can break it. You now understand the machine — lock, mint, burn, unlock, and the conservation invariant that ties them. Next we open the trust models that decide who can authorize that machine, and walk through the largest bridge hacks in history to see, line by line, which assumption failed.