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

Add `GET /pegs` (`/v1/pegs`) endpoint reporting current and rolling-window worst peg deviations (in basis points) and provenance (venues, sample count, and freshness) across all configured pegged assets (`PEGGED_ASSETS_TESTNET`, `PEGGED_ASSETS_MAINNET`, `PEGGED_ASSETS`).
- Defaults to Circle `USDC` pegged to `USD:1.0` per network (`USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5=USD:1.0` on testnet and `USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN=USD:1.0` on mainnet) when `PEGGED_ASSETS*` is unset so deployed instances report the canonical USD stablecoin peg out of the box rather than returning an empty list.
- Shares `DEFAULT_PEG_TOLERANCE_BPS = 100` (100 bps = 1.0%) with `GET /snapshot` (`PEG_TOLERANCE_BPS = 100`) and the peg-deviation alerting engine (#322, default band = 0.01 = 100 bps).
- `GET /snapshot` consults `NetworkConfig.pegged` first when evaluating watched-pair pegs so configured targets and non-1.0 target prices never disagree between `/pegs` and `/snapshot`, while retaining its built-in 1:1 fiat stablecoin ticker map (`PEGGED_ASSETS`) as a fallback for unconfigured watched pairs and stablecoin-sibling reference quotes (`USDC/USDT`).
12 changes: 12 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,10 @@ SEP40_ENABLED_TESTNET=false
# 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, target price, and optional rolling lookback window (hours) on testnet.
# Format: "ASSET[:ISSUER]=TARGET:TARGET_PRICE[:WINDOW_HOURS]" (window defaults to 24h when omitted).
# Unconfigured assets are omitted from GET /pegs rather than reported at zero deviation.
PEGGED_ASSETS_TESTNET=USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5=USD:1.0
# Multi-hop routing configuration for testnet
ROUTING_MAX_HOPS_TESTNET=3
ROUTING_INTERMEDIATES_TESTNET=XLM:native,USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5
Expand Down Expand Up @@ -183,6 +187,11 @@ SEP40_PUBLISHER_SECRET_KEY_MAINNET=
SEP40_ENABLED_MAINNET=false
# Comma-separated pairs to watch on mainnet.
WATCHED_PAIRS_MAINNET=XLM:native/USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN
# Comma-separated pegged assets with explicit target, target price, and optional rolling lookback window (hours) on mainnet.
# Format: "ASSET[:ISSUER]=TARGET:TARGET_PRICE[:WINDOW_HOURS]" (window defaults to 24h when omitted).
# Example with multiple pegs and custom window:
# USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN=USD:1.0,EURC:GDHU6WRG4IEQXM5NZ4BMPKOXHW76MZM4Y2IEMFDVXBSDP6SJY4ITNPP2=EUR:1.0:24
PEGGED_ASSETS_MAINNET=USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN=USD:1.0
# Multi-hop routing configuration for mainnet
ROUTING_MAX_HOPS_MAINNET=3
ROUTING_INTERMEDIATES_MAINNET=XLM:native,USDC:GA5ZSEJYB37JRC5AVCIA5MOP4RHTM335X2KGX3IHOJAPP5RE34K4KZVN
Expand Down Expand Up @@ -211,6 +220,9 @@ DEFINDEX_FACTORY_ADDRESS=CDSCWE4GLNBYYTES2OCYDFQA2LLY4RBIAX6ZI32VSUXD7GO6HRPO4A3
# single-network testnet value cannot override mainnet's built-in factory.
PHOENIX_FACTORY_ADDRESS=
WATCHED_PAIRS=XLM:native/USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5
# Shared fallback for pegged assets when PEGGED_ASSETS_<NETWORK> is unset.
# Format: "ASSET[:ISSUER]=TARGET:TARGET_PRICE[:WINDOW_HOURS]"
PEGGED_ASSETS=USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5=USD:1.0
# Routing configuration fallbacks
ROUTING_MAX_HOPS=3
ROUTING_INTERMEDIATES=XLM:native,USDC:GBBD47IF6LWK7P7MDEVSCWR7DPUWV3NY3DTQEVFL4NAT4AQH3ZLLFLA5
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@ npx @modelcontextprotocol/inspector npm run mcp:server
| GET | `/v1/screener` | Screen pairs by volume, change, liquidity and price |
| GET | `/v1/movers` | Get biggest gainers, biggest losers, and volume movers |
| GET | `/v1/benchmark/:asset` | Get peg-deviation statistics for an asset against a target |
| GET | `/v1/pegs` | Get peg deviation and window statistics for all configured pegged assets |
| GET | `/v1/basket` | Compute the weighted price of a basket of assets |
| GET | `/v1/correlation` | Compute price correlation matrix across a basket of assets over a window |
| GET | `/v1/discovery/resources` | Browse the x402 Bazaar catalog of discoverable resources |
Expand Down
79 changes: 79 additions & 0 deletions clients/typescript/src/schema.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,23 @@ export interface paths {
patch?: never;
trace?: never;
};
"/v1/pegs": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/** Get peg deviation and window statistics for all configured pegged assets */
get: operations["getPegs"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/v1/basket": {
parameters: {
query?: never;
Expand Down Expand Up @@ -1907,6 +1924,30 @@ export interface components {
onChainAgeSeconds?: number | null;
cexAgeSeconds?: number | null;
};
PegReport: {
asset: string;
code: string;
issuer: string | null;
target: string;
targetPrice: number;
pairKey: string | null;
currentPrice: number | null;
currentDeviationBps: number | null;
worstDeviationBps: number | null;
window: string;
provenance: {
venues: string[];
sampleCount: number;
freshnessSeconds: number | null;
/** Format: date-time */
lastSampleTimestamp: string | null;
};
};
PegsResponse: {
/** @enum {string} */
network: "testnet" | "mainnet";
pegs: components["schemas"]["PegReport"][];
};
};
responses: {
/** @description Payment Required */
Expand Down Expand Up @@ -3591,6 +3632,44 @@ export interface operations {
};
};
};
getPegs: {
parameters: {
query?: {
/** @description Stellar network to answer for — `testnet` or `mainnet`. Defaults to this instance's configured `STELLAR_NETWORK`. An unrecognised value is rejected with 400. DB-backed reads are currently served from whichever network this instance indexes, even when another is requested. */
network?: components["parameters"]["NetworkQuery"];
};
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description List of configured pegged assets and their peg deviations */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["PegsResponse"];
};
};
/** @description Invalid network parameter */
400: {
headers: {
[name: string]: unknown;
};
content?: never;
};
429: components["responses"]["TooManyRequests"];
/** @description Internal server error */
500: {
headers: {
[name: string]: unknown;
};
content?: never;
};
};
};
getBasket: {
parameters: {
query: {
Expand Down
140 changes: 140 additions & 0 deletions openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2984,6 +2984,39 @@
}
}
},
"/v1/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"
}
}
}
},
"/v1/basket": {
"get": {
"operationId": "getBasket",
Expand Down Expand Up @@ -7684,6 +7717,113 @@
"nullable": true
}
}
},
"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"
}
}
}
}
}
}
Expand Down
Loading
Loading