Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 0 additions & 49 deletions Dockerfile.interface

This file was deleted.

10 changes: 4 additions & 6 deletions apps/example/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,25 +12,23 @@
"check-circular-imports": "dpdm --no-warning --no-tree src"
},
"devDependencies": {
"@repo/eslint-config": "workspace:*",
"@repo/typescript-config": "workspace:*",
"@types/json-bigint": "^1.0.4",
"@types/node": "^22.15.3",
"dpdm": "^3.14.0",
"eslint": "^9.31.0",
"dpdm": "^3.15.1",
"typescript": "5.8.2"
},
"dependencies": {
"@cardano-ogmios/client": "^6.11.0",
"@minswap/tiny-invariant": "^1.2.0",
"@minswap/felis-dex-v2": "workspace:*",
"@minswap/felis-ledger-core": "workspace:*",
"@minswap/felis-ledger-utils": "workspace:*",
"@minswap/felis-dex-v2": "workspace:*",
"@minswap/felis-provider": "workspace:*",
"@minswap/felis-sundaeswap-v1": "workspace:*",
"@minswap/felis-sundaeswap-v3": "workspace:*",
"@minswap/felis-syncer": "workspace:*",
"@minswap/felis-provider": "workspace:*",
"@minswap/felis-tx-builder": "workspace:*",
"@minswap/tiny-invariant": "^1.2.0",
"socket.io-client": "^4.8.3"
}
}
70 changes: 70 additions & 0 deletions apps/long-short-backend/documents/api-endpoints.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# API Endpoints

## Routes

| Endpoint | Method | Auth | Purpose |
|----------|--------|------|---------|
| `/health` | GET | No | Health check, silent logging |
| `/metadata` | GET | No | List all enabled markets + Liqwid APYs |
| `/position/get` | GET | No | Fetch user's open position by address |
| `/position/create` | POST | CIP-8 | Create new leveraged position |
| `/position/build-tx` | POST | CIP-8 | Build next transaction in order sequence |
| `/position/close` | POST | CIP-8 | Initiate closing of open position |
| `/liqwid/submit` | POST | CIP-8 | Submit signed Liqwid transaction |

## Authentication (CIP-8)

Authenticated endpoints require a signed payload:

```json
{
"data": { /* order data */ },
"user_address": "addr1q...",
"witness": {
"key": "a40101...",
"signature": "844da2..."
}
}
```

Verification flow (in `src/api/helper.ts`):
- SHA256 hash of `JSON.stringify(data)` must match the CIP-8 signature
- The `key` field is a COSEKey hex
- The `signature` field is a COSESign1 hex

## Endpoint Details

### GET /metadata

Returns all enabled market configs with Liqwid APY data. No authentication required.

### GET /position/get

Query params: `user_address`

Returns the user's open position (if any) for the given address.

### POST /position/create

Creates a new leveraged position. Inserts the position record and pre-creates all order steps for the position lifecycle.

- LONG positions: 4 opening orders (LONG_BUY, LONG_SUPPLY, LONG_BORROW, LONG_BUY_MORE)
- SHORT positions: 3 opening orders (SHORT_SUPPLY, SHORT_BORROW, SHORT_SELL)

### POST /position/build-tx

Core endpoint. Builds the next transaction in the order sequence:
1. Checks for waiting orders (awaiting on-chain confirmation)
2. Finds next unhandled order
3. Builds or rebuilds the transaction as needed
4. Returns `tx_raw` for client to sign and submit

### POST /position/close

Initiates closing of an open position. Creates closing order steps:
- LONG: LONG_SELL, LONG_REPAY, LONG_WITHDRAW, LONG_SELL_ALL
- SHORT: SHORT_BUY, SHORT_REPAY, SHORT_WITHDRAW

### POST /liqwid/submit

Submits a signed Liqwid transaction to the blockchain.
56 changes: 56 additions & 0 deletions apps/long-short-backend/documents/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Configuration & Environment

## Environment Variables

| Variable | Required | Default | Description |
|----------|----------|---------|-------------|
| `DATABASE_URL` | Yes | -- | PostgreSQL connection string |
| `CARDANOSCAN_API_KEY` | Yes | -- | Cardanoscan API key |
| `NETWORK` | No | `"mainnet"` | `"mainnet"` or `"testnet_preview"` |
| `API_PORT` | No | `9999` | HTTP server port |
| `API_HOST` | No | `0.0.0.0` | HTTP server host |

## Market Configuration

Loaded from the `market_config` database table at startup and cached in memory.

