A blockchain is expensive and public, so it's the wrong place to store a profile picture, a large file, or a web page. Yet decentralised apps need all of those. This lesson covers the rest of the decentralised stack that sits around smart contracts: how large data is stored off-chain but referenced trustlessly, how human-readable names replace 40-character addresses, and what 'decentralised identity' means. These pieces are what turn a bare contract into a usable Web3 application, and they explain where an NFT's image actually lives.
- Explain why large data isn't stored on-chain and how off-chain storage is referenced
- Describe how IPFS content addressing makes off-chain data tamper-evident
- Explain what a naming system like ENS does
- Describe decentralised identity and self-custody of credentials
Why not store data on-chain
On-chain storage is the most expensive storage in computing: every node must store it forever, so you pay heavily for every byte. Storing a single image on Ethereum could cost more than the hardware to store it a million times elsewhere. So the pattern across Web3 is: keep the small, critical facts on-chain — ownership, balances, rules — and put the large data off-chain, storing only a compact *reference* to it on-chain. An NFT contract, for example, stores who owns token 42 and a link to its metadata, not the artwork itself. The challenge this creates: if the data is off-chain, how do you make sure it hasn't been swapped or lost? That's what content addressing solves.
IPFS and content addressing
IPFS (the InterPlanetary File System) is the most common way to store Web3 data off-chain. Its key idea is content addressing: instead of naming a file by *where* it is (a URL to a server), you name it by *what it is* — a hash of its content, called a CID (content identifier). Because the address is derived from the content, you can verify you got the right file: hash what you received and check it matches the CID. If a single byte were changed, the CID wouldn't match, so tampering is detectable — the same avalanche-effect trick from the cryptography lesson. This is why an on-chain reference to an IPFS CID is trustless: the contract stores the CID, and anyone fetching the data can confirm it's exactly what was referenced.
Location addressing (the web): https://myserver.com/cat.png
-> whoever controls the server can change or remove the file
Content addressing (IPFS): ipfs://bafybeigd...q4 (a CID)
-> the CID IS the hash of the file's content
-> fetch it, hash it, check it matches -> tamper-evident, location-independentContent addressing flips the question from 'where is the file?' to 'what is the file?'. Anyone hosting the exact bytes can serve it, and you can always verify it's genuine. The trade-off: someone must keep hosting (pinning) the data, or it can become unavailable — content addressing guarantees integrity, not permanence.
Names: ENS instead of 0x addresses
A blockchain address is a 40-character hex string — impossible to remember and easy to mistype, which can send funds to the wrong place forever. The Ethereum Name Service (ENS) fixes this by mapping human-readable names to addresses: alice.eth can resolve to Alice's address, much as DNS maps example.com to a server. A name is itself an on-chain record (in fact an NFT you own) that you can point at your address, and update if you change wallets. Wallets and dApps resolve ENS names automatically, so you can send to alice.eth and the wallet looks up the address. Names can also point to a website or an IPFS site, giving a decentralised alternative to DNS.
Decentralised identity
On the normal web, your identity is a set of accounts that companies own — they hold your data and can revoke your access. Decentralised identity flips this: *you* hold your identity and credentials, in your own wallet, and present them without a central provider. The building blocks are decentralised identifiers (DIDs) — identifiers you control rather than rent from a company — and verifiable credentials: tamper-evident, cryptographically signed claims (a diploma, a membership, a proof of age) that you store yourself and show to whoever needs them, who can verify the issuer's signature without contacting the issuer. On Ethereum, a wallet address already acts as a portable identity you carry across every dApp with no sign-up, and richer credential systems build on that idea. This is directly relevant to something like verifiable certificates — a credential you truly own, that anyone can check is genuine.
A wallet is already a login. 'Sign in with your wallet' lets a dApp verify you control an address by asking you to sign a message — no password, no account with them. It's the simplest form of self-owned identity, and it's why Web3 apps rarely have sign-up forms.
The whole stack together
Now the full picture of a real Web3 app assembles. A smart contract holds the ownership and rules on-chain. Large assets — images, documents, metadata — live on IPFS, referenced by CID so they're verifiable. Users are known by ENS names and identified by their wallet, which is both their login and their identity. The frontend ties it together, reading the contract through a provider and asking the wallet to sign transactions. Each piece does what it's best at: the chain for trustless state, off-chain storage for bulk data, naming for usability, the wallet for identity. Knowing where each responsibility lives is what lets you design a Web3 app that's decentralised *and* practical.
- Smart contract — ownership, balances, and rules, on-chain.
- IPFS — large files and metadata, referenced by verifiable CID.
- ENS — human-readable names instead of hex addresses.
- Wallet — the user's login, identity, and credential store.
- Frontend — reads the contract and asks the wallet to sign.