A full-stack DeFi application enabling gasless cross-protocol position switching using ERC-4337 Account Abstraction, Session Keys, and Uniswap V3 Flash Loans on Compound V3.
- Gasless Transactions - Users pay $0 in gas via ERC-4337 Paymaster
- Email Authentication - Login with email via Privy (no MetaMask needed)
- Smart Account - Each user gets an ERC-4337 MultiOwnerModularAccount
- Session Keys - Backend signs transactions on behalf of users
- Compound V3 Integration - Supply WBTC collateral, borrow USDC or WETH
- Cross-Comet Position Switching - Switch between USDC and WETH Comets atomically via flash loans
┌─────────────────────────────────────────────────────────────────┐
│ USER FLOW │
├─────────────────────────────────────────────────────────────────┤
│ │
│ Email Login (Privy) │
│ ↓ │
│ Privy EOA Wallet (embedded) │
│ ↓ │
│ Smart Account (MultiOwnerModularAccount) │
│ ↓ │
│ Session Key Plugin (backend can sign) │
│ ↓ │
│ Gasless DeFi Operations │
│ • Supply WBTC to Compound V3 │
│ • Borrow USDC or WETH │
│ • Switch positions via flash loans │
│ │
└─────────────────────────────────────────────────────────────────┘
| Component | Description |
|---|---|
| Privy EOA | User's embedded wallet (email-based login) |
| Smart Account | ERC-4337 contract (MultiOwnerModularAccount) holding user funds |
| Session Key | Backend key with limited permissions for gasless execution |
| Paymaster | Sponsors gas fees for all user operations |
| Switcher | Flash loan contract for atomic position switching |
| Layer | Technologies |
|---|---|
| Smart Contracts | Solidity, Foundry, Uniswap V3 Flash Loans |
| Backend | Node.js, Express, MongoDB, Alchemy AA SDK |
| Frontend | React, Vite, Privy React SDK |
| Infrastructure | Tenderly Virtual TestNet (Mainnet Fork) |
- Node.js v18+
- MongoDB (local or Atlas)
- Tenderly Account - Create Virtual TestNet
- Privy Account - Get App ID
- Alchemy Account - Get API Key
# Install dependencies
cd backend && npm install
cd ../frontend && npm install
cd ../contracts && forge install- Go to Tenderly Dashboard
- Create a new Virtual TestNet forked from Ethereum Mainnet
- Copy the RPC URL (e.g.,
https://virtual.mainnet.rpc.tenderly.co/xxxx-xxxx) - Note the Fork ID from the URL
Backend (backend/.env):
# Server
PORT=3001
NODE_ENV=development
# MongoDB
MONGODB_URI=mongodb://localhost:27017/defi-borrowing-app
# Privy
PRIVY_APP_ID=your_privy_app_id
PRIVY_APP_SECRET=your_privy_app_secret
# Alchemy
ALCHEMY_API_KEY=your_alchemy_api_key
ALCHEMY_GAS_POLICY_ID=your_gas_policy_id
# Tenderly Virtual TestNet
CHAIN_ID=1
RPC_URL=https://virtual.mainnet.rpc.tenderly.co/YOUR_FORK_ID
# Will be set by setup script
PAYMASTER_ADDRESS=
SWITCHER_ADDRESS=Frontend (frontend/.env):
VITE_PRIVY_APP_ID=your_privy_app_id
VITE_API_URL=http://localhost:3001/api
VITE_RPC_URL=https://virtual.mainnet.rpc.tenderly.co/YOUR_FORK_ID
VITE_TENDERLY_FORK_ID=YOUR_FORK_IDcd backend
npm run setup-forkThis script:
- Deploys
SimplePaymasterV06(gas sponsorship) - Deploys
CompoundV3CrossCometSwitcher(position switching) - Funds paymaster with 10 ETH on EntryPoint
- Updates
.envwith deployed addresses
# Terminal 1 - Backend
cd backend
npm run dev
# Terminal 2 - Frontend
cd frontend
npm run dev- Click "Login" on the landing page
- Enter your email address
- Complete Privy verification (OTP or magic link)
- Backend automatically:
- Creates user in MongoDB
- Stores Privy wallet address
- Click "Activate Account" button
- Backend deploys your Smart Account (MultiOwnerModularAccount)
- Session Key Plugin is installed with backend as authorized signer
- You now have a gasless-enabled smart wallet
- Click "Get Test Tokens" button
- Tenderly funds your EOA with:
- 10 ETH
- 1 WBTC
- 10,000 USDC
- Click "Approve" for WBTC
- Your EOA approves and transfers WBTC to your Smart Account
- Smart Account now holds collateral
- Go to "Supply & Borrow" tab
- Select:
- Comet: USDC Comet or WETH Comet
- Collateral: WBTC
- Amount: e.g., 0.01 WBTC
- Click "Supply"
- After supply, click "Borrow":
- Asset: USDC (if USDC Comet) or WETH (if WETH Comet)
- Amount: e.g., 200 USDC
- Transactions execute gaslessly via session key!
- Go to "Switch Position" tab
- Current position shows your active Comet (e.g., USDC Comet with 200 USDC debt)
- Click "Switch to WETH Comet" (or vice versa)
- The switcher contract atomically:
- Takes flash loan to repay debt on source Comet
- Withdraws collateral from source Comet
- Supplies collateral to target Comet
- Borrows equivalent amount on target Comet
- Swaps tokens and repays flash loan
- View transaction in Tenderly Explorer (link provided)
The Transaction History section shows all your DeFi operations with:
- Transaction type (Supply, Borrow, Switch)
- Amount and asset
- Timestamp
- Link to Tenderly Explorer
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/auth/login |
Login/register with Privy |
| GET | /api/auth/profile |
Get user profile |
| Method | Endpoint | Description |
|---|---|---|
| POST | /api/account/activate |
Deploy smart account + session key |
| GET | /api/account/status |
Get smart account status |
| GET | /api/account/balances |
Get token balances |
| POST | /api/account/fund |
Fund EOA with test tokens (Tenderly) |
| POST | /api/account/approve |
Approve smart account to spend tokens |
| POST | /api/account/pull |
Transfer tokens from EOA to smart account |
| Method | Endpoint | Description |
|---|---|---|
| GET | /api/defi/position |
Get current Compound V3 position |
| GET | /api/defi/markets |
Compare USDC vs WETH Comet rates |
| POST | /api/defi/supply |
Supply collateral to Comet |
| POST | /api/defi/borrow |
Borrow from Comet |
| POST | /api/defi/repay |
Repay borrowed amount |
| POST | /api/defi/withdraw |
Withdraw collateral |
| POST | /api/defi/switch |
Switch position between Comets |
| GET | /api/defi/transactions |
Get transaction history |
| Contract | Address |
|---|---|
| USDC Comet | 0xc3d688B66703497DAA19211EEdff47f25384cdc3 |
| WETH Comet | 0xA17581A9E3356d9A858b789D68B4d866e593aE94 |
| Token | Address |
|---|---|
| WBTC | 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599 |
| USDC | 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48 |
| WETH | 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 |
| Pool | Address | Fee |
|---|---|---|
| Flash Pool | 0x88e6A0c2dDD26FEEb64F039a2c41296FcB3f5640 |
0.05% |
| Swap Pool | 0x7BeA39867e4169DBe237d55C8242a8f2fcDcc387 |
1% |
| Contract | Address |
|---|---|
| EntryPoint v0.6.0 | 0x5FF137D4b0FDCD49DcA30c7CF57E578a026d2789 |
| Session Key Plugin | 0x0000003E0000a96de4058e1E02a62FaaeCf23d8d |
| Multi Owner Plugin | 0xcE0000007B008F50d762D155002600004cD6c647 |
defi-borrowing-app/
├── backend/
│ ├── src/
│ │ ├── controllers/
│ │ │ ├── authController.js # Privy auth
│ │ │ ├── accountController.js # Smart account management
│ │ │ └── defiController.js # DeFi operations
│ │ ├── services/
│ │ │ ├── alchemySmartAccount.service.js # ERC-4337
│ │ │ ├── alchemyPosition.service.js # Position queries
│ │ │ ├── compound.js # Compound V3 integration
│ │ │ └── erc4337.service.js # UserOp execution
│ │ ├── models/
│ │ │ ├── User.js # User schema
│ │ │ ├── Position.js # DeFi positions
│ │ │ └── Transaction.js # Tx history
│ │ └── routes/
│ ├── setup-tenderly-fork.js # Deploy contracts to fork
│ └── test-session-key-defi.js # E2E test
│
├── frontend/
│ ├── src/
│ │ ├── pages/
│ │ │ ├── Landing.jsx # Home page
│ │ │ └── Dashboard.jsx # Main dashboard
│ │ ├── context/
│ │ │ └── AuthContext.jsx # Auth state
│ │ ├── services/
│ │ │ └── api.js # API client
│ │ └── config/
│ │ └── constants.js # Contract addresses
│ └── index.html
│
└── contracts/
├── src/
│ ├── CompoundV3CrossCometSwitcher.sol # Flash loan switcher
│ └── SimplePaymasterV06.sol # Gas sponsorship
├── test/foundry/
│ └── MainnetCrossCometE2E.t.sol # E2E test
└── scripts/
├── start-mainnet-fork.sh # Start local Anvil fork
└── test-mainnet-fork.sh # Run fork tests
The CompoundV3CrossCometSwitcher performs an atomic cross-Comet switch:
1. User calls switchCollateral(USDC_Comet → WETH_Comet)
2. Flash loan USDC from Uniswap (0.05% pool)
3. Repay user's USDC debt on USDC Comet
4. Withdraw WBTC collateral from USDC Comet
5. Supply WBTC to WETH Comet
6. Borrow WETH from WETH Comet
7. Swap WETH → USDC on Uniswap (1% pool)
8. Repay flash loan + fee
9. User now has position on WETH Comet!
Key Design Decisions:
- Uses different pools for flash loan vs swap (avoids reentrancy)
- Uses WBTC as collateral (accepted by both Comets)
- Calculates borrow amount dynamically based on current ETH/USDC price
cd backend
npm run test:e2e# Terminal 1: Start Anvil fork
cd contracts/scripts
./start-mainnet-fork.sh
# Terminal 2: Run tests
./test-mainnet-fork.sh- Ensure embedded wallets are enabled in Privy dashboard
- Check
PRIVY_APP_IDmatches in frontend and backend
- Click "Activate Account" to install session key plugin
- Check backend logs for errors
- Use "Get Test Tokens" button (Tenderly only)
- Ensure tokens are in Smart Account, not EOA
- Ensure you have an active position (collateral + debt)
- Check Tenderly explorer for detailed error
- Session Keys have limited permissions (only DeFi operations)
- Session Keys expire after 7 days
- Private Keys are never exposed to frontend
- Paymaster only sponsors known contract calls
Production Deployment:
- Use proper key management (AWS KMS, HashiCorp Vault)
- Implement rate limiting
- Add transaction amount limits
- Security audit before mainnet
MIT