Welcome to FarmCredit! This guide explains how to contribute to our decentralized agricultural credit platform built on the Stellar network.
New contributor? You should be able to go from git clone to a merged PR in under 30 minutes by following this guide.
- Welcome & Project Overview
- Prerequisites
- Local Development Setup
- Git Hooks & Quality Gates
- Project Architecture
- Coding Standards
- Commit Conventions
- Pull Request Process
- Issue Workflow
- Code Review Guidelines
FarmCredit is a decentralized agricultural credit platform enabling farmers and agricultural businesses to access credit through blockchain-based mechanisms. We're building on the Stellar network, enabling fast, low-cost, and accessible financial services for agricultural communities.
- Fast Transactions — Sub-second settlement times
- Low Fees — Minimal transaction costs
- Accessibility — Open, permissionless network
- Compliance — Built with regulatory frameworks in mind
| Layer | Technology | Version |
|---|---|---|
| Framework | Next.js (App Router) | 16.1.6 |
| Language | TypeScript (strict mode) | 5.x |
| Styling | Tailwind CSS v4 + shadcn/ui | 4.x |
| Blockchain | @stellar/stellar-sdk | 11.2.2 |
| Wallet | @stellar/freighter-api | 1.7.0 |
| Design System | Stellar brand colors + atomic design pattern | Custom |
| Package Manager | pnpm | 10.28.1+ |
Our design system uses brand colors available as Tailwind classes:
| Token | Value | Tailwind Class |
|---|---|---|
| Stellar Blue | #14B6E7 |
bg-stellar-blue, text-stellar-blue |
| Stellar Purple | #3E1BDB |
bg-stellar-purple, text-stellar-purple |
| Stellar Navy | #0D0B21 |
bg-stellar-navy, text-stellar-navy |
| Stellar Cyan | #00C2FF |
bg-stellar-cyan, text-stellar-cyan |
| Stellar Green | #00B36B |
bg-stellar-green, text-stellar-green |
You'll need these installed to contribute:
-
Node.js 20+ — Download
node --version # Should be v20 or higher -
pnpm 10.28.1+ — Install globally:
npm install -g pnpm@10.28.1 pnpm --version
-
Git — Download
git --version
-
Basic Stellar Knowledge (optional for wallet features)
- Familiar with Stellar network concepts
- Have tested Stellar on testnet (future wallet features)
We recommend VS Code with these extensions:
- ES7+ React/Redux/React-Native snippets —
dsznajder.es7-react-js-snippets - Prettier - Code formatter —
esbenp.prettier-vscode - ESLint —
dbaeumer.vscode-eslint - Tailwind CSS IntelliSense —
bradlc.vscode-tailwindcss
git clone https://github.com/Farm-credit/stellar-app-os.git
cd stellar-app-ospnpm installThis installs all dependencies and automatically sets up Husky git hooks via the prepare script.
pnpm devOpen http://localhost:3000 in your browser.
cp .env.example .env.localMost features work without environment variables. When adding features requiring external services, document them in .env.example.
pnpm build # Type checking + full build
pnpm lint # Code quality
pnpm generate-icons # PWA icons (only if modifying app icon)All commands should pass without errors.
Problem: pnpm: command not found
- Solution:
npm install -g pnpmand restart terminal
Problem: Node.js version is too old
- Solution: Use
nvmorfnmto install Node.js 20+nvm install 20 nvm use 20
Problem: ModuleNotFoundError after git pull
- Solution:
pnpm install
Problem: Hot reload not working
- Solution: Clear Next.js cache and restart
rm -rf .next pnpm dev
Problem: TypeScript errors in IDE but pnpm build passes
- Solution: Restart TypeScript server —
Ctrl+Shift+P→TypeScript: Restart TS Server
This project uses Husky to enforce quality gates locally. Hooks are installed automatically when you run pnpm install.
You should never need to configure anything manually. The hooks run silently in the background on every commit and push.
Runs lint-staged before every commit. Only staged .ts and .tsx files are checked — keeping it fast regardless of codebase size.
What it does:
- Runs ESLint and auto-fixes any fixable issues
- Runs Prettier and auto-formats the file
If either fails with unfixable errors, the commit is blocked. Fix the reported errors and try again.
Validates your commit message against Conventional Commits using commitlint. If the format is wrong, the commit is rejected immediately with a clear error.
See Commit Conventions for the full format spec.
Runs pnpm build before every push. If the build fails, the push is blocked.
This is the hard gate — broken code must never reach the remote. Fix all build and type errors locally before pushing.
git commit --no-verify -m "chore: emergency fix"
git push --no-verifyUse --no-verify only when absolutely necessary. CI will still enforce all checks on the remote.
Components are organized by complexity, not by feature:
components/
├── atoms/ # Smallest, single-purpose elements
├── molecules/ # Combinations of atoms
├── organisms/ # Complex sections
├── templates/ # Page-level layouts
├── providers/ # Context providers
└── ui/ # shadcn/ui base components
Smallest building blocks — typically map 1:1 to a single UI concept.
Examples: Button.tsx, Input.tsx, Badge.tsx, Text.tsx
import { Button } from '@/components/atoms/Button';Combinations of atoms forming distinct UI units.
Examples: Card.tsx, FormField.tsx, BlogCard.tsx
Complex sections combining atoms and molecules — usually feature-specific.
Examples: Header.tsx, WalletConnectionStep/, ComparisonTable.tsx
Page-level structural layouts — typically one per major page type.
Location: components/templates/
Provided by shadcn/ui. Do not edit directly unless extending with Stellar variants.
Location: components/ui/
┌─────────────────────────────────────┐
│ Templates (Pages) │
│ ┌───────────────────────────────┐ │
│ │ Organisms (Features) │ │
│ │ ┌─────────────────────────┐ │ │
│ │ │ Molecules (Units) │ │ │
│ │ │ ┌───────────────────┐ │ │ │
│ │ │ │ Atoms (Elements) │ │ │ │
│ │ │ └───────────────────┘ │ │ │
│ │ └─────────────────────────┘ │ │
│ └───────────────────────────────┘ │
└─────────────────────────────────────┘
stellar-app-os/
├── app/ # Next.js App Router
│ ├── globals.css # Stellar tokens + Tailwind config
│ ├── layout.tsx # Root layout
│ ├── page.tsx # Home page
│ ├── api/ # API routes
│ ├── dashboard/ # Dashboard pages
│ ├── credits/ # Credit features
│ └── settings/ # User settings
├── components/ # All UI components (atomic design)
├── contexts/ # React contexts
├── hooks/ # Custom React hooks
├── lib/ # Utilities, types, schemas, API clients
├── public/ # Static assets, PWA manifest, icons
└── scripts/ # Build and utility scripts
Always import directly from the component file. Never use barrel exports (index.ts).
// ✅ Correct
import { Button } from '@/components/atoms/Button';
import { useWallet } from '@/hooks/useWallet';
// ❌ Wrong
import { Button } from '@/components/atoms';
import { useWallet } from '@/hooks';Why: explicit imports enable better tree-shaking, clearer dependencies, and easier refactoring.
This project uses TypeScript strict mode. No escape hatches.
// ❌ Wrong
const handleClick = (e: any) => { ... };
// ✅ Correct
const handleClick = (e: ChangeEvent<HTMLInputElement>) => { ... };Never leave variables unused. Never use any.
import { forwardRef, InputHTMLAttributes } from 'react';
interface InputProps extends InputHTMLAttributes<HTMLInputElement> {
label: string;
error?: string;
}
export const Input = forwardRef<HTMLInputElement, InputProps>(
({ label, error, ...props }, ref) => (
<div>
<label>{label}</label>
<input ref={ref} {...props} />
{error && <span className="text-red-500">{error}</span>}
</div>
)
);
Input.displayName = 'Input';Always set displayName. Always export the props interface.
| Type | Convention | Example |
|---|---|---|
| Components | PascalCase |
WalletConnectionStep |
| Component Folders | PascalCase |
WalletConnectionStep/ |
| Other Folders | kebab-case |
lib/api, scripts |
| Functions | camelCase |
handleSubmit, formatBalance |
| Constants | SCREAMING_SNAKE_CASE |
MAX_AMOUNT, API_BASE_URL |
| Types/Interfaces | PascalCase |
WalletBalance |
| Files (non-component) | kebab-case |
use-wallet.ts |
Always use Stellar color tokens — never arbitrary Tailwind colors:
// ✅ Correct
<header className="bg-stellar-navy text-stellar-blue">
// ❌ Wrong
<header className="bg-blue-600 text-blue-400">Use cn() for conditional classes:
import { cn } from '@/lib/utils';
<div className={cn('rounded-lg border p-4', className)} />This project enforces Conventional Commits and atomic commits. Every commit must be meaningful, buildable, and revertable.
The commit-msg hook will reject any commit that doesn't match the format below.
<type>(<scope>): <short description>
[optional body — explain WHY and HOW]
[optional footer — breaking changes or issue refs]
| Type | When to use |
|---|---|
feat |
New feature or component |
fix |
Bug fix |
docs |
Documentation only |
style |
Formatting, no logic change |
refactor |
Code restructuring, no behavior change |
perf |
Performance improvement |
test |
Adding or updating tests |
build |
Build system or dependency changes |
ci |
CI configuration changes |
chore |
Maintenance tasks |
auth · wallet · dashboard · marketplace · admin · donation · carbon · ui · layout · nav · config · deps
feat(wallet): add freighter connection flow
fix(auth): handle expired session tokens
docs(config): update environment variable reference
chore(deps): upgrade TypeScript to 5.3.3Configure Git to use the provided template:
git config commit.template .gitmessage# ❌ Bad
feat: add dashboard with tabs, fix header bug, update colors
# ✅ Good
feat(dashboard): create page layout
fix(header): correct active link highlightingEvery commit in history must pass pnpm build && pnpm lint. No debugging code, no unused imports.
Reverting one commit must not break unrelated features. Build in logical order: foundation → features → polish.
# Stage specific files
git add app/page.tsx
# Or stage interactively by hunk
git add -pgit checkout main
git pull origin main
git checkout -b feat/<issue-number>-<short-description>Branch naming:
feat/42-wallet-connection-modalfix/78-donation-validation-bugdocs/107-contributor-guide
git rebase main # Rebase onto latest main
pnpm build # Must pass
pnpm lint # Must passEvery PR must include a screen recording showing your feature working.
- macOS:
Cmd+Shift+5→ Record Selected Portion - Windows/Linux: OBS Studio (https://obsproject.com) or built-in recorder
Show: the relevant page loading → user interaction → expected result. 30-60 seconds is ideal.
## Summary
<!-- 1-3 sentences: what does this PR do and why? -->
## Related Issue
Closes #<issue-number>
## What Was Implemented
- [ ] ...
## Implementation Details
<!-- Key technical decisions -->
## How to Test
1. Checkout branch
2. pnpm install && pnpm dev
3. Steps to reproduce the feature
## Screenshots / Recording
[Attach here]Every PR must have:
- ✅ Linked issue (
Closes #<number>) - ✅ Screen recording attached
- ✅ Filled PR template
- ✅ Passing CI (build, lint, types)
- ✅ Atomic commits
PRs missing a screen recording or linked issue will not be reviewed.
- ⏱️ 24-48 hours for initial feedback
- Respond to all comments — make changes or explain decisions
- Re-request review after addressing feedback
- Browse open issues
- Look for labels:
Stellar Wave,good-first-issue,help-wanted - Check comments to confirm it's unclaimed
- Comment
I'll work on thisto claim it
Don't start work on an issue someone else has claimed without coordinating first.
- Unsure about expected behavior → comment on the issue
- Need architectural guidance → ask in the PR
- Conflicting requirements → raise it early, not at review time
- No
anytypes or unused variables forwardRef+displayNamewhere appropriate- Atomic design pattern followed
- Stellar color tokens used (no arbitrary colors)
- Responsive design (mobile-first)
- Atomic, descriptive commits
- Screen recording attached
- Read every comment carefully
- Respond to all of them — even if you disagree
- Push fixes, then reply
DoneorFixed in <sha> - Re-request review
| Comment | How to Respond |
|---|---|
"This has an any type" |
Replace with proper type (HTMLInputElement, etc.) |
| "Missing displayName" | Add Component.displayName = "ComponentName" |
| "Use atomic import" | Change to direct file import |
| "No arbitrary colors" | Replace bg-blue-500 with bg-stellar-blue |
| "Screen recording missing" | Record feature in browser and upload MP4 |
- 📝 General questions: Comment in the relevant issue
- 💬 Found a bug: Open a new issue with reproduction steps
- 🔄 Feature idea: Discuss in an issue before implementing
- 🤔 Contributing questions: Ask in issues or PR comments
By contributing to FarmCredit, you agree that your contributions will be licensed under the same license as the project. See LICENSE for details.
Contributing to open-source agriculture software is meaningful work. We appreciate every pull request, issue report, and question. Together, we're building tools for a more equitable agricultural future.
Happy coding! 🚀