The repository is currently empty, so this plan assumes a fresh TypeScript + React app setup.
Agreed: shadcn/ui with Tailwind CSS.
Recommended: wagmi + viem + Reown AppKit.
Why:
wagmiis the cleanest React integration when usingviemReown AppKitis a strong fit when WalletConnect support is a priority- It works with
wagmi, so our React state and contract interaction model stays clean - WalletConnect support is first-class through the Reown / WalletConnect project configuration
- We still keep contract reads/writes in the
viem/wagmiecosystem instead of mixing stacks
If we want the absolute minimum dependency surface later, we can still replace AppKit with custom wagmi connectors, but for this app I recommend AppKit because WalletConnect matters to us.
Single-page utility app for interacting with supported ERC-4626 vaults.
Primary user actions:
- Connect wallet
- See connected wallet address
- Select supported chain
- Select supported vault from config
- View current vault position for the connected wallet
- View underlying asset details
- Deposit more underlying asset into the vault
- Withdraw part of the position or withdraw all
- React + TypeScript
- Vite 8 for local development and production builds
pnpmas the package manager- Tailwind CSS
shadcn/uiwagmiviem@tanstack/react-queryReown AppKit
We should use pnpm for dependency installation and script execution.
Examples:
- Install dependencies:
pnpm install - Run local dev server:
pnpm dev - Build production bundle:
pnpm build - Preview production bundle:
pnpm preview
We will define supported chains and vaults in a config.ts file.
Suggested shape:
type VaultConfig = {
name: string
address: `0x${string}`
}
type ChainConfig = {
id: number
name: string
rpcUrl: string
vaults: VaultConfig[]
}
export const APP_CONFIG: ChainConfig[] = [
{
id: 1,
name: 'Ethereum',
rpcUrl: `https://nodes.sequence.app/mainnet/${import.meta.env.VITE_SEQUENCE_ACCESS_KEY}`,
vaults: [
{
name: 'Alpha USDC Forex V2',
address: '0x153Bd1abE60104Bd46aa05a27fA12D1346D64A57',
},
],
},
{
id: 137,
name: 'Polygon',
rpcUrl: `https://nodes.sequence.app/polygon/${import.meta.env.VITE_SEQUENCE_ACCESS_KEY}`,
vaults: [],
},
]The UI will derive the chain dropdown and vault dropdown directly from this config.
- Ethereum Mainnet (
id: 1) - Polygon (
id: 137)
RPC endpoints to use:
- Ethereum:
https://nodes.sequence.app/mainnet/<SEQUENCE_ACCESS_KEY> - Polygon:
https://nodes.sequence.app/polygon/<SEQUENCE_ACCESS_KEY>
For the first implementation pass, we should include this Ethereum mainnet vault in config.ts:
- Chain: Ethereum Mainnet (
id: 1) - Vault name:
Alpha USDC Forex V2 - Vault address:
0x153Bd1abE60104Bd46aa05a27fA12D1346D64A57 - Reference:
https://app.morpho.org/ethereum/vault/0x153Bd1abE60104Bd46aa05a27fA12D1346D64A57/alpha-usdc-forex-v2
This gives us a concrete real-world ERC-4626 target while keeping the app config-driven for additional vaults later.
We should configure Reown AppKit with:
- Project ID:
6c925148f99706f295d774b730eba1f2
Suggested environment setup:
VITE_REOWN_PROJECT_ID=6c925148f99706f295d774b730eba1f2
VITE_SEQUENCE_ACCESS_KEY=Even though this value is used client-side, it is still cleaner to keep it in an environment variable rather than hardcoding it across the app.
We should use a .env file for local development.
Suggested initial .env:
VITE_REOWN_PROJECT_ID=6c925148f99706f295d774b730eba1f2
VITE_SEQUENCE_ACCESS_KEY=AQAAAAAAAKF1SMnmyl3VgghxQiiIQtMGULASuggested .env.example:
VITE_REOWN_PROJECT_ID=
VITE_SEQUENCE_ACCESS_KEY=If we later need app metadata, analytics flags, or a deployment-specific base URL, we can add those as additional VITE_ variables.
The app should build the Sequence RPC URLs from VITE_SEQUENCE_ACCESS_KEY rather than hardcoding a specific access key in source.
We should add a standard .gitignore for a Vite + React + TypeScript + pnpm project.
At minimum, ignore:
node_modules/dist/.env.env.local.env.*.local.DS_Store*.log.vite
Reasoning:
node_modules/anddist/are generated artifacts.envfiles can contain deployment-specific values- log and OS files should not be committed
Recommended approach: use Vite itself for the release bundle.
- Local dev:
vite - Production build:
vite build - Local verification of production bundle:
vite preview - Deployment artifact: static files in
dist/
Because this app is a client-side React SPA with direct wallet/onchain access and no backend rendering requirements, Vite's static output is the simplest and best fit.
Deployment note:
- If we deploy at the domain root, the default Vite setup is fine
- If we deploy under a subpath, we should set Vite's
baseconfig before building - If we later add client-side routes, the static host must be configured to serve
index.htmlas the fallback
Good static hosting targets later include Cloudflare Pages, Netlify, Vercel static output, S3 + CloudFront, or any nginx-based static host.
- Add a wallet connect button in the header/top area
- Show the connected wallet address in shortened form
- Support injected wallets plus WalletConnect
- Detect if the user is on the wrong chain for the selected vault
- Prompt network switch when supported by the wallet
- Chain dropdown shows configured chain name and chain ID
- Vault dropdown shows vault name and address
- Changing the chain resets the vault selection to a vault on that chain
- Selected vault drives all reads and write actions
For the selected vault and connected wallet, read:
asset()from the ERC-4626 vault to discover the underlying ERC-20balanceOf(user)from the vault to get user share balanceconvertToAssets(shares)to show the user’s deposited amount in underlying asset terms
For the underlying token, read:
name()symbol()decimals()balanceOf(user)for available wallet balance
Display:
- Vault name
- Vault address
- Underlying token name
- Underlying token symbol
- Underlying token address
- Token decimals-aware deposited amount
- Optional share balance if useful for debugging/transparency
When user clicks Deposit:
- Open a modal
- Show vault name and asset token details
- Show wallet token balance
- Let user enter an amount in underlying asset units
Write flow:
- Check ERC-20 allowance for the vault
- If allowance is insufficient, submit
approve()first - After approval confirmation, submit vault
deposit(assets, receiver) - Wait for transaction receipt
- Show success or error toast/notification
- Refetch vault position and wallet balance after completion
Withdraw button is enabled only when the user has a vault position.
When user clicks Withdraw:
- Open a modal
- Show current deposited amount
- Let user enter an amount to withdraw in underlying asset units
- Offer
Withdraw all
Write flow:
- For partial withdraw, call
withdraw(assets, receiver, owner) - For withdraw all, prefer
redeem(shares, receiver, owner)using the full share balance - Wait for transaction receipt
- Show success or error toast/notification
- Refetch vault position and wallet balance after completion
Using redeem() for withdraw-all avoids rounding issues when converting the entire position back to assets.
- Header
- App title
- Connected wallet button / address
- Vault selector card
- Chain selector
- Vault selector
- Vault position card
- Selected vault name and address
- Asset token name, symbol, address
- Current deposited amount
- Action buttons:
DepositandWithdraw
- Modal dialogs
- Deposit modal
- Withdraw modal
- Keep layout clean and compact
- Prefer cards/forms over dashboard-style complexity
- Use toasts for transaction lifecycle feedback
- Keep labels explicit because this is a financial utility app
Use local React state for selections and modal visibility.
Use wagmi / react-query for:
- Wallet connection state
- Contract reads
- Transaction status
- Refetch/invalidation after writes
No separate global state library should be needed.
- Minimal ERC-4626 ABI
- Minimal ERC-20 ABI
- Vault:
asset,balanceOf,convertToAssets - Token:
name,symbol,decimals,balanceOf,allowance
- Token:
approve - Vault:
deposit,withdraw,redeem
- No wallet connected
- No vault selected
- Wrong network selected in wallet
- User has zero vault balance
- User has insufficient token balance for deposit
- User rejects wallet signature
- Approval succeeds but deposit fails
- Selected vault/token contract call reverts
- Tokens with unusual metadata behavior
src/
app/
providers.tsx
components/
wallet-button.tsx
chain-select.tsx
vault-select.tsx
vault-position-card.tsx
deposit-modal.tsx
withdraw-modal.tsx
config/
config.ts
lib/
abis/
erc20.ts
erc4626.ts
format.ts
wagmi.ts
hooks/
use-selected-vault.ts
use-vault-data.ts
use-deposit.ts
use-withdraw.ts
pages/
home.tsx
main.tsx
We can collapse some of this if you want an even smaller codebase, but this is still fairly lean.
- Scaffold the React + TypeScript app and install base dependencies with
pnpm - Configure Vite 8,
.env, Tailwind,shadcn/ui,wagmi,viem,Reown AppKit - Add a standard
.gitignorebefore installing dependencies - Add
config.tsfor supported chains, RPC URLs, and vaults - Build wallet connection and header UI
- Build chain + vault selection UI
- Add read-only vault position queries
- Implement deposit modal and approval/deposit flow
- Implement withdraw modal and partial/full withdrawal flow
- Add transaction feedback, loading states, and refetch behavior
- Final pass on formatting, empty states, and wrong-network handling
- We are okay using
Reown AppKitfor the wallet connection UI - We are using
shadcn/ui+ Tailwind CSS - The initial version is a single-page app with no routing complexity
- Local development and release builds will use Vite 8
pnpmwill be the package manager- Release deployment target is static files produced by
vite build - We will add a standard
.gitignoreincludingnode_modules/,dist/, and local env files - Supported chains and vaults will be hardcoded in
config.ts - Initial supported chains are Ethereum mainnet and Polygon
- Reown AppKit will use project ID
6c925148f99706f295d774b730eba1f2 - Sequence RPC access will use
VITE_SEQUENCE_ACCESS_KEY - Initial supported vault should include Ethereum mainnet
Alpha USDC Forex V2at0x153Bd1abE60104Bd46aa05a27fA12D1346D64A57 - We will use direct onchain reads only, with no indexer or backend
For this app, I recommend:
- UI:
shadcn/ui+ Tailwind CSS - Wallet stack:
wagmi+viem+Reown AppKit - App style: simple single-page utility UI, fully config-driven for supported chains and vaults
This is the smallest setup that still gives us solid WalletConnect support, clean React ergonomics, and straightforward ERC-4626 contract interactions.