Skip to content
Open
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
7 changes: 7 additions & 0 deletions .changeset/pegs-deviation-route.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
---
'lens': minor
---

Add `GET /pegs` endpoint reporting peg deviations and provenance across all configured pegged assets.
Targets and target prices are configured explicitly per network (omitting unconfigured assets).
Honours and echoes `?network=testnet|mainnet` and includes data provenance (venues, sample count, and freshness).
7 changes: 7 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,9 @@ REFLECTOR_CONTRACT_ID_TESTNET=
# Comma-separated pairs to watch on testnet.
# Format: "CODE:ISSUER/CODE:ISSUER". Use "native" for XLM.
WATCHED_PAIRS_TESTNET=XLM:native/USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5
# Comma-separated pegged assets with explicit target and peg price on testnet.
# Format: "ASSET[:ISSUER]=TARGET:TARGET_PRICE[:WINDOW_HOURS]"
PEGGED_ASSETS_TESTNET=USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5=USD:1.0

# --- Stellar Network — Mainnet ---
HORIZON_URL_MAINNET=https://horizon.stellar.org
Expand All @@ -99,6 +102,9 @@ AQUARIUS_ENABLED_MAINNET=true
REFLECTOR_CONTRACT_ID_MAINNET=
# Comma-separated pairs to watch on mainnet.
WATCHED_PAIRS_MAINNET=XLM:native/USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN
# Comma-separated pegged assets with explicit target and peg price on mainnet.
# Format: "ASSET[:ISSUER]=TARGET:TARGET_PRICE[:WINDOW_HOURS]"
PEGGED_ASSETS_MAINNET=USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN=USD:1.0

# --- Venue endpoint overrides (optional, shared across networks unless a
# paired _TESTNET / _MAINNET variant is set) ---
Expand All @@ -115,6 +121,7 @@ RPC_URL=https://soroban-testnet.stellar.org
NETWORK_PASSPHRASE=Test SDF Network ; September 2015
SOROSWAP_FACTORY_ADDRESS=CDP3HMUH6SMS3S7NPGNDJLULCOXXEPSHY4JKUKMBNQMATHDHWXRRJTBY
WATCHED_PAIRS=XLM:native/USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5
PEGGED_ASSETS=USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5=USD:1.0
# Reflector oracle contract ID on testnet (falls back from paired REFLECTOR_CONTRACT_ID_TESTNET)
REFLECTOR_CONTRACT_ID=

Expand Down
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ Aggregates price data from Stellar's Classic Order Book (SDEX) and AMM Liquidity
| GET | `/compare/:asset` | Lens vs configured oracle prices, with per-oracle deviation and availability |
| GET | `/screener?sortBy=volume&order=desc&limit=20` | Screen pairs by volume, 24h change, liquidity and price |
| GET | `/benchmark/:asset?target=USD` | Peg-deviation statistics versus a target |
| GET | `/pegs` | All configured pegged assets with current and worst deviation and provenance |
| GET | `/basket?asset=XLM&asset=USDC&weight=0.6&weight=0.4` | Weighted price of a basket of assets |
| GET | `/status` | Indexer health |
| GET | `/snapshot?network=testnet\|mainnet` | Whole-network snapshot: venues, TVL, 24h volume, top pairs, peg readings and data freshness in one cached, timestamped object |
Expand Down Expand Up @@ -59,7 +60,7 @@ ones listed below still read a single instance-wide network.
Per-request today: `/price/:assetA/:assetB` (its VWAP, OHLCV, AMM and
best-route reads), `/price/:assetA/:assetB/route`, `/price/:assetA/:assetB/depth`, `/book/:assetA/:assetB`,
`/prices/history`, `/screener`, `/pools`, `/pairs`, `/spreads/:asset`,
`/volumes/:asset`, `/status`, and the x402 payment `network`/`payTo`. That list
`/volumes/:asset`, `/pegs`, `/status`, and the x402 payment `network`/`payTo`. That list
is the same one `openapi.yaml` declares a `network` parameter on — if they ever
disagree, the spec is the thing to fix.

