From 2029c2c4d283d3c5c388fe9b69731ede2cd36671 Mon Sep 17 00:00:00 2001 From: Amin Moghaddam Date: Wed, 11 Dec 2024 14:35:57 -0800 Subject: [PATCH 1/3] feat: better per svm docs --- .../integrate-as-searcher/svm.mdx | 119 +++++++++++++----- .../express-relay/websocket-api-reference.mdx | 59 +++++++-- 2 files changed, 133 insertions(+), 45 deletions(-) diff --git a/pages/express-relay/integrate-as-searcher/svm.mdx b/pages/express-relay/integrate-as-searcher/svm.mdx index 6a56c893..b89a7ea3 100644 --- a/pages/express-relay/integrate-as-searcher/svm.mdx +++ b/pages/express-relay/integrate-as-searcher/svm.mdx @@ -4,43 +4,59 @@ import { Callout, Tabs, Steps } from "nextra/components"; SVM Express Relay searchers fulfill opportunities representing limit orders on the [Limo](https://solscan.io/account/LiMoM9rMhrdYrfzUCxQppvxCSG1FcrUK9G8uLq4A1GF) program. +Bids are represented via [SVM transactions](https://solana.com/docs/core/transactions) that include instructions for submitting the bid. + -### Subscribe to New Opportunities +### Subscribe to SVM chain -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. +Searchers can use [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 auction server. Searchers can also directly fetch available opportunities via HTTP or subscribe to them via WebSocket. - + + Polling the server via HTTP for new opportunities is not recommended because + of the additional delay it may incur. This can lead to late bid submissions + which will be rejected. Some submission information such as the most recent + blockhash and priority fees are **only** broadcasted via websocket. + - -Pyth provides a Typescript SDK, which allows searchers to subscribe to opportunities: + + ```typescript import { Client, Opportunity } from "@pythnetwork/express-relay-js"; +latestChainUpdate: Record = {} + const handleOpportunity = async (opportunity: Opportunity) => { - console.log("Received opportunity"); - // Implement your opportunity handler here +console.log("Received opportunity"); +// Implement your opportunity handler here }; +const svmChainUpdateHandler = async (update: SvmChainUpdate) { +// Store chain updates to use when constructing the transaction +latestChainUpdate[update.chainId] = update; +} + const client = new Client( - { baseUrl: "https://pyth-express-relay-mainnet.asymmetric.re" }, - undefined, // Default WebSocket options - handleOpportunity +{ baseUrl: "https://pyth-express-relay-mainnet.asymmetric.re" }, +undefined, // Default WebSocket options +handleOpportunity, +undefined, +svmChainUpdateHandler ); async function main() { - await client.subscribeChains(["solana"]); +await client.subscribeChains(["solana"]); } main(); -``` + +```` -Pyth provides a Python SDK, which allows searchers to subscribe to opportunities: - ```python copy import asyncio from express_relay.client import ( @@ -48,15 +64,22 @@ from express_relay.client import ( ) from express_relay.models import Opportunity +latest_chain_update = {} + async def opportunity_callback(opportunity: Opportunity): print("Received opportunity") # Implement your opportunity handler here +async def svm_chain_update_callback(svm_chain_update: SvmChainUpdate): + # Store chain updates to use when constructing the transaction + latest_chain_update[svm_chain_update.chain_id] = svm_chain_update + client = ExpressRelayClient( "https://pyth-express-relay-mainnet.asymmetric.re", None, opportunity_callback, None, + svm_chain_update_callback ) async def main(): @@ -66,7 +89,7 @@ async def main(): if __name__ == "__main__": asyncio.run(main()) -``` +```` @@ -77,8 +100,6 @@ curl -X 'GET' \ 'https://pyth-express-relay-mainnet.asymmetric.re/v1/opportunities?chain_id=solana&mode=live' ``` -Opportunities are short-lived and could be executed in a matter of seconds. So, the above endpoint could return an empty response. - 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: } ``` -Consult [`Websocket API reference`](./websocket-api-reference.mdx) for a complete list of methods and parameters. +Consult [`WebSocket API reference`](./websocket-api-reference.mdx) for a complete list of methods and parameters. The server responds with opportunities in the following format: -```bash copy +```json copy { - "order": "UxMUbQAsjrfQUp5stVwMJ6Mucq7VWTvt4ICe69BJ8lVXqwM+0sysV8OqZTdM0W4p...", // The Limo order to be executed, encoded in base64 - "order_address": "DUcTi3rDyS5QEmZ4BNRBejtArmDCWaPYGfN44vBJXKL5", // Address of the order account - "program": "limo", // Identifier of the program that the order exists in - "chain_id": "development-solana", - "version": "v1" // Opportunity format version + // The Limo order to be executed, encoded in base64 + "order": "UxMUbQAsjrfQUp5stVwMJ6Mucq7VWTvt4ICe69BJ8lVXqwM+0sysV8OqZTdM0W4p...", + // Address of the order account + "order_address": "DUcTi3rDyS5QEmZ4BNRBejtArmDCWaPYGfN44vBJXKL5", + // Identifier of the program that the order exists in + "program": "limo", + "chain_id": "solana", + // Opportunity format version + "version": "v1", + "opportunity_id": "271f2a7b-1ec5-420f-b5c0-e3e4317f3d7b", + // Creation time of the opportunity (in microseconds since the Unix epoch) + "creation_time": 1733503592579589, + "slot": 305802439 +} +``` + +The `order` field includes the [Limo](https://solscan.io/account/LiMoM9rMhrdYrfzUCxQppvxCSG1FcrUK9G8uLq4A1GF) program +order data that can be decoded using the SDKs provided or the program anchor idl. It includes all the necessary information +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) + + + Limo limit orders can also be filled partially in a linear fashion. For + example, if the order is to buy 10 SOL for \$2000, you can provide 5 SOL and + get back \$1000. + + +The auction server also broadcast chain specific information that are necessary for building the transaction: + +```json copy +{ + "type": "svm_chain_update", + "update": { + "chain_id": "development-solana", + // Recent blockhash that you can use when constructing the transaction + "blockhash": "6YK9yt6T1NhWNLhRGMapFxreYx6HPcW7RRcNSDsXjnLb", + // Latest prioritization fee that is necessary to include in the transaction + "latest_prioritization_fee": 319592 + } } ``` @@ -189,7 +243,7 @@ from solders.transaction import Transaction from express_relay.models.svm import BidSvm from express_relay.svm.limo_client import OrderStateAndAddress -DEADLINE = 2**62 +DEADLINE = 2 * 10**10 logger = logging.getLogger(__name__) async def assess_opportunity(self, opp: OpportunitySvm) -> BidSvm | None: @@ -242,18 +296,19 @@ The bid you construct will look like { // serialized transaction object, in base-64 encoding "transaction": "SGVsbG8sIFdvcmxkIQ==", - "chain_id": "solana", - "env": "svm" + "chain_id": "solana" } ``` -where the serialized transaction object should contain an Express Relay `SubmitBid` instruction that specifies the amount you are bidding and the permission details. +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. +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 +advertised via websocket. - It should contain valid signatures for all signers except the relayer - It should pass simulation ### Submit Bids on Opportunities to Express Relay Searchers can submit their constructed bids to Express Relay via the SDKs, an HTTP POST request, or a WebSocket connection. - + @@ -303,7 +358,7 @@ curl -X POST https://pyth-express-relay-mainnet.asymmetric.re/v1/bids \ -Searchers can submit bids via Websocket to avoid additional network round-trips and get notified about changes to the bid status. +Searchers can submit bids via WebSocket to avoid additional network round-trips and get notified about changes to the bid status. ```bash copy { @@ -322,7 +377,7 @@ A successful response to a bid submission has the following schema: ```bash copy { - "id": "1", // Websocket request id + "id": "1", // WebSocket request id "status": "success", "result": { "id": "beedbeed-b346-4fa1-8fab-2541a9e1872d", // Bid id @@ -331,7 +386,7 @@ A successful response to a bid submission has the following schema: } ``` -Consult [`Websocket API reference`](./websocket-api-reference.mdx) for more details. +Consult [`WebSocket API reference`](../websocket-api-reference.mdx) for more details. diff --git a/pages/express-relay/websocket-api-reference.mdx b/pages/express-relay/websocket-api-reference.mdx index dc04f1bb..315215bf 100644 --- a/pages/express-relay/websocket-api-reference.mdx +++ b/pages/express-relay/websocket-api-reference.mdx @@ -37,43 +37,76 @@ In case of error, the `status` field will be `error`, and the error message will } ``` -## Subscribing to opportunities +## Subscribing to chains -To subscribe to opportunities, you can send a request using the `chain_ids` parameter, which specifies the chains as an array. +To subscribe to opportunities and chain updates, you can send a request using the `chain_ids` parameter, which specifies the chains as an array. ```json { "id": "1", "method": "subscribe", "params": { - "chain_ids": ["op_sepolia"] + "chain_ids": ["solana"] } } ``` -After a successful subscription, you will receive new opportunities for the selected chains via the WebSocket in the following format: +To unsubscribe from a list of chains, you can send the following message: + +```json copy +{ + "id": "1", + "method": "unsubscribe", + "params": { + "chain_ids": ["solana"] + } +} +``` + +After a successful subscription, you will receive updates for the selected chains via the WebSocket in the following format: + + + + ```json { - "type": "new_opportunity", - "opportunity": {...} + "type": "new_opportunity", + "opportunity": {...} } ``` -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) + + -To unsubscribe from a list of chains, you can send the following message: +```json +{ + "type": "new_opportunity", + "opportunity": {...} +} +``` -```json copy +You will receive svm specific updates in the following format: + +```json { - "id": "1", - "method": "unsubscribe", - "params": { - "chain_ids": ["op_sepolia"] + "type": "svm_chain_update", + "update": { + "chain_id": "development-solana", + // Recent blockhash that you can use when constructing the transaction + "blockhash": "6YK9yt6T1NhWNLhRGMapFxreYx6HPcW7RRcNSDsXjnLb", + // Latest prioritization fee that is necessary to include in the transaction + "latest_prioritization_fee": 319592 } } ``` + + + + +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) + ## Submitting bids 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. From fd8c370377bfdfb044e074d834652894672cada4 Mon Sep 17 00:00:00 2001 From: Amin Moghaddam Date: Wed, 11 Dec 2024 14:56:26 -0800 Subject: [PATCH 2/3] fix indentations --- .../integrate-as-searcher/svm.mdx | 44 ++++++++++++------- 1 file changed, 27 insertions(+), 17 deletions(-) diff --git a/pages/express-relay/integrate-as-searcher/svm.mdx b/pages/express-relay/integrate-as-searcher/svm.mdx index b89a7ea3..38f03e30 100644 --- a/pages/express-relay/integrate-as-searcher/svm.mdx +++ b/pages/express-relay/integrate-as-searcher/svm.mdx @@ -30,25 +30,25 @@ import { Client, Opportunity } from "@pythnetwork/express-relay-js"; latestChainUpdate: Record = {} const handleOpportunity = async (opportunity: Opportunity) => { -console.log("Received opportunity"); -// Implement your opportunity handler here + console.log("Received opportunity"); + // Implement your opportunity handler here }; const svmChainUpdateHandler = async (update: SvmChainUpdate) { -// Store chain updates to use when constructing the transaction -latestChainUpdate[update.chainId] = update; + // Store chain updates to use when constructing the transaction + latestChainUpdate[update.chainId] = update; } const client = new Client( -{ baseUrl: "https://pyth-express-relay-mainnet.asymmetric.re" }, -undefined, // Default WebSocket options -handleOpportunity, -undefined, -svmChainUpdateHandler + { baseUrl: "https://pyth-express-relay-mainnet.asymmetric.re" }, + undefined, // Default WebSocket options + handleOpportunity, + undefined, + svmChainUpdateHandler ); async function main() { -await client.subscribeChains(["solana"]); + await client.subscribeChains(["solana"]); } main(); @@ -142,7 +142,10 @@ The server responds with opportunities in the following format: The `order` field includes the [Limo](https://solscan.io/account/LiMoM9rMhrdYrfzUCxQppvxCSG1FcrUK9G8uLq4A1GF) program order data that can be decoded using the SDKs provided or the program anchor idl. It includes all the necessary information -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) +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) Limo limit orders can also be filled partially in a linear fashion. For @@ -300,9 +303,14 @@ The bid you construct will look like } ``` -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. -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 -advertised via websocket. - It should contain valid signatures for all signers except the relayer - It should pass simulation +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. +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 +advertised via websocket. + - It should contain valid signatures for all signers except the relayer + - It should pass simulation ### Submit Bids on Opportunities to Express Relay @@ -331,7 +339,7 @@ const handleOpportunity = async (opportunity: Opportunity) => { The code snippet below demonstrates how to submit a bid using the Python SDK: -```python {5} copy +```python {8} copy import typing async def generate_bid(opp: OpportunitySvm) -> BidSvm: @@ -377,10 +385,12 @@ A successful response to a bid submission has the following schema: ```bash copy { - "id": "1", // WebSocket request id + // WebSocket request id + "id": "1", "status": "success", "result": { - "id": "beedbeed-b346-4fa1-8fab-2541a9e1872d", // Bid id + // Bid id + "id": "beedbeed-b346-4fa1-8fab-2541a9e1872d", "status": "OK" } } From 8ec2a65fdd783d8afea7d1a5180a563c3f7009eb Mon Sep 17 00:00:00 2001 From: Amin Moghaddam Date: Wed, 11 Dec 2024 15:00:52 -0800 Subject: [PATCH 3/3] fix indentations --- .../integrate-as-searcher/svm.mdx | 42 ++++++++++--------- 1 file changed, 23 insertions(+), 19 deletions(-) diff --git a/pages/express-relay/integrate-as-searcher/svm.mdx b/pages/express-relay/integrate-as-searcher/svm.mdx index 38f03e30..f5dc86d8 100644 --- a/pages/express-relay/integrate-as-searcher/svm.mdx +++ b/pages/express-relay/integrate-as-searcher/svm.mdx @@ -24,39 +24,41 @@ Searchers can also directly fetch available opportunities via HTTP or subscribe + ```typescript import { Client, Opportunity } from "@pythnetwork/express-relay-js"; latestChainUpdate: Record = {} const handleOpportunity = async (opportunity: Opportunity) => { - console.log("Received opportunity"); - // Implement your opportunity handler here + console.log("Received opportunity"); + // Implement your opportunity handler here }; const svmChainUpdateHandler = async (update: SvmChainUpdate) { - // Store chain updates to use when constructing the transaction - latestChainUpdate[update.chainId] = update; + // Store chain updates to use when constructing the transaction + latestChainUpdate[update.chainId] = update; } const client = new Client( - { baseUrl: "https://pyth-express-relay-mainnet.asymmetric.re" }, - undefined, // Default WebSocket options - handleOpportunity, - undefined, - svmChainUpdateHandler + { baseUrl: "https://pyth-express-relay-mainnet.asymmetric.re" }, + undefined, // Default WebSocket options + handleOpportunity, + undefined, + svmChainUpdateHandler ); async function main() { - await client.subscribeChains(["solana"]); + await client.subscribeChains(["solana"]); } main(); -```` +``` + ```python copy import asyncio from express_relay.client import ( @@ -89,7 +91,7 @@ async def main(): if __name__ == "__main__": asyncio.run(main()) -```` +``` @@ -143,6 +145,7 @@ The server responds with opportunities in the following format: The `order` field includes the [Limo](https://solscan.io/account/LiMoM9rMhrdYrfzUCxQppvxCSG1FcrUK9G8uLq4A1GF) program order data that can be decoded using the SDKs provided or the program anchor idl. It includes all the necessary information 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) @@ -304,13 +307,14 @@ The bid you construct will look like ``` 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. -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 -advertised via websocket. - - It should contain valid signatures for all signers except the relayer - - It should pass simulation + +- It should contain an Express Relay `SubmitBid` instruction that specifies the amount you are bidding, the permission details, and the deadline. + 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 + advertised via websocket. +- It should contain valid signatures for all signers except the relayer +- It should pass simulation ### Submit Bids on Opportunities to Express Relay