Files
impersonator/docs/04-development.md
defiQUG 55fe7d10eb feat: comprehensive project improvements and fixes
- 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
2026-01-14 02:17:26 -08:00

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

  1. Create a feature branch
  2. Make your changes
  3. Write/update tests
  4. Run linter and tests
  5. Commit changes
  6. 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 any type
  • 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 features
  • fix/description - Bug fixes
  • refactor/description - Refactoring
  • docs/description - Documentation
  • test/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

  1. Create feature branch
  2. Make changes and commit
  3. Write/update tests
  4. Run tests and linter
  5. Create PR with description
  6. Address review comments
  7. Merge after approval

Common Tasks

Adding a New Wallet Type

  1. Create helper in helpers/smartWallet/
  2. Add type to types.ts
  3. Update SmartWalletContext
  4. Add UI component
  5. Write tests

Adding a New Transaction Type

  1. Update TransactionRequest type
  2. Add validation in utils/security.ts
  3. Update execution logic
  4. Add UI component
  5. Write tests

Adding a New Network

  1. Add to NETWORKS in utils/constants.ts
  2. Update network validation
  3. Add to network list component
  4. Test connection

Resources