Skip to content

Commit 6b793db

Browse files
committed
finish up docs add of mkt orders
1 parent 2fb32b6 commit 6b793db

File tree

7 files changed

+131
-58
lines changed

7 files changed

+131
-58
lines changed
240 KB
Loading

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

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,17 @@ Searchers can integrate with Express Relay in three steps:
1414
2. Construct the bid
1515
3. Submit the bid to Express Relay.
1616

17-
Searchers can integrate with Express Relay on Solana Virtual Machine (SVM) chains.
17+
Searchers can integrate with Express Relay on Solana Virtual Machine (SVM) chains to fulfill [market order](./integrate-as-searcher/market-orders.mdx) opportunities as well as [limit orders](./integrate-as-searcher/limit-orders.mdx) on the [Limo](https://solscan.io/account/LiMoM9rMhrdYrfzUCxQppvxCSG1FcrUK9G8uLq4A1GF) program.
1818

1919
<Cards>
2020
<Card
2121
icon={<CodeIcon />}
22-
title="Integrate with Express Relay on SVM Chains"
23-
href="/express-relay/integrate-as-searcher/svm"
22+
title="Integrate with Market Orders"
23+
href="/express-relay/integrate-as-searcher/market-orders"
24+
/>
25+
<Card
26+
icon={<CodeIcon />}
27+
title="Integrate with Limit Orders"
28+
href="/express-relay/integrate-as-searcher/limit-orders"
2429
/>
2530
</Cards>
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"svm": "Integrate on SVM chains",
3-
"market-orders": "Integrate on Market Orders"
2+
"market-orders": "Integrate on Market Orders",
3+
"limit-orders": "Integrate on Limit Orders"
44
}

pages/express-relay/integrate-as-searcher/svm.mdx renamed to pages/express-relay/integrate-as-searcher/limit-orders.mdx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
import { Callout, Tabs, Steps } from "nextra/components";
22