Expand Down
156 changes: 155 additions & 1 deletion openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -1539,6 +1539,7 @@
"name": "liquidity",
"in": "query",
"required": false,
"description": "Filter for pairs with total AMM pool liquidity greater than or equal to this value. Liquidity is defined as the sum across all active AMM pools for the pair of (reserve_a * spot_price + reserve_b), valued in the quote asset (asset_b) at the latest snapshot spot price.",
"schema": {
"type": "number"
}
Expand Down Expand Up @@ -1676,6 +1677,39 @@
}
}
},
"/pegs": {
"get": {
"operationId": "getPegs",
"summary": "Get peg deviation and window statistics for all configured pegged assets",
"security": [],
"parameters": [
{
"$ref": "#/components/parameters/NetworkQuery"
}
],
"responses": {
"200": {
"description": "List of configured pegged assets and their peg deviations",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/PegsResponse"
}
}
}
},
"400": {
"description": "Invalid network parameter"
},
"429": {
"$ref": "#/components/responses/TooManyRequests"
},
"500": {
"description": "Internal server error"
}
}
}
},
"/basket": {
"get": {
"operationId": "getBasket",
Expand Down Expand Up @@ -3624,7 +3658,12 @@
"type": "number"
},
"liquidity": {
"type": "number"
"type": "number",
"description": "Total liquidity across all active AMM pools for this pair on the network, computed as sum(reserve_a * spot_price + reserve_b), valued in the quote asset (asset_b) at each pool's latest snapshot spot price."
},
"liquidity_unit": {
"type": "string",
"description": "The asset symbol/identifier that liquidity is denominated in, corresponding to the counter/quote asset (asset_b) of the pair (the asset after the slash in pair_key)."
}
}
},
Expand All @@ -3638,6 +3677,14 @@
],
"additionalProperties": false,
"properties": {
"network": {
"type": "string",
"enum": [
"testnet",
"mainnet"
],
"description": "The network answered for."
},
"data": {
"type": "array",
"items": {
Expand Down Expand Up @@ -3720,6 +3767,113 @@
}
}
},
"PegReport": {
"type": "object",
"required": [
"asset",
"code",
"issuer",
"target",
"targetPrice",
"pairKey",
"currentPrice",
"currentDeviationBps",
"worstDeviationBps",
"window",
"provenance"
],
"additionalProperties": false,
"properties": {
"asset": {
"type": "string"
},
"code": {
"type": "string"
},
"issuer": {
"type": "string",
"nullable": true
},
"target": {
"type": "string"
},
"targetPrice": {
"type": "number"
},
"pairKey": {
"type": "string",
"nullable": true
},
"currentPrice": {
"type": "number",
"nullable": true
},
"currentDeviationBps": {
"type": "number",
"nullable": true
},
"worstDeviationBps": {
"type": "number",
"nullable": true
},
"window": {
"type": "string"
},
"provenance": {
"type": "object",
"required": [
"venues",
"sampleCount",
"freshnessSeconds",
"lastSampleTimestamp"
],
"additionalProperties": false,
"properties": {
"venues": {
"type": "array",
"items": {
"type": "string"
}
},
"sampleCount": {
"type": "integer"
},
"freshnessSeconds": {
"type": "integer",
"nullable": true
},
"lastSampleTimestamp": {
"type": "string",
"format": "date-time",
"nullable": true
}
}
}
}
},
"PegsResponse": {
"type": "object",
"required": [
"network",
"pegs"
],
"additionalProperties": false,
"properties": {
"network": {
"type": "string",
"enum": [
"testnet",
"mainnet"
]
},
"pegs": {
"type": "array",
"items": {
"$ref": "#/components/schemas/PegReport"
}
}
}
},
"BasketResponse": {
"type": "object",
"required": [
Expand Down
116 changes: 116 additions & 0 deletions openapi.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -1020,6 +1020,11 @@ paths:
- name: liquidity
in: query
required: false
description: >-
Filter for pairs with total AMM pool liquidity greater than or equal to this value.
Liquidity is defined as the sum across all active AMM pools for the pair of
(reserve_a * spot_price + reserve_b), valued in the quote asset (asset_b) at the
latest snapshot spot price.
schema:
type: number
- name: sortBy
Expand Down Expand Up @@ -1103,6 +1108,27 @@ paths:
'500':
description: Benchmark computation failed

/pegs:
get:
operationId: getPegs
summary: Get peg deviation and window statistics for all configured pegged assets
security: []
parameters:
- $ref: '#/components/parameters/NetworkQuery'
responses:
'200':
description: List of configured pegged assets and their peg deviations
content:
application/json:
schema:
$ref: '#/components/schemas/PegsResponse'
'400':
description: Invalid network parameter
'429':
$ref: '#/components/responses/TooManyRequests'
'500':
description: Internal server error

/basket:
get:
operationId: getBasket
Expand Down Expand Up @@ -2330,12 +2356,25 @@ components:
type: number
liquidity:
type: number
description: >-
Total liquidity across all active AMM pools for this pair on the network,
computed as sum(reserve_a * spot_price + reserve_b), valued in the quote
asset (asset_b) at each pool's latest snapshot spot price.
liquidity_unit:
type: string
description: >-
The asset symbol/identifier that liquidity is denominated in, corresponding
to the counter/quote asset (asset_b) of the pair (the asset after the slash in pair_key).

ScreenerResponse:
type: object
required: [data, nextCursor, hasMore, total]
additionalProperties: false
properties:
network:
type: string
enum: [testnet, mainnet]
description: The network answered for.
data:
type: array
items:
Expand Down Expand Up @@ -2386,6 +2425,83 @@ components:
sampleCount:
type: integer

PegReport:
type: object
required:
- asset
- code
- issuer
- target
- targetPrice
- pairKey
- currentPrice
- currentDeviationBps
- worstDeviationBps
- window
- provenance
additionalProperties: false
properties:
asset:
type: string
code:
type: string
issuer:
type: string
nullable: true
target:
type: string
targetPrice:
type: number
pairKey:
type: string
nullable: true
currentPrice:
type: number
nullable: true
currentDeviationBps:
type: number
nullable: true
worstDeviationBps:
type: number
nullable: true
window:
type: string
provenance:
type: object
required:
- venues
- sampleCount
- freshnessSeconds
- lastSampleTimestamp
additionalProperties: false
properties:
venues:
type: array
items:
type: string
sampleCount:
type: integer
freshnessSeconds:
type: integer
nullable: true
lastSampleTimestamp:
type: string
format: date-time
nullable: true

PegsResponse:
type: object
required: [network, pegs]
additionalProperties: false
properties:
network:
type: string
enum: [testnet, mainnet]
pegs:
type: array
items:
$ref: '#/components/schemas/PegReport'

BasketResponse:
type: object
required: [basketPrice, components, weightSum, computedAt]
Expand Down
Loading
Loading