Project goal: A lightweight Svelte‑Kit 5 dApp that lets a user connect / disconnect a wallet (MetaMask), read their USDT balance on Sepolia, and — if the balance is positive — send a transfer.
The UI supports light / dark themes, is fully responsive, and emits toast notifications for every important action or error.
Library / tool | Version | Purpose |
---|---|---|
Svelte‑Kit | 5.x | Application framework (file‑based routing, SSR, Vite under the hood). |
TypeScript | 5.x | Static typing across the whole code‑base. |
Vite 6 | 6.3.5 | Dev‑server & bundler (comes with Svelte‑Kit). |
Tailwind CSS 4 | 4.1.10 | Utility‑first styling; dark‑mode via class strategy; custom palette. |
@tailwindcss/vite | 4.x | Injects Tailwind into the Vite pipeline (zero PostCSS config). |
Viem | 2.31.x | Low‑level EVM JSON‑RPC client (read‑only). Used for readContract , network check, and writing txs. |
Ethers v6 | 6.14.x | High‑level helper for signing & sending transfer transactions. |
wagmi core | 2.15.x | (Optional) Helper hooks; we finally only used Viem + direct window.ethereum calls to keep bundle small. |
svelte‑toast | latest | A11y‑friendly toast notifications. |
@reown/appkit | 1.7.x | Internal design system (buttons / colors). Only a tiny subset is imported. |
No additional backend or database is required – everything talks directly to the Sepolia RPC (
https://rpc.sepolia.org
).
usdt-wallet-fresh/
├─ src/
│ ├─ lib/
│ │ ├─ components/
│ │ │ ├─ ThemeToggle.svelte // light / dark switcher
│ │ │ └─ Toast.svelte // reusable toast component
│ │ ├─ stores/
│ │ │ └─ walletStore.ts // (optional) Svelte stores, not mandatory
│ │ └─ constants.ts // chain, RPC, contract ABI
│ ├─ routes/
│ │ ├─ +layout.svelte // global CSS import & theme toggle
│ │ └─ +page.svelte // main dApp logic (connect, balance, transfer)
│ ├─ app.css // Tailwind directives
│ └─ app.d.ts // augment Window.ethereum typings
├─ static/ // 1‑trans.png, 2‑trans.png avatars
├─ tailwind.config.js // custom palette + dark mode
├─ vite.config.ts // adds tailwind plugin
└─ README.md // this file
Feature | File(s) | Notes |
---|---|---|
Connect / disconnect MetaMask | +page.svelte |
Uses window.ethereum.request({method:'eth_requestAccounts'}) + accountsChanged listener. |
Network guard (Sepolia only) | +page.svelte |
On each connect checks publicClient.getChainId() – if !== 11155111 it notifies & aborts. |
Live USDT balance | constants.ts (ABI) + fetchBalance() |
Calls balanceOf(address) and formats with formatUnits(_, 6) . Loading state shows animated dots. |
Manual address input | +page.svelte |
For users without MetaMask. Validates checksum/length before querying balance. |
Transfer form | +page.svelte ({#if balance>'0'} ) |
Two inputs → receiver + amount; calls erc20.transfer() via an ethers.Wallet signer built from MetaMask provider. Gas‐safe decimals handling via parseUnits(_,6) . |
Toast notifications | Toast.svelte |
Top‑right; auto‑dismiss after 3 s; color‑coded by type. |
Light / dark theme | ThemeToggle.svelte , Tailwind darkMode:'class' |
Persisted with localStorage ; system preference on first load. |
Responsive layout | Tailwind grids | Mobile hides avatar & collapses buttons. |
File | Purpose |
---|---|
tailwind.config.js |
Content glob, darkMode:'class' , extended colors (primary , secondary , semantic tokens for card/button). |
vite.config.ts |
Adds @tailwindcss/vite before sveltekit() plugin. |
svelte.config.js |
Enables PostCSS preprocessing (postcss:true ). |
postcss.config.js |
Minimal – handled by @tailwindcss/vite , but present if editors need it. |
app.d.ts |
Declares window.ethereum to avoid TS errors. |
pnpm install # or npm i / yarn
pnpm dev # Vite dev‑server on http://localhost:5173
Need test USDT? Use a Sepolia faucet for ETH, then the Tether USD (Sepolia) faucet or a peer‑to‑peer transfer.
pnpm build # static SSR build in /build
pnpm preview # preview the production build
const raw = await publicClient.readContract({
address: USDT_ADDRESS,
abi: ERC20_ABI,
functionName: 'balanceOf',
args: [address]
});
const tx = await erc20.write.transfer([recipient, parseUnits(amount, 6)]);
await tx.wait();
notify('Transfer sent');
The signer is injected by MetaMask; on manual‑address mode the transfer form is hidden.
if (await publicClient.getChainId() !== 11155111) {
notify('Switch MetaMask to Sepolia');
return;
}
window.ethereum.on('accountsChanged', async (accs:string[]) => {
if(accs.length){ address = accs[0] as `0x${string}`; await fetchBalance(); }
else disconnect();
});
* Persist last‑used address in localStorage
for instant balance on reload.
* Add unit tests with Vitest for helpers (address validator, decimal math).
* CI pipeline (GitHub Actions) for type‑check + Playwright e2e. * i18n (English / Spanish) via svelte‑i18n
. * Progressive Web App manifest for mobile installation.
MIT – do whatever you want, no warranty.
Tether® logo used only for demonstration; trademarks belong to their respective owners.