Skip to content

About

Tokenized vault dapp — ERC-20 vault with non-transferable shares, cooldown faucet, and live Sepolia demo. Foundry contracts + Vite/React/TypeScript/wagmi frontend.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

Tokenized Vault — a tokenized vault dapp

CI License: MIT Solidity Live demo

⚠️ Educational / testnet demo. This project is an unaudited demonstration built for learning and testing on Anvil (31337) and Sepolia (11155111). It is not for mainnet use with real funds. No security audit has been performed; expect bugs.

Live demo: https://tokenized-vault.vercel.app


Table of contents

Overview

An ERC-4626-inspired tokenized vault: deposit an ERC-20 token, receive non-transferable shares priced by vault balance / totalSupply, withdraw back to tokens, and claim test tokens from a cooldown faucet. It is not ERC-4626 compliant — shares are not an ERC-20 token and the vault exposes no preview*/convert* view functions; the resemblance is limited to the deposit→shares→withdraw shape. The frontend is a Vite + React + TypeScript (strict) single-page app using wagmi v2, viem, and a custom connect modal — no RainbowKit or ConnectKit.

Features

# User story What it delivers
US1 Connect & dashboard Custom wallet modal, wrong-network banner, six-stat dashboard
US2 Deposit Live share estimate, approve → deposit flow, first-deposit (dead shares) explanation
US3 Withdraw Max shares, live token estimate, inline validation
US4 Faucet 1000-token claim with a live 24 h cooldown countdown
US5 Activity feed Newest 20 Deposit/Withdraw events, live updates, explorer links only on Sepolia

Architecture

flowchart LR
  subgraph frontend["Frontend — web/ (Vite · React · TypeScript strict)"]
    UI["UI components<br/>Dashboard · Deposit · Withdraw · Faucet · Activity · Modal"]
    Hooks["wagmi v2 + viem hooks<br/>useVaultStats · useDeposit · useWithdraw<br/>useFaucetClaim · useActivity"]
    CFG[("web/src/config/deployments.json<br/>generated — addresses + deployBlock")]
    UI --> Hooks
    Hooks --> CFG
  end

  subgraph networks["Networks (31337 / 11155111 only)"]
    A["Anvil — local chain<br/>chainId 31337"]
    S["Sepolia — public testnet<br/>chainId 11155111"]
  end

  subgraph contracts["Contracts — contracts/src (OpenZeppelin submodule)"]
    V["Vault.sol<br/>shares · deposit · withdraw"]
    F["FaucetToken.sol<br/>ERC-20 · faucet()"]
  end

  subgraph tooling["Tooling — scripts/"]
    DEPLOY["deploy-anvil.sh · deploy-sepolia.sh<br/>forge script --broadcast"]
    SYNC["sync-deployments.mjs · sync-abis.mjs<br/>writes generated config"]
  end

  Hooks -->|"JSON-RPC: reads + transactions"| A
  Hooks -->|"JSON-RPC: reads + transactions"| S
  A -.hosts.-> V
  A -.hosts.-> F
  S -.hosts.-> V
  S -.hosts.-> F
  DEPLOY -->|"broadcast"| A
  DEPLOY -->|"broadcast"| S
  DEPLOY --> SYNC --> CFG
Loading

The frontend never hardcodes chain data: addresses, deploy blocks, and ABIs flow from deployments via the scripts/ tooling into web/src/config/, and every read is pinned to the wallet's active chain.

Design notes

  • Shares are non-transferable: the vault tracks balances internally and deliberately has no transfer/approve surface for shares.
  • Fee-on-transfer tokens are unsupported: deposits assume transferFrom moves exactly the amount the depositor authorized.
  • 1000 BASE UNITS of dead shares protect against first-depositor inflation attacks: the first deposit mints 1000 base units (not whole tokens) to 0xdead…dead, so a later donor cannot skew the share price.

Known limitations

  • Shares are non-transferable: an internal ledger only — they cannot be sent, traded, or approved.
  • Fee-on-transfer and rebasing tokens are unsupported; deposits assume transfers move exactly the requested amount.
  • The contracts are unaudited — a learning demo, not production code.
  • Testnet/local only: Anvil (31337) and Sepolia (11155111); never mainnet with real funds.
  • With VITE_SEPOLIA_RPC_URL unset, the site falls back to a public Sepolia RPC that may rate-limit under load.
  • The activity feed reads logs from the contract's deploy block (chunked getLogs, newest 20 Deposit/Withdraw events); events outside that window are not listed.

