Skip to content

sp0oby/cadence-vaults

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

1 Commit
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Cadence

Non-custodial yield aggregation protocol built on the ERC-4626 tokenized vault standard. Users deposit underlying assets, receive vault shares, and earn yield passively as the protocol harvests returns from pluggable strategies.

Live on SepoliaWETH Vault · USDC Vault

Architecture

┌─────────────┐     ┌──────────────┐     ┌───────────────────┐
│   Frontend   │────▶│  YieldVault  │────▶│  IYieldStrategy   │
│  Next.js 14  │     │   ERC-4626   │     │  (pluggable)      │
└──────┬───────┘     └──────┬───────┘     └───────────────────┘
       │                    │
       ▼                    ▼
┌─────────────┐     ┌──────────────┐
│  Subgraph   │     │ AccessControl│
│  The Graph  │     │  3 roles     │
└─────────────┘     └──────────────┘

Three layers:

  • Vault Layer — ERC-4626 vaults that custody funds, manage share accounting, and enforce role-based access control (Governor, Guardian, Harvester).
  • Strategy Layer — Pluggable yield strategies behind the IYieldStrategy interface. The vault calls deposit(), withdraw(), and harvest() on whatever strategy is active. Strategies are upgradeable by governance without migrating user funds.
  • Automation Layer — ERC-4337 account abstraction via ZeroDev session keys for gasless recurring deposits with spending limits and time bounds.

How It Works

  1. User deposits WETH or USDC into a vault and receives shares
  2. deployIdleToStrategy() moves idle funds into the active strategy
  3. The strategy generates yield over time
  4. A keeper (Chainlink Automation, cron bot, etc.) calls harvest() periodically to realize yield — this is protocol-level, not user-facing
  5. Harvest increases the vault's totalAssets, so each share is worth more
  6. User withdraws any time — shares redeem for principal + accumulated yield
  7. A performance fee (configurable, max 20%) is taken from harvested yield and sent to feeRecipient

What's Mock vs Production-Ready

This is a Sepolia demo. The contracts, frontend, and subgraph are fully functional, but some components are simulated.

Production-ready (no changes needed)

  • YieldVault.sol — fully auditable ERC-4626 vault with role-based access, pause/unpause, inflation attack protection (6-decimal virtual offset), deposit limits, and emergency withdraw
  • IYieldStrategy interface — the strategy abstraction layer. Any new strategy just implements this interface
  • Frontend — deposit, withdraw, approve, faucet, activity feed, session key lifecycle, dark mode, all pages
  • Subgraph — indexes Deposit, Withdraw, Harvested, Transfer events with aggregate tracking and daily snapshots
  • Access control — Governor, Guardian, Harvester roles with proper separation of powers

Mock / demo-only (would need replacement for production)

Component What it does now What production needs
MockYieldStrategy Linear 5% APR from a pre-funded reservoir. Yield is real on-chain value but comes from a finite pool, not market activity A real strategy that deploys to Aave, Compound, Lido, etc. Same interface, different implementation
MockERC20 tokens mWETH and mUSDC with public mint(). Anyone can mint unlimited tokens Point vaults at canonical WETH/USDC on mainnet. Remove faucet button
Session keys Signs EIP-712 typed data client-side, persists to localStorage. Demonstrates the UX flow Wire up ZeroDev SDK with a funded paymaster to submit real UserOperations through the EntryPoint
Harvest automation Manual — someone with HARVESTER_ROLE calls harvest() Chainlink Automation keeper or a cron job calling harvest on a schedule

To go to production

  1. Write a real IYieldStrategy implementation (e.g., AaveV3Strategy.sol) that deposits into a lending protocol
  2. Deploy new vaults pointing at canonical tokens (WETH, USDC) on your target chain
  3. Fund a ZeroDev paymaster for gasless session-key deposits
  4. Set up a Chainlink Automation keeper for periodic harvests
  5. Contracts would need to be redeployed — the vault and strategy addresses change

Project Structure

