Skip to content

About

Open-source Stellar and Soroban API for peer-to-peer USDC settlement, wallet authentication, orders, and escrow coordination.

Topics

Resources

Contributing

Stars

133 stars

Watchers

0 watching

Forks

Latest commit

 

History

339 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

LocalSettle API

LocalSettle is a peer-to-peer marketplace that connects local payment methods with USDC settlement on Stellar. This repository contains the NestJS API: it manages accounts, offers, orders, chat, payment evidence, wallet-authenticated sessions, transaction preparation, and escrow coordination. The Next.js client is maintained in the LocalSettle frontend repository.

Network status: configuration defaults to Stellar Testnet and the Trustless Work development API. Testnet assets have no real-world value. Configure production services, network, issuer, and secrets deliberately before any mainnet deployment.

What the API does

  • Authenticates Stellar wallet owners by issuing a one-time challenge, verifying the wallet signature, and issuing a JWT.
  • Reads Stellar accounts, balances, and payment history through Horizon.
  • Resolves a recipient address or LocalSettle alias and prepares an unsigned USDC payment transaction for the user to sign.
  • Coordinates order lifecycle, payment details, chat, KYC status, receipt uploads, and audit events.
  • Integrates with Trustless Work to deploy and operate USDC multi-release escrow on Soroban, and polls Soroban RPC for contract events.

Stellar transaction flows

Wallet authentication

POST /auth/challenge creates a short-lived random challenge for a public key. The connected wallet signs it locally. POST /auth/login verifies the signature, consumes the challenge once, and returns a session token. The API verifies public-key ownership; it does not need the user's secret key for this flow. Each newly issued JWT is tied to a server-side session. Send the current bearer token to POST /auth/logout to revoke only that session; other signed-in devices remain active. Tokens issued before session tracking was deployed must be refreshed before they can be revoked.

The login token is temporary and can be used to validate an alias and complete POST /users/:id/setup. That bootstrap route remains available before setup is complete. The setup response returns a final token with the setupComplete: true claim; profile, payment, trading, chat, and wallet routes that declare SetupCompleteGuard require that final token.

Challenge issuance is limited independently by client IP and wallet (defaults: 10 requests per 60 seconds). Failed signature verification is limited independently by IP and wallet (defaults: 5 failures per 60 seconds). Configure these windows with AUTH_CHALLENGE_RATE_LIMIT_MAX, AUTH_CHALLENGE_RATE_LIMIT_TTL_MS, AUTH_FAILED_SIGNATURE_RATE_LIMIT_MAX, and AUTH_FAILED_SIGNATURE_RATE_LIMIT_TTL_MS. A legitimate user who reaches a limit should wait for that window to expire before requesting a new challenge or retrying a signature. Authentication audit events record only the operation and limit scope; wallet identifiers in those events are hashed.

Direct USDC payment

The authenticated client calls GET /send/resolve for a Stellar address or LocalSettle alias, then POST /send/prepare. The API reads recipient account information, builds an unsigned Stellar transaction XDR, and returns it for review. The client asks the user's wallet to sign. POST /send/submit broadcasts the signed XDR through Horizon and returns the transaction result.

Peer-to-peer escrow

The API opens a Trustless Work multi-release escrow for an order. It signs and broadcasts the deployment transaction with the configured platform operator key, then returns an unsigned funding transaction for the seller to sign with their wallet. Soroban events and escrow API operations update the application view of the trade. Local fiat transfer, payment instructions, chat, and evidence remain off-chain.

sequenceDiagram
    participant Buyer
    participant Seller
    participant Web as LocalSettle frontend
    participant API as LocalSettle API
    participant TW as Trustless Work
    participant Soroban as Stellar Soroban
    Seller->>Web: Create or accept trade
    Web->>API: Open order and escrow
    API->>TW: Initialize multi-release escrow
    TW-->>API: Unsigned deployment XDR
    API->>Soroban: Sign deployment with platform operator key
    API->>TW: Prepare escrow funding
    TW-->>API: Unsigned funding XDR
    API-->>Web: Contract ID and funding XDR
    Web->>Seller: Request wallet signature
    Seller->>Soroban: Sign and submit funding transaction
    Buyer->>Seller: Pay local currency off-chain
    Buyer->>Web: Confirm payment and upload evidence
    Web->>API: Update trade and escrow state
    API->>Soroban: Read Soroban events and contract state
Loading

On-chain and off-chain boundaries

Responsibility System
USDC transfer and escrow contract state Stellar network; escrow operations are orchestrated through Trustless Work
Wallet challenge and user transaction signatures User-selected wallet, requested by the frontend
Offers, orders, aliases, chat, KYC status, and audit records LocalSettle API and PostgreSQL
Payment evidence files Configured storage provider (Google Cloud Storage or mock provider)
Local bank or cash payment Directly between the marketplace participants, outside Stellar

Escrow is currently limited to USDC. The backend holds a separate configured operator secret for escrow deployment; deployments must restrict and protect it. The application does not provide an in-app on-chain refund operation. This repository integrates with Trustless Work; it does not contain or independently audit the escrow contract.

Architecture

  • src/modules/ — NestJS feature modules for auth, Stellar, offers, orders, escrow, users, chat, KYC, storage, and audit logs.
  • src/common/ — shared guards, validation, error handling, and request utilities.
  • prisma/schema.prisma — PostgreSQL data model; migrations and seed data are under prisma/.
  • test/ — end-to-end test configuration and suites.
  • docs/architecture.md — component map, data flows, and trust boundaries.

