Skip to content

Commit 7913e75

Browse files
committed
feat: OpenAPI docs & JS Client
1 parent 4cbd147 commit 7913e75

File tree

7 files changed

+2126
-0
lines changed

7 files changed

+2126
-0
lines changed

.cspell/custom-dictionary.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ Solana
1717
struct
1818
structs
1919
Unichain
20+
Redoc

README.md

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,97 @@ The API spec (TBD) for the off-chain service should be adhered to by all Relay P
162162

163163
Explorers should be updated to track execution requests and link to their designated provider via quoter address.
164164

165+
## API Documentation
166+
167+
The Messaging Executor provides HTTP APIs for interacting with the relay service. The API specification is defined using [TypeSpec](https://typespec.io/) and can be generated as OpenAPI documentation.
168+
169+
### Building API Documentation
170+
171+
```bash
172+
# Navigate to the api-docs directory
173+
cd api-docs
174+
175+
# Install dependencies
176+
npm install
177+
178+
# Compile TypeSpec to generate OpenAPI spec and JS client & models
179+
npm run compile
180+
# or
181+
tsp compile .
182+
```
183+
184+
This will generate:
185+
186+
- **OpenAPI Specification**: `tsp-output/schema/openapi.yaml`
187+
- **JavaScript Client & Models**: `tsp-output/clients/js/`
188+
189+
### Viewing API Documentation
190+
191+
After building, you can view the API documentation using:
192+
193+
1. **Swagger Editor** (Online)
194+
195+
- Visit https://editor.swagger.io/
196+
- Copy and paste the contents of `tsp-output/schema/openapi.yaml`
197+
198+
2. **Redoc** (Online)
199+
- Visit https://redocly.github.io/redoc/
200+
- Upload your `openapi.yaml` file
201+
202+
### API Endpoints
203+
204+
The API provides three main endpoints:
205+
206+
#### GET `/capabilities`
207+
208+
Returns the capabilities for all supported chains, including:
209+
210+
- Supported request prefixes (ERV1, ERN1, ERC1, ERC2)
211+
- Gas limits and message value limits
212+
- Supported destination chains
213+
214+
#### POST `/quote`
215+
216+
Generates a signed quote for cross-chain execution:
217+
218+
- **Request**: Source chain, destination chain, optional relay instructions
219+
- **Response**: Signed quote and estimated cost
220+
221+
#### POST `/status/tx`
222+
223+
Retrieves the status of an execution request:
224+
225+
- **Request**: Transaction hash and optional chain ID
226+
- **Response**: Relay transaction details including status, costs, and execution results
227+
228+
### Using the JavaScript Client
229+
230+
The generated JavaScript client can be used to interact with the API programmatically:
231+
232+
```javascript
233+
// Import the generated client
234+
import { Client } from "./api-docs/tsp-output/clients/js";
235+
236+
// Create a client instance
237+
const client = new Client({ baseUrl: "http://localhost:3000/v0" });
238+
239+
// Get capabilities
240+
const capabilities = await client.capabilities.list();
241+
242+
// Generate a quote
243+
const quote = await client.quote.create({
244+
srcChain: 1,
245+
dstChain: 2,
246+
relayInstructions: "0x...",
247+
});
248+
249+
// Check transaction status
250+
const status = await client.status.getTransaction({
251+
txHash: "0x...",
252+
chainId: 1,
253+
});
254+
```
255+
165256
### API / Database Schema
166257

167258
#### Off-Chain Quote

api-docs/.gitignore

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
# MacOS
2+
.DS_Store
3+
4+
# Default TypeSpec output
5+
tsp-output/
6+
dist/
7+
8+
# Dependency directories
9+
node_modules/

api-docs/main.tsp

Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
1+
import "@typespec/http";
2+
import "@typespec/openapi";
3+
4+
using Http;
5+
using OpenAPI;
6+
7+
@service()
8+
@info(#{
9+
title: "Messaging Executor API",
10+
version: "1.0.0",
11+
})
12+
@server("http://localhost:3000/v0", "Local development server")
13+
namespace MessagingExecutor;
14+
15+
// ============= Models =============
16+
17+
model Capabilities {
18+
@doc("Request prefixes enabled for given chain")
19+
requestPrefixes: string[];
20+
21+
@doc("Gas Drop-off limit, native chain token bigint represented as string")
22+
gasDropOffLimit?: string;
23+
24+
@doc("Maximum Gas limit")
25+
maxGasLimit?: string;
26+
27+
@doc("Max message value, native chain token bigint represented as string")
28+
maxMsgValue?: string;
29+
}
30+
31+
@doc("Object with chain IDs (Wormhole Chain ID) as keys and their capabilities as values")
32+
model CapabilitiesResponse is Record<Capabilities>;
33+
34+
model QuoteRequest {
35+
@doc("Source wormhole chain ID")
36+
srcChain: int32;
37+
38+
@doc("Destination wormhole chain ID")
39+
dstChain: int32;
40+
41+
@doc("Optional hex-encoded relay instructions for cost estimation")
42+
relayInstructions?: string;
43+
}
44+
45+
model QuoteResponse {
46+
@doc("Hex-encoded signed quote")
47+
signedQuote: string;
48+
49+
@doc("Estimated cost in source chain native token units")
50+
estimatedCost?: string;
51+
}
52+
53+
model StatusRequest {
54+
@doc("Wormhole chain ID for the transaction")
55+
chainId?: int32;
56+
57+
@doc("Transaction hash")
58+
txHash: string;
59+
}
60+
61+
model TxInfo {
62+
@doc("Relayed transaction hash")
63+
txHash: string;
64+
65+
@doc("Wormhole chain ID for the transaction")
66+
chainId: int32;
67+
68+
@doc("Block number (or slot/epoch)")
69+
blockNumber: string;
70+
71+
@doc("Block time as timestamp")
72+
blockTime?: string;
73+
74+
@doc("Relay cost in native token units (wei/lamports/etc)")
75+
cost: string;
76+
}
77+
78+
model RequestForExecution {
79+
@doc("Public key of Quoter")
80+
quoterAddress: string;
81+
82+
@doc("Amount paid for the relay in source chain native token")
83+
amtPaid: string;
84+
85+
@doc("Destination Wormhole chain ID")
86+
dstChain: int32;
87+
88+
@doc("Destination address to execute the transaction")
89+
dstAddr: string;
90+
91+
@doc("Refund address for excess payment")
92+
refundAddr: string;
93+
94+
@doc("Hex-encoded signed quote bytes")
95+
signedQuoteBytes: string;
96+
97+
@doc("Hex-encoded request bytes")
98+
requestBytes: string;
99+
100+
@doc("Hex-encoded relay instruction bytes")
101+
relayInstructionsBytes: string;
102+
103+
@doc("Timestamp when the request was created")
104+
timestamp: string;
105+
}
106+
107+
model SignedQuote {
108+
@doc("Quote details")
109+
quote: {
110+
@doc("Quote prefix identifier (e.g., 'EQ01')")
111+
prefix: string;
112+
113+
@doc("Address of the quoter providing the quote")
114+
quoterAddress: string;
115+
116+
@doc("Address that will receive payment")
117+
payeeAddress: string;
118+
119+
@doc("Source Wormhole chain ID")
120+
srcChain: int32;
121+
122+
@doc("Destination Wormhole chain ID")
123+
dstChain: int32;
124+
125+
@doc("Quote expiration time")
126+
expiryTime: string;
127+
128+
@doc("Base fee for the relay")
129+
baseFee: string;
130+
131+
@doc("Gas price on destination chain")
132+
dstGasPrice: string;
133+
134+
@doc("Source chain token price")
135+
srcPrice: string;
136+
137+
@doc("Destination chain token price")
138+
dstPrice: string;
139+
};
140+
141+
@doc("Cryptographic signature of the quote")
142+
signature?: string;
143+
}
144+
145+
// Request instruction variants based on prefix
146+
model ERV1Request {
147+
@doc("Request prefix identifier")
148+
prefix: "ERV1";
149+
150+
@doc("Chain ID")
151+
chain: int32;
152+
153+
@doc("Contract address")
154+
address: string;
155+
156+
@doc("Sequence number")
157+
sequence: string;
158+
}
159+
160+
model ERN1Request {
161+
@doc("Request prefix identifier")
162+
prefix: "ERN1";
163+
164+
@doc("Source chain ID")
165+
srcChain: int32;
166+
167+
@doc("Source manager address")
168+
srcManager: string;
169+
170+
@doc("Message identifier")
171+
messageId: string;
172+
}
173+
174+
model ERC1Request {
175+
@doc("Request prefix identifier")
176+
prefix: "ERC1";
177+
178+
@doc("Source domain")
179+
sourceDomain: int32;
180+
181+
@doc("Nonce value")
182+
nonce: string;
183+
}
184+
185+
model ERC2Request {
186+
@doc("Request prefix identifier")
187+
prefix: "ERC2";
188+
189+
@doc("CCTP V2 request configuration")
190+
cctpV2Request: {
191+
@doc("CCTP V2 request prefix mode")
192+
cctpV2RequestPrefix: string;
193+
};
194+
}
195+
196+
@doc("Request instruction details that vary by prefix type")
197+
model RequestInstruction {
198+
@doc("The request details, structure depends on the prefix type")
199+
request: ERV1Request | ERN1Request | ERC1Request | ERC2Request;
200+
}
201+
202+
model RelayTransaction {
203+
@doc("Wormhole chain ID where the transaction originated")
204+
chainId: int32;
205+
206+
@doc("Estimated cost for the relay in source chain native token")
207+
estimatedCost: string;
208+
209+
@doc("Unique identifier for the relay transaction")
210+
id: string;
211+
212+
@doc("Timestamp when the transaction was indexed")
213+
indexedAt: string;
214+
215+
@doc("Parsed relay instruction details")
216+
instruction?: RequestInstruction;
217+
218+
@doc("Original request for execution details")
219+
requestForExecution: RequestForExecution;
220+
221+
@doc("Signed quote used for this transaction")
222+
signedQuote: SignedQuote;
223+
224+
@doc("Current status of the relay (pending, submitted, failed, etc.)")
225+
status: string;
226+
227+
@doc("Original transaction hash")
228+
txHash: string;
229+
230+
@doc("Reason for failure if status is 'failed'")
231+
failureCause?: string;
232+
233+
@doc("List of destination chain transactions executed")
234+
txs?: TxInfo[];
235+
}
236+
237+
@error
238+
model ErrorResponse {
239+
message: string;
240+
statusCode?: int32;
241+
}
242+
243+
// ============= Interfaces =============
244+
245+
@route("/capabilities")
246+
@tag("Capabilities")
247+
interface CapabilitiesInterface {
248+
@summary("Get capabilities for all supported chains")
249+
@doc("Retrieves the supported features and capabilities for all supported blockchains (as wormhole chain ID)")
250+
@get
251+
list(): CapabilitiesResponse | ErrorResponse;
252+
}
253+
254+
@route("/quote")
255+
@tag("Quote")
256+
interface QuoteInterface {
257+
@summary("Generate a quote for cross-chain transaction")
258+
@doc("Provides a signed quote for cross-chain relay with optional cost estimation")
259+
@post
260+
create(@body body: QuoteRequest): QuoteResponse | ErrorResponse;
261+
}
262+
263+
@route("/status")
264+
@tag("Status")
265+
interface StatusInterface {
266+
@summary("Get status of a transaction")
267+
@doc("Retrieves the status of a request for execution transaction and potentially initiates processing (when first time statusing and chain ID provided)")
268+
@route("tx")
269+
@post
270+
getTransaction(@body body: StatusRequest): RelayTransaction[] | ErrorResponse;
271+
}

0 commit comments

Comments
 (0)