3-
# SVM Searcher Integration
4-
5-
SVM Express Relay searchers fulfill market order opportunities as well as limit orders on the [Limo](https://solscan.io/account/LiMoM9rMhrdYrfzUCxQppvxCSG1FcrUK9G8uLq4A1GF) program.
3+
# Limit Orders Integration
64

75
<Steps>
86

pages/express-relay/integrate-as-searcher/market-orders.mdx

Lines changed: 105 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -7,81 +7,133 @@ Unlike limit orders, market orders are created by users and need instant fulfilm
77
### Request Flow
88

99
The following diagram illustrates the flow of a market order:
10-
![Market Orders Flow](./market-orders-flow.webp)
10+
![Market Orders Flow](images/express_relay/market-orders-flow.webp)
1111

1212
### Opportunity Structure
1313

14-
Quote requests are broadcasted through the same websocket channel used for receiving other opportunities. Here is a sample payload:
14+
Quote requests are broadcast through the same WebSocket channel used for receiving other opportunities. To receive market order quote requests, you should subscribe to WebSocket updates; see the [WebSocket API reference](../websocket-api-reference.mdx) for more details.
15+
16+
Here is a sample payload:
1517

1618
```jsx
1719
{
18-
"opportunity_id": "44382da2-5971-453c-b647-2ba2317c7d56",
19-
"creation_time": 1737219684914126,
20-
"version": "v1",
21-
"program": "swap",
22-
"user_wallet_address": "FuBFy9TKJWxQdvramP8WTfvhVSm1h5dPiveTpyjAtwPd",
23-
"permission_account": "DExvQJLGprLLHuNcoRgALtn24nqSc99yuuKexza768s8",
24-
"router_account": "3hv8L8UeBbyM3M25dF3h2C5p8yA4FptD7FFZu4Z1jCMn",
25-
"referral_fee_bps": 0,
26-
"platform_fee_bps": 10,
27-
"fee_token": "searcher_token",
28-
"tokens": {
29-
"side_specified": "user",
30-
"searcher_token": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
31-
"user_token": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
32-
"user_amount": 1000245,
33-
"user_amount_including_fees": 1000245,
34-
"token_program_searcher": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
35-
"token_program_user": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
20+
'opportunity_id': '27dc5825-5b7f-4146-b00d-76be08a7139c',
21+
'creation_time': 1751077051829889,
22+
'version': 'v1',
23+
'program': 'swap',
24+
'user_wallet_address': '64Z52fmBUqAL6MFdC8garjrbvnV1w8iFGnGLWWbieYhc',
25+
'user_mint_user_balance': 99999999979999552,
26+
'permission_account': 'Hsto7eviBUvgFNS4KWRtVYRMnCcMHYEYMuq6YmBLkNJL',
27+
'router_account': '69ib85nGQS2Hzr4tQ8twbkGh76gKFUfWJFeJfQ37R3hW',
28+
'referral_fee_bps': 0,
29+
'referral_fee_ppm': 0,
30+
'platform_fee_bps': 10,
31+
'platform_fee_ppm': 1000,
32+
'fee_token': 'user_token',
33+
'tokens': {
34+
'side_specified': 'user',
35+
'searcher_token': 'EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v',
36+
'user_token': 'Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB',
37+
'user_amount': 2218,
38+
'user_amount_including_fees': 2220,
39+
'token_program_searcher': 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA',
40+
'token_program_user': 'TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA'
3641
},
37-
"token_account_initialization_configs": {
38-
"user_ata_mint_searcher": "user_payer",
39-
"user_ata_mint_user": "unneeded",
40-
"router_fee_receiver_ta": "unneeded",
41-
"relayer_fee_receiver_ata": "searcher_payer",
42-
"express_relay_fee_receiver_ata": "searcher_payer"
42+
'token_account_initialization_configs': {
43+
'user_ata_mint_searcher': 'user_payer',
44+
'user_ata_mint_user': 'unneeded',
45+
'router_fee_receiver_ta': 'unneeded',
46+
'relayer_fee_receiver_ata': 'unneeded',
47+
'express_relay_fee_receiver_ata': 'unneeded'
4348
},
44-
"chain_id": "solana"
49+
'memo': 'memo',
50+
'cancellable': True,
51+
'minimum_deadline': 1751077057,
52+
'profile_id': None,
53+
'chain_id': 'solana'
4554
}
4655
```
4756

48-
In this example the user with wallet address `FuBFy9TKJWxQdvramP8WTfvhVSm1h5dPiveTpyjAtwPd` wants to sell 1.000000 USDT (`Es9vMF…`) in exchange for the maximum USDC (`EPjFWdd…`) possible.
57+
The user token refers to the token that the user puts in to the trade, while the searcher token refers to the token that the searcher contributes to the trade. In this example the user with wallet address `64Z52fmBUqAL6MFdC8garjrbvnV1w8iFGnGLWWbieYhc` wants to sell 1.000000 USDT (`Es9vMF…`) in exchange for the maximum USDC (`EPjFWdd…`) possible.
4958

50-
The `side_specified` field in the opportunity shows whether the user has specified a certain amount for the token they are selling(`side_specified=user`) or the token they are buying(`side_specified=searcher`). Here is a sample payload when `side_specified` is `searcher`:
59+
The `side_specified` field in the opportunity shows whether the user has specified a certain amount for the token they are selling (`side_specified="user"`) or the token they are buying (`side_specified="searcher"`). Here is a sample payload when `side_specified` is `searcher`:
5160

5261
```jsx
5362
"tokens": {
5463
"side_specified": "searcher",
5564
"searcher_token": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
5665
"user_token": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
57-
"searcher_amount": 1000000,
66+
"searcher_amount": 4023,
5867
"token_program_searcher": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
5968
"token_program_user": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
6069
}
6170
```
6271

63-
This means the user wants to the send the least amount of USDT (`Es9vMF…`) to receive exactly 1.000000 USDC (`EPjFWdd…`).
72+
This means the user wants to receive the best possible quote--i.e. send the least amount of USDT (`Es9vMF…`)--to receive exactly 1.000000 USDC (`EPjFWdd…`).
6473

6574
Other fields necessary for constructing the transaction include:
6675

76+
- `user_wallet_address` The address of the counterparty in the trade. Should be included as a signer in the Express Relay program instruction
6777
- `fee_token` Whether the fees will be deducted from the searcher token or the user token
68-
69-
When `fee_token` is set to `user_token` the fees will be deducted from the token users provide. In this case `user_amount_including_fees > user_amount` but searchers will receive the `user_amount` in the end. You need still need to provide `user_amount_including_fees` to the contract as parameters for correct validation (SDK handles this automatically)
70-
7178
- `router_account` The account receiving the referral fees
72-
- `referral_fee_bps` Amount of fees deduced from the swap in basis points
79+
- `permission_account` The account to be specified as the permission in the Express Relay program instruction
80+
- `referral_fee_bps` Amount of referral fees deducted from the swap in basis points
81+
- `referral_fee_ppm` Amount of referral fees deducted from the swap in parts per million (1 basis point = 100 parts per million)
82+
- `platform_fee_bps` Amount of platform fees deducted from the swap in basis points
83+
- `platform_fee_ppm` Amount of platform fees deducted from the swap in parts per million
7384
- `token_account_initialization_configs` Specifies which token accounts are required to be created in this transaction. Each of the internal fields represent one of the token accounts that potentially needs to be created.
7485
The value for each field can be one of the following: - `unneeded`: This token account already exists and no creation instructions are needed - `user_payer`: This token account should be created and the account storage can be paid by the user - `searcher_payer`: This token account should be created and the account storage must be paid by the searcher
86+
- `memo` A field indicating a string that should be included via a memo program instruction in the submitted transaction (SDK handles this automatically)
87+
- `cancellable` A boolean indicating whether the searcher's quote can be cancelled
88+
- `minimum_deadline` The minimum acceptable deadline for the quote, in seconds since the Unix epoch. The transaction must have a deadline greater than this value.
89+
90+
#### Fee Token Dynamics
91+
92+
There are four possible combinations of `side_specified` and `fee_token`, and the way searchers should handle each case differs. We cover them here for clarity:
93+
94+
1. **side_specified = "user", fee_token = "user_token"**
95+
96+
- The user specifies the amount of tokens they want to sell.
97+
- Fees are deducted from the user's token.
98+
- `user_amount_including_fees` is greater than `user_amount`.
99+
- The searcher receives `user_amount`.
100+
- You need still need to provide `user_amount_including_fees` to the contract as parameters for correct validation (SDK handles this automatically).
101+
102+
2. **side_specified = "user", fee_token = "searcher_token"**
103+
104+
- The user specifies the amount of tokens they want to sell.
105+
- Fees are deducted from the searcher's token.
106+
- `user_amount_including_fees` equals `user_amount`.
107+
- The searcher receives `user_amount`.
108+
109+
3. **side_specified = "searcher", fee_token = "user_token"**
110+
111+
- The user specifies the amount of tokens they want to receive.
112+
- Fees are deducted from the user's token.
113+
- The searcher provides the exact `searcher_amount`.
114+
- The searcher's quote should include the fact that fees will be taken from the user's token. For example, if the searcher is willing to receive 1000 tokens but there is a total fee of 10 bps, the searcher should quote 1001 tokens to include the 1 token fee.
115+
- Here, the searcher needs to specify in the Express Relay contract instruction the total amount that the user should pay inclusive of fees (SDK handles this automatically).
116+
117+
4. **side_specified = "searcher", fee_token = "searcher_token"**
118+
119+
- The user specifies the amount of tokens they want to receive.
120+
- Fees are deducted from the searcher's token.
121+
- The opportunity broadcast from the server will already include fees in the `searcher_amount` shown to searchers. So the searcher just needs to provide exactly `searcher_amount`.
122+
- The user receives the exact requested amount.
123+
124+
Again, the SDK handles all four of these cases; refer to the logic and documentation therein.
125+
126+
### Bid Submission
75127

76128
The transaction containing the bid should include the following instructions:
77129

78-
- A `setComputeUnitPrice` instruction to adjust priority fees (similar to limit order bids)
79-
- A set of instructions ensuring that the fee token accounts for the `user wallet` , `router`, `searcher` and the `protocol` exist. You can use the `createAssociatedTokenAccountIdempotentInstruction` instruction in the spl-token library to do this.
80-
- A single `swap` instruction calling the Pyth Express Relay program with the necessary accounts and data. If the input amount is already specified by user, you need to set the output amount and vice versa. This would essentially represent your bid or quote for the request.
130+
- A `setComputeUnitPrice` instruction to adjust priority fees (similar to limit order bids). Priority fees should be set in line with the [prioritization fee updates](../websocket-api-reference.mdx#prioritization-fees) broadcast from the server via WebSocket.
131+
- A set of instructions ensuring that the fee token accounts for the `user_wallet_address` , `router`, `searcher` and the `protocol` exist, as needed. You can use the `createAssociatedTokenAccountIdempotentInstruction` instruction in the spl-token library to do this.
132+
- A single `swap_v2` instruction calling the Pyth Express Relay program with the necessary accounts and data. If the input amount is already specified by user, you need to set the output amount and vice versa. This would essentially represent your bid or quote for the request.
81133

82-
The SDKs provided will help you constructing these instructions. Please note that at the moment, these are the only instructions permitted in an swap transaction; if other types of instructions are included, the bid will not be accepted. This is for security reasons, and if you find that you need to use a different instruction not listed above please inform us. All calculations for fee distribution are done on-chain and nothing should be implemented in the SDKs or in your code.
134+
The SDKs provided will help you construct these instructions. Please note that at the moment, these are the only instructions permitted in an swap transaction; if other types of instructions are included, the bid will not be accepted. This is for security reasons, and if you find that you need to use a different instruction not listed above please inform us.
83135

84-
The schema for swap bids include a partially signed transaction (with searcher as the fee payer, i.e. the first signature is the searchers) along with the `opportunity_id` received:
136+
The schema for swap bids includes a partially signed transaction (with searcher as the fee payer, i.e. the first signature is the searchers) along with the `opportunity_id` received:
85137

86138
```jsx
87139
{
@@ -92,30 +144,33 @@ The schema for swap bids include a partially signed transaction (with searcher a
92144
}
93145
```
94146

95-
You must include `opportunity_id` and `type` field in bids related to market orders.
147+
You must include `opportunity_id` and `type` field in bids related to market orders. Once the opportunity is created and broadcast, there is a window of 250 milliseconds that the server waits to receive bids. You must submit your bid within that window for it to be considered in the auction. Otherwise, if your bid arrives too late, the opportunity corresponding to the `opportunity_id` you provide will have expired, and you will receive the error message `No swap opportunity with the given id found`.
96148

97149
The winner bid will be communicated to the users for the final signature and on-chain submission.
98150

99151
### Status Updates
100152

101-
Bid status notifications will be sent in the same websocket channel. The bid status can have the following values:
153+
Bid status notifications will be sent in the same WebSocket channel. The bid status can have the following values:
102154

103155
- `lost`: Bid was not high enough and another searcher won the auction.
104156
- `won` : Bid was submitted on-chain and is now confirmed and successful.
105-
- `failed`: Bid was submitted on-chain and is now confirmed but the transaction failed.
106-
- `expired`: Bid couldn’t land on-chain due to congestion.
157+
- `failed`: Bid was submitted on-chain but the transaction failed.
158+
- `expired`: Bid didn't land on-chain, either because the user didn't submit their signature or due to congestion.
107159
- `awaiting_signature`: Bid won the auction and is sent to the user. Waiting for user to sign the transaction.
108-
- `submitted`: User has signed the transaction and is now being submitted on-chain.
109-
- `sent_to_user_for_submission`: Waiting for the user to sign the transaction and submit it directly (only or non-cancellable requests).
160+
- `submitted`: User has signed the transaction, and the transaction is now being submitted on-chain.
161+
- `sent_to_user_for_submission`: Waiting for the user to sign the transaction and submit it directly (only for non-cancellable requests).
110162
- `submission_failed_cancelled`: The user submitted the signature but the searcher submitted a cancellation in the meantime, so the transaction will not be submitted on-chain. This will count towards the cancellation ratio of searchers.
111163
- `submission_failed_deadline_passed`: The user submitted the signature but it already passed the specified deadline, so it will not be submitted on-chain.
112164

165+
Below is a thorough transition diagram that details the interactions among the different statuses.
166+
167+
![B](images/express_relay/bid_status_diagram.png)
168+
113169
### Cancelling Bids
114170

115-
Searchers are able to cancel their submitted bids for cancellable quotes as long as
116-
it’s not signed by the user (before step 6). This feature increases flexibility for more competitive pricing without additional risk of adverse selection.
117-
This functionality is available via the `cancel_bid` api.
118-
Here is a sample payload that can be sent via websocket:
171+
Searchers are able to cancel their submitted bids for quotes marked with `'cancellable': True` as long as the transactions are not signed by the user (before step 6 in the flow diagram above). This feature increases flexibility for more competitive pricing without additional risk of adverse selection.
172+
173+
This functionality is available via the `cancel_bid` api. Here is a sample payload that can be sent via WebSocket:
119174

120175
```jsx
121176
{

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

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -221,6 +221,21 @@ Refer to the examples below:
221221

222222
</Tabs>
223223

224+
## Prioritization Fees
225+
226+
For SVM chains, you will also receive `svm_chain_update` messages which include recent prioritization fee estimates needed for your transactions to land on chain. These prioritization fees will be specified in microlamports per compute unit; refer to [Solana docs](https://solana.com/developers/guides/advanced/how-to-use-priority-fees) to learn more about prioritization fees. The format for these updates is:
227+
228+
```json
229+
{
230+
"type": "svm_chain_update",
231+
"update": {
232+
"chain_id": "solana",
233+
"blockhash": "PTgMRAF9GXRvYnWnpmDcX9gc1zesYV37kDjrax8kz3o",
234+
"latest_prioritization_fee": 1000
235+
}
236+
}
237+
```
238+
224239
## Connection Persistence
225240

226241
The WebSocket server responds to ping messages according to WebSocket standards.

0 commit comments

Comments
 (0)