abc-scaffold 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kimberley Bezuidenhout
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,109 @@
1
+ # abc-scaffold
2
+
3
+ **Africa's Blockchain Club** — project-agnostic blockchain scaffolding tool. Bootstrap a complete Hardhat + React workspace in seconds.
4
+
5
+ ```bash
6
+ npx abc-scaffold my-project
7
+ ```
8
+
9
+ ---
10
+
11
+ ## What gets scaffolded
12
+
13
+ ```
14
+ my-project/
15
+ ├── contracts/ # Drop your Solidity files here
16
+ ├── scripts/ # Deployment and utility scripts
17
+ ├── test/ # Hardhat test files
18
+ ├── frontend/ # React + Vite UI
19
+ │ └── src/
20
+ │ ├── App.jsx
21
+ │ └── components/
22
+ │ └── Navbar.jsx ← sticky nav with Connect Wallet button
23
+ ├── hardhat.config.js
24
+ ├── .env.example
25
+ ├── .gitignore
26
+ └── README.md
27
+ ```
28
+
29
+ Both root and frontend dependencies are installed automatically.
30
+
31
+ ---
32
+
33
+ ## Features
34
+
35
+ - **Project-agnostic** — works for NFTs, DeFi, DAOs, smart accounts, or anything else
36
+ - **Hardhat pre-configured** — Solidity 0.8.26, Cancun EVM, optimizer on, Sepolia network
37
+ - **OpenZeppelin + Account Abstraction** — ERC-20/721/1155, upgradeable contracts, ERC-4337 interfaces included as dependencies
38
+ - **React frontend** — Vite + React with a navbar and MetaMask wallet connection (ethers.js v6)
39
+ - **Auto install** — runs `npm install` in both the root and `frontend/` at the end
40
+ - **Plain JavaScript** — no TypeScript, no build step
41
+
42
+ ---
43
+
44
+ ## Usage
45
+
46
+ ```bash
47
+ # With a name argument
48
+ npx abc-scaffold my-nft-project
49
+
50
+ # Or let it prompt you
51
+ npx abc-scaffold
52
+ ? Project name: my-dao
53
+ ```
54
+
55
+ ---
56
+
57
+ ## Inside the generated project
58
+
59
+ ### Compile contracts
60
+ ```bash
61
+ npm run compile
62
+ ```
63
+
64
+ ### Run tests
65
+ ```bash
66
+ npm test
67
+ ```
68
+
69
+ ### Start a local node
70
+ ```bash
71
+ npx hardhat node
72
+ ```
73
+
74
+ ### Deploy locally
75
+ ```bash
76
+ npx hardhat run scripts/deploy.js --network localhost
77
+ ```
78
+
79
+ ### Start the frontend
80
+ ```bash
81
+ cd frontend
82
+ npm run dev # → http://localhost:5173
83
+ ```
84
+
85
+ ---
86
+
87
+ ## Repository structure
88
+
89
+ ```
90
+ abc-scaffold/ (this repo)
91
+ ├── bin/
92
+ │ └── abc-scaffold.js # CLI entry point
93
+ ├── src/
94
+ │ ├── scaffold.js # Scaffolding logic
95
+ │ └── logger.js # Coloured output
96
+ ├── templates/
97
+ │ ├── package.json # Template for generated projects
98
+ │ ├── hardhat.config.js
99
+ │ ├── .env.example
100
+ │ ├── .gitignore
101
+ │ └── frontend/ # Full React app template
102
+ └── package.json
103
+ ```
104
+
105
+ ---
106
+
107
+ ## License
108
+
109
+ MIT — Africa's Blockchain Club
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { scaffold } from '../src/scaffold.js';
4
+ import { printBanner } from '../src/logger.js';
5
+
6
+ const arg = process.argv[2];
7
+
8
+ if (arg === '--help' || arg === '-h') {
9
+ printBanner();
10
+ console.log(' Usage: npx abc-scaffold [project-name]\n');
11
+ console.log(' Scaffolds a Hardhat + React blockchain project.\n');
12
+ console.log(' If project-name is omitted you will be prompted.\n');
13
+ process.exit(0);
14
+ }
15
+
16
+ printBanner();
17
+ scaffold(arg);
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "abc-scaffold",
3
+ "version": "1.0.0",
4
+ "description": "Africa's Blockchain Club — project-agnostic blockchain scaffolding tool",
5
+ "author": "Africa's Blockchain Club",
6
+ "license": "MIT",
7
+ "type": "module",
8
+ "bin": {
9
+ "abc-scaffold": "bin/abc-scaffold.js"
10
+ },
11
+ "files": [
12
+ "bin",
13
+ "src",
14
+ "templates",
15
+ "README.md",
16
+ "LICENSE"
17
+ ],
18
+ "scripts": {
19
+ "start": "node bin/abc-scaffold.js"
20
+ },
21
+ "keywords": [
22
+ "hardhat",
23
+ "blockchain",
24
+ "scaffold",
25
+ "ethereum",
26
+ "smart-contracts",
27
+ "africa",
28
+ "web3",
29
+ "erc4337",
30
+ "openzeppelin"
31
+ ],
32
+ "dependencies": {
33
+ "chalk": "^5.3.0",
34
+ "fs-extra": "^11.2.0"
35
+ },
36
+ "engines": {
37
+ "node": ">=18.0.0"
38
+ },
39
+ "publishConfig": {
40
+ "access": "public"
41
+ }
42
+ }
package/src/logger.js ADDED
@@ -0,0 +1,16 @@
1
+ import chalk from 'chalk';
2
+
3
+ export function printBanner() {
4
+ console.log('');
5
+ console.log(chalk.yellow('╔══════════════════════════════════════════════╗'));
6
+ console.log(chalk.yellow('║') + chalk.bold.green(" Africa's Blockchain Club ") + chalk.yellow('║'));
7
+ console.log(chalk.yellow('║') + chalk.cyan(' abc-scaffold v1.0.0 ') + chalk.yellow('║'));
8
+ console.log(chalk.yellow('╚══════════════════════════════════════════════╝'));
9
+ console.log('');
10
+ }
11
+
12
+ export const step = (msg) => console.log('\n' + chalk.bold.blue('▶ ' + msg));
13
+ export const success = (msg) => console.log(chalk.green(' ✔ ') + msg);
14
+ export const info = (msg) => console.log(chalk.cyan(' → ') + msg);
15
+ export const warn = (msg) => console.log(chalk.yellow(' ⚠ ') + msg);
16
+ export const error = (msg) => console.log(chalk.red(' ✖ ') + msg);
@@ -0,0 +1,341 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
+ import { createInterface } from 'readline';
5
+ import fse from 'fs-extra';
6
+ import chalk from 'chalk';
7
+ import * as logger from './logger.js';
8
+
9
+ const __filename = fileURLToPath(import.meta.url);
10
+ const __dirname = path.dirname(__filename);
11
+ const TEMPLATES = path.join(__dirname, '..', 'templates');
12
+
13
+ function prompt(question) {
14
+ return new Promise((resolve) => {
15
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
16
+ rl.question(question, (answer) => { rl.close(); resolve(answer.trim()); });
17
+ });
18
+ }
19
+
20
+ function isValidName(name) {
21
+ return /^[a-z0-9][a-z0-9\-_]*$/.test(name);
22
+ }
23
+
24
+ function copyTemplate(filename, targetDir) {
25
+ fse.copySync(path.join(TEMPLATES, filename), path.join(targetDir, filename));
26
+ logger.success(filename);
27
+ }
28
+
29
+ function buildReadme(projectName) {
30
+ return `# ${projectName}
31
+
32
+ > Scaffolded with [abc-scaffold](https://github.com/africas-blockchain-club/abc-scaffold) by **Africa's Blockchain Club**
33
+
34
+ A project-agnostic Hardhat workspace ready for smart contract development, testing, and frontend integration. Build anything — NFTs, DeFi protocols, DAOs, smart accounts, or any on-chain application.
35
+
36
+ ---
37
+
38
+ ## Prerequisites
39
+
40
+ | Tool | Version |
41
+ |------|---------|
42
+ | Node.js | >= 18.0.0 |
43
+ | npm | >= 9.0.0 |
44
+ | MetaMask | Latest |
45
+
46
+ ---
47
+
48
+ ## Getting Started
49
+
50
+ ### 1. Install dependencies
51
+
52
+ \`\`\`bash
53
+ npm install
54
+ \`\`\`
55
+
56
+ ### 2. Set up environment variables
57
+
58
+ \`\`\`bash
59
+ cp .env.example .env
60
+ \`\`\`
61
+
62
+ Fill in your values in \`.env\`:
63
+
64
+ - \`SEPOLIA_RPC_URL\` — get one free from [Infura](https://infura.io) or [Alchemy](https://alchemy.com)
65
+ - \`PRIVATE_KEY\` — your deployer wallet private key (**never commit this file**)
66
+ - \`ETHERSCAN_API_KEY\` — for contract verification on [Etherscan](https://etherscan.io)
67
+
68
+ ### 3. Compile contracts
69
+
70
+ \`\`\`bash
71
+ npm run compile
72
+ \`\`\`
73
+
74
+ ### 4. Run tests
75
+
76
+ \`\`\`bash
77
+ npm test
78
+ \`\`\`
79
+
80
+ ### 5. Start a local Hardhat node
81
+
82
+ \`\`\`bash
83
+ npx hardhat node
84
+ \`\`\`
85
+
86
+ ### 6. Deploy contracts locally
87
+
88
+ \`\`\`bash
89
+ npx hardhat run scripts/deploy.js --network localhost
90
+ \`\`\`
91
+
92
+ ### 7. Start the frontend
93
+
94
+ \`\`\`bash
95
+ cd frontend
96
+ npm install
97
+ npm run dev
98
+ # → http://localhost:5173
99
+ \`\`\`
100
+
101
+ ---
102
+
103
+ ## Directory Structure
104
+
105
+ \`\`\`
106
+ ${projectName}/
107
+ ├── contracts/ # Solidity smart contracts
108
+ ├── scripts/ # Deployment and utility scripts
109
+ ├── test/ # Hardhat test files
110
+ ├── frontend/ # React + Vite frontend
111
+ │ └── src/
112
+ │ ├── App.jsx
113
+ │ └── components/
114
+ │ └── Navbar.jsx ← Connect Wallet button lives here
115
+ ├── hardhat.config.js # Hardhat configuration
116
+ ├── .env.example # Environment variable template
117
+ └── README.md
118
+ \`\`\`
119
+
120
+ ---
121
+
122
+ ## Writing Smart Contracts
123
+
124
+ Place your Solidity files in \`contracts/\`. The config targets Solidity \`0.8.26\` with the Cancun EVM and the optimizer enabled (200 runs).
125
+
126
+ **Example** — \`contracts/MyContract.sol\`:
127
+
128
+ \`\`\`solidity
129
+ // SPDX-License-Identifier: MIT
130
+ pragma solidity ^0.8.26;
131
+
132
+ contract MyContract {
133
+ // Your logic here
134
+ }
135
+ \`\`\`
136
+
137
+ ### Included libraries
138
+
139
+ | Package | Use case |
140
+ |---------|----------|
141
+ | \`@openzeppelin/contracts\` | ERC-20, ERC-721, ERC-1155, access control, etc. |
142
+ | \`@openzeppelin/contracts-upgradeable\` | UUPS / Transparent proxy upgradeable variants |
143
+ | \`@openzeppelin/hardhat-upgrades\` | Deploy and upgrade helper tasks |
144
+ | \`@account-abstraction/contracts\` | ERC-4337 interfaces and EntryPoint |
145
+
146
+ ---
147
+
148
+ ## Writing Tests
149
+
150
+ Place test files in \`test/\`. Hardhat ships with Mocha + Chai.
151
+
152
+ **Example** — \`test/MyContract.test.js\`:
153
+
154
+ \`\`\`javascript
155
+ const { expect } = require('chai');
156
+ const { ethers } = require('hardhat');
157
+
158
+ describe('MyContract', function () {
159
+ it('should deploy successfully', async function () {
160
+ const MyContract = await ethers.getContractFactory('MyContract');
161
+ const contract = await MyContract.deploy();
162
+ expect(await contract.getAddress()).to.not.equal(ethers.ZeroAddress);
163
+ });
164
+ });
165
+ \`\`\`
166
+
167
+ ---
168
+
169
+ ## Writing Deployment Scripts
170
+
171
+ **Example** — \`scripts/deploy.js\`:
172
+
173
+ \`\`\`javascript
174
+ const { ethers } = require('hardhat');
175
+
176
+ async function main() {
177
+ const [deployer] = await ethers.getSigners();
178
+ console.log('Deploying from:', deployer.address);
179
+
180
+ const MyContract = await ethers.getContractFactory('MyContract');
181
+ const contract = await MyContract.deploy();
182
+ await contract.waitForDeployment();
183
+
184
+ console.log('MyContract deployed to:', await contract.getAddress());
185
+ }
186
+
187
+ main().catch((err) => {
188
+ console.error(err);
189
+ process.exitCode = 1;
190
+ });
191
+ \`\`\`
192
+
193
+ ---
194
+
195
+ ## Networks
196
+
197
+ | Network | RPC | Chain ID |
198
+ |---------|-----|----------|
199
+ | Hardhat (local) | \`http://127.0.0.1:8545\` | 31337 |
200
+ | Sepolia (testnet) | \`SEPOLIA_RPC_URL\` in \`.env\` | 11155111 |
201
+
202
+ ---
203
+
204
+ ## Verifying Contracts
205
+
206
+ \`\`\`bash
207
+ npx hardhat verify --network sepolia <CONTRACT_ADDRESS> <CONSTRUCTOR_ARGS>
208
+ \`\`\`
209
+
210
+ ---
211
+
212
+ ## Frontend
213
+
214
+ The \`frontend/\` folder is a Vite + React app with:
215
+
216
+ - A sticky navigation bar with a **Connect Wallet** button (MetaMask / EIP-1193)
217
+ - Ethers.js v6 already wired up
218
+
219
+ ### Connecting to a deployed contract
220
+
221
+ Copy the ABI from \`artifacts/contracts/YourContract.sol/YourContract.json\` and import it:
222
+
223
+ \`\`\`javascript
224
+ import { BrowserProvider, Contract } from 'ethers';
225
+ import MyContractABI from '../../artifacts/contracts/MyContract.sol/MyContract.json';
226
+
227
+ const CONTRACT_ADDRESS = '0x...';
228
+
229
+ async function interact() {
230
+ const provider = new BrowserProvider(window.ethereum);
231
+ const signer = await provider.getSigner();
232
+ const contract = new Contract(CONTRACT_ADDRESS, MyContractABI.abi, signer);
233
+ // call contract methods here
234
+ }
235
+ \`\`\`
236
+
237
+ ---
238
+
239
+ ## Resources
240
+
241
+ - [Hardhat Docs](https://hardhat.org/docs)
242
+ - [Solidity Docs](https://docs.soliditylang.org)
243
+ - [OpenZeppelin Docs](https://docs.openzeppelin.com)
244
+ - [EIP-4337 (Account Abstraction)](https://eips.ethereum.org/EIPS/eip-4337)
245
+ - [Ethers.js v6 Docs](https://docs.ethers.org/v6)
246
+ - [Vite Docs](https://vitejs.dev)
247
+
248
+ ---
249
+
250
+ *Built with Africa's Blockchain Club — scaffold your next project with \`npx abc-scaffold\`.*
251
+ `;
252
+ }
253
+
254
+ function scaffoldFrontend(frontendDir, projectName) {
255
+ fse.ensureDirSync(path.join(frontendDir, 'src', 'components'));
256
+ fse.copySync(path.join(TEMPLATES, 'frontend'), frontendDir);
257
+
258
+ // Substitute project name in index.html
259
+ const htmlPath = path.join(frontendDir, 'index.html');
260
+ const html = fs.readFileSync(htmlPath, 'utf-8').replace(/{{PROJECT_NAME}}/g, projectName);
261
+ fs.writeFileSync(htmlPath, html);
262
+
263
+ logger.success('frontend/ (React + Vite + Connect Wallet)');
264
+ }
265
+
266
+ export async function scaffold(initialName) {
267
+ let projectName = initialName;
268
+
269
+ if (!projectName) {
270
+ projectName = await prompt(chalk.bold('? Project name: '));
271
+ }
272
+
273
+ if (!projectName) {
274
+ logger.error('Project name is required.');
275
+ process.exit(1);
276
+ }
277
+
278
+ // Normalise: lowercase, spaces → hyphens
279
+ projectName = projectName.trim().toLowerCase().replace(/\s+/g, '-');
280
+
281
+ if (!isValidName(projectName)) {
282
+ logger.error(`"${projectName}" is not a valid package name. Use lowercase letters, numbers, hyphens, or underscores.`);
283
+ process.exit(1);
284
+ }
285
+
286
+ const targetDir = path.resolve(process.cwd(), projectName);
287
+
288
+ if (fs.existsSync(targetDir)) {
289
+ logger.error(`Directory "${projectName}" already exists. Choose a different name or delete it first.`);
290
+ process.exit(1);
291
+ }
292
+
293
+ // ── Create directory structure ──────────────────────────────────────────────
294
+ logger.step('Creating project structure');
295
+
296
+ fs.mkdirSync(targetDir, { recursive: true });
297
+
298
+ for (const dir of ['contracts', 'scripts', 'test']) {
299
+ fs.mkdirSync(path.join(targetDir, dir), { recursive: true });
300
+ logger.success(`${dir}/`);
301
+ }
302
+
303
+ // ── Copy configuration files ────────────────────────────────────────────────
304
+ logger.step('Copying configuration files');
305
+
306
+ copyTemplate('hardhat.config.js', targetDir);
307
+ copyTemplate('.env.example', targetDir);
308
+ copyTemplate('.gitignore', targetDir);
309
+
310
+ // package.json — substitute project name
311
+ const pkgTemplate = fs.readFileSync(path.join(TEMPLATES, 'package.json'), 'utf-8');
312
+ fs.writeFileSync(
313
+ path.join(targetDir, 'package.json'),
314
+ pkgTemplate.replace('"{{PROJECT_NAME}}"', `"${projectName}"`)
315
+ );
316
+ logger.success('package.json');
317
+
318
+ // ── Generate README ─────────────────────────────────────────────────────────
319
+ logger.step('Generating README.md');
320
+ fs.writeFileSync(path.join(targetDir, 'README.md'), buildReadme(projectName));
321
+ logger.success('README.md');
322
+
323
+ // ── Scaffold React frontend ─────────────────────────────────────────────────
324
+ logger.step('Scaffolding React frontend');
325
+ scaffoldFrontend(path.join(targetDir, 'frontend'), projectName);
326
+
327
+ // ── Done ────────────────────────────────────────────────────────────────────
328
+ console.log('');
329
+ console.log(chalk.bold.green('✨ Project scaffolded!'));
330
+ console.log('');
331
+ console.log(' Next steps:');
332
+ console.log(chalk.cyan(` cd ${projectName}`));
333
+ console.log(chalk.cyan(' npm install') + chalk.gray(' # install root dependencies'));
334
+ console.log(chalk.cyan(' cp .env.example .env') + chalk.gray(' # fill in your keys'));
335
+ console.log(chalk.cyan(' npm run compile') + chalk.gray(' # compile contracts'));
336
+ console.log(chalk.cyan(' cd frontend && npm install') + chalk.gray(' # install frontend dependencies'));
337
+ console.log(chalk.cyan(' npm run dev') + chalk.gray(' # start the UI'));
338
+ console.log('');
339
+ console.log(chalk.gray(' Docs → see README.md'));
340
+ console.log('');
341
+ }
@@ -0,0 +1,4 @@
1
+ # Example .env file - Duplicate this file to '.env' and fill in your values
2
+ SEPOLIA_RPC_URL="https://sepolia.infura.io/v3/YOUR_INFURA_KEY"
3
+ PRIVATE_KEY="YOUR_WALLET_PRIVATE_KEY"
4
+ ETHERSCAN_API_KEY="YOUR_ETHERSCAN_API_KEY"
@@ -0,0 +1,12 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
+ <title>{{PROJECT_NAME}}</title>
7
+ </head>
8
+ <body>
9
+ <div id="root"></div>
10
+ <script type="module" src="/src/main.jsx"></script>
11
+ </body>
12
+ </html>
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "frontend",
3
+ "version": "1.0.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "vite build",
9
+ "preview": "vite preview"
10
+ },
11
+ "dependencies": {
12
+ "ethers": "^6.16.0",
13
+ "react": "^18.2.0",
14
+ "react-dom": "^18.2.0"
15
+ },
16
+ "devDependencies": {
17
+ "@vitejs/plugin-react": "^4.2.1",
18
+ "vite": "^5.0.0"
19
+ }
20
+ }
@@ -0,0 +1,93 @@
1
+ *,
2
+ *::before,
3
+ *::after {
4
+ margin: 0;
5
+ padding: 0;
6
+ box-sizing: border-box;
7
+ }
8
+
9
+ body {
10
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
11
+ background-color: #0d0d1a;
12
+ color: #e2e2e2;
13
+ min-height: 100vh;
14
+ }
15
+
16
+ .app {
17
+ min-height: 100vh;
18
+ display: flex;
19
+ flex-direction: column;
20
+ }
21
+
22
+ /* ── Main content ── */
23
+ .main {
24
+ flex: 1;
25
+ max-width: 1100px;
26
+ margin: 0 auto;
27
+ padding: 5rem 2rem;
28
+ width: 100%;
29
+ }
30
+
31
+ /* ── Hero ── */
32
+ .hero {
33
+ text-align: center;
34
+ }
35
+
36
+ .hero h1 {
37
+ font-size: 3rem;
38
+ font-weight: 800;
39
+ color: #ffffff;
40
+ margin-bottom: 0.75rem;
41
+ letter-spacing: -0.5px;
42
+ }
43
+
44
+ .subtitle {
45
+ font-size: 1.1rem;
46
+ color: #e94560;
47
+ margin-bottom: 3.5rem;
48
+ font-weight: 500;
49
+ }
50
+
51
+ /* ── Cards ── */
52
+ .cards {
53
+ display: grid;
54
+ grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
55
+ gap: 1.5rem;
56
+ margin-top: 1rem;
57
+ }
58
+
59
+ .card {
60
+ background-color: #1a1a2e;
61
+ border: 1px solid #2a2a4a;
62
+ border-radius: 12px;
63
+ padding: 1.75rem 1.5rem;
64
+ text-align: left;
65
+ transition: border-color 0.2s ease, transform 0.2s ease;
66
+ }
67
+
68
+ .card:hover {
69
+ border-color: #e94560;
70
+ transform: translateY(-3px);
71
+ }
72
+
73
+ .card h3 {
74
+ font-size: 1.05rem;
75
+ font-weight: 700;
76
+ color: #ffffff;
77
+ margin-bottom: 0.6rem;
78
+ }
79
+
80
+ .card p {
81
+ font-size: 0.9rem;
82
+ color: #9090a8;
83
+ line-height: 1.5;
84
+ }
85
+
86
+ .card code {
87
+ background-color: #0d0d1a;
88
+ color: #e94560;
89
+ padding: 0.1rem 0.4rem;
90
+ border-radius: 4px;
91
+ font-size: 0.85rem;
92
+ font-family: 'Fira Code', 'Cascadia Code', monospace;
93
+ }
@@ -0,0 +1,37 @@
1
+ import Navbar from './components/Navbar.jsx';
2
+ import './App.css';
3
+
4
+ function App() {
5
+ return (
6
+ <div className="app">
7
+ <Navbar />
8
+ <main className="main">
9
+ <div className="hero">
10
+ <h1>Your Blockchain Project</h1>
11
+ <p className="subtitle">Scaffolded by Africa's Blockchain Club</p>
12
+
13
+ <div className="cards">
14
+ <div className="card">
15
+ <h3>📄 Write Contracts</h3>
16
+ <p>Add your Solidity files to <code>contracts/</code></p>
17
+ </div>
18
+ <div className="card">
19
+ <h3>⚙️ Compile</h3>
20
+ <p>Run <code>npm run compile</code> in the project root</p>
21
+ </div>
22
+ <div className="card">
23
+ <h3>🚀 Deploy</h3>
24
+ <p>Add a script to <code>scripts/</code> and run it with Hardhat</p>
25
+ </div>
26
+ <div className="card">
27
+ <h3>🔗 Connect</h3>
28
+ <p>Use the wallet button above to connect MetaMask, then interact with your contracts</p>
29
+ </div>
30
+ </div>
31
+ </div>
32
+ </main>
33
+ </div>
34
+ );
35
+ }
36
+
37
+ export default App;
@@ -0,0 +1,90 @@
1
+ .navbar {
2
+ display: flex;
3
+ align-items: center;
4
+ justify-content: space-between;
5
+ padding: 1rem 2rem;
6
+ background-color: #1a1a2e;
7
+ border-bottom: 1px solid #2a2a4a;
8
+ position: sticky;
9
+ top: 0;
10
+ z-index: 100;
11
+ }
12
+
13
+ /* ── Brand ── */
14
+ .navbar-brand {
15
+ display: flex;
16
+ align-items: center;
17
+ gap: 0.6rem;
18
+ text-decoration: none;
19
+ }
20
+
21
+ .navbar-logo {
22
+ font-size: 1.4rem;
23
+ line-height: 1;
24
+ }
25
+
26
+ .navbar-title {
27
+ font-size: 1.1rem;
28
+ font-weight: 700;
29
+ color: #e94560;
30
+ letter-spacing: 0.3px;
31
+ }
32
+
33
+ /* ── Actions ── */
34
+ .navbar-actions {
35
+ display: flex;
36
+ align-items: center;
37
+ }
38
+
39
+ /* Connect button */
40
+ .connect-btn {
41
+ background-color: #e94560;
42
+ color: #ffffff;
43
+ border: none;
44
+ border-radius: 8px;
45
+ padding: 0.55rem 1.3rem;
46
+ font-size: 0.9rem;
47
+ font-weight: 600;
48
+ cursor: pointer;
49
+ transition: background-color 0.18s ease, transform 0.12s ease;
50
+ white-space: nowrap;
51
+ }
52
+
53
+ .connect-btn:hover:not(:disabled) {
54
+ background-color: #c73652;
55
+ transform: translateY(-1px);
56
+ }
57
+
58
+ .connect-btn:active:not(:disabled) {
59
+ transform: translateY(0);
60
+ }
61
+
62
+ .connect-btn:disabled {
63
+ background-color: #555566;
64
+ cursor: not-allowed;
65
+ }
66
+
67
+ /* Connected badge */
68
+ .wallet-badge {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: 0.5rem;
72
+ background-color: #0f3460;
73
+ border: 1px solid #e94560;
74
+ border-radius: 8px;
75
+ padding: 0.45rem 1rem;
76
+ }
77
+
78
+ .wallet-dot {
79
+ width: 8px;
80
+ height: 8px;
81
+ background-color: #4caf50;
82
+ border-radius: 50%;
83
+ flex-shrink: 0;
84
+ }
85
+
86
+ .wallet-address {
87
+ color: #e2e2e2;
88
+ font-family: 'Fira Code', 'Cascadia Code', monospace;
89
+ font-size: 0.875rem;
90
+ }
@@ -0,0 +1,56 @@
1
+ import { useState } from 'react';
2
+ import { BrowserProvider } from 'ethers';
3
+ import './Navbar.css';
4
+
5
+ export default function Navbar() {
6
+ const [account, setAccount] = useState(null);
7
+ const [connecting, setConnecting] = useState(false);
8
+
9
+ async function connectWallet() {
10
+ if (!window.ethereum) {
11
+ alert('MetaMask is not installed. Please install it at https://metamask.io');
12
+ return;
13
+ }
14
+
15
+ setConnecting(true);
16
+ try {
17
+ const provider = new BrowserProvider(window.ethereum);
18
+ const accounts = await provider.send('eth_requestAccounts', []);
19
+ setAccount(accounts[0]);
20
+ } catch (err) {
21
+ console.error('Wallet connection rejected:', err);
22
+ } finally {
23
+ setConnecting(false);
24
+ }
25
+ }
26
+
27
+ function truncate(addr) {
28
+ return `${addr.slice(0, 6)}…${addr.slice(-4)}`;
29
+ }
30
+
31
+ return (
32
+ <nav className="navbar">
33
+ <div className="navbar-brand">
34
+ <span className="navbar-logo">🔗</span>
35
+ <span className="navbar-title">Africa's Blockchain Club</span>
36
+ </div>
37
+
38
+ <div className="navbar-actions">
39
+ {account ? (
40
+ <div className="wallet-badge">
41
+ <span className="wallet-dot" />
42
+ <span className="wallet-address">{truncate(account)}</span>
43
+ </div>
44
+ ) : (
45
+ <button
46
+ className="connect-btn"
47
+ onClick={connectWallet}
48
+ disabled={connecting}
49
+ >
50
+ {connecting ? 'Connecting…' : 'Connect Wallet'}
51
+ </button>
52
+ )}
53
+ </div>
54
+ </nav>
55
+ );
56
+ }
@@ -0,0 +1,10 @@
1
+ import { StrictMode } from 'react';
2
+ import { createRoot } from 'react-dom/client';
3
+ import App from './App.jsx';
4
+ import './App.css';
5
+
6
+ createRoot(document.getElementById('root')).render(
7
+ <StrictMode>
8
+ <App />
9
+ </StrictMode>
10
+ );
@@ -0,0 +1,6 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react';
3
+
4
+ export default defineConfig({
5
+ plugins: [react()],
6
+ });
@@ -0,0 +1,33 @@
1
+ require("@nomicfoundation/hardhat-toolbox");
2
+ require("@openzeppelin/hardhat-upgrades");
3
+ require("dotenv").config();
4
+
5
+ module.exports = {
6
+ solidity: {
7
+ compilers: [
8
+ {
9
+ version: "0.8.26",
10
+ settings: {
11
+ evmVersion: "cancun",
12
+ optimizer: { enabled: true, runs: 200 }
13
+ }
14
+ }
15
+ ]
16
+ },
17
+ networks: {
18
+ localhost: {
19
+ url: "http://127.0.0.1:8545"
20
+ },
21
+ hardhat: {},
22
+ sepolia: {
23
+ url: process.env.SEPOLIA_RPC_URL || "",
24
+ accounts: process.env.PRIVATE_KEY ? [process.env.PRIVATE_KEY] : []
25
+ }
26
+ },
27
+ etherscan: {
28
+ apiKey: process.env.ETHERSCAN_API_KEY || ""
29
+ },
30
+ sourcify: {
31
+ enabled: true
32
+ }
33
+ };
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "{{PROJECT_NAME}}",
3
+ "version": "1.0.0",
4
+ "description": "Project agnostic blockchain repo",
5
+ "main": "index.js",
6
+ "scripts": {
7
+ "compile": "hardhat compile",
8
+ "test": "hardhat test"
9
+ },
10
+ "devDependencies": {
11
+ "@nomicfoundation/hardhat-toolbox": "^5.0.0",
12
+ "dotenv": "^16.4.5"
13
+ },
14
+ "dependencies": {
15
+ "@account-abstraction/contracts": "^0.6.0",
16
+ "@openzeppelin/contracts": "^5.0.2",
17
+ "@openzeppelin/contracts-upgradeable": "^5.4.0",
18
+ "@openzeppelin/hardhat-upgrades": "^3.9.1",
19
+ "cors": "^2.8.6",
20
+ "ethers": "^6.16.0",
21
+ "express": "^5.2.1",
22
+ "hardhat": "^2.19.4"
23
+ }
24
+ }