A decentralized OTC (Over-The-Counter) platform for Stellar stablecoins
Enabling peer-to-peer trading of CRCX, MXNX, and USDC using regional payment rails like SINPE and SPEI.
Pacto P2P is a non-custodial trading platform that connects buyers and sellers of Stellar stablecoins through secure, blockchain-backed escrows. Every trade is secured by Trustless Work smart contracts on the Stellar blockchain, ensuring transparency and security without requiring a trusted intermediary.
- π Non-Custodial Trading: Your funds are secured by smart contracts, not held by us
- π Borderless Payments: Trade using regional payment methods (SINPE, SPEI, etc.)
- β‘ Fast Settlements: Stellar blockchain enables near-instant transactions
- π‘οΈ Dispute Resolution: Built-in dispute system for trade conflicts
- πΌ Merchant Profiles: Verified merchant accounts with public profiles
- π Real-time Tracking: Live status updates for trades and escrows
- CRCX - Costa Rican ColΓ³n Token
- MXNX - Mexican Peso Token
- USDC - USD Coin (Global, various payment methods)
- Node.js 18+
- npm 9+
- Docker Desktop (required for local Supabase)
- Supabase CLI (
npx supabaseworks out of the box, or install globally withnpm i -g supabase)
# Clone the repository
git clone https://github.com/your-username/pacto-p2p.git
cd pacto-p2p
# Install all dependencies
npm install
# Build all packages
npm run build
# Start development server
npm run devThe application will be available at http://localhost:3000
The project uses Supabase for its PostgreSQL database. A full local setup is included so you can develop without depending on a remote instance.
1. Start the local Supabase stack
Make sure Docker Desktop is running, then:
npm run db:startThis pulls the required Docker images (first run only), applies all migrations in supabase/migrations/ in order, runs supabase/seed.sql to populate sample data, and starts the local services:
| Service | URL |
|---|---|
| Studio (UI) | http://127.0.0.1:54323 |
| API (REST) | http://127.0.0.1:54321/rest/v1 |
| Database | postgresql://postgres:postgres@127.0.0.1:54322/postgres |
| Mailpit | http://127.0.0.1:54324 |
2. Reset the database (re-apply migrations + seed)
If you want a clean slate at any point:
npm run db:resetThis drops all data, re-runs every migration from scratch, and re-seeds.
3. Stop the local stack
npm run db:stopData is persisted in Docker volumes, so npm run db:start will restore where you left off. Use supabase stop --no-backup to discard volumes entirely.
When you need to modify the database schema (add tables, columns, indexes, etc.):
# Generate a new timestamped migration file
npm run db:migration your_migration_nameThis creates supabase/migrations/<timestamp>_your_migration_name.sql. Write your SQL there, then apply it:
# Option A: Reset everything (migrations + seed)
npm run db:reset
# Option B: Apply only pending migrations to a running instance
npx supabase migration upGuidelines for writing migrations:
- Make migrations idempotent when possible (
CREATE OR REPLACE,IF NOT EXISTS, etc.) so repeated runs don't fail. - Each migration should be a self-contained change. Don't modify a previous migration file β always create a new one.
- If your migration adds columns that the seed data depends on, update
supabase/seed.sqlas well. - Test locally with
npm run db:resetbefore pushing.
If you made changes directly through Studio or psql and want to capture them as a migration:
npx supabase db diff -f describe_your_changeThis compares the live local database against the migration history and generates a new migration file with the differences.
Create a .env.local file in apps/web with the following variables:
# Stellar & Trustless Work
NEXT_PUBLIC_TLW_API_KEY=your_trustless_work_api_key
NEXT_PUBLIC_ROLE_ADDRESS=your_stellar_role_address
NEXT_PUBLIC_PLATFORM_FEE=0.01
# Supabase (use these defaults for local development)
NEXT_PUBLIC_SUPABASE_URL=http://127.0.0.1:54321
NEXT_PUBLIC_SUPABASE_ANON_KEY=<publishable key from supabase start output>
# Optional: Mock Mode
NEXT_PUBLIC_USE_MOCK=0
# Optional: Landing supported assets. JSON object keyed by asset symbol; each value must have name, region, paymentMethods, color. If unset or invalid, default assets (CRCX, MXNX, USDC) are used. See apps/web/.env.example for format.
# NEXT_PUBLIC_SUPPORTED_ASSETS={"CRCX":{"name":"Costa Rican ColΓ³n Token","region":"Costa Rica","paymentMethods":"SINPE","color":"bg-green-500"},...}pacto-p2p/
βββ apps/
β βββ web/ # Next.js web application
β βββ app/ # Next.js App Router pages
β βββ components/ # React components
β βββ hooks/ # Custom React hooks
β βββ lib/ # Utilities and services
β βββ providers/ # React context providers
β
βββ packages/
β βββ shared/ # Shared utilities and services
β βββ ui/ # Reusable UI components (Radix UI)
β βββ types/ # TypeScript type definitions
β βββ config/ # Configuration and scripts
β
βββ supabase/
β βββ config.toml # Local Supabase configuration
β βββ migrations/ # SQL migrations (applied in order)
β βββ seed.sql # Sample data for local development
β
βββ docs/ # Documentation
β βββ DEVELOPMENT.md # Development guide
β βββ DATABASE_SCHEMA.md # Database schema
β
βββ scripts/ # Utility scripts
βββ CONTRIBUTING.md # Contribution guidelines
βββ README.md # This file
- Frontend: Next.js 15, React 19, TypeScript
- Styling: TailwindCSS, Shadcn UI, Radix UI
- State Management: Zustand, TanStack Query
- Blockchain: Stellar, Trustless Work
- Database: Supabase (PostgreSQL)
- Code Quality: Biome, TypeScript
- Build System: Turborepo, npm workspaces
apps/web- Main Next.js web application- Dashboard for managing listings, escrows, and trades
- Stellar wallet integration via Trustless Work
- Supabase backend integration
- Merchant profiles and public pages
-
packages/shared- Common utilities and services- Database services (Supabase client)
- Stellar wallet utilities
- State management (Zustand stores)
- Validation schemas (Zod)
-
packages/ui- Reusable UI components- Radix UI primitives
- Custom themed components
- Form components
- Layout components
-
packages/types- TypeScript type definitions- Escrow types
- API response types
- Database schema types
-
packages/config- Configuration and scripts- Database initialization scripts
- Environment configurations
- Build configurations
- Click "Sign In" on the homepage
- Choose your preferred Stellar wallet (Freighter, WalletConnect, etc.)
- Approve the connection in your wallet
- You'll be redirected to the dashboard
- Navigate to "Listings" in the dashboard
- Click "New Listing"
- Fill in the trade details:
- Token type (CRCX, MXNX, USDC)
- Amount and rate
- Payment method
- Description
- Submit the listing
- Browse available listings on the marketplace
- Click on a listing to view details
- Click "Trade" to initiate an escrow
- Follow the escrow process:
- Deposit funds to the escrow contract
- Send fiat payment to the seller
- Upload payment receipt
- Wait for seller confirmation
- Funds are automatically released
# Development
npm run dev # Start web app in development mode
npm run build # Build all packages and apps
npm run start # Start web app in production mode
# Code Quality
npm run lint # Lint all packages
npm run biome:check # Check code with Biome
npm run biome:format # Format code with Biome
npm run biome:fix # Fix code issues with Biome
npm run type-check # Type check all packages
# Database
npm run db:start # Start local Supabase (Docker required)
npm run db:stop # Stop local Supabase
npm run db:reset # Drop, re-migrate, and re-seed
npm run db:migration # Create a new migration file
# Maintenance
npm run clean # Clean all build artifacts-
Create a feature branch:
git checkout -b feature/your-feature-name
-
Make your changes following our coding standards
-
Test your changes:
npm run type-check npm run biome:check npm run build
-
Commit using Conventional Commits:
git commit -m "feat: add new feature" -
Push and create a Pull Request
For detailed development instructions, see docs/DEVELOPMENT.md.
- Create a new directory in
packages/ - Add
package.jsonwith workspace dependencies - Add TypeScript configuration
- Update root
package.jsonworkspaces if needed
- Create a new directory in
apps/ - Add
package.jsonwith workspace dependencies - Add necessary configuration files (next.config.ts, tsconfig.json, etc.)
- Update root
package.jsonworkspaces if needed
- Root
tsconfig.jsonprovides base configuration - Each package extends the root config
- Path mapping for workspace dependencies
- Consistent code formatting and linting
- Shared configuration across all packages
- Automatic fixes and formatting
Use workspace:* for internal dependencies:
{
"dependencies": {
"@pacto-p2p/shared": "workspace:*",
"@pacto-p2p/ui": "workspace:*"
}
}# Build the web app
npm run build
# Start production server
npm run startEnsure all required environment variables are set in your deployment environment. See Quick Start for the list of required variables.
We welcome contributions! Please see our Contributing Guide for details on:
- Code of Conduct
- Development workflow
- Coding standards
- Pull request process
- Reporting issues
- Fork the repository
- Create a feature branch (
git checkout -b feature/amazing-feature) - Make your changes
- Run tests and linting (
npm run type-check && npm run biome:check) - Commit your changes (
git commit -m 'feat: add amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
- Development Guide - Detailed development instructions and architecture
- Database Schema - Database structure and relationships
- Contributing Guide - How to contribute to the project
If you discover a security vulnerability, please email security concerns privately to the maintainers. Do not open public issues for security vulnerabilities.
This project is licensed under the MIT License - see the LICENSE file for details.
- Stellar Development Foundation
- Trustless Work for escrow infrastructure
- Next.js for the amazing framework
- All our contributors and supporters
Made with β€οΈ by the Pacto P2P team
Documentation β’ Contributing β’ Issues