The API uses @stellar/stellar-sdk for Horizon account and transaction operations. Soroban contract events are read from RPC. PostgreSQL stores application workflow state; it is not a substitute for reading the on-chain contract state.

Protocol references: Stellar developer documentation, Horizon API guide, and Stellar RPC methods.

Run locally

Requirements: Node.js 20+, npm, and PostgreSQL. Copy .env.example to .env; configure DATABASE_URL, DIRECT_URL, and a local JWT_SECRET. For Stellar features, configure STELLAR_HORIZON_URL, STELLAR_RPC_URL, STELLAR_NETWORK, and the relevant issuer and Trustless Work values. Use test credentials and Testnet accounts only.

npm install
npx prisma generate
npx prisma migrate dev
npm run start:dev

The API listens on port 3001 by default. The frontend should point NEXT_PUBLIC_API_URL at http://localhost:3001. Never commit real secrets. The existing IKASH_* escrow environment variable names are retained for deployment compatibility; see .env.example before configuring them.

Useful API routes

Route Purpose
POST /auth/challenge, POST /auth/login Wallet challenge-response session
POST /auth/logout Revokes the current bearer-token session
GET /stellar/account/:publicKey, GET /stellar/balances/:publicKey Stellar account and balance reads
GET /stellar/transactions Authenticated wallet payment history
GET /send/resolve, POST /send/prepare, POST /send/submit Prepare and submit a signed USDC payment
POST /escrows/open, POST /escrows/sync, GET /escrows/:id/status Escrow workflow and status

Routes have endpoint-specific validation and guards. Check the controller and DTO before relying on an endpoint's authentication or ownership behavior.

Active payment providers are checked against provider-type metadata schema version 1 before being returned or used to create payment methods. The provider response includes metadataSchemaVersion; malformed UI requirements fail with a validation error so they do not reach trade screens.

POST /users/:id/setup applies profile updates, an optional payment method, and the account setup audit row in one database transaction. Repeated setup requests reuse an existing matching payment method instead of creating another one.

PATCH /orders/:id accepts only escrow-backed forward transitions: created to locked when escrow is funded, and locked to released or disputed when escrow has the matching terminal status. The write is conditional on the observed order and escrow states, so stale requests return HTTP 409 with the stable ORDER_INVALID_TRANSITION error code. Cancellation and expiration must use their dedicated flows. Terminal order states cannot be reopened through PATCH.

GET /orders/user-stats/:userId requires a valid JWT. These private account metrics are available to the account owner and administrators only; the response is limited to total and completed order counts and contains no individual order or counterparty details.

Offer updates require the current expectedVersion from the offer response. Successful updates increment the persisted version; stale versions return HTTP 409 with OFFER_VERSION_CONFLICT. Existing rows start at version 0 through the add_offer_version migration.

Escrow evidence uploads accept only matching PDF, JPEG, PNG, or WebP extensions and MIME types. Storage keys use generated UUID filenames and are persisted in evidence_key; apply the add_escrow_evidence_key migration before deploying this change.

Order creation records ORDER_CREATED in the same database transaction as the order, escrow record, and offer execution update. Audit storage errors therefore abort the complete database transaction instead of leaving a committed order without its audit record.

Order creation derives and persists fiatAmount from the selected offer price and asset amount. The total is rounded to two decimal places using half-up rounding; a legacy client-supplied fiatAmount is accepted but ignored.

Each payment history record retains the legacy asset and asset_issuer fields and adds asset_descriptor with the asset type, code, and issuer. Native XLM uses code XLM and a null issuer; issued assets include their issuer address.

Stellar payment history accepts limit (default 10, maximum 200) and an optional Horizon cursor. The response is { records, nextCursor? }; pass nextCursor as cursor to fetch the next page. nextCursor is omitted on the final page.

Sensitive profile changes are recorded as USER_PROFILE_UPDATED audit events. Their metadata contains only changed field names (or requested field names on failure); submitted values are never copied into audit metadata. No-op requests are recorded with an empty field list. Profile-image updates use the same event with profileImageUrl as the sole field name.

Development checks

npm run build       # Compile the NestJS application
npm test            # Run Jest unit tests
npm run test:e2e    # Run API and gateway end-to-end tests
npm run test:cov    # Collect Jest coverage
npm run format      # Format TypeScript source and tests

Unit tests use Jest *.spec.ts files near the implementation. Add regression coverage for authentication, transaction preparation, escrow transitions, access control, and event processing when changing those areas.

Contributing to the Stellar ecosystem

See CONTRIBUTING.md for setup, security, commit, and pull request expectations. Keep API changes coordinated with the frontend. Explain the affected user flow and include tests, migrations, or environment changes in your pull request. Drips Wave repository participation requires applying to the relevant program and organizer approval; see the Drips maintainer guide.

Security

Never commit or log private keys, JWTs, webhook secrets, full bank details, or identity documents. Keep the escrow operator key in a secret manager with restricted access. Treat fiat payment evidence, profile data, and KYC status as sensitive application data. Report vulnerabilities privately through the repository's GitHub security channel rather than a public issue.

License

LocalSettle is licensed under the MIT License. See LICENSE.

About

Open-source Stellar and Soroban API for peer-to-peer USDC settlement, wallet authentication, orders, and escrow coordination.

Topics

Resources

Contributing

Stars

133 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages