Project Deep-Dives

Three projects, built in sequence: a mainnet portfolio dashboard, a testnet swap interface, and a staking vault with its own ERC-20. This page covers the engineering decisions — what was chosen, what was deliberately not chosen, and why. Everything is live and the code is public.

01 · Ethereum mainnet

On-Chain Portfolio Dashboard

live demosource

The first decision was the one most demo dApps get wrong: this is a read-only mainnet lookup, not a wallet-connect app. Real portfolio tools — Zapper, DeBank — work exactly this way: you inspect any address without connecting anything. That choice avoids the empty-wallet demo problem (a testnet wallet-connect demo shows a reviewer their own empty wallet and asks them to fight a faucet first), and it forces the harder engineering problem: handling arbitrary addresses holding hundreds of unknown tokens, including the spam and dust every real wallet accumulates. Spam is filtered server-side; anything without a real price feed is segregated from the portfolio math instead of quietly inflating it, and when a whale wallet exceeds the scan cap, the API returns a truncated flag and the UI says the total may be incomplete rather than presenting a partial number as truth.

~20msrepeat lookup from the TTL cache
60s / 24hprice and metadata cache windows
$17.6Mone Ethereum Foundation lookup returned (July 2026) — run it yourself on the dashboard

Every call to Alchemy (balances, token metadata) and CoinGecko (pricing) runs server-side only — the browser talks to one internal endpoint and the API keys never appear in a client bundle or network tab. Pricing is queried by contract address, not by symbol: ERC-20 symbols aren't unique, so symbol mapping silently mixes up duplicate tickers and invents prices for unlisted tokens. Contract-address lookups make identity unambiguous — a token CoinGecko doesn't track comes back empty and is treated as unpriced, never guessed.

browser/api/portfolioone internal endpointTTL cache60s · dedupAlchemy · CoinGeckokeys live here
the API key never crosses the first arrow

Both providers run on free tiers, so a caching layer sits in front of them: an in-memory TTL cache with in-flight request deduplication, batched lookups so a 50-token wallet costs a handful of upstream calls rather than a hundred, a per-IP rate limiter, and a timeout on every upstream fetch so one hung socket can't stall a serverless instance. The same interface would take Redis for production scale — the point is that rate-limit awareness was designed in, not bolted on.

// src/lib/cache.ts — concurrent lookups share one upstream call
async getOrSet<T>(key: string, ttlMs: number, fn: () => Promise<T>) {
  const cached = this.get<T>(key);
  if (cached !== undefined) return cached;
  const pending = this.inFlight.get(key);
  if (pending) return pending as Promise<T>; // dedup the stampede
  const promise = fn()
    .then((value) => { this.set(key, value, ttlMs); return value; })
    .finally(() => this.inFlight.delete(key));
  this.inFlight.set(key, promise);
  return promise;
}
02 · Sepolia testnet

Token Swap Interface

live demosource

The swap integrates Uniswap v3's deployed contracts instead of a custom AMM, and that's the point. Writing a toy constant-product pool is a tutorial exercise; most real front-end and full-stack Web3 roles need the other skill — integrating battle-tested protocols correctly. That means the unglamorous specifics: SwapRouter02's parameter struct differs from the original router (no deadline field), quotes come from QuoterV2's revert-based simulation path, native ETH needs the router's payable wrap-in-flight path going in and a multicall that bundles the swap with unwrapWETH9 coming out, and slippage tolerance has to become an on-chain amountOutMinimum, not just a display number. Every contract address was verified on-chain before being committed, and the exact multicall calldata the UI produces was simulated against the live router before a user ever signed it.

swap UIwallet signsquote frozenSwapRouter02multicallswap → unwrapnative ETH out
ERC-20 → native ETH in one signature

The wallet layer is RainbowKit, Wagmi, and viem — and it was deliberately scoped as the second project, not the first. Wallet-connect is where a signature is actually required; leading with it would have made the flagship demo worse (see project one). Two implementation details carried over from the dashboard's discipline: price quotes are fetched server-side through the same keys-stay-on-the-server rule — the wallet only signs and submits — and displayed quotes auto-refresh every 15 seconds but freeze the moment the wallet prompt opens, so the numbers on screen always match the calldata being signed.

// src/lib/swap/quoter.ts — QuoterV2 runs server-side; keys stay here
const { result } = await client().simulateContract({
  address: UNISWAP.QUOTER_V2,
  abi: quoterV2Abi,
  functionName: "quoteExactInputSingle",
  args: [{ tokenIn, tokenOut, amountIn,
           fee: POOL_FEE, sqrtPriceLimitX96: 0n }],
});
const [amountOut, , , gasEstimate] = result;
03 · Sepolia testnet · Foundry

Staking Vault & Bochi Credits

live democontracts sourceverified contract

This is the end-to-end piece: a custom ERC-20 (Bochi Credits, BWC) and a staking vault, written in Solidity with Foundry, deployed to Sepolia, and verified on Etherscan so the source is publicly provable, not just claimed. The token ships with a public faucet — 100 BWC per address per day — because a staking demo a reviewer can't actually try is a screenshot. Anyone can mint tokens, stake, and watch 10% APR accrue per second in the UI, with nothing to ask for and nothing at stake: BWC is explicitly a valueless testnet token.

faucet()100 BWC / daystake()accrue10% APR · per secondclaim()minted, not from principal
rewards are minted on claim — principal is never touched

The core design trade-off is documented in the contract's own NatSpec: rewards are minted on claim (the vault holds a minter role on the token) rather than paid from a pre-funded reward pool. For a faucet-fed demo token this is the honest choice — staked principal is never touched to pay yield, and the vault can't run dry mid-demo. The production-scale alternative is schedule-funded distribution in the Synthetix style, where a finite reward budget is streamed over a period with global accumulator accounting; that's more machinery than a fixed-APR demo needs, and knowing which pattern fits which situation is the actual skill. Accrual here is per-position (staked × rate × elapsed), snapshotted on every interaction, with floor rounding always in the protocol's favor.

22adversarial review agents, every claim independently verified
2real test gaps found by mutation testing — both fixed
35passing tests, fuzz suites at 512 runs

The review process is a better story told accurately than inflated: the contracts went through an adversarial review in which 22 independent agents attacked them — reentrancy, access-control chains, donation attacks, overflow at extreme values — and every claimed vulnerability was independently verified rather than taken at face value. No security defects survived that verification. What did survive were two real gaps in the test suite, found by mutation testing: reviewers deliberately broke the contract to see whether the tests noticed. A mutant that emitted wrong event amounts passed everything (there were no event assertions), and a mutant that broke exit()'s claim-after-full-withdraw branch also passed (that path was untested). Both gaps got targeted tests that now kill those exact mutants.

// the test that kills the mutant the review found
function test_ExitClaimsFrozenRewardsAfterFullWithdraw() public {
    vm.prank(alice); vault.stake(100e18);
    vm.warp(block.timestamp + YEAR);
    vm.prank(alice); vault.withdraw(100e18); // rewards frozen, staked 0
    vm.warp(block.timestamp + 30 days);
    vm.prank(alice); vault.exit();           // must still claim
    assertEq(token.balanceOf(alice), 110e18);
}