diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 00000000..5be36b22 --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,13 @@ +# AGENTS.md + +This file provides guidance to AI agents (OpenAI Codex, etc.) when working with this repository. + +## Context + +**Read `llms.txt` first** for complete project context, architecture decisions, and coding conventions. + +The `llms.txt` file references: +- `README.md` - Project overview and quick start +- `DEVELOPER_GUIDE.md` - Complete development workflow and setup + +Read those files for detailed information. The `llms.txt` file provides a concise summary optimized for AI assistance. diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md new file mode 100644 index 00000000..1d38388f --- /dev/null +++ b/ARCHITECTURE.md @@ -0,0 +1,28 @@ +# Architecture Overview + +This document describes the architecture of the Open Cap Table Protocol (OCP). + +## Codebase Structure + +- `chain/` - Smart contracts (Solidity, Foundry) + - `src/core/` - Core contracts (CapTable, CapTableFactory, Storage) + - `src/facets/` - Facet contracts + - `src/libraries/` - Shared libraries + - `src/interfaces/` - Contract interfaces + - `test/` - Foundry tests + +- `src/` - Server code (Node.js/TypeScript) + - `routes/` - Route handlers + - `controllers/` - Business logic controllers + - `db/` - MongoDB models and operations + - `chain-operations/` - Blockchain interaction utilities + - `utils/` - Utility functions + - `fairmint/` - Fairmint integration + - `rxjs/` - Reactive data processing + +## Related Documentation + +- [Data Model](docs/DATA_MODEL.md) - Database schema +- [Configuration](CONFIG.md) - Environment setup +- [Developer Guide](DEVELOPER_GUIDE.md) - Development workflow +- [README](README.md) - Project overview diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..430e9f1d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,13 @@ +# CLAUDE.md + +This file provides guidance to Claude Code when working with this repository. + +## Context + +**Read `llms.txt` first** for complete project context, architecture decisions, and coding conventions. + +The `llms.txt` file references: +- `README.md` - Project overview and quick start +- `DEVELOPER_GUIDE.md` - Complete development workflow and setup + +Read those files for detailed information. The `llms.txt` file provides a concise summary optimized for AI assistance. diff --git a/DEVELOPER_GUIDE.md b/DEVELOPER_GUIDE.md new file mode 100644 index 00000000..e0f3bd65 --- /dev/null +++ b/DEVELOPER_GUIDE.md @@ -0,0 +1,449 @@ +# Developer Guide + +This guide provides comprehensive information for developers working on the Open Cap Table Protocol (OCP) repository. + +## Table of Contents + +- [Getting Started](#getting-started) +- [Project Structure](#project-structure) +- [Development Setup](#development-setup) +- [Architecture Overview](#architecture-overview) +- [Development Workflow](#development-workflow) +- [Testing](#testing) +- [Code Standards](#code-standards) +- [Common Tasks](#common-tasks) +- [Troubleshooting](#troubleshooting) + +## Getting Started + +### Prerequisites + +Before you begin, ensure you have the following installed: + +- **Node.js** (version as specified in `package.json`) +- **Yarn** package manager +- **Foundry** (Forge, Anvil, Cast) - [Installation Guide](https://book.getfoundry.sh/getting-started/installation) +- **Docker** and **Docker Compose** (for MongoDB) +- **Git** + +### Quick Start + +1. **Clone the repository:** + ```bash + git clone https://github.com/Fairmint/open-captable-protocol.git + cd open-captable-protocol + ``` + +2. **Set up the smart contract dependencies:** + ```bash + ./setup.sh + ``` + +3. **Install Node.js dependencies:** + ```bash + yarn install + ``` + +4. **Configure environment:** + ```bash + cp .env.example .env.local + # Edit .env.local with your configuration + ``` + +5. **Start local development:** + - Terminal 1: Start Anvil (local blockchain) + ```bash + anvil + ``` + - Terminal 2: Deploy contracts + ```bash + yarn deploy:local + ``` + - Terminal 3: Start MongoDB + ```bash + docker compose up + ``` + - Terminal 4: Start the server + ```bash + yarn dev + ``` + +## Project Structure + +``` +open-captable-protocol/ +├── chain/ # Smart contracts (Solidity) +│ ├── src/ +│ │ ├── facets/ # Facet contracts +│ │ ├── libraries/ # Shared libraries +│ │ ├── core/ # Core contracts +│ │ └── interfaces/ # Contract interfaces +│ └── script/ # Deployment scripts +├── src/ # Server code (Node.js/TypeScript) +│ ├── app.js # Application entry point +│ ├── routes/ # Route handlers +│ ├── controllers/ # Business logic controllers +│ ├── db/ # MongoDB models and operations +│ ├── chain-operations/ # Blockchain interaction utilities +│ ├── utils/ # Utility functions +│ ├── tests/ # Test files +│ └── examples/ # Example scripts +├── docs/ # Documentation +│ ├── DATA_MODEL.md # Data model documentation +│ └── openapi.yaml # OpenAPI specification +├── scripts/ # Deployment and utility scripts +└── .github/ # GitHub workflows and templates +``` + +## Development Setup + +### Environment Configuration + +The project uses environment-specific configuration files: + +- `.env.local` - Local development +- `.env.dev` - Development/testnet environment +- `.env.prod` - Production/mainnet environment + +Key environment variables: + +```bash +# Database +DATABASE_URL="mongodb://ocp:ocp@localhost:27017/mongo?authSource=admin&retryWrites=true&w=majority" +DATABASE_REPLSET="0" +DATABASE_OVERRIDE="" # Optional database name override + +# Blockchain +RPC_URL="http://127.0.0.1:8545" +CHAIN_ID=31337 # 31337 for Anvil, 84532 for Base Sepolia, 8453 for Base Mainnet +PRIVATE_KEY="your_private_key_here" + +# Server +PORT=8293 + +# Contract Addresses (set after deployment) +FACTORY_ADDRESS= +ISSUER_FACET= +# ... other facet addresses +``` + +See [CONFIG.md](./CONFIG.md) for detailed configuration information. + +### Smart Contract Setup + +1. **Install Foundry dependencies:** + ```bash + cd chain + forge install + ``` + +2. **Build contracts:** + ```bash + forge build + ``` + +3. **Run tests:** + ```bash + forge test + ``` + +4. **Format code:** + ```bash + forge fmt + ``` + +### Database Setup + +MongoDB runs in Docker. To start: + +```bash +docker compose up -d +``` + +Connect using MongoDB Compass: +``` +mongodb://ocp:ocp@localhost:27017/mongo?authSource=admin&retryWrites=true&w=majority +``` + +### Local Blockchain Setup + +1. **Start Anvil:** + ```bash + anvil + ``` + +2. **Copy a private key from Anvil output** and set it in `.env.local`: + ```bash + PRIVATE_KEY= + ``` + +3. **Deploy contracts:** + ```bash + yarn deploy:local + ``` + +4. **Update `.env.local`** with the deployed contract addresses from the output. + +## Architecture Overview + +See [ARCHITECTURE.md](./ARCHITECTURE.md) for architecture details. + +## Development Workflow + +### Branch Strategy + +- **`main`** - Production-ready code +- **`dev`** - Development branch (default for PRs) +- **Feature branches** - Created from `dev` for new features + +**Important:** Never commit directly to `main` or `dev`. Always create a feature branch. + +### Creating a Feature Branch + +```bash +# Ensure you're on dev and up to date +git checkout dev +git pull origin dev + +# Create and checkout new branch +git checkout -b feature/your-feature-name + +# Make your changes and commit +git add . +git commit -m "feat(scope): description of changes" +``` + +### Commit Message Format + +Follow [Conventional Commits](https://www.conventionalcommits.org/): + +``` +(): + +[optional body] + +[optional footer] +``` + +Types: +- `feat`: New feature +- `fix`: Bug fix +- `docs`: Documentation changes +- `style`: Code style changes (formatting, etc.) +- `refactor`: Code refactoring +- `test`: Adding or updating tests +- `chore`: Maintenance tasks + +Example: +``` +feat(stakeholder): add support for stakeholder transfers +``` + +### Pull Request Process + +1. **Create PR** from your feature branch to `dev` +2. **PR Title** should follow Conventional Commits format: + ``` + feat(ocf-validation): creates OCF validation helper for routes + ``` +3. **Ensure tests pass** and code is formatted +4. **Request review** from team members +5. **Address feedback** and update PR +6. **Merge** after approval + +### Code Quality Checks + +Before committing, run: + +```bash +# Type checking +yarn typecheck + +# Linting +yarn lint:check + +# Format checking +yarn format:check + +# All checks +yarn flightcheck +``` + +Auto-fix issues: + +```bash +# Fix linting issues +yarn lint + +# Fix formatting +yarn format +``` + +## Testing + +### Running Tests + +**Smart Contract Tests:** +```bash +yarn test:chain +# or +cd chain && forge test +``` + +**JavaScript/TypeScript Tests:** +```bash +yarn test:js +``` + +**Integration Tests:** +```bash +yarn test-js-integration +``` + +### Writing Tests + +**Smart Contract Tests:** +- Located in `chain/test/` +- Use Foundry's testing framework +- Example: + ```solidity + function testExample() public { + // Test code + } + ``` + +**Server Tests:** +- Located in `src/tests/` +- Use Jest testing framework +- Example: + ```javascript + describe('Feature', () => { + it('should do something', async () => { + // Test code + }); + }); + ``` + +See [TESTING.md](./TESTING.md) for detailed testing guidelines. + +## Code Standards + +### TypeScript/JavaScript + +- **Type Safety**: Avoid `any` and `unknown`. Use precise, explicit types. +- **Formatting**: Use Prettier (configured in `.prettierrc`) +- **Linting**: Follow ESLint rules (configured in `eslint.config.js`) +- **Imports**: Use ES6 import/export syntax +- **Error Handling**: Use try/catch blocks and proper error messages + +### Solidity + +- **Formatting**: Use `forge fmt` +- **Linting**: Follow Solhint rules (configured in `.solhintrc`) +- **Naming**: Use camelCase for variables, PascalCase for contracts +- **Documentation**: Add NatSpec comments for public functions + +### File Organization + +- Group related functionality together +- Keep files focused and single-purpose +- Use descriptive file and function names +- Add comments for complex logic + +## Common Tasks + +### Adding a New Route + +1. **Create route handler** in `src/routes/` +2. **Create controller** in `src/controllers/` +3. **Add route** to `src/app.js` +4. **Update OpenAPI spec** in `docs/openapi.yaml` if applicable +5. **Add tests** in `src/tests/` + +### Adding a New Smart Contract + +1. **Create contract** in appropriate directory (`chain/src/core/`, `chain/src/facets/`, etc.) +2. **Define storage** if needed in `chain/src/core/Storage.sol` +3. **Add deployment script** in `chain/script/` if needed +4. **Update factory** if needed +5. **Add tests** in `chain/test/` + +### Adding Support for a New Chain + +1. **Add chain config** in `src/utils/chains.js`: + ```javascript + 12345: { + name: "New Chain", + rpcUrl: process.env.NEW_CHAIN_RPC_URL, + wsUrl: (process.env.NEW_CHAIN_RPC_URL || "").replace("https://", "wss://"), + } + ``` +2. **Add RPC URL** to environment files +3. **Deploy contracts** to the new chain +4. **Update documentation** + +### Resetting Local Environment + +To reset your local database: + +```bash +yarn deseed +``` + +This removes all test data from MongoDB. + +### Viewing Logs + +**API Server:** +- Logs are output to console +- Use `console.log()`, `console.error()` for debugging + +**Smart Contracts:** +- Use Foundry's logging: + ```solidity + console.log("Value:", value); + ``` + +## Troubleshooting + +### Common Issues + +**"Cannot connect to MongoDB"** +- Ensure Docker is running: `docker compose up` +- Check `DATABASE_URL` in `.env.local` +- Verify MongoDB container is healthy: `docker ps` + +**"Contract not found"** +- Ensure contracts are deployed: `yarn deploy:local` +- Check contract addresses in `.env.local` +- Verify Anvil is running and on the correct chain ID + +**"Type errors in TypeScript"** +- Run `yarn typecheck` to see all errors +- Ensure types are properly imported +- Check `tsconfig.json` configuration + +**"Tests failing"** +- Ensure all services are running (Anvil, MongoDB) +- Check environment variables are set correctly +- Clear test database: `yarn deseed` + +**"WebSocket connection issues"** +- Verify RPC URL supports WebSocket connections +- Check `WS_RECONNECT_INTERVAL` and `WS_MAX_RECONNECT_ATTEMPTS` in env +- Review logs for connection errors + +### Getting Help + +- Check existing documentation in `docs/` +- Review [CONFIG.md](./CONFIG.md) for configuration issues +- Check [CONTRIBUTING.md](./CONTRIBUTING.md) for contribution guidelines +- Open an issue on GitHub for bugs or feature requests + +## Additional Resources + +- [OpenAPI Specification](./docs/openapi.yaml) - API specification +- [Data Model Documentation](./docs/DATA_MODEL.md) - Database schema +- [Configuration Guide](./CONFIG.md) - Environment configuration +- [Foundry Book](https://book.getfoundry.sh/) - Foundry documentation +- [OCF Standard](https://github.com/Open-Cap-Table-Coalition/Open-Cap-Format-OCF) - Open Cap Table Format specification diff --git a/GEMINI.md b/GEMINI.md new file mode 100644 index 00000000..28d6bcc7 --- /dev/null +++ b/GEMINI.md @@ -0,0 +1,13 @@ +# GEMINI.md + +This file provides guidance to Gemini CLI when working with this repository. + +## Context + +**Read `llms.txt` first** for complete project context, architecture decisions, and coding conventions. + +The `llms.txt` file references: +- `README.md` - Project overview and quick start +- `DEVELOPER_GUIDE.md` - Complete development workflow and setup + +Read those files for detailed information. The `llms.txt` file provides a concise summary optimized for AI assistance. diff --git a/TESTING.md b/TESTING.md new file mode 100644 index 00000000..91cd4676 --- /dev/null +++ b/TESTING.md @@ -0,0 +1,638 @@ +# Testing Guide + +This guide covers testing practices, test structure, and how to write and run tests for the Open Cap Table Protocol (OCP). + +## Table of Contents + +- [Overview](#overview) +- [Test Structure](#test-structure) +- [Running Tests](#running-tests) +- [Smart Contract Testing](#smart-contract-testing) +- [API Testing](#api-testing) +- [Integration Testing](#integration-testing) +- [Writing Tests](#writing-tests) +- [Test Utilities](#test-utilities) +- [Best Practices](#best-practices) +- [Troubleshooting](#troubleshooting) + +## Overview + +OCP uses two testing frameworks: + +- **Foundry (Forge)** - For smart contract testing +- **Jest** - For API and integration testing + +Tests are organized by type: +- **Unit Tests** - Test individual functions/contracts in isolation +- **Integration Tests** - Test interactions between components +- **End-to-End Tests** - Test complete workflows + +## Test Structure + +### Smart Contract Tests + +Location: `chain/test/` + +``` +chain/test/ +├── TestBase.sol # Base test contract with common setup +├── StockIssuance.t.sol # Stock issuance tests +├── StockTransfer.t.sol # Stock transfer tests +├── StakeholderPositions.t.sol +├── AccessControl.t.sol +└── mocks/ # Mock contracts for testing +``` + +### API Tests + +Location: `src/tests/` + +``` +src/tests/ +├── errorHandling.test.js # Error handling tests +├── transactionHash.test.js # Transaction hash tests +└── integration/ # Integration tests + └── utils.ts # Test utilities +``` + +## Running Tests + +### Smart Contract Tests + +**Run all contract tests:** +```bash +yarn test:chain +# or +cd chain && forge test +``` + +**Run specific test file:** +```bash +cd chain +forge test --match-path test/StockIssuance.t.sol +``` + +**Run with verbosity:** +```bash +forge test -vvv # Very verbose output +``` + +**Run specific test function:** +```bash +forge test --match-test testIssueStock +``` + +**Run with gas reporting:** +```bash +forge test --gas-report +``` + +**Run with coverage:** +```bash +forge coverage +``` + +### API Tests + +**Run all JavaScript tests:** +```bash +yarn test:js +``` + +**Run integration tests:** +```bash +yarn test-js-integration +``` + +**Run specific test file:** +```bash +yarn jest src/tests/errorHandling.test.js +``` + +**Run in watch mode:** +```bash +yarn jest --watch +``` + +**Run with coverage:** +```bash +yarn jest --coverage +``` + +### Running Tests in CI/CD + +Tests should pass before merging PRs. The CI pipeline runs: + +```bash +yarn flightcheck # Lint and format checks +yarn typecheck # TypeScript type checking +yarn test:chain # Smart contract tests +yarn test:js # API tests +``` + +## Smart Contract Testing + +### Test Setup + +Smart contract tests extend a base test contract which provides: + +- Deployed factory and cap table contracts +- Helper functions for creating stakeholders, stock classes, etc. +- Common test fixtures + +Example: + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.0; + +import "./TestBase.sol"; +import { IStockFacet } from "@interfaces/IStockFacet.sol"; + +contract MyTest is TestBase { + function testMyFeature() public { + // Test code here + } +} +``` + +### Writing Contract Tests + +**Basic Test Structure:** + +```solidity +function testFeatureName() public { + // Arrange - Set up test data + bytes16 stakeholderId = createStakeholder(); + bytes16 stockClassId = createStockClass(0x123...); + + // Act - Execute the function + IStockFacet(address(capTable)).issueStock(params); + + // Assert - Verify results + assertEq(quantity, expectedQuantity); +} +``` + +**Testing Reverts:** + +```solidity +function test_RevertInvalidInput() public { + bytes16 invalidId = 0x00000000000000000000000000000000; + + vm.expectRevert( + abi.encodeWithSignature("NoStakeholder(bytes16)", invalidId) + ); + + IStockFacet(address(capTable)).issueStock(params); +} +``` + +**Testing Events:** + +```solidity +function testEmitsEvent() public { + vm.expectEmit(true, true, false, true, address(capTable)); + emit StockIssued(stakeholderId, stockClassId, quantity, price); + + IStockFacet(address(capTable)).issueStock(params); +} +``` + +**Using Forge's Cheatcodes:** + +```solidity +// Set block timestamp +vm.warp(block.timestamp + 1 days); + +// Set block number +vm.roll(block.number + 100); + +// Impersonate an address +vm.prank(userAddress); +contract.function(); + +// Deal tokens/ETH +vm.deal(address(this), 100 ether); + +// Expect a call +vm.expectCall(address(target), abi.encodeWithSelector(...)); +``` + +### Test Helpers + +The base test contract provides helper functions: + +```solidity +// Create a stakeholder +bytes16 stakeholderId = createStakeholder(); + +// Create a stock class +bytes16 stockClassId = createStockClass(0x123..., 100000); + +// Create a stock plan +bytes16 stockPlanId = createStockPlan(stockClassIds); +``` + +## API Testing + +### Test Setup + +API tests use Jest and require: + +- MongoDB connection (test database) +- Local blockchain (Anvil) running +- Contract deployment + +Example setup: + +```javascript +import { describe, test, beforeAll, afterAll, expect } from "@jest/globals"; +import { connectDB } from "../db/config/mongoose"; +import { deseedDatabase } from "../tests/integration/utils"; + +describe("Feature Tests", () => { + beforeAll(async () => { + // Connect to test database + await connectDB(); + + // Deploy contracts + // Set up test data + }); + + afterAll(async () => { + // Clean up + await deseedDatabase(); + await disconnectDB(); + }); + + test("should do something", async () => { + // Test code + }); +}); +``` + +### Writing API Tests + +**Basic Test:** + +```javascript +test("should create issuer", async () => { + const response = await request(app) + .post("/issuer") + .send({ + chain_id: 31337, + legal_name: "Test Company", + // ... other fields + }); + + expect(response.status).toBe(200); + expect(response.body.id).toBeDefined(); +}); +``` + +**Testing Error Cases:** + +```javascript +test("should return 400 for invalid input", async () => { + const response = await request(app) + .post("/issuer") + .send({ + // Missing required fields + }); + + expect(response.status).toBe(400); + expect(response.body.error).toBeDefined(); +}); +``` + +**Testing Database Operations:** + +```javascript +test("should save to database", async () => { + const issuer = await createIssuer(issuerData); + + const found = await Issuer.findById(issuer.id); + expect(found).toBeDefined(); + expect(found.legal_name).toBe("Test Company"); +}); +``` + +**Testing Contract Interactions:** + +```javascript +test("should deploy contract", async () => { + const { contract, address } = await deployCapTable( + issuerIdBytes16, + initialShares, + chainId + ); + + expect(contract).toBeDefined(); + expect(address).toMatch(/^0x[a-fA-F0-9]{40}$/); +}); +``` + +### Test Utilities + +**Database Utilities:** + +```javascript +import { deseedDatabase, deleteIssuerData } from "./integration/utils"; + +// Clear entire database +await deseedDatabase(); + +// Delete specific issuer data +await deleteIssuerData(issuerId); +``` + +**Contract Utilities:** + +```javascript +import { deployCapTable } from "../chain-operations/deployCapTable"; +import { convertUUIDToBytes16 } from "../utils/convertUUID"; +import { toScaledBigNumber } from "../utils/convertToFixedPointDecimals"; + +// Deploy a cap table +const { contract, address } = await deployCapTable( + convertUUIDToBytes16(issuerId), + "1000000", + "31337" +); + +// Convert values for contract calls +const quantity = toScaledBigNumber("1000"); +``` + +**Error Decoding:** + +```javascript +import { decodeError } from "../utils/errorDecoder"; + +try { + await contract.issueStock(params); +} catch (error) { + const decoded = decodeError(error); + expect(decoded.name).toBe("NoStakeholder"); +} +``` + +## Integration Testing + +Integration tests verify interactions between: + +- API endpoints and database +- API endpoints and smart contracts +- Database and blockchain events +- Complete workflows + +### Example Integration Test + +```javascript +describe("Stock Issuance Integration", () => { + let contract; + let issuerId; + let stockClassId; + let stakeholderId; + + beforeAll(async () => { + // Set up complete environment + issuerId = await createIssuer(); + contract = await deployCapTable(issuerId); + stockClassId = await createStockClass(contract); + stakeholderId = await createStakeholder(contract); + }); + + test("should issue stock end-to-end", async () => { + // 1. Create issuance via API + const response = await request(app) + .post("/transactions/issuance") + .send({ + issuerId, + stockClassId, + stakeholderId, + quantity: "1000", + // ... + }); + + expect(response.status).toBe(200); + + // 2. Verify on-chain + const position = await contract.getPosition( + stakeholderId, + stockClassId + ); + expect(position.quantity).toBe(toScaledBigNumber("1000")); + + // 3. Verify in database + const transaction = await Issuance.findById(response.body.id); + expect(transaction).toBeDefined(); + expect(transaction.quantity).toBe("1000"); + }); +}); +``` + +## Writing Tests + +### Test Naming Conventions + +**Smart Contracts:** +- `testFeatureName()` - Happy path tests +- `test_RevertReason()` - Revert tests +- `testFuzz_FeatureName()` - Fuzz tests + +**API Tests:** +- `should do something` - Descriptive test names +- Use `describe` blocks to group related tests + +### Test Organization + +1. **Arrange** - Set up test data and environment +2. **Act** - Execute the code being tested +3. **Assert** - Verify the results + +### Test Isolation + +- Each test should be independent +- Clean up after tests (use `afterEach` or `afterAll`) +- Don't rely on test execution order +- Use unique IDs for test data + +### Mocking + +**Mocking External Services:** + +```javascript +jest.mock("../utils/externalService", () => ({ + callExternalAPI: jest.fn().mockResolvedValue({ data: "test" }) +})); +``` + +**Mocking Contract Calls:** + +```javascript +const mockContract = { + issueStock: jest.fn().mockResolvedValue({ hash: "0x..." }) +}; +``` + +## Test Utilities + +### UUID Conversion + +```javascript +import { convertUUIDToBytes16 } from "../utils/convertUUID"; + +const bytes16 = convertUUIDToBytes16(uuid); +``` + +### Fixed-Point Decimals + +```javascript +import { toScaledBigNumber } from "../utils/convertToFixedPointDecimals"; + +const scaled = toScaledBigNumber("1000.50"); // Returns BigNumber +``` + +### Error Decoding + +```javascript +import { decodeError } from "../utils/errorDecoder"; + +try { + await contract.call(); +} catch (error) { + const decoded = decodeError(error); + console.log(decoded.name, decoded.args); +} +``` + +### Waiting for Transactions + +```javascript +const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms)); + +// Wait for transaction to be mined +await wait(5000); +``` + +## Best Practices + +### 1. Test Coverage + +- Aim for high coverage of critical paths +- Test both happy paths and error cases +- Test edge cases and boundary conditions + +### 2. Test Speed + +- Keep tests fast (use mocks where appropriate) +- Run tests in parallel when possible +- Use separate test databases + +### 3. Test Data + +- Use factories or fixtures for test data +- Clean up test data after tests +- Use unique identifiers to avoid conflicts + +### 4. Assertions + +- Use descriptive assertion messages +- Test one thing per test +- Verify both positive and negative cases + +### 5. Test Maintenance + +- Keep tests up to date with code changes +- Refactor tests when refactoring code +- Remove obsolete tests + +### 6. Documentation + +- Add comments for complex test logic +- Document test setup requirements +- Explain test scenarios + +## Troubleshooting + +### Common Issues + +**Tests Failing Intermittently:** + +- Check for race conditions +- Ensure proper cleanup between tests +- Verify test isolation + +**Contract Tests Failing:** + +- Check Anvil is running (for integration tests) +- Verify contract addresses in test setup +- Check gas limits + +**API Tests Failing:** + +- Verify MongoDB is running +- Check database connection string +- Ensure test database is clean + +**Timeout Errors:** + +- Increase test timeout: + ```javascript + jest.setTimeout(30000); // 30 seconds + ``` +- Check for hanging promises +- Verify async/await usage + +**Type Errors in Tests:** + +- Run `yarn typecheck` to see all errors +- Ensure test files are included in `tsconfig.json` +- Check import paths + +### Debugging Tests + +**Smart Contract Tests:** + +```bash +# Run with verbose output +forge test -vvv + +# Run specific test +forge test --match-test testName -vvv + +# Use console.log in tests +console.log("Value:", value); +``` + +**API Tests:** + +```bash +# Run with verbose output +yarn jest --verbose + +# Run specific test +yarn jest testName --verbose + +# Use debugger +node --inspect-brk node_modules/.bin/jest --runInBand testName +``` + +**Debugging Database Issues:** + +```javascript +// Log database operations +console.log("Issuer:", await Issuer.findById(id)); + +// Check connection +console.log("DB State:", mongoose.connection.readyState); +``` + +## Additional Resources + +- [Foundry Testing Documentation](https://book.getfoundry.sh/forge/tests) +- [Jest Documentation](https://jestjs.io/docs/getting-started) +- [Developer Guide](./DEVELOPER_GUIDE.md) - Development setup +- [Architecture Documentation](./ARCHITECTURE.md) - System architecture diff --git a/llms.txt b/llms.txt new file mode 100644 index 00000000..f1a39fa7 --- /dev/null +++ b/llms.txt @@ -0,0 +1,189 @@ +# Open Cap Table Protocol (OCP) + +> A comprehensive solution for managing capitalization tables on the blockchain. Implements the Open Cap Table Coalition (OCF) standard, providing secure, transparent, and standardized equity ownership management across multiple EVM-compatible chains. + +**For project overview, setup, and usage, see [README.md](README.md).** +**For development workflow, testing, and code standards, see [DEVELOPER_GUIDE.md](DEVELOPER_GUIDE.md).** + +## Key Technical Details + +- **Framework**: Express.js (Node.js) + TypeScript +- **Database**: MongoDB (Mongoose ODM) +- **Blockchain**: Solidity (Foundry) +- **Runtime**: Node.js v22+ with ES modules +- **Package Manager**: Yarn 4 +- **Testing**: Jest (API) + Forge (Smart Contracts) +- **Validation**: OCF JSON Schema validation (AJV) + +## Architecture Patterns + +See [ARCHITECTURE.md](ARCHITECTURE.md) for architecture overview. + +## File Structure Overview + +See [README.md](README.md) for complete project structure. Key directories: + +- `chain/` - Smart contracts +- `src/` - API server (Express.js + MongoDB) + - `routes/` - API route handlers + - `controllers/` - Business logic + - `db/` - MongoDB models and operations + - `chain-operations/` - Blockchain interaction utilities + - `utils/` - Utility functions +- `docs/` - Documentation + +## ADR Quick Reference + +| ADR | Decision | What This Means for Development | +|-----|----------|--------------------------------| +| N/A | OCF Standard | **All data must validate against OCF schemas**. Use `validateInputAgainstOCF()` before saving. | +| N/A | Multi-Chain | **Always pass `chain_id` for issuer creation**. Use `getChainConfig()` for chain-specific operations. | +| N/A | Event Sync | **WebSocket listeners auto-sync events**. Don't manually sync - listeners handle it. | + +## Code Patterns (Do / Don't) + +### UUID Conversion +```javascript +// ✅ DO: Convert UUIDs to bytes16 for contract calls +import { convertUUIDToBytes16 } from "../utils/convertUUID.js"; +const bytes16 = convertUUIDToBytes16(uuid); + +// ❌ DON'T: Pass UUID strings directly to contracts +await contract.createStakeholder(uuid); // Wrong! +``` + +### Fixed-Point Decimals +```javascript +// ✅ DO: Use scaled BigNumber for contract calls +import { toScaledBigNumber } from "../utils/convertToFixedPointDecimals.js"; +const quantity = toScaledBigNumber("1000.50"); // Returns BigNumber + +// ❌ DON'T: Pass decimal strings directly +await contract.issueStock({ quantity: "1000.50" }); // Wrong! +``` + +### Contract Instance Caching +```javascript +// ✅ DO: Use contract middleware or cache +// Middleware automatically caches: req.contract +// Or use: contractCache[`${chainId}-${issuerId}`] + +// ❌ DON'T: Create new contract instances for every call +const contract = await getContractInstance(address, chainId); // Inefficient if called repeatedly +``` + +### OCF Validation +```javascript +// ✅ DO: Validate against OCF schema before saving +import validateInputAgainstOCF from "../utils/validateInputAgainstSchema.js"; +await validateInputAgainstOCF(data, schema); + +// ❌ DON'T: Save data without validation +await createIssuer(data); // Missing validation! +``` + +### Error Handling +```javascript +// ✅ DO: Use error decoder for contract errors +import { decodeError } from "../utils/errorDecoder.js"; +try { + await contract.issueStock(params); +} catch (error) { + const decoded = decodeError(error); + console.log(decoded.name, decoded.args); +} + +// ❌ DON'T: Log raw errors without decoding +catch (error) { + console.log(error); // Not helpful! +} +``` + +### Chain Configuration +```javascript +// ✅ DO: Use getChainConfig for chain-specific operations +import { getChainConfig } from "../utils/chains.js"; +const config = getChainConfig(chainId); +const provider = new ethers.JsonRpcProvider(config.rpcUrl); + +// ❌ DON'T: Hardcode RPC URLs +const provider = new ethers.JsonRpcProvider("http://localhost:8545"); // Wrong! +``` + +### Database Operations +```javascript +// ✅ DO: Use atomic save operations +import { save } from "../db/operations/atomic.ts"; +const issuer = await save(new Issuer(issuerData)); + +// ❌ DON'T: Use model.save() directly (not atomic) +const issuer = new Issuer(issuerData); +await issuer.save(); // Not atomic! +``` + +## API Endpoints + +See `docs/openapi.yaml` for complete API specification. + +## Key Utilities + +### UUID Conversion +- `convertUUIDToBytes16(uuid)` - Convert UUID string to bytes16 for contracts +- `convertBytes16ToUUID(bytes16)` - Convert bytes16 back to UUID + +### Fixed-Point Decimals +- `toScaledBigNumber(value)` - Convert decimal string to scaled BigNumber +- Used for all contract calls involving quantities/prices + +### Error Decoding +- `decodeError(error)` - Decode contract revert errors to readable format +- Returns `{ name, args, message }` structure + +### Chain Configuration +- `getChainConfig(chainId)` - Get RPC/WebSocket URLs for chain +- `SUPPORTED_CHAINS` - Map of supported chain IDs + +### Contract Operations +- `deployCapTable(issuerIdBytes16, shares, chainId)` - Deploy new cap table +- `getContractInstance(address, chainId)` - Get contract instance (cached) + +## Testing + +See [TESTING.md](TESTING.md) for complete testing guidelines. + +- Smart contracts: `chain/test/` (Foundry/Forge) - Run: `yarn test:chain` +- API: `src/tests/` (Jest) - Run: `yarn test:js` +- Test database: `DATABASE_OVERRIDE=jest-integration` + +## Environment Configuration + +See [CONFIG.md](CONFIG.md) for complete configuration details. + +## Development Workflow + +See [DEVELOPER_GUIDE.md](DEVELOPER_GUIDE.md) for complete development workflow, setup instructions, and common tasks. + +## Documentation Links + +**Human-focused documentation:** +- [README.md](README.md) - Project overview, setup, and usage +- [DEVELOPER_GUIDE.md](DEVELOPER_GUIDE.md) - Complete development guide +- [TESTING.md](TESTING.md) - Testing practices +- [ARCHITECTURE.md](ARCHITECTURE.md) - System architecture overview +- [CONFIG.md](CONFIG.md) - Environment configuration + +**Technical documentation:** +- [docs/DATA_MODEL.md](docs/DATA_MODEL.md) - Database schema +- [docs/openapi.yaml](docs/openapi.yaml) - API specification +- [CONTRIBUTING.md](CONTRIBUTING.md) - Contribution guidelines + +## Important Notes + +- **Always validate against OCF schemas** before saving data +- **UUIDs must be converted** to bytes16 for all contract calls +- **Quantities/prices must be scaled** using `toScaledBigNumber()` +- **Tests use separate database** - `DATABASE_OVERRIDE=jest-integration` + +## Common Tasks + +See [DEVELOPER_GUIDE.md](DEVELOPER_GUIDE.md) for detailed common tasks.