Thank you for considering contributing to ChainFinity! This document provides guidelines for contributing code, documentation, and tests to the project.
- Getting Started
- Development Workflow
- Code Style Guidelines
- Testing Requirements
- Documentation Guidelines
- Pull Request Process
- Issue Guidelines
- Community Guidelines
Before contributing, ensure you have:
- Forked and cloned the repository
- Set up the development environment (see Installation Guide)
- Read the Architecture Documentation
- Familiarized yourself with the codebase
# Fork the repository on GitHub, then clone your fork
git clone https://github.com/abrar2030/ChainFinity.git
cd ChainFinity
# Add upstream remote
git remote add upstream https://github.com/abrar2030/ChainFinity.git
# Verify remotes
git remote -v# Run automated setup
./scripts/env_setup.sh --environment development
# Or manual setup
cd code/backend
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
pip install -r requirements-dev.txt # Development dependencies
cd ../blockchain
npm install
cd ../web-frontend
npm install# Update main branch
git checkout main
git pull upstream main
# Create feature branch
git checkout -b feature/my-new-feature
# Or for bug fixes
git checkout -b fix/bug-descriptionBranch Naming Convention:
feature/feature-name— New featuresfix/bug-description— Bug fixesdocs/documentation-update— Documentation changesrefactor/code-improvement— Code refactoringtest/test-addition— Test additions
Follow the code style guidelines below and ensure your changes:
- Are focused on a single issue or feature
- Include appropriate tests
- Update documentation as needed
- Don't break existing functionality
# Run all tests
./scripts/test_chainfinity.sh
# Run specific component tests
./scripts/test_chainfinity.sh --component backend
./scripts/test_chainfinity.sh --component blockchain
./scripts/test_chainfinity.sh --component frontend
# Check code coverage
./scripts/test_chainfinity.sh --coverage-threshold 80# Run linters for all components
./scripts/lint-all.sh
# Auto-fix issues where possible
./scripts/lint-all.sh --fixUse conventional commit messages:
git add .
git commit -m "feat: add new risk assessment algorithm"Commit Message Format:
<type>(<scope>): <subject>
<body>
<footer>
Types:
feat— New featurefix— Bug fixdocs— Documentation changesstyle— Code style changes (formatting, no logic changes)refactor— Code refactoringtest— Adding or updating testschore— Maintenance tasks
Examples:
feat(api): add portfolio export endpoint
Implement CSV and JSON export for portfolio data
with pagination and filtering support.
Closes #123
fix(contract): prevent reentrancy in withdraw function
Add ReentrancyGuard to AssetVault withdraw function
to prevent reentrancy attacks.
Fixes #456
# Push to your fork
git push origin feature/my-new-feature
# Create pull request on GitHubStyle Guide: PEP 8 with Black formatter
Formatting:
# Format code with Black
black code/backend/
# Check with Flake8
flake8 code/backend/
# Type checking with mypy
mypy code/backend/Code Standards:
-
Imports: Group imports (standard library, third-party, local)
import logging from datetime import datetime from typing import List, Optional from fastapi import APIRouter, Depends from sqlalchemy.ext.asyncio import AsyncSession from models.user import User from schemas.auth import LoginRequest
-
Type Hints: Always use type hints
def calculate_risk_score( portfolio_value: float, volatility: float ) -> float: return portfolio_value * volatility
-
Docstrings: Use Google style docstrings
def authenticate_user(email: str, password: str) -> Optional[User]: """ Authenticate user with email and password. Args: email: User email address password: User password Returns: User object if authentication successful, None otherwise Raises: ValidationError: If email format is invalid """ pass
-
Async/Await: Use async functions for I/O operations
async def get_user(db: AsyncSession, user_id: str) -> User: result = await db.execute( select(User).where(User.id == user_id) ) return result.scalar_one_or_none()
Style Guide: Airbnb JavaScript Style Guide
Formatting:
# Format with Prettier
npm run format
# Lint with ESLint
npm run lint
# Auto-fix issues
npm run lint:fixCode Standards:
-
Use ES6+ Features:
// Use arrow functions const calculateTotal = (items) => items.reduce((sum, item) => sum + item.price, 0); // Use destructuring const { name, email, wallet_address } = user; // Use template literals const message = `Welcome, ${user.name}!`;
-
React Component Style:
// Functional components with hooks import React, { useState, useEffect } from "react"; const PortfolioCard = ({ portfolio }) => { const [expanded, setExpanded] = useState(false); useEffect(() => { // Fetch portfolio details if expanded if (expanded) { fetchPortfolioDetails(portfolio.id); } }, [expanded, portfolio.id]); return ( <Card onClick={() => setExpanded(!expanded)}>{/* Component JSX */}</Card> ); }; export default PortfolioCard;
-
Async/Await:
const fetchPortfolios = async () => { try { const response = await api.get("/portfolios"); return response.data; } catch (error) { console.error("Error fetching portfolios:", error); throw error; } };
Style Guide: Solidity Style Guide
Formatting:
# Format with Prettier
npm run format
# Lint with Solhint
npm run lintCode Standards:
-
Contract Structure:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19; import "@openzeppelin/contracts/security/ReentrancyGuard.sol"; import "@openzeppelin/contracts/access/AccessControl.sol"; /** * @title ContractName * @dev Contract description */ contract ContractName is ReentrancyGuard, AccessControl { // State variables uint256 public totalValue; mapping(address => uint256) public balances; // Events event Deposit(address indexed user, uint256 amount); // Modifiers modifier onlyPositive(uint256 amount) { require(amount > 0, "Amount must be positive"); _; } // Constructor constructor(address admin) { _grantRole(DEFAULT_ADMIN_ROLE, admin); } // External functions // Public functions // Internal functions // Private functions }
-
NatSpec Comments:
/** * @notice Deposits tokens into the vault * @dev Requires prior token approval * @param token Token address to deposit * @param amount Amount to deposit * @return success Whether deposit was successful */ function deposit( address token, uint256 amount ) external nonReentrant returns (bool success) { // Implementation }
-
Security Patterns:
- Always use ReentrancyGuard
- Follow checks-effects-interactions pattern
- Use pull over push for payments
- Implement circuit breakers for critical functions
| Component | Minimum Coverage | Target Coverage |
|---|---|---|
| Backend | 80% | 85%+ |
| Smart Contracts | 85% | 90%+ |
| Frontend | 70% | 80%+ |
Test Structure:
# tests/test_auth_service.py
import pytest
from services.auth import AuthService
@pytest.mark.asyncio
async def test_authenticate_user_success(test_db):
"""Test successful user authentication"""
auth_service = AuthService()
user = await auth_service.authenticate_user(
db=test_db,
email="test@example.com",
password="TestPass123!"
)
assert user is not None
assert user.email == "test@example.com"
@pytest.mark.asyncio
async def test_authenticate_user_invalid_password(test_db):
"""Test authentication with invalid password"""
auth_service = AuthService()
with pytest.raises(AuthenticationError):
await auth_service.authenticate_user(
db=test_db,
email="test@example.com",
password="wrongpassword"
)Test Structure:
// test/AssetVault.test.js
const { expect } = require("chai");
const { ethers } = require("hardhat");
describe("AssetVault", function () {
let assetVault;
let owner, user1, user2;
let token;
beforeEach(async function () {
[owner, user1, user2] = await ethers.getSigners();
// Deploy test token
const Token = await ethers.getContractFactory("TestToken");
token = await Token.deploy();
// Deploy AssetVault
const AssetVault = await ethers.getContractFactory("AssetVault");
assetVault = await AssetVault.deploy();
});
describe("deposit", function () {
it("should allow token deposits", async function () {
const amount = ethers.utils.parseEther("100");
// Approve and deposit
await token.connect(user1).approve(assetVault.address, amount);
await expect(assetVault.connect(user1).deposit(token.address, amount))
.to.emit(assetVault, "Deposit")
.withArgs(user1.address, token.address, amount);
// Check balance
const balance = await assetVault.balanceOf(user1.address, token.address);
expect(balance).to.equal(amount);
});
it("should reject zero amount deposits", async function () {
await expect(
assetVault.connect(user1).deposit(token.address, 0),
).to.be.revertedWith("Amount must be positive");
});
});
});Test Structure:
// src/__tests__/Portfolio.test.js
import React from "react";
import { render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Portfolio from "../components/Portfolio";
describe("Portfolio Component", () => {
it("renders portfolio data", async () => {
const mockPortfolio = {
id: "123",
name: "Test Portfolio",
total_value_usd: 10000,
};
render(<Portfolio portfolio={mockPortfolio} />);
expect(screen.getByText("Test Portfolio")).toBeInTheDocument();
expect(screen.getByText("$10,000.00")).toBeInTheDocument();
});
it("handles portfolio deletion", async () => {
const mockOnDelete = jest.fn();
const mockPortfolio = { id: "123", name: "Test Portfolio" };
render(<Portfolio portfolio={mockPortfolio} onDelete={mockOnDelete} />);
const deleteButton = screen.getByRole("button", { name: /delete/i });
await userEvent.click(deleteButton);
await waitFor(() => {
expect(mockOnDelete).toHaveBeenCalledWith("123");
});
});
});- Document public APIs — All public functions, classes, and endpoints
- Use examples — Include usage examples in docstrings
- Keep it current — Update docs when changing code
- Explain why — Not just what, but why design decisions were made
When adding features, update:
- README.md — If it changes setup or usage
- API.md — For new API endpoints
- CLI.md — For new CLI commands
- FEATURE_MATRIX.md — For new features
- examples/ — Add usage examples
- Use Markdown format
- Include code examples with proper syntax highlighting
- Provide command examples with expected output
- Link between related documentation
- Keep table of contents updated
- Code follows style guidelines
- Tests pass locally (
./scripts/test_chainfinity.sh) - Linting passes (
./scripts/lint-all.sh) - Documentation updated
- Commit messages follow convention
- Branch is up to date with upstream main
## Description
Brief description of changes
## Type of Change
- [ ] Bug fix
- [ ] New feature
- [ ] Breaking change
- [ ] Documentation update
## Testing
Describe testing performed:
- [ ] Unit tests added/updated
- [ ] Integration tests added/updated
- [ ] Manual testing performed
## Checklist
- [ ] Code follows style guidelines
- [ ] Self-reviewed code
- [ ] Commented complex code
- [ ] Updated documentation
- [ ] No new warnings generated
- [ ] Tests pass locally
- [ ] Added tests for new features