- Fix all TypeScript compilation errors (40+ fixes) - Add missing type definitions (TransactionRequest, SafeInfo) - Fix TransactionRequestStatus vs TransactionStatus confusion - Fix import paths and provider type issues - Fix test file errors and mock providers - Implement comprehensive security features - AES-GCM encryption with PBKDF2 key derivation - Input validation and sanitization - Rate limiting and nonce management - Replay attack prevention - Access control and authorization - Add comprehensive test suite - Integration tests for transaction flow - Security validation tests - Wallet management tests - Encryption and rate limiter tests - E2E tests with Playwright - Add extensive documentation - 12 numbered guides (setup, development, API, security, etc.) - Security documentation and audit reports - Code review and testing reports - Project organization documentation - Update dependencies - Update axios to latest version (security fix) - Update React types to v18 - Fix peer dependency warnings - Add development tooling - CI/CD workflows (GitHub Actions) - Pre-commit hooks (Husky) - Linting and formatting (Prettier, ESLint) - Security audit workflow - Performance benchmarking - Reorganize project structure - Move reports to docs/reports/ - Clean up root directory - Organize documentation - Add new features - Smart wallet management (Gnosis Safe, ERC4337) - Transaction execution and approval workflows - Balance management and token support - Error boundary and monitoring (Sentry) - Fix WalletConnect configuration - Handle missing projectId gracefully - Add environment variable template
9.9 KiB
9.9 KiB
Development Guide
This guide covers the development workflow, best practices, and common patterns used in the Impersonator project.
Development Workflow
1. Starting Development
# Start development server
pnpm dev
# Server runs on http://localhost:3000
2. Making Changes
- Create a feature branch
- Make your changes
- Write/update tests
- Run linter and tests
- Commit changes
- Push and create PR
3. Testing Changes
# Run all tests
pnpm test
# Run tests in watch mode
pnpm test:watch
# Run with coverage
pnpm test:coverage
# Run specific test suite
pnpm test:security
pnpm test:integration
4. Code Quality Checks
# Run linter
pnpm lint
# Fix linting issues
pnpm lint --fix
Development Patterns
Context Usage
Using SmartWalletContext
import { useSmartWallet } from "@/contexts/SmartWalletContext";
function MyComponent() {
const {
activeWallet,
smartWallets,
connectToWallet,
createWallet,
addOwner,
removeOwner,
updateThreshold,
} = useSmartWallet();
// Use context values and methods
}
Using TransactionContext
import { useTransaction } from "@/contexts/TransactionContext";
function MyComponent() {
const {
transactions,
pendingTransactions,
createTransaction,
approveTransaction,
executeTransaction,
estimateGas,
} = useTransaction();
// Use context values and methods
}
Using SafeInjectContext
import { useSafeInject } from "@/contexts/SafeInjectContext";
function MyComponent() {
const {
address,
appUrl,
setAddress,
setAppUrl,
iframeRef,
latestTransaction,
} = useSafeInject();
// Use context values and methods
}
Component Patterns
Functional Components with Hooks
"use client";
import { useState, useEffect } from "react";
import { Box, Button } from "@chakra-ui/react";
export default function MyComponent() {
const [state, setState] = useState<string>("");
useEffect(() => {
// Side effects
}, []);
return (
<Box>
<Button onClick={() => setState("new value")}>
Click me
</Button>
</Box>
);
}
Form Handling
import { useState } from "react";
import { useToast } from "@chakra-ui/react";
import { validateAddress } from "@/utils/security";
function AddressForm() {
const [address, setAddress] = useState("");
const toast = useToast();
const handleSubmit = async () => {
// Validate input
const validation = validateAddress(address);
if (!validation.valid) {
toast({
title: "Invalid Address",
description: validation.error,
status: "error",
});
return;
}
// Process valid address
const checksummed = validation.checksummed!;
// ... rest of logic
};
return (
// Form JSX
);
}
Error Handling
Try-Catch Pattern
try {
const result = await someAsyncOperation();
// Handle success
} catch (error: any) {
console.error("Operation failed:", error);
toast({
title: "Error",
description: error.message || "Operation failed",
status: "error",
});
}
Error Boundary
import ErrorBoundary from "@/components/ErrorBoundary";
function App() {
return (
<ErrorBoundary>
<YourComponent />
</ErrorBoundary>
);
}
Validation Patterns
Address Validation
import { validateAddress } from "@/utils/security";
const validation = validateAddress(address);
if (!validation.valid) {
throw new Error(validation.error);
}
const checksummed = validation.checksummed!;
Transaction Validation
import { validateTransactionRequest } from "@/utils/security";
const validation = validateTransactionRequest({
from: "0x...",
to: "0x...",
value: "1000000000000000000",
data: "0x",
});
if (!validation.valid) {
console.error("Validation errors:", validation.errors);
}
Async Operations
Using Async/Await
async function fetchData() {
try {
const data = await someAsyncCall();
return data;
} catch (error) {
console.error("Failed to fetch:", error);
throw error;
}
}
Promise Handling
someAsyncCall()
.then((result) => {
// Handle success
})
.catch((error) => {
// Handle error
});
State Management
Local State
const [value, setValue] = useState<string>("");
const [loading, setLoading] = useState<boolean>(false);
Context State
// Access context state
const { activeWallet } = useSmartWallet();
Derived State
const pendingCount = transactions.filter(
(tx) => tx.status === TransactionStatus.PENDING
).length;
Security Best Practices
Input Validation
Always validate user input:
import { validateAddress, validateTransactionValue } from "@/utils/security";
// Validate address
const addressValidation = validateAddress(userInput);
if (!addressValidation.valid) {
// Handle invalid input
}
// Validate transaction value
const valueValidation = validateTransactionValue(value);
if (!valueValidation.valid) {
// Handle invalid value
}
Secure Storage
Use SecureStorage for sensitive data:
import { SecureStorage } from "@/utils/encryption";
const storage = new SecureStorage();
await storage.setItem("key", JSON.stringify(sensitiveData));
const data = await storage.getItem("key");
Rate Limiting
Respect rate limits:
import { RateLimiter } from "@/utils/security";
const limiter = new RateLimiter();
if (!limiter.checkLimit(userAddress)) {
throw new Error("Rate limit exceeded");
}
Code Style Guidelines
TypeScript
- Use strict mode
- Define types for all functions
- Use interfaces for object shapes
- Avoid
anytype - Use type guards when needed
Naming Conventions
- Components: PascalCase (
WalletManager) - Functions: camelCase (
validateAddress) - Constants: UPPER_SNAKE_CASE (
MAX_GAS_LIMIT) - Types/Interfaces: PascalCase (
SmartWalletConfig) - Files: Match export name
Code Formatting
- Use Prettier for formatting
- 2 spaces for indentation
- Semicolons required
- Single quotes for strings
- Trailing commas in objects/arrays
Comments
- Use JSDoc for public APIs
- Explain "why" not "what"
- Keep comments up to date
- Remove commented-out code
Testing Patterns
Unit Tests
import { validateAddress } from "@/utils/security";
describe("validateAddress", () => {
it("should validate correct addresses", () => {
const result = validateAddress("0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb");
expect(result.valid).toBe(true);
});
it("should reject invalid addresses", () => {
const result = validateAddress("invalid");
expect(result.valid).toBe(false);
});
});
Component Tests
import { render, screen } from "@testing-library/react";
import WalletManager from "@/components/SmartWallet/WalletManager";
describe("WalletManager", () => {
it("should render wallet list", () => {
render(<WalletManager />);
expect(screen.getByText("Wallets")).toBeInTheDocument();
});
});
Debugging
Console Logging
// Use monitoring service for production
import { monitoring } from "@/utils/monitoring";
monitoring.debug("Debug message", { context });
monitoring.info("Info message", { context });
monitoring.warn("Warning message", { context });
monitoring.error("Error message", error, { context });
React DevTools
- Install React DevTools browser extension
- Inspect component tree
- View props and state
- Profile performance
Browser DevTools
- Use Network tab for API calls
- Use Console for errors
- Use Application tab for storage
- Use Sources for debugging
Performance Optimization
Memoization
import { useMemo, useCallback } from "react";
// Memoize expensive calculations
const expensiveValue = useMemo(() => {
return computeExpensiveValue(data);
}, [data]);
// Memoize callbacks
const handleClick = useCallback(() => {
doSomething();
}, [dependencies]);
Lazy Loading
import { lazy, Suspense } from "react";
const HeavyComponent = lazy(() => import("./HeavyComponent"));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<HeavyComponent />
</Suspense>
);
}
Code Splitting
Next.js automatically code-splits by route. For manual splitting:
import dynamic from "next/dynamic";
const DynamicComponent = dynamic(() => import("./Component"), {
ssr: false,
});
Git Workflow
Branch Naming
feature/description- New featuresfix/description- Bug fixesrefactor/description- Refactoringdocs/description- Documentationtest/description- Test additions
Commit Messages
Follow conventional commits:
feat: add wallet connection
fix: resolve address validation bug
docs: update API documentation
test: add integration tests
refactor: extract constants
Pull Request Process
- Create feature branch
- Make changes and commit
- Write/update tests
- Run tests and linter
- Create PR with description
- Address review comments
- Merge after approval
Common Tasks
Adding a New Wallet Type
- Create helper in
helpers/smartWallet/ - Add type to
types.ts - Update
SmartWalletContext - Add UI component
- Write tests
Adding a New Transaction Type
- Update
TransactionRequesttype - Add validation in
utils/security.ts - Update execution logic
- Add UI component
- Write tests
Adding a New Network
- Add to
NETWORKSinutils/constants.ts - Update network validation
- Add to network list component
- Test connection