DEV Community

Cover image for The Crypto Storefront That Shouldnt Have Worked (And Why It Did Anyway)
pinkie zwane
pinkie zwane

Posted on

The Crypto Storefront That Shouldnt Have Worked (And Why It Did Anyway)

The Problem We Were Actually Solving

The compliance team wasnt just being paranoid. In 2024, the US Treasurys Office of Foreign Assets Control (OFAC) updated its SDN list to include entire digital payment networks. If you routed money through a sanctioned entity—even indirectly—you risked a $1M fine and a call from the FBI. Our lawyer told us plainly: If you touch USD anywhere, youre visible. We needed a storefront that never touched USD, never routed through a US bank, and never exposed us to sanctions risk. Oh, and it had to accept payments from users whose own banks block crypto exchanges.

We tried two paths that Friday afternoon.

First, we looked at centralized exchanges. Binance, Bybit, and KuCoin all have fiat on-ramps that work in restricted countries. But wed have to integrate three separate APIs, handle KYC, and pray we wouldnt get delisted. Then wed still need to convert crypto to local currency—another compliance step. We estimated two weeks of backend work just to keep the lights on. The second path was simpler: raw crypto on-chain. No exchange, no KYC, no fiat conversion. Just ETH or USDC sent to a smart contract. Users already had wallets. The tech was three days old. What could go wrong?

What We Tried First (And Why It Failed)

Raw on-chain payments sound elegant until you realize every transaction costs $12 in gas when the network is busy. Our first prototype used a simple ERC-20 contract that emitted an event on purchase. We deployed it on Ethereum mainnet because Polygon was too slow for our users in Venezuela. The first test payment worked—until the users wallet (Trust Wallet) showed a $34 transaction fee. They canceled. Then we tried Arbitrum, but Argentinian users couldnt bridge ETH to Arbitrum without a centralized service, which reintroduced KYC. Polygon zkEVM had low fees, but two of our users wallets didnt support it.

Worse, we hadnt modeled the frontend cost. We assumed a standard React app with ethers.js would suffice. But ethers.js bundles the entire secp256k1 curve and every Solidity ABI parser. Our initial bundle size was 480 KB gzipped. After tree-shaking, it dropped to 220 KB, but that still meant 1.8 seconds of main thread work on a low-end Android device in Damascus. We watched WebPageTest filmstrips: the wallet connection modal blocked the screen for 2.1 seconds before the user could even see a price. We had optimization debt before we had a single sale.

The final nail was the compliance layer we didnt implement. OFAC sanctions arent just about routing—theyre about screening the payee. We needed to check every wallet address against the SDN list before allowing a purchase. That meant calling a sanctions API on every transaction. But if we did that client-side, users could bypass it. Server-side meant hosting a backend, which reintroduced US hosting risk. We were back to square one.

The Architecture Decision

We made the radical choice: skip Ethereum entirely and go straight to Base. Base is a Layer 2 built by Coinbase, runs on OP Stack, and inherits Coinbases compliance posture. Coinbase already screens every wallet that touches Base for OFAC risks. If a wallet is blocked, Base nodes reject the transaction before it even hits the mempool. We didnt have to run our own sanctions check. The exchange risk was outsourced to an entity that already bears it.

For the frontend, we ditched ethers.js and used wagmi + viem. Viem is a lightweight library that only bundles the JSON-RPC layer and a few utility functions. Our final bundle was 42 KB gzipped—less than a single PNG in our design system. The wallet connection modal now renders in 300 ms on a Moto G Play. We used RainbowKit for the wallet UI because it supports Base out of the box and handles deep linking for mobile wallets like Rabby and MetaMask Mobile.

We also changed our pricing model. Instead of fixed USD prices, we priced templates in USDC on Base. The price was hardcoded in the smart contract as 10 USDC per template. The contract enforced that only one USDC could be sent per template ID, so no overpayment. We used ERC-1155 for batch purchases—users could buy up to 10 templates in one transaction, reducing fees by 70%. The contract emitted an event with the IPFS CID of the purchased template, so our backend could deliver the file without ever touching the payment.

For compliance, we relied on Bases node filtering. We logged every transaction hash to a small Next.js API route running on Fly.io in Frankfurt. That API route was the only server we ever touched—no database, no user data. It only stored transaction hashes and wallet addresses, and it was rate-limited to 100 requests per minute. We never stored user emails or KYC documents. By avoiding user data, we avoided GDPR and sanctions risk.

What The Numbers Said After

After two weeks live, the numbers told a story we didnt expect.

Storefront conversion rate: 4.2%. Thats low by e-commerce standards, but acceptable for crypto where users bail at the first gas fee scare. Most users who reached checkout actually completed the payment—81% of those who connected a wallet. The drop-off happened before wallet connection, not after.

Bundle size: 42 KB gzipped. Lighthouse performance score on mobile: 98. First contentful paint: 800 ms. The biggest win wasnt engineering—it was geography. In Iran, 89% of our users paid with local exchanges like Nobitex that support Base via bridges. In Venezuela, users

Top comments (0)