```typescript
type MarketConfig = {
marketId: string; // e.g. "ADA-NIGHT"
assetA: Asset; // Base asset (lovelace for ADA)
assetB: Asset; // Quote asset
ammLpAsset: string; // Minswap LP token
assetAQTokenTicker: string; // e.g. "qAda"
assetAQTokenRaw: string; // policyId of qToken
assetBQTokenTicker: string; // e.g. "qNIGHT"
assetBQTokenRaw: string;
longCollateralMarketId: string; // Liqwid market ID for long collateral
shortCollateralMarketId: string; // Liqwid market ID for short collateral
borrowMarketIdLong: string; // Liqwid market ID for long borrow
borrowMarketIdShort: string; // Liqwid market ID for short borrow
longLeverage: number; // e.g. 1.5
shortLeverage: number; // e.g. 0.5
minCollateral: bigint; // Minimum collateral in lovelace
enable: boolean;
};
```

Hot reload available via `reloadMarketConfigs(db)`.

## Dependencies

Key runtime dependencies:

- **Fastify** v5 -- HTTP server
- **Kysely** v0.28 -- PostgreSQL query builder / ORM
- **TypeBox** v0.34 -- JSON schema validation
- **pg** v8 -- PostgreSQL driver

Cardano-specific:
- `@cardano-ogmios/client` -- Ogmios client
- `@emurgo/cardano-message-signing-nodejs` -- CIP-8 message signing
- Felis workspace packages (see external-integrations.md)

## Docker

PostgreSQL and Redis are available via `docker-compose.yml` at the repo root.
86 changes: 86 additions & 0 deletions apps/long-short-backend/documents/database-schema.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
# Database Schema

Technology: **Kysely ORM + PostgreSQL**

Migrations located in `.config/migrations/` (12 migration files).

## Tables

### `position`

```sql
CREATE TABLE position (
id BIGSERIAL PRIMARY KEY,
market_id VARCHAR(64) NOT NULL, -- FK -> market_config.market_id
user_address VARCHAR(128) NOT NULL,
side VARCHAR(8) NOT NULL, -- LONG | SHORT
status VARCHAR(16) NOT NULL DEFAULT 'PENDING', -- PENDING | OPEN | CLOSING | CLOSED
amount_in NUMERIC NOT NULL, -- Collateral (lovelace)
amount_borrow NUMERIC NOT NULL, -- Borrow amount
created_at TIMESTAMP NOT NULL DEFAULT now(),
closed_at TIMESTAMP
);

-- Unique constraint: (user_address, market_id) WHERE closed_at IS NULL
-- Indexes: (market_id), (user_address), (closed_at)
```

### `order`

```sql
CREATE TABLE "order" (
id BIGSERIAL PRIMARY KEY,
position_id BIGINT NOT NULL,
order_type VARCHAR(32) NOT NULL, -- LONG_BUY, SHORT_SUPPLY, etc.
asset_in VARCHAR(128),
amount_in NUMERIC,
asset_out VARCHAR(128),
amount_out NUMERIC,
built_tx_id VARCHAR(64), -- Hash after local build
built_valid_to TIMESTAMP, -- Transaction expiry
created_tx_id VARCHAR(64), -- Hash when confirmed on-chain
created_tx_index INTEGER, -- Output index
waiting BOOLEAN DEFAULT FALSE -- True while awaiting confirmation
);

-- Indexes: (position_id), (created_tx_id)
```

### `market_config`

```sql
CREATE TABLE market_config (
market_id VARCHAR(64) PRIMARY KEY,
asset_a VARCHAR(128) NOT NULL,
asset_b VARCHAR(128) NOT NULL,
amm_lp_asset VARCHAR(128) NOT NULL,
asset_a_q_token_ticker VARCHAR(32) NOT NULL,
asset_a_q_token_raw VARCHAR(128) NOT NULL,
asset_b_q_token_ticker VARCHAR(32) NOT NULL,
asset_b_q_token_raw VARCHAR(128) NOT NULL,
long_collateral_market_id VARCHAR(64) NOT NULL,
short_collateral_market_id VARCHAR(64),
borrow_market_id_long VARCHAR(64),
borrow_market_id_short VARCHAR(64),
long_leverage NUMERIC NOT NULL,
short_leverage NUMERIC NOT NULL,
min_collateral NUMERIC NOT NULL,
enable BOOLEAN DEFAULT TRUE
);

-- Index: (enable)
```

## Migration Commands

```bash
pnpm --filter=long-short-backend run migrate:latest # Apply all pending migrations
pnpm --filter=long-short-backend run migrate:down # Rollback last migration
pnpm --filter=long-short-backend run codegen # Regenerate src/database/db.d.ts
```