cadence/
├── contracts/          Foundry project — Solidity contracts + tests
│   ├── src/
│   │   ├── YieldVault.sol           ERC-4626 vault
│   │   ├── MockYieldStrategy.sol    Simulated yield strategy
│   │   └── interfaces/
│   │       └── IYieldStrategy.sol   Strategy interface
│   ├── test/
│   │   ├── YieldVault.t.sol         23 unit + fuzz tests
│   │   └── invariants/              3 invariant tests (solvency, shares, totalAssets)
│   └── script/
│       └── Deploy.s.sol             Sepolia deployment script
├── frontend/           Next.js 14 + wagmi + viem + shadcn/ui
│   └── src/
│       ├── app/                     Pages (/, /activity, /docs)
│       ├── components/              UI components
│       ├── hooks/                   useVaultData, useVaultActions, useSessionKey
│       └── lib/                     Contracts, utils, subgraph client
└── subgraph/           The Graph subgraph
    ├── schema.graphql               6 entities
    ├── src/yield-vault.ts           Event handlers
    └── tests/                       7 Matchstick tests

Smart Contracts

Contract Address (Sepolia) Description
YieldVault (WETH) 0x5A3d1215F6993534E58b4B669a11804e2CDea94F ERC-4626 vault for WETH
YieldVault (USDC) 0x8D29A34E52b5f5aB15eca24326F698b088F8Ea72 ERC-4626 vault for USDC
MockWETH 0x683C3Af05cce94dE2116f86B18B905d6C470240f Test token (18 decimals)
MockUSDC 0x7BcCD3a3F460D769F6AdA465C5D7917AA7EC89Ce Test token (6 decimals)
WETH Strategy 0x04E18F0Aceb1dc09c18f2C376A3F389F0Aff0C10 Mock 5% APR strategy
USDC Strategy 0xE3e39625ab42dCC66bf7b855DE1cC312258A04E2 Mock 5% APR strategy

All contracts are verified on Etherscan.

Roles

Role Permissions
GOVERNOR_ROLE Set strategy, update fees, pause/unpause
GUARDIAN_ROLE Pause vault, emergency withdraw from strategy
HARVESTER_ROLE Call harvest() to realize yield

Security

  • Inflation attack protection — 6-decimal virtual offset via _decimalsOffset(), making first-depositor attacks uneconomical
  • Checks-Effects-Interactions pattern enforced throughout
  • ReentrancyGuard as defense-in-depth
  • SafeERC20 for non-standard token compatibility
  • Performance fee hard cap — max 20% (2000 bps), enforced at the contract level
  • Per-transaction deposit limit — prevents single-tx pool domination

Testing

# Contract tests (23 unit + 3 invariant suites)
cd contracts
forge test -vvv

# Subgraph tests (7 Matchstick tests)
cd subgraph
npx graph test
  • Fuzz tests: 1,000 runs — deposit/redeem roundtrip, multi-depositor, harvest fee distribution
  • Invariant tests: 256 runs, depth 128 — solvency (vault never owes more than it has), share accounting, totalAssets consistency

Setup

Prerequisites

Contracts

cd contracts
cp .env.example .env
# Fill in PRIVATE_KEY, SEPOLIA_RPC_URL, ETHERSCAN_API_KEY

# Run tests
forge test

# Deploy to Sepolia
forge script script/Deploy.s.sol --rpc-url $SEPOLIA_RPC_URL --broadcast --verify

Frontend

cd frontend
npm install
cp .env.example .env
# Fill in contract addresses from deployment output + RPC URL

npm run dev

Subgraph

cd subgraph
npm install

# Update addresses in subgraph.yaml and networks.json
# Then authenticate and deploy:
npx graph auth --studio <DEPLOY_KEY>
npx graph deploy --studio cadence-yield-vault --version-label v0.1.0

Operational Commands

After deployment, funds need to be deployed to strategies and harvested periodically:

# Deploy idle funds to strategy (anyone can call)
cast send <VAULT_ADDRESS> "deployIdleToStrategy()" --rpc-url $SEPOLIA_RPC_URL --private-key $PRIVATE_KEY

# Harvest yield (requires HARVESTER_ROLE)
cast send <VAULT_ADDRESS> "harvest()" --rpc-url $SEPOLIA_RPC_URL --private-key $PRIVATE_KEY

Tech Stack

  • Contracts: Solidity 0.8.24, OpenZeppelin v5, Foundry
  • Frontend: Next.js 14, TypeScript, wagmi v2, viem, shadcn/ui, Recharts
  • Indexing: The Graph (Subgraph Studio), AssemblyScript mappings
  • Account Abstraction: ZeroDev SDK, ERC-4337, session keys
  • Fonts: DM Sans, JetBrains Mono

About

Non-custodial ERC-4626 yield vault protocol with pluggable strategies, session-key automation, and a Graph Protocol subgraph. Deployed on Sepolia.

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors