diff --git a/backend/.env.docker b/backend/.env.docker index 01034a9..8c262bf 100644 --- a/backend/.env.docker +++ b/backend/.env.docker @@ -4,6 +4,9 @@ NODE_ENV=production PORT=3000 +# Enable local development initialization +INIT_LOCAL_DEV=true + # JWT Secrets - IMPORTANT: Use secure random strings in production JWT_ACCESS_SECRET=dd01992053448ebc129e4f12366596351e4c2d95ffe9b798104fcc80eff11c28 JWT_REFRESH_SECRET=67bf4928a666ac3d46e991398a0a7e5aa2ec15ca206316b1126f31d2247498d4 @@ -14,9 +17,10 @@ REDIS_URL=redis://redis:6379 # Database - Use Docker service name and container port DATABASE_URL=postgresql://postgres:password@postgres:5432/boxmeout_dev?schema=public -# Stellar/Soroban -STELLAR_NETWORK=testnet -STELLAR_SOROBAN_RPC_URL=https://soroban-testnet.stellar.org +# Stellar/Soroban - Local standalone network +STELLAR_NETWORK=standalone +STELLAR_HORIZON_URL=http://soroban:8000 +STELLAR_SOROBAN_RPC_URL=http://soroban:8001 # Contract Addresses (set after deployment) MARKET_CONTRACT_ADDRESS= diff --git a/backend/DOCKER_LOCAL_DEV.md b/backend/DOCKER_LOCAL_DEV.md new file mode 100644 index 0000000..bad3e57 --- /dev/null +++ b/backend/DOCKER_LOCAL_DEV.md @@ -0,0 +1,197 @@ +# Docker Compose Local Development Setup + +This document describes the local development environment with Soroban integration. + +## Overview + +The Docker Compose setup now includes: +- **PostgreSQL** - Main database +- **PostgreSQL Test** - Test database +- **Redis** - Caching and session storage +- **Soroban Node** - Local Stellar blockchain with Soroban smart contracts +- **Backend** - Node.js API server with auto-initialization + +## Services + +### Soroban (Stellar Quickstart) +- **Image**: `stellar/quickstart:testing` +- **Ports**: + - `8000` - Horizon API (Stellar REST API) + - `8001` - Soroban RPC (Smart contract interaction) + - `11626` - Stellar Core peer port + - `11625` - Stellar Core admin port +- **Network**: Standalone (local development) + +### Backend +- **Auto-initialization**: On startup, the backend will: + 1. Wait for Soroban node to be ready + 2. Generate a deployer identity + 3. Deploy test USDC token + 4. Build and deploy all 5 Soroban contracts (Oracle, Factory, Treasury, AMM, Market) + 5. Initialize contracts with proper configuration + 6. Run database migrations + 7. Seed database with test markets and users + +## Quick Start + +### 1. Start all services +```bash +cd backend +docker-compose up -d +``` + +### 2. View logs +```bash +# All services +docker-compose logs -f + +# Specific service +docker-compose logs -f backend +docker-compose logs -f soroban +``` + +### 3. Check initialization status +The backend container will show initialization progress in the logs: +```bash +docker-compose logs -f backend | grep INIT +``` + +### 4. Access services +- **Backend API**: http://localhost:3000 +- **Horizon API**: http://localhost:8000 +- **Soroban RPC**: http://localhost:8001 +- **PostgreSQL**: localhost:5434 +- **Redis**: localhost:6379 + +## Test Credentials + +After seeding, you can login with: +- **Email**: `admin@boxmeout.com` +- **Password**: `password123` + +Other test users: +- `john@example.com` / `password123` +- `sarah@example.com` / `password123` +- `mike@example.com` / `password123` + +## Contract Addresses + +After initialization, contract addresses are saved to `/app/.env.local` inside the container. + +To view them: +```bash +docker-compose exec backend cat /app/.env.local +``` + +## Manual Contract Interaction + +### Using Stellar CLI from host +```bash +# Install stellar CLI locally +curl -L https://github.com/stellar/stellar-cli/releases/download/v21.5.0/stellar-cli-21.5.0-x86_64-unknown-linux-musl.tar.gz | tar xz + +# Interact with contracts +./stellar contract invoke \ + --id \ + --network standalone \ + --rpc-url http://localhost:8001 \ + -- +``` + +### Using Stellar CLI from container +```bash +docker-compose exec backend stellar contract invoke \ + --id \ + --network standalone \ + --rpc-url http://soroban:8001 \ + -- +``` + +## Troubleshooting + +### Backend fails to start +Check if Soroban is healthy: +```bash +curl http://localhost:8000/ +``` + +### Contracts not deploying +1. Check if contracts are built: +```bash +docker-compose exec backend ls -la /app/contracts/contracts/boxmeout/target/wasm32-unknown-unknown/release/ +``` + +2. Rebuild contracts manually: +```bash +docker-compose exec backend bash +cd /app/contracts +cargo build --release --target wasm32-unknown-unknown +``` + +### Reset everything +```bash +docker-compose down -v +docker-compose up -d +``` + +## Environment Variables + +Key environment variables in `.env.docker`: +- `INIT_LOCAL_DEV=true` - Enables auto-initialization +- `STELLAR_NETWORK=standalone` - Uses local Soroban node +- `STELLAR_HORIZON_URL=http://soroban:8000` - Horizon API endpoint +- `STELLAR_SOROBAN_RPC_URL=http://soroban:8001` - Soroban RPC endpoint + +## Development Workflow + +1. **Start services**: `docker-compose up -d` +2. **Make code changes**: Edit files locally +3. **Rebuild backend**: `docker-compose up -d --build backend` +4. **View logs**: `docker-compose logs -f backend` +5. **Run tests**: `docker-compose exec backend npm test` +6. **Stop services**: `docker-compose down` + +## Database Management + +### Run migrations +```bash +docker-compose exec backend npx prisma migrate dev +``` + +### Seed database +```bash +docker-compose exec backend npx prisma db seed +``` + +### Access database +```bash +docker-compose exec postgres psql -U postgres -d boxmeout_dev +``` + +## Backup and Restore + +The `db-backup` service automatically backs up the database every 6 hours. + +### Manual backup +```bash +docker-compose exec db-backup /scripts/backup.sh +``` + +### Restore from backup +```bash +docker-compose exec db-backup /scripts/restore.sh +``` + +## Production Deployment + +For production, disable auto-initialization: +1. Set `INIT_LOCAL_DEV=false` in `.env.docker` +2. Use testnet or mainnet Soroban RPC URLs +3. Deploy contracts manually using `../deploy.sh` +4. Update contract addresses in `.env` + +## Additional Resources + +- [Stellar Documentation](https://developers.stellar.org/) +- [Soroban Documentation](https://soroban.stellar.org/) +- [Stellar Quickstart](https://github.com/stellar/quickstart) diff --git a/backend/Dockerfile b/backend/Dockerfile index 40084f8..751e93f 100644 --- a/backend/Dockerfile +++ b/backend/Dockerfile @@ -4,8 +4,20 @@ FROM node:20-alpine # Set working directory WORKDIR /app -# Install OpenSSL for Prisma (use openssl3 for modern Alpine) -RUN apk add --no-cache openssl +# Install system dependencies +# - OpenSSL for Prisma +# - curl for health checks and API calls +# - bash for init scripts +# - cargo/rust for building Soroban contracts (if needed) +RUN apk add --no-cache \ + openssl \ + curl \ + bash \ + git \ + build-base + +# Install Stellar CLI +RUN curl -L https://github.com/stellar/stellar-cli/releases/download/v21.5.0/stellar-cli-21.5.0-x86_64-unknown-linux-musl.tar.gz | tar xz -C /usr/local/bin # Copy package files COPY package*.json ./ @@ -26,8 +38,26 @@ COPY . . # Build the application RUN npm run build +# Copy initialization script +COPY scripts/init-local-dev.sh /app/scripts/init-local-dev.sh +RUN chmod +x /app/scripts/init-local-dev.sh + # Expose port EXPOSE 3000 -# Start the application -CMD ["npm", "start"] \ No newline at end of file +# Create entrypoint script +RUN echo '#!/bin/bash\n\ +set -e\n\ +\n\ +# Run initialization if INIT_LOCAL_DEV is set\n\ +if [ "$INIT_LOCAL_DEV" = "true" ]; then\n\ + echo "Running local development initialization..."\n\ + /app/scripts/init-local-dev.sh\n\ +fi\n\ +\n\ +# Start the application\n\ +exec npm start\n\ +' > /app/entrypoint.sh && chmod +x /app/entrypoint.sh + +# Use custom entrypoint +ENTRYPOINT ["/app/entrypoint.sh"] \ No newline at end of file diff --git a/backend/docker-compose.yml b/backend/docker-compose.yml index 60cd76a..88d1e74 100644 --- a/backend/docker-compose.yml +++ b/backend/docker-compose.yml @@ -1,6 +1,28 @@ version: '3.8' services: + soroban: + image: stellar/quickstart:testing + container_name: boxmeout_soroban + command: --standalone --enable-soroban-rpc + ports: + - "8000:8000" # Horizon API + - "8001:8001" # Soroban RPC + - "11626:11626" # Stellar Core peer port + - "11625:11625" # Stellar Core admin port + environment: + - NETWORK_PASSPHRASE=Standalone Network ; February 2017 + - SOROBAN_RPC_ENABLED=true + healthcheck: + test: ["CMD", "curl", "-f", "http://localhost:8000/"] + interval: 10s + timeout: 5s + retries: 10 + start_period: 30s + networks: + - boxmeout_network + restart: unless-stopped + backend: build: . container_name: boxmeout_backend @@ -13,6 +35,8 @@ services: condition: service_healthy redis: condition: service_healthy + soroban: + condition: service_healthy networks: - boxmeout_network restart: unless-stopped diff --git a/backend/scripts/init-local-dev.sh b/backend/scripts/init-local-dev.sh new file mode 100644 index 0000000..6df0611 --- /dev/null +++ b/backend/scripts/init-local-dev.sh @@ -0,0 +1,210 @@ +#!/bin/bash +# ============================================================================= +# BoxMeOut Stella - Local Development Initialization Script +# ============================================================================= +# This script runs inside the Docker container to: +# 1. Wait for Soroban node to be ready +# 2. Deploy and initialize all contracts to local Soroban +# 3. Run database migrations +# 4. Seed database with test markets +# ============================================================================= + +set -e + +# Colors +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[0;34m' +NC='\033[0m' + +log_info() { echo -e "${BLUE}[INIT]${NC} $1"; } +log_success() { echo -e "${GREEN}[OK]${NC} $1"; } +log_error() { echo -e "${RED}[ERROR]${NC} $1"; } + +# Configuration +SOROBAN_RPC_URL="${SOROBAN_RPC_URL:-http://soroban:8001}" +HORIZON_URL="${HORIZON_URL:-http://soroban:8000}" +MAX_RETRIES=30 +RETRY_DELAY=2 + +# Wait for Soroban RPC to be ready +log_info "Waiting for Soroban RPC at $SOROBAN_RPC_URL..." +for i in $(seq 1 $MAX_RETRIES); do + if curl -s -X POST "$SOROBAN_RPC_URL" \ + -H "Content-Type: application/json" \ + -d '{"jsonrpc":"2.0","id":1,"method":"getHealth"}' | grep -q "healthy"; then + log_success "Soroban RPC is ready" + break + fi + + if [ $i -eq $MAX_RETRIES ]; then + log_error "Soroban RPC failed to become ready after $MAX_RETRIES attempts" + exit 1 + fi + + log_info "Attempt $i/$MAX_RETRIES - waiting ${RETRY_DELAY}s..." + sleep $RETRY_DELAY +done + +# Check if stellar CLI is available +if ! command -v stellar &> /dev/null; then + log_error "stellar CLI not found. Install it in the Docker image." + exit 1 +fi + +# Generate or load deployer identity +log_info "Setting up deployer identity..." +if ! stellar keys address deployer &> /dev/null 2>&1; then + stellar keys generate deployer --network standalone + log_success "Generated new deployer identity" +else + log_info "Using existing deployer identity" +fi + +ADMIN_ADDRESS=$(stellar keys address deployer) +log_info "Admin address: $ADMIN_ADDRESS" + +# Fund the admin account on standalone network +log_info "Funding admin account..." +curl -s "$HORIZON_URL/friendbot?addr=$ADMIN_ADDRESS" > /dev/null || true +log_success "Admin account funded" + +# Deploy test USDC token +log_info "Deploying test USDC token..." +USDC_TOKEN_ADDRESS=$(stellar contract asset deploy \ + --asset "USDC:$ADMIN_ADDRESS" \ + --source deployer \ + --network standalone \ + --rpc-url "$SOROBAN_RPC_URL" 2>&1 | tail -1) +log_success "USDC token deployed: $USDC_TOKEN_ADDRESS" + +# Build contracts if WASMs don't exist +WASM_DIR="/app/contracts/contracts/boxmeout/target/wasm32-unknown-unknown/release" +if [ ! -f "$WASM_DIR/oracle.wasm" ]; then + log_info "Building contracts..." + cd /app/contracts + cargo build --release --target wasm32-unknown-unknown + log_success "Contracts built" +fi + +# Deploy contracts +log_info "Deploying contracts..." +declare -A CONTRACT_IDS + +for contract in oracle factory treasury amm market; do + log_info "Deploying ${contract}..." + wasm_file="$WASM_DIR/${contract}.wasm" + + if [ ! -f "$wasm_file" ]; then + log_error "WASM file not found: $wasm_file" + exit 1 + fi + + contract_id=$(stellar contract deploy \ + --wasm "$wasm_file" \ + --source deployer \ + --network standalone \ + --rpc-url "$SOROBAN_RPC_URL" 2>&1 | tail -1) + + CONTRACT_IDS[$contract]="$contract_id" + log_success "${contract} deployed: $contract_id" +done + +# Initialize contracts +log_info "Initializing Oracle..." +stellar contract invoke \ + --id "${CONTRACT_IDS[oracle]}" \ + --source deployer \ + --network standalone \ + --rpc-url "$SOROBAN_RPC_URL" \ + -- initialize \ + --admin "$ADMIN_ADDRESS" \ + --required_consensus 2 + +log_info "Initializing Factory..." +stellar contract invoke \ + --id "${CONTRACT_IDS[factory]}" \ + --source deployer \ + --network standalone \ + --rpc-url "$SOROBAN_RPC_URL" \ + -- initialize \ + --admin "$ADMIN_ADDRESS" \ + --usdc "$USDC_TOKEN_ADDRESS" \ + --treasury "${CONTRACT_IDS[treasury]}" + +log_info "Initializing Treasury..." +stellar contract invoke \ + --id "${CONTRACT_IDS[treasury]}" \ + --source deployer \ + --network standalone \ + --rpc-url "$SOROBAN_RPC_URL" \ + -- initialize \ + --admin "$ADMIN_ADDRESS" \ + --usdc_contract "$USDC_TOKEN_ADDRESS" \ + --factory "${CONTRACT_IDS[factory]}" + +log_info "Initializing AMM..." +stellar contract invoke \ + --id "${CONTRACT_IDS[amm]}" \ + --source deployer \ + --network standalone \ + --rpc-url "$SOROBAN_RPC_URL" \ + -- initialize \ + --admin "$ADMIN_ADDRESS" \ + --factory "${CONTRACT_IDS[factory]}" \ + --usdc_token "$USDC_TOKEN_ADDRESS" \ + --max_liquidity_cap 10000000000000 + +log_success "All contracts initialized" + +# Update environment variables +log_info "Updating environment variables..." +cat > /app/.env.local <