diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..c5152a4a
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,384 @@
+# Contributing to GenLayer Points
+
+Thank you for your interest in contributing to the GenLayer Points system. This document provides guidelines and instructions for contributing.
+
+## Table of Contents
+
+- [Code of Conduct](#code-of-conduct)
+- [Getting Started](#getting-started)
+- [Development Setup](#development-setup)
+- [Project Architecture](#project-architecture)
+- [Making Contributions](#making-contributions)
+- [Pull Request Process](#pull-request-process)
+- [Coding Standards](#coding-standards)
+- [Testing Guidelines](#testing-guidelines)
+- [Security Considerations](#security-considerations)
+- [Common Issues](#common-issues)
+
+## Code of Conduct
+
+By participating in this project, you agree to:
+
+- Be respectful and inclusive in all interactions
+- Provide constructive feedback
+- Focus on the best outcomes for the community
+- Report any unacceptable behavior
+
+## Getting Started
+
+### Prerequisites
+
+- **Python 3.8+** - Backend runtime
+- **Node.js 16+** - Frontend build tools
+- **PostgreSQL** (optional) - Production database (SQLite works for development)
+- **Git** - Version control
+
+### Forking and Cloning
+
+1. Fork the repository on GitHub
+2. Clone your fork:
+ ```bash
+ git clone https://github.com/YOUR_USERNAME/points.git
+ cd points
+ ```
+3. Add the upstream remote:
+ ```bash
+ git remote add upstream https://github.com/genlayer-foundation/points.git
+ ```
+
+## Development Setup
+
+### Backend Setup
+
+```bash
+cd backend
+
+# Create virtual environment
+python -m venv env
+
+# Activate virtual environment
+# On Windows:
+env\Scripts\activate
+# On macOS/Linux:
+source env/bin/activate
+
+# Install dependencies
+pip install -r requirements.txt
+
+# Copy environment variables
+cp .env.example .env
+
+# Run migrations
+python manage.py migrate
+
+# Create superuser (optional)
+python manage.py createsuperuser
+
+# Start development server
+python manage.py runserver
+```
+
+### Frontend Setup
+
+```bash
+cd frontend
+
+# Install dependencies
+npm install
+
+# Copy environment variables
+cp .env.example .env
+
+# Start development server
+npm run dev
+```
+
+### Environment Variables
+
+#### Backend (.env)
+
+```env
+DEBUG=True
+SECRET_KEY=your-secret-key
+DATABASE_URL=sqlite:///db.sqlite3
+ALLOWED_HOSTS=localhost,127.0.0.1
+FRONTEND_URL=http://localhost:5173
+SIWE_DOMAIN=localhost:5173
+RECAPTCHA_SECRET_KEY=your-recaptcha-secret
+```
+
+#### Frontend (.env)
+
+```env
+VITE_API_URL=http://localhost:8000
+VITE_RECAPTCHA_SITE_KEY=your-recaptcha-site-key
+```
+
+## Project Architecture
+
+### Backend (Django REST Framework)
+
+```
+backend/
+├── api/ # Main API configuration and metrics
+├── contributions/ # Contribution tracking system
+│ ├── models.py # ContributionType, Contribution, Evidence
+│ ├── views.py # ViewSets for contributions
+│ └── serializers.py
+├── ethereum_auth/ # SIWE authentication
+├── leaderboard/ # Points and rankings
+├── users/ # User management
+├── validators/ # Validator profiles
+├── builders/ # Builder profiles
+├── stewards/ # Steward management
+└── tally/ # Django project settings
+```
+
+### Frontend (Svelte 5)
+
+```
+frontend/
+├── src/
+│ ├── components/ # Reusable UI components
+│ ├── routes/ # Page components
+│ ├── lib/ # Utilities and API clients
+│ │ ├── api.js # API endpoint definitions
+│ │ ├── auth.js # Authentication logic
+│ │ └── wallet/ # Wallet integration
+│ └── assets/ # Static assets
+└── public/ # Public files
+```
+
+## Making Contributions
+
+### Branch Naming
+
+**Important**: The `dev` branch is the main development branch. Create feature branches from `dev`.
+
+```bash
+# Update your local dev branch
+git checkout dev
+git pull upstream dev
+
+# Create feature branch
+git checkout -b feature/your-feature-name
+
+# Or for bug fixes
+git checkout -b fix/issue-description
+```
+
+### Types of Contributions
+
+1. **Bug Fixes** - Fix issues in existing functionality
+2. **Features** - Add new functionality
+3. **Documentation** - Improve docs, comments, or examples
+4. **Tests** - Add or improve test coverage
+5. **Performance** - Optimize existing code
+6. **Security** - Fix vulnerabilities or improve security
+
+### Finding Issues
+
+- Check the [Issues](https://github.com/genlayer-foundation/points/issues) page
+- Look for issues labeled `good first issue` or `help wanted`
+- Browse the codebase for `TODO` or `FIXME` comments
+
+## Pull Request Process
+
+### Before Submitting
+
+1. **Sync with upstream**:
+ ```bash
+ git fetch upstream
+ git rebase upstream/dev
+ ```
+
+2. **Run tests**:
+ ```bash
+ # Backend
+ cd backend
+ python manage.py test
+
+ # Frontend
+ cd frontend
+ npm test
+ ```
+
+3. **Check linting**:
+ ```bash
+ # Backend
+ flake8 .
+
+ # Frontend
+ npm run lint
+ ```
+
+### PR Guidelines
+
+1. **Target the `dev` branch** - Not `main`
+2. **Write clear titles** - Use conventional commits (feat:, fix:, docs:, etc.)
+3. **Include description** - Explain what and why
+4. **Link issues** - Reference related issues with "Fixes #123"
+5. **Keep PRs focused** - One feature or fix per PR
+6. **Add tests** - For new functionality
+7. **Update docs** - If behavior changes
+
+### Commit Message Format
+
+```
+type(scope): subject
+
+body (optional)
+
+footer (optional)
+```
+
+**Types**: feat, fix, docs, style, refactor, test, chore
+
+**Examples**:
+```
+feat(contributions): add evidence URL validation
+fix(auth): resolve wallet connection timeout
+docs(readme): update setup instructions
+```
+
+## Coding Standards
+
+### Python (Backend)
+
+- Follow PEP 8 style guide
+- Use type hints where appropriate
+- Document functions with docstrings
+- Keep functions focused and small
+- Use meaningful variable names
+
+```python
+def calculate_points(
+ contribution_type: ContributionType,
+ base_points: int,
+ multiplier: Decimal
+) -> int:
+ """
+ Calculate frozen global points for a contribution.
+
+ Args:
+ contribution_type: The type of contribution
+ base_points: Raw points before multiplier
+ multiplier: Current multiplier value
+
+ Returns:
+ Calculated frozen global points
+ """
+ return int(base_points * multiplier)
+```
+
+### JavaScript/Svelte (Frontend)
+
+- Use modern ES6+ syntax
+- Follow Svelte 5 runes patterns (`$state`, `$derived`, `$effect`)
+- Use meaningful component and variable names
+- Keep components focused and reusable
+
+```svelte
+
+
+
+```
+
+## Testing Guidelines
+
+### Backend Testing
+
+```python
+from django.test import TestCase
+from rest_framework.test import APITestCase
+
+class ContributionTestCase(APITestCase):
+ def setUp(self):
+ self.user = User.objects.create_user(
+ email='test@example.com',
+ address='0x1234...'
+ )
+
+ def test_create_contribution(self):
+ response = self.client.post('/api/v1/contributions/', {
+ 'contribution_type': 1,
+ 'points': 10
+ })
+ self.assertEqual(response.status_code, 201)
+```
+
+### Frontend Testing
+
+```javascript
+import { render, screen } from '@testing-library/svelte';
+import { describe, it, expect } from 'vitest';
+import Component from './Component.svelte';
+
+describe('Component', () => {
+ it('renders correctly', () => {
+ render(Component, { props: { value: 'test' } });
+ expect(screen.getByText('test')).toBeInTheDocument();
+ });
+});
+```
+
+## Security Considerations
+
+### Authentication
+
+- Never expose sensitive data in client-side code
+- Validate all user inputs on the server
+- Use SIWE (Sign-In With Ethereum) for authentication
+- Store session data securely
+
+### API Security
+
+- Implement proper permission checks
+- Use rate limiting for sensitive endpoints
+- Sanitize user inputs to prevent injection
+- Validate file uploads and URLs
+
+### Common Vulnerabilities to Avoid
+
+1. **SQL Injection** - Use Django ORM, never raw queries with user input
+2. **XSS** - Sanitize user-generated content
+3. **CSRF** - Django handles this, ensure it's not disabled
+4. **Sensitive Data Exposure** - Don't log or expose private keys
+
+## Common Issues
+
+### Wallet Connection Issues
+
+Multiple wallets installed can cause conflicts. See the wallet detection logic in `frontend/src/components/WalletSelector.svelte` for handling.
+
+### CAPTCHA Validation Fails
+
+Ensure reCAPTCHA keys match between frontend and backend environments.
+
+### Session Authentication Errors
+
+Check CORS and cookie settings if authentication state isn't persisting.
+
+### Database Migration Errors
+
+Always run migrations after pulling new changes:
+```bash
+python manage.py migrate
+```
+
+## Questions?
+
+- Open a [Discussion](https://github.com/genlayer-foundation/points/discussions)
+- Join the GenLayer community channels
+- Review existing issues and PRs
+
+Thank you for contributing to GenLayer Points!
diff --git a/README.md b/README.md
index 6ba1602d..4a7c662f 100644
--- a/README.md
+++ b/README.md
@@ -66,60 +66,69 @@ eventually be part of the Deepthought DAO.
### Backend Setup
1. Clone the repository:
- ```
- git clone https://github.com/yourusername/points.git
+ ```bash
+ git clone https://github.com/genlayer-foundation/points.git
cd points
```
2. Create and activate a virtual environment:
- ```
+ ```bash
cd backend
python -m venv env
source env/bin/activate # On Windows: env\Scripts\activate
```
3. Install dependencies:
- ```
+ ```bash
pip install -r requirements.txt
```
-4. Setup Node.js environment using nodeenv:
- ```
- nodeenv -p
+4. Configure environment variables:
+ ```bash
+ cp .env.example .env
+ # Edit .env with your settings
```
5. Run migrations:
- ```
+ ```bash
python manage.py migrate
```
-6. Create a superuser:
- ```
+6. Create a superuser (optional):
+ ```bash
python manage.py createsuperuser
```
7. Run the development server:
- ```
+ ```bash
python manage.py runserver
```
### Frontend Setup
1. Navigate to the frontend directory:
- ```
+ ```bash
cd frontend
```
2. Install dependencies:
- ```
+ ```bash
npm install
```
-3. Run the development server:
+3. Configure environment variables:
+ ```bash
+ cp .env.example .env
+ # Edit .env to point to your backend
```
+
+4. Run the development server:
+ ```bash
npm run dev
```
+5. Open http://localhost:5173 in your browser
+
## Development
### Backend Development
@@ -159,15 +168,22 @@ The frontend is built with Svelte 5 and will include:
## Contributing
-Contributions are welcome! Please feel free to submit a Pull Request.
+Contributions are welcome! Please read [CONTRIBUTING.md](./CONTRIBUTING.md) for detailed guidelines.
+
+### Quick Start
+
+1. Fork the repository
+2. Create a feature branch from `dev`: `git checkout -b feature/your-feature-name`
+3. Make your changes
+4. Run tests: `python manage.py test` and `npm test`
+5. Push your branch: `git push origin feature/your-feature-name`
+6. Submit a pull request to the `dev` branch
+
+**Note**: All pull requests should target the `dev` branch, not `main`.
-### Development Workflow
+## Security
-1. Create a feature branch: `git checkout -b feature/your-feature-name`
-2. Make your changes
-3. Run tests: `python manage.py test`
-4. Push your branch: `git push origin feature/your-feature-name`
-5. Submit a pull request
+For security concerns, please see [SECURITY.md](./SECURITY.md).
## License
diff --git a/SECURITY.md b/SECURITY.md
new file mode 100644
index 00000000..15beaa32
--- /dev/null
+++ b/SECURITY.md
@@ -0,0 +1,151 @@
+# Security Policy
+
+## Supported Versions
+
+| Version | Supported |
+| ------- | ------------------ |
+| latest | :white_check_mark: |
+
+## Reporting a Vulnerability
+
+We take security seriously. If you discover a security vulnerability, please report it responsibly.
+
+### How to Report
+
+1. **Do NOT create a public issue** for security vulnerabilities
+2. Email security concerns to the GenLayer Foundation team
+3. Include detailed steps to reproduce the vulnerability
+4. Provide any relevant proof-of-concept code
+
+### What to Include
+
+- Type of vulnerability
+- Full path of affected file(s)
+- Steps to reproduce
+- Potential impact
+- Suggested fix (if any)
+
+### Response Timeline
+
+- **Initial Response**: Within 48 hours
+- **Assessment**: Within 7 days
+- **Fix Timeline**: Depends on severity, typically 30-90 days
+
+## Security Best Practices
+
+### Authentication
+
+The Points system uses Sign-In With Ethereum (SIWE) for authentication:
+
+1. **Nonce Handling**
+ - Nonces are generated server-side with cryptographic randomness
+ - Nonces expire after 5 minutes
+ - Each nonce can only be used once
+
+2. **Session Management**
+ - Sessions are stored server-side
+ - Session cookies are httpOnly and secure in production
+ - Sessions expire after inactivity
+
+3. **Wallet Integration**
+ - No private keys are ever transmitted to the server
+ - Only signed messages are verified
+
+### API Security
+
+1. **Input Validation**
+ - All user inputs are validated and sanitized
+ - File uploads are restricted by type and size
+ - URLs are validated before storage
+
+2. **Rate Limiting**
+ - Consider implementing rate limiting on sensitive endpoints:
+ - `/api/auth/nonce/` - Nonce generation
+ - `/api/auth/login/` - Login attempts
+ - `/api/v1/submissions/` - Submission creation
+
+3. **CORS Configuration**
+ - CORS is restricted to known frontend origins
+ - Credentials are only allowed for specific origins
+
+### Known Security Considerations
+
+#### Nonce Cleanup
+
+Expired nonces should be periodically cleaned from the database to prevent storage bloat:
+
+```python
+# Add to a management command or scheduled task
+from django.utils import timezone
+from ethereum_auth.models import Nonce
+
+def cleanup_expired_nonces():
+ """Remove expired nonces older than 1 hour."""
+ cutoff = timezone.now() - timedelta(hours=1)
+ Nonce.objects.filter(expires_at__lt=cutoff).delete()
+```
+
+#### Email Generation
+
+Auto-generated emails for wallet users follow a predictable pattern. While this doesn't expose any sensitive data, consider:
+
+- Not using these emails for any communication
+- Implementing email verification for actual contact
+
+#### Session Fixation
+
+The application regenerates session IDs on login to prevent session fixation attacks.
+
+### Development Security
+
+1. **Environment Variables**
+ - Never commit `.env` files
+ - Use different secrets for development and production
+ - Rotate secrets periodically
+
+2. **Dependencies**
+ - Regularly update dependencies
+ - Run security audits:
+ ```bash
+ # Python
+ pip-audit
+
+ # Node.js
+ npm audit
+ ```
+
+3. **Code Review**
+ - All changes require code review
+ - Security-sensitive changes require additional review
+
+### Deployment Security
+
+1. **HTTPS Only**
+ - All production traffic must use HTTPS
+ - HSTS headers are set
+
+2. **Content Security Policy**
+ - Implement appropriate CSP headers
+ - Restrict script sources
+
+3. **Database**
+ - Use parameterized queries (Django ORM handles this)
+ - Regular backups with encryption
+ - Principle of least privilege for database users
+
+## Security Checklist for Contributors
+
+Before submitting a PR, ensure:
+
+- [ ] No sensitive data in logs or error messages
+- [ ] User inputs are validated on the server
+- [ ] Authentication/authorization checks are in place
+- [ ] No hardcoded secrets or credentials
+- [ ] SQL queries use ORM or parameterized queries
+- [ ] File uploads validate content type and size
+- [ ] External URLs are validated before use
+- [ ] Error messages don't leak implementation details
+
+## Acknowledgments
+
+We appreciate security researchers who responsibly disclose vulnerabilities. Contributors who report valid security issues will be acknowledged (unless they prefer to remain anonymous).
diff --git a/backend/ethereum_auth/management/__init__.py b/backend/ethereum_auth/management/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/backend/ethereum_auth/management/commands/__init__.py b/backend/ethereum_auth/management/commands/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/backend/ethereum_auth/management/commands/cleanup_nonces.py b/backend/ethereum_auth/management/commands/cleanup_nonces.py
new file mode 100644
index 00000000..7c58fd68
--- /dev/null
+++ b/backend/ethereum_auth/management/commands/cleanup_nonces.py
@@ -0,0 +1,78 @@
+"""
+Management command to clean up expired authentication nonces.
+
+Run manually:
+ python manage.py cleanup_nonces
+
+Schedule with cron (recommended daily):
+ 0 3 * * * cd /path/to/backend && python manage.py cleanup_nonces
+
+Or use Django-celery-beat for scheduled tasks.
+"""
+from django.core.management.base import BaseCommand
+from django.utils import timezone
+from datetime import timedelta
+
+from ethereum_auth.models import Nonce
+
+
+class Command(BaseCommand):
+ help = 'Clean up expired and used nonces from the database'
+
+ def add_arguments(self, parser):
+ parser.add_argument(
+ '--hours',
+ type=int,
+ default=1,
+ help='Delete nonces that expired more than this many hours ago (default: 1)'
+ )
+ parser.add_argument(
+ '--dry-run',
+ action='store_true',
+ help='Show what would be deleted without actually deleting'
+ )
+
+ def handle(self, *args, **options):
+ hours = options['hours']
+ dry_run = options['dry_run']
+
+ cutoff_time = timezone.now() - timedelta(hours=hours)
+
+ # Find nonces to delete:
+ # 1. Expired nonces older than cutoff
+ # 2. Used nonces (regardless of age, they're no longer needed)
+ expired_nonces = Nonce.objects.filter(expires_at__lt=cutoff_time)
+ used_nonces = Nonce.objects.filter(used=True, created_at__lt=cutoff_time)
+
+ expired_count = expired_nonces.count()
+ used_count = used_nonces.count()
+
+ # Get total count of nonces to delete (combine queries)
+ nonces_to_delete = Nonce.objects.filter(
+ models.Q(expires_at__lt=cutoff_time) |
+ models.Q(used=True, created_at__lt=cutoff_time)
+ )
+ total_count = nonces_to_delete.count()
+
+ if dry_run:
+ self.stdout.write(
+ self.style.WARNING(
+ f'DRY RUN: Would delete {total_count} nonces '
+ f'({expired_count} expired, {used_count} used)'
+ )
+ )
+ else:
+ deleted_count, _ = nonces_to_delete.delete()
+ self.stdout.write(
+ self.style.SUCCESS(
+ f'Successfully deleted {deleted_count} nonces'
+ )
+ )
+
+ # Report remaining nonces
+ remaining = Nonce.objects.count()
+ self.stdout.write(f'Remaining nonces in database: {remaining}')
+
+
+# Import models for Q objects
+from django.db import models
diff --git a/docs/API_REFERENCE.md b/docs/API_REFERENCE.md
new file mode 100644
index 00000000..b2ae80c3
--- /dev/null
+++ b/docs/API_REFERENCE.md
@@ -0,0 +1,515 @@
+# API Reference
+
+This document provides a comprehensive reference for the GenLayer Points API.
+
+## Base URL
+
+```
+Production: https://api.points.genlayer.com/api/v1
+Development: http://localhost:8000/api/v1
+```
+
+## Authentication
+
+The API uses Sign-In With Ethereum (SIWE) for authentication with session-based cookies.
+
+### Authentication Endpoints
+
+#### Get Nonce
+
+```http
+GET /api/auth/nonce/
+```
+
+Returns a nonce for SIWE message signing.
+
+**Response:**
+```json
+{
+ "nonce": "abc123xyz..."
+}
+```
+
+#### Login
+
+```http
+POST /api/auth/login/
+Content-Type: application/json
+```
+
+**Request Body:**
+```json
+{
+ "message": "domain wants you to sign in...",
+ "signature": "0x...",
+ "referral_code": "ABC12345" // optional
+}
+```
+
+**Response:**
+```json
+{
+ "authenticated": true,
+ "address": "0x123...",
+ "user_id": 1,
+ "created": false,
+ "referral_code": "XYZ98765",
+ "referred_by": null
+}
+```
+
+#### Verify Authentication
+
+```http
+GET /api/auth/verify/
+```
+
+**Response:**
+```json
+{
+ "authenticated": true,
+ "address": "0x123...",
+ "user_id": 1
+}
+```
+
+#### Logout
+
+```http
+POST /api/auth/logout/
+```
+
+**Response:**
+```json
+{
+ "message": "Logged out successfully."
+}
+```
+
+---
+
+## Users
+
+### Get Current User
+
+```http
+GET /api/v1/users/me/
+```
+
+Requires authentication.
+
+**Response:**
+```json
+{
+ "id": 1,
+ "email": "user@example.com",
+ "name": "John Doe",
+ "address": "0x123...",
+ "description": "Bio text",
+ "profile_image_url": "https://...",
+ "banner_image_url": "https://...",
+ "website": "https://example.com",
+ "twitter_handle": "johndoe",
+ "discord_handle": "johndoe#1234",
+ "github_username": "johndoe",
+ "referral_code": "ABC12345"
+}
+```
+
+### Update Profile
+
+```http
+PATCH /api/v1/users/me/
+Content-Type: application/json
+```
+
+**Request Body:**
+```json
+{
+ "name": "New Name",
+ "description": "Updated bio",
+ "twitter_handle": "newhandle"
+}
+```
+
+### Get User by Address
+
+```http
+GET /api/v1/users/by-address/{address}/
+```
+
+**Response:** Same as current user object
+
+### Get User Highlights
+
+```http
+GET /api/v1/users/by-address/{address}/highlights/
+```
+
+**Query Parameters:**
+- `limit` (default: 5) - Number of highlights to return
+
+---
+
+## Contributions
+
+### List Contributions
+
+```http
+GET /api/v1/contributions/
+```
+
+**Query Parameters:**
+- `page` (default: 1)
+- `page_size` (default: 10)
+- `user_address` - Filter by user
+- `category` - Filter by category slug
+- `group_consecutive` (default: false) - Group same-type contributions
+
+**Response:**
+```json
+{
+ "count": 100,
+ "next": "http://.../contributions/?page=2",
+ "previous": null,
+ "results": [
+ {
+ "id": 1,
+ "user": 1,
+ "user_details": {...},
+ "contribution_type": 1,
+ "contribution_type_name": "Blog Post",
+ "points": 50,
+ "frozen_global_points": 100,
+ "multiplier_at_creation": "2.00",
+ "contribution_date": "2024-01-15T10:30:00Z",
+ "notes": "Published article about...",
+ "evidence_items": [...],
+ "created_at": "2024-01-15T10:30:00Z"
+ }
+ ]
+}
+```
+
+### Get Contribution Highlights
+
+```http
+GET /api/v1/contributions/highlights/
+```
+
+**Query Parameters:**
+- `limit` (default: 10)
+- `category` - Filter by category slug
+- `waitlist_only` (default: false)
+
+---
+
+## Contribution Types
+
+### List Contribution Types
+
+```http
+GET /api/v1/contribution-types/
+```
+
+**Query Parameters:**
+- `category` - Filter by category slug
+- `is_submittable` - Filter by submittable status
+
+**Response:**
+```json
+{
+ "count": 10,
+ "results": [
+ {
+ "id": 1,
+ "name": "Blog Post",
+ "slug": "blog-post",
+ "description": "Write articles about GenLayer",
+ "category": "builder",
+ "min_points": 10,
+ "max_points": 100,
+ "current_multiplier": 2.0,
+ "is_submittable": true,
+ "examples": ["Tutorial", "Review"]
+ }
+ ]
+}
+```
+
+### Get Type Statistics
+
+```http
+GET /api/v1/contribution-types/statistics/
+```
+
+**Query Parameters:**
+- `category` - Filter by category slug
+
+### Get Top Contributors for Type
+
+```http
+GET /api/v1/contribution-types/{id}/top_contributors/
+```
+
+Returns top 10 contributors for a specific contribution type.
+
+---
+
+## User Submissions
+
+Endpoints for managing user-submitted contributions.
+
+### List My Submissions
+
+```http
+GET /api/v1/submissions/my/
+```
+
+Requires authentication.
+
+**Query Parameters:**
+- `state` - Filter by state (pending, accepted, rejected, more_info_needed)
+- `page` (default: 1)
+- `page_size` (default: 20)
+
+**Response:**
+```json
+{
+ "count": 5,
+ "results": [
+ {
+ "id": "uuid...",
+ "contribution_type": 1,
+ "contribution_type_name": "Blog Post",
+ "contribution_date": "2024-01-15",
+ "notes": "My submission notes",
+ "state": "pending",
+ "state_display": "Pending Review",
+ "staff_reply": "",
+ "evidence_items": [...],
+ "can_edit": true,
+ "created_at": "2024-01-15T10:30:00Z"
+ }
+ ]
+}
+```
+
+### Create Submission
+
+```http
+POST /api/v1/submissions/
+Content-Type: application/json
+```
+
+**Request Body:**
+```json
+{
+ "contribution_type": 1,
+ "contribution_date": "2024-01-15",
+ "notes": "Description of my contribution",
+ "recaptcha": "recaptcha-token",
+ "mission": 1 // optional
+}
+```
+
+### Update Submission
+
+```http
+PATCH /api/v1/submissions/{id}/
+Content-Type: application/json
+```
+
+Only allowed when state is `pending` or `more_info_needed`.
+
+**Request Body:**
+```json
+{
+ "notes": "Updated description",
+ "evidence_items": [
+ {"description": "Link to work", "url": "https://..."}
+ ]
+}
+```
+
+### Cancel Submission
+
+```http
+DELETE /api/v1/submissions/{id}/
+```
+
+Soft deletes by marking as rejected.
+
+### Add Evidence
+
+```http
+POST /api/v1/submissions/{id}/add-evidence/
+Content-Type: application/json
+```
+
+**Request Body:**
+```json
+{
+ "description": "Additional proof",
+ "url": "https://example.com/proof"
+}
+```
+
+---
+
+## Leaderboard
+
+### Get Leaderboard
+
+```http
+GET /api/v1/leaderboard/
+```
+
+**Query Parameters:**
+- `type` - Leaderboard type (global, validator, builder, etc.)
+- `page` (default: 1)
+- `page_size` (default: 50)
+- `order` (default: asc) - Rank order
+
+**Response:**
+```json
+{
+ "count": 1000,
+ "results": [
+ {
+ "rank": 1,
+ "user": {...},
+ "total_points": 5000,
+ "contribution_count": 25
+ }
+ ]
+}
+```
+
+### Get Stats
+
+```http
+GET /api/v1/leaderboard/stats/
+```
+
+**Query Parameters:**
+- `type` - Filter by leaderboard type
+
+**Response:**
+```json
+{
+ "participant_count": 1500,
+ "contribution_count": 10000,
+ "total_points": 500000
+}
+```
+
+### Get Trending Contributors
+
+```http
+GET /api/v1/leaderboard/trending/
+```
+
+**Query Parameters:**
+- `limit` (default: 10)
+
+---
+
+## Missions
+
+### List Missions
+
+```http
+GET /api/v1/missions/
+```
+
+**Query Parameters:**
+- `contribution_type` - Filter by contribution type ID
+- `active` (default: false) - Only show active missions
+
+### Get Mission
+
+```http
+GET /api/v1/missions/{id}/
+```
+
+---
+
+## Error Responses
+
+### 400 Bad Request
+
+```json
+{
+ "error": "Validation failed",
+ "details": {
+ "field_name": ["Error message"]
+ }
+}
+```
+
+### 401 Unauthorized
+
+```json
+{
+ "detail": "Authentication credentials were not provided."
+}
+```
+
+### 403 Forbidden
+
+```json
+{
+ "error": "You do not have permission to perform this action."
+}
+```
+
+### 404 Not Found
+
+```json
+{
+ "detail": "Not found."
+}
+```
+
+### 500 Internal Server Error
+
+```json
+{
+ "error": "An unexpected error occurred."
+}
+```
+
+---
+
+## Rate Limiting
+
+API endpoints may be rate limited. When rate limited, you'll receive:
+
+```http
+HTTP/1.1 429 Too Many Requests
+Retry-After: 60
+```
+
+```json
+{
+ "detail": "Request was throttled. Expected available in 60 seconds."
+}
+```
+
+---
+
+## Pagination
+
+List endpoints return paginated responses:
+
+```json
+{
+ "count": 100,
+ "next": "http://api/v1/resource/?page=2",
+ "previous": null,
+ "results": [...]
+}
+```
+
+Use `page` and `page_size` query parameters to navigate.
diff --git a/docs/BEST_PRACTICES.md b/docs/BEST_PRACTICES.md
new file mode 100644
index 00000000..1fcbcc26
--- /dev/null
+++ b/docs/BEST_PRACTICES.md
@@ -0,0 +1,354 @@
+# Developer Best Practices
+
+This document outlines best practices for developing and maintaining the GenLayer Points system.
+
+## Table of Contents
+
+- [Code Organization](#code-organization)
+- [Django Best Practices](#django-best-practices)
+- [Svelte 5 Best Practices](#svelte-5-best-practices)
+- [API Design](#api-design)
+- [Database Optimization](#database-optimization)
+- [Error Handling](#error-handling)
+- [Testing](#testing)
+- [Performance](#performance)
+
+## Code Organization
+
+### Directory Structure
+
+Follow the existing project structure:
+
+```
+backend/
+├── app_name/
+│ ├── models.py # Data models
+│ ├── views.py # ViewSets and views
+│ ├── serializers.py # DRF serializers
+│ ├── permissions.py # Custom permissions
+│ ├── admin.py # Admin configuration
+│ ├── urls.py # URL routing
+│ └── tests/ # Test files
+```
+
+### File Naming
+
+- Use lowercase with underscores for Python files
+- Use PascalCase for Svelte components
+- Keep file names descriptive but concise
+
+## Django Best Practices
+
+### Models
+
+1. **Use abstract base models for common fields**:
+ ```python
+ from utils.models import BaseModel
+
+ class MyModel(BaseModel):
+ # Inherits created_at, updated_at
+ name = models.CharField(max_length=100)
+ ```
+
+2. **Add proper help_text and verbose names**:
+ ```python
+ class Contribution(models.Model):
+ points = models.PositiveIntegerField(
+ help_text="Base points before multiplier"
+ )
+
+ class Meta:
+ verbose_name = "Contribution"
+ verbose_name_plural = "Contributions"
+ ```
+
+3. **Use database constraints**:
+ ```python
+ class Meta:
+ constraints = [
+ models.UniqueConstraint(
+ fields=['user', 'contribution_type'],
+ name='unique_user_contribution_type'
+ )
+ ]
+ ```
+
+### Views
+
+1. **Use select_related and prefetch_related**:
+ ```python
+ def get_queryset(self):
+ return Contribution.objects.select_related(
+ 'user', 'contribution_type'
+ ).prefetch_related(
+ 'evidence_items'
+ )
+ ```
+
+2. **Return appropriate HTTP status codes**:
+ ```python
+ from rest_framework import status
+
+ return Response(data, status=status.HTTP_201_CREATED)
+ ```
+
+3. **Use DRF decorators for actions**:
+ ```python
+ @action(detail=True, methods=['post'])
+ def approve(self, request, pk=None):
+ instance = self.get_object()
+ # ...
+ ```
+
+### Serializers
+
+1. **Use nested serializers carefully**:
+ ```python
+ # Use SerializerMethodField for complex logic
+ class ContributionSerializer(serializers.ModelSerializer):
+ user_details = serializers.SerializerMethodField()
+
+ def get_user_details(self, obj):
+ return LightUserSerializer(obj.user).data
+ ```
+
+2. **Implement validation in validate() method**:
+ ```python
+ def validate(self, data):
+ if data['min_points'] > data['max_points']:
+ raise serializers.ValidationError(
+ "min_points cannot exceed max_points"
+ )
+ return data
+ ```
+
+## Svelte 5 Best Practices
+
+### State Management
+
+1. **Use runes properly**:
+ ```svelte
+
+ ```
+
+2. **Avoid unnecessary reactivity**:
+ ```svelte
+
+ let filtered = $derived(
+ items.filter(item => item.active)
+ );
+
+
+ {#each items.filter(item => item.active) as item}
+ ```
+
+### Component Design
+
+1. **Use props with defaults**:
+ ```svelte
+
+ ```
+
+2. **Keep components focused**:
+ - One responsibility per component
+ - Extract reusable logic to lib/
+
+### API Integration
+
+1. **Handle loading and error states**:
+ ```svelte
+
+
+ {#if loading}
+
+ {:else if error}
+
+ {:else}
+
+ {/if}
+ ```
+
+## API Design
+
+### URL Patterns
+
+- Use nouns, not verbs: `/contributions/` not `/getContributions/`
+- Use plural names: `/users/` not `/user/`
+- Nest resources logically: `/users/{id}/contributions/`
+
+### Response Format
+
+```json
+{
+ "count": 100,
+ "next": "http://api/v1/items/?page=2",
+ "previous": null,
+ "results": [...]
+}
+```
+
+### Error Responses
+
+```json
+{
+ "error": "Validation failed",
+ "details": {
+ "field_name": ["Error message"]
+ }
+}
+```
+
+## Database Optimization
+
+### Query Optimization
+
+1. **Use database indexes**:
+ ```python
+ class Meta:
+ indexes = [
+ models.Index(fields=['created_at']),
+ models.Index(fields=['user', 'contribution_type']),
+ ]
+ ```
+
+2. **Avoid N+1 queries**:
+ ```python
+ # Bad: N+1 query
+ for contribution in Contribution.objects.all():
+ print(contribution.user.name)
+
+ # Good: Single query with join
+ for contribution in Contribution.objects.select_related('user'):
+ print(contribution.user.name)
+ ```
+
+3. **Use `only()` and `defer()` for partial selects**:
+ ```python
+ User.objects.only('id', 'name', 'address')
+ ```
+
+### Caching
+
+Consider caching for:
+- Leaderboard rankings
+- Contribution type statistics
+- User profile data
+
+## Error Handling
+
+### Backend
+
+```python
+from rest_framework.exceptions import ValidationError, PermissionDenied
+
+def my_view(request):
+ try:
+ # operation
+ except Model.DoesNotExist:
+ raise ValidationError("Resource not found")
+ except PermissionError:
+ raise PermissionDenied("Not authorized")
+```
+
+### Frontend
+
+```javascript
+try {
+ const response = await api.post('/endpoint/', data);
+ showSuccess('Success!');
+} catch (error) {
+ if (error.response?.status === 400) {
+ showError(error.response.data.error || 'Validation failed');
+ } else if (error.response?.status === 403) {
+ showError('Permission denied');
+ } else {
+ showError('An unexpected error occurred');
+ }
+}
+```
+
+## Testing
+
+### Backend Tests
+
+```python
+from django.test import TestCase
+from rest_framework.test import APITestCase
+
+class ContributionTests(APITestCase):
+ def setUp(self):
+ self.user = User.objects.create_user(...)
+ self.client.force_authenticate(user=self.user)
+
+ def test_create_contribution(self):
+ response = self.client.post('/api/v1/contributions/', {...})
+ self.assertEqual(response.status_code, 201)
+```
+
+### Frontend Tests
+
+```javascript
+import { render, fireEvent } from '@testing-library/svelte';
+import Component from './Component.svelte';
+
+test('handles click', async () => {
+ const { getByRole } = render(Component);
+ const button = getByRole('button');
+ await fireEvent.click(button);
+ // assertions
+});
+```
+
+## Performance
+
+### Backend
+
+1. Use database connection pooling in production
+2. Implement pagination for list endpoints
+3. Use async views for I/O-bound operations
+4. Cache expensive computations
+
+### Frontend
+
+1. Lazy load routes and large components
+2. Debounce search inputs
+3. Use virtual scrolling for large lists
+4. Optimize images and assets
+
+### Monitoring
+
+- Log slow queries (>100ms)
+- Monitor API response times
+- Track frontend performance metrics
diff --git a/frontend/src/lib/api.js b/frontend/src/lib/api.js
index e3eb9e94..7cd852b0 100644
--- a/frontend/src/lib/api.js
+++ b/frontend/src/lib/api.js
@@ -154,6 +154,34 @@ export const buildersAPI = {
getNewestBuilders: (limit = 5) => api.get('/builders/newest/', { params: { limit } })
};
+// User Submissions API (for user's own submissions)
+export const submissionsAPI = {
+ // Get current user's submissions
+ getMySubmissions: (params = {}) => api.get('/submissions/my/', { params }),
+
+ // Get a single submission by ID
+ getSubmission: (id) => api.get(`/submissions/${id}/`),
+
+ // Create a new submission
+ createSubmission: (data) => {
+ if (data instanceof FormData) {
+ return api.post('/submissions/', data, {
+ headers: { 'Content-Type': 'multipart/form-data' }
+ });
+ }
+ return api.post('/submissions/', data);
+ },
+
+ // Update an existing submission
+ updateSubmission: (id, data) => api.patch(`/submissions/${id}/`, data),
+
+ // Cancel (soft delete) a submission
+ cancelSubmission: (id) => api.delete(`/submissions/${id}/`),
+
+ // Add evidence to a submission
+ addEvidence: (id, data) => api.post(`/submissions/${id}/add-evidence/`, data)
+};
+
// Journey API
export const journeyAPI = {
startValidatorJourney: () => api.post('/users/start_validator_journey/'),