Quickstart

Prerequisites: Foundry and Node 24 installed. Start an Anvil node in a second terminal (anvil), then run the happy path below — every line is executed from the repo root:

git clone --recursive https://github.com/MohammedSoliman10/tokenized-vault.git
cd tokenized-vault

(cd web && npm install)            # frontend dependencies
(cd contracts && forge test)       # contracts suite (30 tests)
./scripts/deploy-anvil.sh          # FaucetToken + Vault → the Anvil node
node scripts/sync-deployments.mjs  # write addresses + ABIs for the frontend
cd web && npm run dev              # dev server → http://localhost:5173

Full scenarios (Sepolia deploys, secret hygiene, CI expectations, Definition of Done): specs/001-tokenized-vault-dapp/quickstart.md

Verification

Every task gates on these commands (all must exit 0):

cd contracts && forge fmt --check && forge test
cd ../web && npm run typecheck && npm run lint && npm test && npm run build

CI runs the same commands on every push — see .github/workflows/ci.yml.

Screenshots

The first shot is the live production site; the rest were captured by the automated browser walkthrough against local Anvil (chain 31337). All views are 1280×800 in the blue-and-white light theme except the mobile shot (360 px viewport).

Screenshot What it shows
Live production dashboard on Sepolia Live site on Sepolia: disconnected visitor reads TVL 50, share price 1, and the Deposit/Withdraw activity rows with working sepolia.etherscan.io tx links
Connected dashboard after the faucet claim US1/US4: connected header, six-stat dashboard, 1000-token claim with live cooldown countdown
First deposit success US2: first deposit of 1001 base units accepted — share price exactly 1, your share 0.0999%, event-derived success copy, own feed row
Live activity after an external deposit US5: a second account deposited 200 via cast — TVL 250, your share 19.9999%, newest row arrived live, plain tx hashes (no explorer links on Anvil)
Wrong-network banner Wallet on an unsupported network — amber banner with switch CTA, every action disabled
Mobile layout at 360 px Narrow viewport: two-column stats, stacked forms, activity table scrolls inside its own container — no page-level horizontal overflow

Deployment

Live site (Vercel)

Production: https://tokenized-vault.vercel.app — Vercel Root Directory: web, built via web/vercel.json (Vite framework, install/build commands, dist output). The Vercel project connects to this repository with main as the production branch; deployment protection is off so the demo is publicly reachable.

Environment variables (both optional — the app degrades gracefully without them):

Variable Purpose
VITE_SEPOLIA_RPC_URL Custom Sepolia RPC; falls back to a public RPC when unset
VITE_REOWN_PROJECT_ID Enables the WalletConnect option in the connect modal (injected wallets always work)

The current production deployment sets neither: reads use the public RPC fallback and the connect modal offers injected wallets only. Environment values belong in Vercel/dashboard .env files only — never committed (the repo gitignores .env* and CI ships no secrets).

Sepolia contract addresses (chain 11155111)

Deployed by scripts/deploy-sepolia.sh at deploy block 11865644; both sources are verified on Etherscan:

Contract Address Notes
FaucetToken 0x8f6BaF9e021a57dddb2A70E7e186e6Ffb6f000f3 "Vault Test Token" (VTT), 1000-token faucet
Vault 0xBF27A9d9b4f636783B1b19D94b7D1BE4e51998B0 vault.token() → the FaucetToken above

scripts/sync-deployments.mjs mirrors both addresses and the deploy block into web/src/config/deployments.json (chain 11155111), which is the single source the frontend reads.

Contributing

See CONTRIBUTING.md: Conventional Commits and the Definition of Done gates. By participating you agree to the Code of Conduct.

Security

This code is unaudited and demo-scoped. To report a vulnerability, use GitHub's private vulnerability reporting (Security tab → Report a vulnerability) as described in SECURITY.md — please do not open a public issue.

License

MIT © 2026 Mohammed Soliman

About

Tokenized vault dapp — ERC-20 vault with non-transferable shares, cooldown faucet, and live Sepolia demo. Foundry contracts + Vite/React/TypeScript/wagmi frontend.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages