Journal
7 min read

Patolabs OG: NFTs to unlock my betas

1,000 NFTs on Base, three tiers, 100% on-chain art, and a single purpose: giving early adopters verified access to all Patolabs TestFlight betas.

I had a simple problem: how do I give TestFlight beta access to people who support me, without managing a Google Form and an email list by hand?

TestFlight is well made. But the invitation flow is designed for teams, not for an open community. You add people one by one, manage emails, chase down those who haven't accepted the invite. For a solo dev with a personal project, that's admin work that has nothing to do with code.

I wanted to try something different. Something verifiable, permanent, and that doesn't depend on me to function.

The idea

An NFT as an access key. Not a speculative NFT, not a PFP project, not a ten-phase roadmap. An ERC-721 token on Base that says: "this person supports Patolabs." And that concretely unlocks something — access to beta builds of every app I ship.

The collection is called Patolabs OG (ticker PATOG). 1,000 tokens, three tiers, a custom contract deployed on Base mainnet. Everything is on-chain: the contract, the art, the metadata. No IPFS, no Arweave, no external server. As long as Base exists, the tokens exist.

Three tiers, one rule

The three tiers map to three levels of support:

Supporter, at 0.003 ETH — 600 available, in blue. The base card.

Backer, at 0.008 ETH — 300 available, in orange. A ring around the logo on top.

Founder, at 0.028 ETH — 100 available, in purple. Two rings and corner brackets. The rarest tier.

At current ETH prices, that's roughly $7 for a Supporter, $18 for a Backer, $65 for a Founder. Deliberately accessible — not designed as an investment, but as a tangible gesture of support.

The supplies are frozen forever in the contract's constructor: 600, 300, 100. No extension possible, no phase 2 where we add 2,000 more tokens. It's hardcoded. The prices, however, can be adjusted by the owner via setTierPrice — if ETH moons or crashes, I don't want a Supporter to cost $50 or $0.50. That's a deliberate design choice to keep pricing coherent over time. The owner can also withdraw ETH from the contract via withdraw(). Full transparency — the contract is verified, anyone can read it.

And the rule that holds it all together: one NFT per tier per wallet. You can mint a Supporter, a Backer, and a Founder if you want — but not two Supporters. No accumulation possible. Distribution is necessarily wide.

This isn't just a mint-time check. The invariant is enforced on every transfer, including secondary market sales. If someone tries to buy a Supporter on OpenSea when they already own one, the transaction reverts. It's coded in the contract's _update() hook — impossible to bypass.

The art, built at read time

This is the technical choice I'm happiest with. When a wallet or marketplace calls tokenURI(), the contract doesn't return a link to an image hosted somewhere. It builds the SVG on the fly and returns it as base64.

Black background with a subtle gradient, the big "OG" in the center, the Patolabs logo top-left, the tier name at the bottom, the edition number bottom-right. The decorations — rings, brackets — depend on the tier. Everything is generated by the Solidity, on every call.

The result is deterministic: token #42 will always have exactly the same rendering, whether you look at it today or in five years. No pinning to maintain, no IPFS gateway dying, no delayed reveal. The art exists as long as the contract exists.

There's a cost: the contract is larger than a standard ERC-721 because it embeds all the SVG in Solidity. But on Base, gas is low enough that it stays negligible.

The gate: from blockchain to TestFlight

Having a nice NFT is great, but it needed to actually do something.

On patolabs.dev/en/beta, holders access a dashboard that lists beta builds of all Patolabs apps — Souffle for now, and everything that follows.

The flow is straightforward: you connect your wallet, the frontend sends your address to the backend, the backend calls balanceOf() directly on the contract via a Base RPC. If you hold at least one token, you get the TestFlight links. If not, you don't.

No signature required, no complex authentication. Just a server-side balance check. TestFlight URLs are never sent to the client without prior on-chain verification — they stay server-side until proof of ownership is established.

To be honest: without a signature, someone who knows a holder's address could theoretically call the API directly. For stricter verification, we could require a wallet signature. But for TestFlight links — which can be revoked at any time — the extra friction isn't worth the cost. A deliberate trade-off.

The app list is stored in Vercel KV and editable from an admin panel built into the same page. The admin panel is only visible if the connected wallet matches the contract's owner() — not an environment variable, a verification derived from the contract itself.

Why Base

Pragmatism.

Base is a cheap L2 — a Supporter mint costs a few cents of gas on top of the token price. It's EVM-compatible, so all the standard tooling works: Foundry for the contract, wagmi and Viem on the frontend. And the Coinbase ecosystem makes onboarding less painful for people who don't live in a wallet every day.

Before the mainnet deployment, everything was tested on Base Sepolia. The contract is verified and readable on Basescan.

The experience

If you want to see what it looks like: you land on the beta page, you see three cards — one per tier — with a button to connect your wallet. If you're not on Base, the app prompts you to switch networks.

You pick your tier, confirm in your wallet, and a few seconds later your NFT appears with a small reveal animation. The SVG is right there, generated on the spot by the contract. Below, the dashboard opens with available apps and their TestFlight links.

That's it. No whitelist, no Discord to join, no form to fill out. You support, you get access.

One important note: your access is tied to your wallet. If you lose your seed phrase, you lose your NFT and your beta access. There's no "forgot my password" button on the blockchain.

Why all this

I could have done a Google Form. Seriously. "Enter your email, I'll add you to TestFlight." It works, it's simple, nobody would have blamed me.

But I liked the idea that support would be visible, verifiable, permanent. That an early adopter could say "I was there from the start" with on-chain proof rather than an email in an inbox. That the access key wouldn't depend on me — if I lose my email database, the tokens are still there.

And above all, I liked the technical challenge. A custom contract with SVG generated on-chain, a server-side gate via balance verification, the whole thing wired up in a weekend. The kind of side project where every layer — Solidity, RPC, Next.js frontend — has to fit together cleanly for the whole thing to hold.

Patolabs OG is a support program materialized as NFTs. Nothing more, nothing less. If you want to support the project and get early beta access, it's over here.

— Pato