Skip to content

Commit 2029c2c

Browse files
committed
feat: better per svm docs
1 parent 0135b24 commit 2029c2c

File tree

2 files changed

+133
-45
lines changed

2 files changed

+133
-45
lines changed

pages/express-relay/integrate-as-searcher/svm.mdx

Lines changed: 87 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -4,59 +4,82 @@ import { Callout, Tabs, Steps } from "nextra/components";
44

55
SVM Express Relay searchers fulfill opportunities representing limit orders on the [Limo](https://solscan.io/account/LiMoM9rMhrdYrfzUCxQppvxCSG1FcrUK9G8uLq4A1GF) program.
66

7+
Bids are represented via [SVM transactions](https://solana.com/docs/core/transactions) that include instructions for submitting the bid.
8+
79
<Steps>
810

9-
### Subscribe to New Opportunities
11+
### Subscribe to SVM chain
1012

11-
Express Relay provides searchers with [Typescript](https://github.com/pyth-network/per/tree/4be711525948cf24c0ebd4ebab007dc7f51b7069/sdk/js) and [Python](https://github.com/pyth-network/per/tree/4be711525948cf24c0ebd4ebab007dc7f51b7069/sdk/python) SDKs to interact with Express Relay.
13+
Searchers can use [Typescript](https://github.com/pyth-network/per/tree/4be711525948cf24c0ebd4ebab007dc7f51b7069/sdk/js)
14+
and [Python](https://github.com/pyth-network/per/tree/4be711525948cf24c0ebd4ebab007dc7f51b7069/sdk/python) SDKs to interact with auction server.
1215
Searchers can also directly fetch available opportunities via HTTP or subscribe to them via WebSocket.
1316

14-
<Tabs items={['Typescript', 'Python', 'HTTP', 'Websocket']}>
17+
<Callout type="warning" emoji="⚠️">
18+
Polling the server via HTTP for new opportunities is not recommended because
19+
of the additional delay it may incur. This can lead to late bid submissions
20+
which will be rejected. Some submission information such as the most recent
21+
blockhash and priority fees are **only** broadcasted via websocket.
22+
</Callout>
1523

16-
<Tabs.Tab>
17-
Pyth provides a Typescript SDK, which allows searchers to subscribe to opportunities:
24+
<Tabs items={['Typescript', 'Python', 'HTTP', 'WebSocket']}>
1825

26+
<Tabs.Tab>
1927
```typescript
2028
import { Client, Opportunity } from "@pythnetwork/express-relay-js";
2129

30+
latestChainUpdate: Record<string, SvmChainUpdate> = {}
31+
2232
const handleOpportunity = async (opportunity: Opportunity) => {
23-
console.log("Received opportunity");
24-
// Implement your opportunity handler here
33+
console.log("Received opportunity");
34+
// Implement your opportunity handler here
2535
};
2636

37+
const svmChainUpdateHandler = async (update: SvmChainUpdate) {
38+
// Store chain updates to use when constructing the transaction
39+
latestChainUpdate[update.chainId] = update;
40+
}
41+
2742
const client = new Client(
28-
{ baseUrl: "https://pyth-express-relay-mainnet.asymmetric.re" },
29-
undefined, // Default WebSocket options
30-
handleOpportunity
43+
{ baseUrl: "https://pyth-express-relay-mainnet.asymmetric.re" },
44+
undefined, // Default WebSocket options
45+
handleOpportunity,
46+
undefined,
47+
svmChainUpdateHandler
3148
);
3249

3350
async function main() {
34-
await client.subscribeChains(["solana"]);
51+
await client.subscribeChains(["solana"]);
3552
}
3653

3754
main();
38-
```
55+
56+
````
3957

4058
</Tabs.Tab>
4159
<Tabs.Tab>
42-
Pyth provides a Python SDK, which allows searchers to subscribe to opportunities:
43-
4460
```python copy
4561
import asyncio
4662
from express_relay.client import (
4763
ExpressRelayClient,
4864
)
4965
from express_relay.models import Opportunity
5066
67+
latest_chain_update = {}
68+
5169
async def opportunity_callback(opportunity: Opportunity):
5270
print("Received opportunity")
5371
# Implement your opportunity handler here
5472
73+
async def svm_chain_update_callback(svm_chain_update: SvmChainUpdate):
74+
# Store chain updates to use when constructing the transaction
75+
latest_chain_update[svm_chain_update.chain_id] = svm_chain_update
76+
5577
client = ExpressRelayClient(
5678
"https://pyth-express-relay-mainnet.asymmetric.re",
5779
None,
5880
opportunity_callback,
5981
None,
82+
svm_chain_update_callback
6083
)
6184
6285
async def main():
@@ -66,7 +89,7 @@ async def main():
6689
6790
if __name__ == "__main__":
6891
asyncio.run(main())
69-
```
92+
````
7093
7194
</Tabs.Tab>
7295
<Tabs.Tab>
@@ -77,8 +100,6 @@ curl -X 'GET' \
77100
'https://pyth-express-relay-mainnet.asymmetric.re/v1/opportunities?chain_id=solana&mode=live'
78101
```
79102

80-
Opportunities are short-lived and could be executed in a matter of seconds. So, the above endpoint could return an empty response.
81-
82103
</Tabs.Tab>
83104
<Tabs.Tab>
84105
Searchers can connect to the server via WebSocket to reduce latency and subscribe to various events. The WebSocket endpoint lives at `/v1/ws`(e.g `wss://pyth-express-relay-mainnet.asymmetric.re/v1/ws`).
@@ -94,20 +115,53 @@ Here is a sample JSON payload to subscribe to opportunities:
94115
}
95116
```
96117

97-
Consult [`Websocket API reference`](./websocket-api-reference.mdx) for a complete list of methods and parameters.
118+
Consult [`WebSocket API reference`](./websocket-api-reference.mdx) for a complete list of methods and parameters.
98119

99120
</Tabs.Tab>
100121
</Tabs>
101122

102123
The server responds with opportunities in the following format:
103124

104-
```bash copy
125+
```json copy
105126
{
106-
"order": "UxMUbQAsjrfQUp5stVwMJ6Mucq7VWTvt4ICe69BJ8lVXqwM+0sysV8OqZTdM0W4p...", // The Limo order to be executed, encoded in base64
107-
"order_address": "DUcTi3rDyS5QEmZ4BNRBejtArmDCWaPYGfN44vBJXKL5", // Address of the order account
108-
"program": "limo", // Identifier of the program that the order exists in
109-
"chain_id": "development-solana",
110-
"version": "v1" // Opportunity format version
127+
// The Limo order to be executed, encoded in base64
128+
"order": "UxMUbQAsjrfQUp5stVwMJ6Mucq7VWTvt4ICe69BJ8lVXqwM+0sysV8OqZTdM0W4p...",
129+
// Address of the order account
130+
"order_address": "DUcTi3rDyS5QEmZ4BNRBejtArmDCWaPYGfN44vBJXKL5",
131+
// Identifier of the program that the order exists in
132+
"program": "limo",
133+
"chain_id": "solana",
134+
// Opportunity format version
135+
"version": "v1",
136+
"opportunity_id": "271f2a7b-1ec5-420f-b5c0-e3e4317f3d7b",
137+
// Creation time of the opportunity (in microseconds since the Unix epoch)
138+
"creation_time": 1733503592579589,
139+
"slot": 305802439
140+
}
141+
```
142+
143+
The `order` field includes the [Limo](https://solscan.io/account/LiMoM9rMhrdYrfzUCxQppvxCSG1FcrUK9G8uLq4A1GF) program
144+
order data that can be decoded using the SDKs provided or the program anchor idl. It includes all the necessary information
145+
to fill the limit order: - Maker address - Input token and amount (what the maker is selling) - Output token and amount (what the maker is buying in exchange for the input token)
146+
147+
<Callout type="info">
148+
Limo limit orders can also be filled partially in a linear fashion. For
149+
example, if the order is to buy 10 SOL for \$2000, you can provide 5 SOL and
150+
get back \$1000.
151+
</Callout>
152+
153+
The auction server also broadcast chain specific information that are necessary for building the transaction:
154+
155+
```json copy
156+
{
157+
"type": "svm_chain_update",
158+
"update": {
159+
"chain_id": "development-solana",
160+
// Recent blockhash that you can use when constructing the transaction
161+
"blockhash": "6YK9yt6T1NhWNLhRGMapFxreYx6HPcW7RRcNSDsXjnLb",
162+
// Latest prioritization fee that is necessary to include in the transaction
163+
"latest_prioritization_fee": 319592
164+
}
111165
}
112166
```
113167

@@ -189,7 +243,7 @@ from solders.transaction import Transaction
189243
from express_relay.models.svm import BidSvm
190244
from express_relay.svm.limo_client import OrderStateAndAddress
191245
192-
DEADLINE = 2**62
246+
DEADLINE = 2 * 10**10
193247
logger = logging.getLogger(__name__)
194248
195249
async def assess_opportunity(self, opp: OpportunitySvm) -> BidSvm | None:
@@ -242,18 +296,19 @@ The bid you construct will look like
242296
{
243297
// serialized transaction object, in base-64 encoding
244298
"transaction": "SGVsbG8sIFdvcmxkIQ==",
245-
"chain_id": "solana",
246-
"env": "svm"
299+
"chain_id": "solana"
247300
}
248301
```
249302

250-
where the serialized transaction object should contain an Express Relay `SubmitBid` instruction that specifies the amount you are bidding and the permission details.
303+
The transaction submitted to the auction server as the bid should meet the following criteria: - It should contain an Express Relay `SubmitBid` instruction that specifies the amount you are bidding, the permission details, and the deadline.
304+
This instruction can be created via the SDKs. - The deadline specified in the `SubmitBid` instruction should be at least 5 seconds in the future. - It should contain an instruction to set the [transaction priority fee](https://solana.com/developers/guides/advanced/how-to-use-priority-fees#what-are-priority-fees). The priority fee should be at least as large the amount
305+
advertised via websocket. - It should contain valid signatures for all signers except the relayer - It should pass simulation
251306

252307
### Submit Bids on Opportunities to Express Relay
253308

254309
Searchers can submit their constructed bids to Express Relay via the SDKs, an HTTP POST request, or a WebSocket connection.
255310

256-
<Tabs items={['Typescript', 'Python', 'HTTP', 'Websocket']}>
311+
<Tabs items={['Typescript', 'Python', 'HTTP', 'WebSocket']}>
257312

258313
<Tabs.Tab>
259314

@@ -303,7 +358,7 @@ curl -X POST https://pyth-express-relay-mainnet.asymmetric.re/v1/bids \
303358
</Tabs.Tab>
304359
<Tabs.Tab>
305360

306-
Searchers can submit bids via Websocket to avoid additional network round-trips and get notified about changes to the bid status.
361+
Searchers can submit bids via WebSocket to avoid additional network round-trips and get notified about changes to the bid status.
307362

308363
```bash copy
309364
{
@@ -322,7 +377,7 @@ A successful response to a bid submission has the following schema:
322377

323378
```bash copy
324379
{
325-
"id": "1", // Websocket request id
380+
"id": "1", // WebSocket request id
326381
"status": "success",
327382
"result": {
328383
"id": "beedbeed-b346-4fa1-8fab-2541a9e1872d", // Bid id
@@ -331,7 +386,7 @@ A successful response to a bid submission has the following schema:
331386
}
332387
```
333388

334-
Consult [`Websocket API reference`](./websocket-api-reference.mdx) for more details.
389+
Consult [`WebSocket API reference`](../websocket-api-reference.mdx) for more details.
335390

336391
</Tabs.Tab>
337392
</Tabs>

pages/express-relay/websocket-api-reference.mdx

Lines changed: 46 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -37,43 +37,76 @@ In case of error, the `status` field will be `error`, and the error message will
3737
}
3838
```
3939

40-
## Subscribing to opportunities
40+
## Subscribing to chains
4141

42-
To subscribe to opportunities, you can send a request using the `chain_ids` parameter, which specifies the chains as an array.
42+
To subscribe to opportunities and chain updates, you can send a request using the `chain_ids` parameter, which specifies the chains as an array.
4343

4444
```json
4545
{
4646
"id": "1",
4747
"method": "subscribe",
4848
"params": {
49-
"chain_ids": ["op_sepolia"]
49+
"chain_ids": ["solana"]
5050
}
5151
}
5252
```
5353

54-
After a successful subscription, you will receive new opportunities for the selected chains via the WebSocket in the following format:
54+
To unsubscribe from a list of chains, you can send the following message:
55+
56+
```json copy
57+
{
58+
"id": "1",
59+
"method": "unsubscribe",
60+
"params": {
61+
"chain_ids": ["solana"]
62+
}
63+
}
64+
```
65+
66+
After a successful subscription, you will receive updates for the selected chains via the WebSocket in the following format:
67+
68+
<Tabs items={['EVM', 'SVM']}>
69+
70+
<Tabs.Tab>
5571

5672
```json
5773
{
58-
"type": "new_opportunity",
59-
"opportunity": {...}
74+
"type": "new_opportunity",
75+
"opportunity": {...}
6076
}
6177
```
6278

63-
The schema for the opportunity is similar to what’s returned in the [HTTP requests](https://pyth-express-relay-mainnet.asymmetric.re/docs#tag/opportunity/operation/get_opportunities)
79+
</Tabs.Tab>
80+
<Tabs.Tab>
6481

65-
To unsubscribe from a list of chains, you can send the following message:
82+
```json
83+
{
84+
"type": "new_opportunity",
85+
"opportunity": {...}
86+
}
87+
```
6688

67-
```json copy
89+
You will receive svm specific updates in the following format:
90+
91+
```json
6892
{
69-
"id": "1",
70-
"method": "unsubscribe",
71-
"params": {
72-
"chain_ids": ["op_sepolia"]
93+
"type": "svm_chain_update",
94+
"update": {
95+
"chain_id": "development-solana",
96+
// Recent blockhash that you can use when constructing the transaction
97+
"blockhash": "6YK9yt6T1NhWNLhRGMapFxreYx6HPcW7RRcNSDsXjnLb",
98+
// Latest prioritization fee that is necessary to include in the transaction
99+
"latest_prioritization_fee": 319592
73100
}
74101
}
75102
```
76103

104+
</Tabs.Tab>
105+
106+
</Tabs>
107+
108+
The schema for the opportunity is similar to what’s returned in the [HTTP requests](https://pyth-express-relay-mainnet.asymmetric.re/docs#tag/opportunity/operation/get_opportunities)
109+
77110
## Submitting bids
78111

79112
In addition to the HTTP methods, you can submit your bids via WebSocket in order to avoid additional network round trips and get notified about changes to your bid status.

0 commit comments

Comments
 (0)