This project has two halves: a smart contract that creates a token called NIM, and a website where you can connect a wallet, check your NIM balance, and send some to someone else.
If you've never touched blockchain development before, that's fine nothing here assumes you already know this stuff. Every step below tells you what to type, what you should see when it works, and what to do if it doesn't. If you already know your way around Foundry and React, skip to "Setup" and just run the commands.
DEMO.mp4
A "token" on a blockchain isn't a file sitting somewhere it's a small piece of code (the smart contract) that keeps a ledger of who owns how much, plus a set of rules for moving that balance around. When you "send" someone tokens, you're calling a function on that contract that subtracts from your row in the ledger and adds to theirs. Once that happens, it's permanent and public nobody can quietly undo it, including you.
ERC-20 is just a standard — an agreed-upon shape for that contract, so every wallet, exchange, and app knows how to talk to it, the same way every USB drive follows the USB standard even though the brand and size differ.
erc20-token-wallet/
├── contracts/ Foundry project (Solidity)
│ ├── src/Token.sol the token contract itself
│ ├── test/Token.t.sol 27 automated tests
│ ├── script/Deploy.s.sol puts the contract on a blockchain
│ └── foundry.toml Foundry's settings file
└── frontend/ React + Vite + ethers.js
└── src/
├── App.jsx the main page
├── components/ Header, NetworkBanner, VerifyPanel,
│ BalanceCard, TransferForm, TransactionHistory
├── lib/
│ ├── wallet.js connect + sign-message ownership verification
│ ├── network.js network detection + auto switch/add network
│ └── contract.js contract read/write helpers
└── abi/Token.json tells the frontend what functions the contract has
- The contract was written from scratch rather than copied from a template
library, so every function is something you can actually explain if someone asks
you about it. It has all the standard ERC-20 functions, plus a
mintfunction so the contract owner can create test tokens, and aburnfunction to destroy tokens. - The tests try to break the contract on purpose — sending more than your balance, sending to an invalid address — to prove it actually refuses to allow that, rather than just hoping it does.
- The frontend doesn't just display data — it checks you're on the right network and makes you prove you own the wallet you connected (more on both below) before it lets you send anything.
Four things, one-time installs. Skip whatever you already have.
Get VS Code: https://code.visualstudio.com. Install it, then open this whole
erc20-token-wallet folder (File → Open Folder).
Every command below goes into VS Code's built-in terminal — open it with Ctrl + `
(the backtick key, usually top-left under Esc), or Terminal → New Terminal.
Runs the frontend website on your computer while you build it. Get the LTS version from https://nodejs.org — click through the installer's defaults.
How to check it worked:
node -v
npm -v✅ Success looks like: two version numbers print, e.g. v20.11.0 and 10.2.4.
❌ If you get "not recognized" or "command not found," close and fully reopen VS
Code (sometimes a full restart is needed) so it picks up the new install.
The toolkit that compiles, tests, and deploys the Solidity contract. It's a bundle
of a few tools: forge (build/test), anvil (runs a fake blockchain), cast
(pokes at contracts directly).
Mac/Linux:
curl -L https://foundry.paradigm.xyz | bash
foundryupWindows: Foundry doesn't install directly on Windows — install WSL first (it gives you a real Linux terminal inside Windows), restart when it asks, then run the two commands above inside the "Ubuntu" app it installs, not your regular Windows terminal.
How to check it worked:
forge --version
anvil --version✅ Success looks like: both print a version string with a git commit hash.
❌ "command not found" almost always means either foundryup didn't finish, or
you're in the wrong terminal (regular Windows cmd/PowerShell instead of WSL/Ubuntu).
Your wallet — a browser extension holding your accounts and signing transactions. Install from https://metamask.io, open it, create a new wallet. It'll show a 12-word recovery phrase — write it on paper, keep it safe, never screenshot or paste it anywhere. Nothing in this project uses real money, but build the habit now.
cd contractsFoundry projects use a small library called forge-std for writing tests. It isn't
bundled in install it separately:
forge install foundry-rs/forge-std --no-commit✅ Success: a new lib/forge-std folder appears, and the terminal prints
Installing forge-std in ... (url: ..., tag: ...) without an error afterward.
❌ If you see already exists in the index and is not a submodule, something git
-related got tangled up. Delete the leftover lib/forge-std folder, run
git rm -r --cached contracts/lib/forge-std from wherever your .git folder
actually lives, and try the install again.
Now compile:
forge build✅ Success: Compiler run successful! at the bottom.
❌ Import errors here almost always mean an import path got edited wrong somewhere.
Token.t.sol and Deploy.s.sol should import using relative paths like
../src/Token.sol and plain forge-std/Test.sol — not anything starting with
contracts/....
❌ Member "Transfer" not found means a test is trying to reference the Transfer
event as Token.Transfer(...). That event actually lives on the IERC20
interface, so it needs to be IERC20.Transfer(...) instead, with the import line
updated to import {IERC20, Token} from "../src/Token.sol";.
Now run the tests:
forge test -vv✅ Success: a long list of green [PASS] lines — 27 of them. Scroll to the very
bottom and you should see something like Suite result: ok. 27 passed; 0 failed;.
❌ Any [FAIL] line means something's actually wrong with the contract logic —
don't move on to deploying until every test passes. Paste the failing test's name
and the error under it if you're stuck.
You don't need real ETH or a real network for any of this. Anvil (bundled with Foundry) runs a complete fake Ethereum blockchain on your own computer, instantly, with 10 test accounts already loaded with 10,000 fake ETH each.
Open a second terminal — don't close the first — and run:
anvil✅ Success: it prints a banner and a list of 10 account addresses with matching private keys, then sits there waiting. That's correct — it's supposed to keep running, not finish and return you to the prompt. ❌ If it exits immediately or errors, Foundry likely didn't install correctly — re-check the Foundry step above.
Leave this terminal open for as long as you're testing. Close it and your local blockchain — along with anything deployed on it — disappears.
Back in your first terminal, still inside contracts:
cp .env.example .env(Windows Command Prompt uses copy instead of cp — everything else is the same.)
Open the new .env in VS Code. The PRIVATE_KEY already filled in is Anvil's first
default test account — identical every time you run Anvil, and publicly known.
That's exactly why it's fine for local testing and exactly why you'd never use a
key from a .env file on a real network with real funds.
Deploy:
source .env
forge script script/Deploy.s.sol:Deploy --rpc-url http://127.0.0.1:8545 --broadcast --private-key $PRIVATE_KEY(On Windows, source .env won't work the same way — just copy the PRIVATE_KEY
value out of the .env file in VS Code and paste it directly in place of
$PRIVATE_KEY in the command.)
✅ Success: output ending with something like:
Token deployed at: 0x5FbDB2315678afecb367f032d93F642f64180aa
##### anvil-hardhat
✅ [Success] Hash: 0x...
Block: 1
Copy that Token deployed at: address — you'll need it in Step 5.
❌ connection refused or similar means Anvil (Step 2) isn't running, or you're
pointed at the wrong RPC URL. Double check that second terminal is still open.
Copy Private Key #0 from your Anvil terminal (scroll up in that second window if you need to).
In MetaMask: account icon (top right) → Import account → paste the key → Import.
✅ Success: MetaMask now shows an account holding roughly 10,000 ETH. That's fake, local-only ETH — it exists purely so you can pay gas fees while testing.
You don't need to manually add the local network to MetaMask — the website adds it for you automatically the first time you connect, in Step 6.
Open a third terminal:
cd frontend(from the project root: cd erc20-token-wallet/frontend)
Install its dependencies — this downloads React, ethers.js, and everything else it needs:
npm install✅ Success: ends with something like added 150 packages in 12s and no red error
text above it. A node_modules folder appears in frontend/.
❌ If you later get 'vite' is not recognized as an internal or external command,
it means this step didn't actually finish. Check whether node_modules exists —
if it's missing or empty, run npm install again and let it fully complete.
cp .env.example .envOpen frontend/.env in VS Code and paste in the address from Step 3:
VITE_CONTRACT_ADDRESS=0x5FbDB2315678afecb367f032d93F642f64180aa
Leave the other variables (chain ID, network name, etc.) as they are — they already match a local Anvil blockchain.
Start it:
npm run dev✅ Success: output like
VITE v5.4.11 ready in 400 ms
Local: http://localhost:5173/
Open that link in your browser — you should see the app's homepage, not a blank page or an error.
- Click Connect Wallet, approve in the MetaMask popup. ✅ You'll see your shortened address appear in the header where the button was.
- If you're not on the right network, a "Wrong network" banner appears with a switch button. Click it — MetaMask will ask to add the network (first time only) and then switch. Nothing to configure by hand. ✅ The banner disappears once you're on the correct network.
- Click Verify wallet, sign the message MetaMask shows. No gas, no transaction — just proof you control the address. ✅ A green Verified badge appears, and the send form becomes usable.
- If you connected with the account you deployed from, your balance shows 1,000,000 NIM.
- Send a test transfer: copy Account #1's address (not the private key — the address) from your Anvil terminal, paste it into the recipient field, enter an amount, hit send, confirm in MetaMask. ✅ Success: the balance updates, and a new row appears in "Recent transactions" with a timestamp, the recipient, the amount, and a transaction hash.
- Try breaking it on purpose — an amount bigger than your balance, or an address missing a character. ✅ Success looks like: the error shows right in the form, before MetaMask is ever asked to sign anything, because the same rules the contract enforces are checked on the website first.
- Make sure MetaMask is on the local network the website added.
- Scroll down in MetaMask → Import tokens.
- Paste your contract address.
- Symbol and decimals should auto-fill as
NIMand18— type them in yourself if they don't. - Click Import.
✅ Success: your NIM balance now sits next to your ETH balance in MetaMask, and it matches the number shown on the website.
You're on a private local blockchain, so there's no Etherscan-style website to look things up on — but you've got three ways to check, in order of how much detail they give you:
- The website — the "Recent transactions" table is your quickest check. If a row appears there with a real hash after you hit send, it worked.
- Your Anvil terminal — every transaction gets printed live the moment it's mined, including which block it landed in and the gas used.
cast, if you want to check the raw result directly:Replacecast receipt <TX_HASH> --rpc-url http://127.0.0.1:8545
<TX_HASH>with the full hash from the website (click to select the whole thing — the table only shows a shortened version). ✅ Success: look forstatus: 1in the output — that means it succeeded.status: 0means it reverted (failed on-chain, gas was still spent).