## Notes

- Position IDs are `BIGSERIAL` -- always use `BigInt(row.id)` when mapping rows
- DB types are defined in `src/database/db.d.ts`, use `Generated<T>` for columns with defaults
- `json-bigint` is used for JSON serialization with BigInt values (never native `JSON.stringify`)
57 changes: 57 additions & 0 deletions apps/long-short-backend/documents/external-integrations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# External Integrations

## Cardanoscan Provider

**File:** `src/provider/cardanoscan.ts`

On-chain transaction queries to confirm built transactions.

- `getTransactionList(options)` -- Fetch transactions for an address

**URLs:**
- Mainnet: `https://api.cardanoscan.io/api/v1`
- Preview: `https://api-preview.cardanoscan.io/api/v1`

**Auth:** Header `apiKey: {CARDANOSCAN_API_KEY}`

**Note:** Use `address.toHex()` (not bech32) for Cardanoscan API calls.

## Liqwid V2 Provider

**Package:** `@minswap/felis-lending-market`

Lending protocol integration for supply, borrow, repay, and withdraw operations.

Key methods:
- `LiqwidProviderV2.Transactions.borrow()` -- Build borrow transaction
- `LiqwidProviderV2.Data.markets()` -- Fetch market APY data
- `LiqwidProviderV2.Data.loansForUser()` -- Fetch active loans

Legacy `LiqwidProvider` (V1) used for supply transactions.

## Minswap Aggregator

**File:** `src/provider/minswap-aggregator.ts`

Price estimation for swaps.

- `estimate(request)` -- GET swap output amount

**URLs:**
- Mainnet: `https://aggr-monorepo-mainnet-prod.minswap.org/aggregator/estimate`
- Preview: `https://aggr.dev-3.minswap.org/aggregator/estimate`

Used to estimate SHORT borrow amounts: `amount_in * shortLeverage` ADA worth of asset B.

## Felis Workspace Libraries

Core Cardano and trading infrastructure from the monorepo:

| Package | Purpose |
|---------|---------|
| `@minswap/felis-ledger-core` | Address, Asset, Utxo, NetworkEnvironment |
| `@minswap/felis-ledger-utils` | RustModule, Duration, Result, crypto |
| `@minswap/felis-tx-builder` | TxBuilder, CoinSelectionAlgorithm |
| `@minswap/felis-build-tx` | DEXOrderTransaction for Minswap orders |
| `@minswap/felis-dex-v2` | OrderV2, DexVersion, swap direction enums |
| `@minswap/felis-lending-market` | LiqwidProviderV2 for Liqwid lending |
41 changes: 41 additions & 0 deletions apps/long-short-backend/documents/overview.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# Long-Short Backend Overview

A **Fastify API** that orchestrates leveraged long/short positions on Cardano by coordinating DEX swaps (Minswap) with lending (Liqwid) through a multi-step order state machine.

## Architecture

```
src/
├── cmd/run-api.ts # Entry point
├── api/
│ ├── server.ts # Fastify setup & route registration
│ ├── routes/ # position, liqwid, metadata endpoints
│ ├── schemas.ts # TypeBox request/response schemas
│ ├── helper.ts # CIP-8 authentication
│ └── state-machine.ts # Order building & waiting logic (CORE)
├── config/market.ts # Market config loading & caching
├── database/ # Kysely types, Postgres, Redis
├── provider/ # Cardanoscan, Kupo, Minswap Aggregator
├── repository/ # Position, Order, MarketConfig repos
├── services/position-service.ts # Business logic orchestration
└── utils/ # Logger, CIP-8 signature, helpers
```

## Startup Sequence

Entry point: `src/cmd/run-api.ts`

1. Validate environment: `DATABASE_URL`, `CARDANOSCAN_API_KEY`, `NETWORK`
2. Parse network: `"mainnet"` -> MAINNET; else -> TESTNET_PREVIEW
3. Load WASM: `await RustModule.load()` (required for crypto operations)
4. Connect to PostgreSQL via `newKyselyClient(DATABASE_URL)`
5. Load market configs: `loadMarketConfigs(db)` -> in-memory cache
6. Initialize providers: CardanoscanProvider, MinswapAggregatorProvider
7. Start Fastify server: `createApiServer({ port, host, db, networkEnv, cardanoscanProvider })`

Start commands:

```bash
npm start # node --import tsx src/cmd/run-api.ts
npm run dev # node --import tsx --watch src/cmd/run-api.ts
```
Loading
Loading