MeowFi DeFi Monorepo
Learn how I scaled MeowFi into a full-stack DeFi monorepo. Covers tRPC contracts, multicall data pipelines, and resilient wallet transaction UX patterns.
Project Overview
MeowFi is a DeFi application on Monad that combines yield vaults, liquidity pools, token swaps, referral mechanics, and NFT-gated role verification.
I joined to build frontend product surfaces, then expanded ownership into backend architecture and protocol data flows. The project evolved into a full-stack monorepo with shared type contracts and production-focused transaction handling.
Scope and Ownership
- Designed and shipped a feature-based React frontend covering vaults, pools, swaps, dashboard, and verification flows.
- Architected the backend API surface with Express + tRPC routers for vaults, assets, tokens, referrals, and role assignment.
- Built and maintained on-chain data pipelines using Viem multicall and server-side aggregation.
- Implemented transaction orchestration patterns with
neverthrowfor safer multi-step wallet interactions.
Core Engineering Challenges
- RPC fan-out and latency: Vault UIs required many independent contract reads. Naive per-field calls introduced high latency and brittle client logic.
- Unstable first render: Vault card metadata and live vault stats arrived at different times, causing inconsistent initial render behavior.
- Error-prone transaction flows: Approval, Permit2 setup, swaps, and deposits are multi-step processes where partial failures are common.
- Cross-system identity complexity: Wallet ownership, Discord identity, JWT auth, and NFT role eligibility had to stay consistent across systems.
Engineering Solutions
1) Monorepo and Typed API Contracts
I moved the project to an NPM workspaces monorepo (apps/frontend, apps/backend) and standardized communication through tRPC.
- Implementation: Frontend tRPC hooks are generated from backend
AppRoutertypes, giving compile-time guarantees across request/response boundaries. - Outcome: Backend schema changes surfaced immediately in frontend compile errors instead of runtime breakage.
2) High-Throughput On-Chain Reads with Multicall
I centralized blockchain reads in backend procedures and replaced scattered reads with batched calls.
vault.bribe.liveStatsbatches 16 vault getters per vault into one multicall request.asset.userAssetscomposes large batched reads for vault positions, bribes, pool balances, and BPT rates in a single execution path.token.userBalance,token.prices, andasset.homePagealso use multicall patterns to keep frontend queries lean.
This removed RPC fan-out from the client and made dashboards materially faster and more consistent under load.
3) Stable Rendering via Static Metadata + Live Hydration
I separated static vault metadata from volatile on-chain stats.
- Static layer: Vault definitions (title, images, NFT info, addresses) are declared in local feature data files.
- Dynamic layer: APY, periods, balances, and live protocol stats are hydrated asynchronously through tRPC queries.
This preserved deterministic layout while still delivering real-time protocol data.
4) Result-Based Transaction Pipeline with neverthrow
I replaced nested try/catch flows with ResultAsync pipelines for wallet interactions.
- Shared utility layer: Approval checks, Permit2 approval, spender approval, tx submission, and receipt confirmation were wrapped into composable result functions.
- UI behavior: Components chain these steps with
.andThen(...)and emit explicit toast stages (approval, execution, confirmation, success/failure).
This made transaction behavior easier to reason about and significantly improved failure visibility for users.
5) Hybrid Analytics Pipeline for Liquidity Pools
I built a backend job that computes and persists pool analytics consumed by the frontend.
- A scheduled process computes TVL, volume, fees, APR/APY, and daily series using on-chain reads plus indexed swap/liquidity events.
- Results are stored in MongoDB and served via
vault.liquidityPool.statsfor fast dashboard rendering.
6) Auth and Role Verification Across Wallet + Discord
I implemented verification flows that bridge Web3 ownership with community permissions.
- Discord OAuth callback issues JWTs for authenticated API access.
- Protected role assignment verifies signed wallet messages, checks ERC721 ownership on-chain, binds wallet addresses, and assigns mapped Discord roles.
- A scheduled reconciliation process removes roles when NFT ownership is no longer valid.
Results and Impact
- Type-safe delivery: Frontend and backend development moved faster with fewer integration regressions.
- Performance: Core DeFi views shifted from many client-side RPC calls to batched server-side reads.
- Reliability: Multi-step transactions became explicit, traceable, and user-friendly under failure conditions.
- Scalability: Feature-first modules and domain routers supported growth across vaults, pools, referrals, and verification without architectural rewrites.
Architecture Snapshot
apps/
frontend/
src/
app/ # Routing, providers, app wiring
features/
bribeVault/
meowPools/
userDashboard/
verifyRole/
shared/ # Reusable UI, data, utilities
backend/
src/
routers/
vaults/ # bribe, autoCompound, liquidityPool
asset/
token/
referral/
role/
setup/ # viem client, discord, analytics jobs
models/ # MongoDB schemas