Skip to content

LIME-Protocol/lime-backend

Repository files navigation

LIME Backend

LIME Backend is the off-chain service layer for LIME Markets. It exposes the API used by the frontend for signed order intake, order book reads, and future matching workflows while relying on the on-chain LIME Programs for custody, Positions, Resolution, Settlement, Claims, and Refunds.

The current backend slice focuses on Signed Limit Order intake. It verifies wallet authorization, checks on-chain Market and Collateral state, persists Open Orders, and reserves collateral in Postgres so the Matching Engine has a canonical off-chain state to build on.

What This Service Owns

LIME Backend owns the canonical off-chain state for:

  • Signed Orders
  • Reserved Collateral
  • Order Book state
  • future Fills, Trades, and Trade Execution idempotency

Supabase may still support frontend-owned app data, but it is not the source of truth for Matching Engine correctness. The frontend should talk to this backend when reading or writing matching state.

Current Scope

Implemented:

  • NestJS API under /v1
  • Prisma/Postgres persistence
  • Signed Limit Order validation for lime.signed-limit-order.v1
  • Solana RPC reads for Market and User Collateral state
  • Open Order persistence
  • Reserved Collateral tracking
  • order book and order read APIs
  • unit and e2e tests

Out of scope for this first slice:

  • matching execution
  • cancellations
  • fills and trade production
  • WebSockets
  • partial fills
  • on-chain Trade Execution
  • post-Resolution Settlement

Tech Stack

  • Node.js and TypeScript
  • NestJS
  • Prisma
  • Postgres
  • Zod
  • @solana/web3.js
  • tweetnacl
  • Jest and Supertest

Domain Language

Read CONTEXT.md before changing matching behavior. That file defines the project vocabulary and the boundaries between Backend, Matching Engine, Orders, Positions, Trade Execution, and Settlement.

Key rules:

  • A Signed Order is wallet-authorized order intent, not a backend-only record.
  • A Buy Order increases Long exposure when filled.
  • A Sell Order increases Short exposure when filled.
  • Reserved Collateral is tracked by the backend and prevents double spending across open orders.
  • Trade Execution is distinct from post-Resolution Settlement.
  • Settlement is not produced by the Backend or Matching Engine.

Architectural decisions live in docs/adr:

  • Backend owns matching state in Postgres.
  • The MVP backend uses Node, TypeScript, NestJS, and Postgres.
  • Signed Order V1 verification uses deterministic JSON serialization compatible with the current frontend serializer.

Getting Started

Install dependencies:

npm install

Create a local environment file:

cp .env.example .env

Set the required values in .env:

DATABASE_URL="postgresql://postgres:postgres@localhost:5432/lime_backend"
SOLANA_CLUSTER="devnet"
SOLANA_CHAIN_ID="solana-devnet"
SOLANA_RPC_URL="https://api.devnet.solana.com"
LIME_MARKET_PROGRAM_ID="<market-program-id>"
LIME_VAULT_PROGRAM_ID="<vault-program-id>"
PORT="3001"
CORS_ORIGIN="http://localhost:5173"

Start local Postgres:

docker compose up -d

Generate Prisma client and run migrations:

npm run prisma:generate
npm run prisma:migrate

Run the API in development mode:

npm run dev

The API listens on PORT and uses the /v1 global prefix. With the default config, the base URL is:

http://localhost:3001/v1

Scripts

npm run dev              # start NestJS in watch mode
npm run build            # compile TypeScript
npm run start            # run compiled app from dist
npm run test             # run unit tests
npm run test:e2e         # run e2e tests
npm run prisma:generate  # generate Prisma client
npm run prisma:migrate   # run development migrations

API

Submit a Signed Limit Order

POST /v1/orders/limit

Request body:

{
  "payload": {
    "schema": "lime.signed-limit-order.v1",
    "market_id": "42",
    "owner": "<solana-public-key>",
    "action": "buy",
    "exposure": "long",
    "quantity": 25,
    "limit_price_scaled": 425000,
    "expiry_ts": 1800000000,
    "nonce": "nonce-1",
    "chain_id": "solana-devnet"
  },
  "message": "{\"schema\":\"lime.signed-limit-order.v1\",...}",
  "signature": "<base64-ed25519-signature>"
}

Accepted response:

{
  "status": "accepted",
  "orderId": "<sha256-order-id>"
}

Rejected response:

{
  "status": "rejected",
  "reason": "INVALID_SIGNATURE",
  "message": "Wallet signature does not authorize this signed order."
}

Expected rejection reasons include:

  • INVALID_SCHEMA
  • INVALID_MESSAGE
  • INVALID_SIGNATURE
  • INVALID_CHAIN
  • INVALID_MARKET
  • MARKET_NOT_ACTIVE
  • ORDER_EXPIRED
  • NONCE_ALREADY_USED
  • ACTION_EXPOSURE_MISMATCH
  • INVALID_PRICE
  • INVALID_QUANTITY
  • INSUFFICIENT_AVAILABLE_COLLATERAL
  • INTERNAL_ERROR

Read Market Order Book

GET /v1/markets/:marketId/orderbook

Returns aggregated bid and ask levels for Open Orders in the given Market.

Read Market Orders

GET /v1/markets/:marketId/orders

Returns Open Orders for a Market.

Read Owner Orders

GET /v1/owners/:owner/orders

Returns Open Orders for a wallet owner.

Signed Order Rules

The backend verifies Signed Limit Orders by reconstructing the canonical V1 JSON message from the submitted payload, comparing it to the submitted message, and verifying the wallet signature over that exact UTF-8 string.

Validation includes:

  • schema must be lime.signed-limit-order.v1
  • chain_id must match SOLANA_CHAIN_ID
  • buy must pair with long
  • sell must pair with short
  • expiry_ts must be in the future
  • market_id must fit in a Solana u64
  • owner must be a valid Solana public key
  • signature must be a valid Ed25519 signature from the owner
  • price must be between 0 and 1 as a scaled integer
  • quantity must be positive after scaling

Required Collateral:

  • Buy: Quantity * Price
  • Sell: Quantity - (Quantity * Price)

The backend checks Available Collateral on-chain and subtracts already Reserved Collateral recorded in Postgres before accepting a new order.

Persistence Model

The initial Prisma schema stores:

  • orders: accepted Signed Orders, keyed by deterministic Order ID
  • reserved_collateral: collateral reserved for Open Orders

Replay protection is enforced with a unique constraint on:

chainId + marketId + owner + nonce

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors