Skip to content

Commit 2fb32b6

Browse files
committed
feat(per): add market orders page
1 parent 9c52c60 commit 2fb32b6

File tree

3 files changed

+154
-1
lines changed

3 files changed

+154
-1
lines changed
Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
{
2-
"svm": "Integrate on SVM chains"
2+
"svm": "Integrate on SVM chains",
3+
"market-orders": "Integrate on Market Orders"
34
}
Binary file not shown.
Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
import { Callout, Tabs, Steps } from "nextra/components";
2+
3+
# Market Orders Integration
4+
5+
Unlike limit orders, market orders are created by users and need instant fulfilment within a single transaction.
6+
7+
### Request Flow
8+
9+
The following diagram illustrates the flow of a market order:
10+
![Market Orders Flow](./market-orders-flow.webp)
11+
12+
### Opportunity Structure
13+
14+
Quote requests are broadcasted through the same websocket channel used for receiving other opportunities. Here is a sample payload:
15+
16+
```jsx
17+
{
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"
36+
},
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"
43+
},
44+
"chain_id": "solana"
45+
}
46+
```
47+
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.
49+
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`:
51+
52+
```jsx
53+
"tokens": {
54+
"side_specified": "searcher",
55+
"searcher_token": "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v",
56+
"user_token": "Es9vMFrzaCERmJfrF4H2FYD4KCoNkY11McCe8BenwNYB",
57+
"searcher_amount": 1000000,
58+
"token_program_searcher": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA",
59+
"token_program_user": "TokenkegQfeZyiNwAJbNbGKPFXCWuBvf9Ss623VQ5DA"
60+
}
61+
```
62+
63+
This means the user wants to the send the least amount of USDT (`Es9vMF…`) to receive exactly 1.000000 USDC (`EPjFWdd…`).
64+
65+
Other fields necessary for constructing the transaction include:
66+
67+
- `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+
71+
- `router_account` The account receiving the referral fees
72+
- `referral_fee_bps` Amount of fees deduced from the swap in basis points
73+
- `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.
74+
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
75+
76+
The transaction containing the bid should include the following instructions:
77+
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.
81+
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.
83+
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:
85+
86+
```jsx
87+
{
88+
chain_id: 'solana',
89+
opportunity_id: '44382da2-5971-453c-b647-2ba2317c7d56',
90+
type: 'swap',
91+
transaction: 'Ai22...'
92+
}
93+
```
94+
95+
You must include `opportunity_id` and `type` field in bids related to market orders.
96+
97+
The winner bid will be communicated to the users for the final signature and on-chain submission.
98+
99+
### Status Updates
100+
101+
Bid status notifications will be sent in the same websocket channel. The bid status can have the following values:
102+
103+
- `lost`: Bid was not high enough and another searcher won the auction.
104+
- `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.
107+
- `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).
110+
- `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.
111+
- `submission_failed_deadline_passed`: The user submitted the signature but it already passed the specified deadline, so it will not be submitted on-chain.
112+
113+
### Cancelling Bids
114+
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:
119+
120+
```jsx
121+
{
122+
"method": "cancel_bid",
123+
"params": {
124+
"data": {
125+
"bid_id": "fd586110-b22c-4e7e-a199-99c4db9c7515",
126+
"chain_id": "solana"
127+
}
128+
}
129+
}
130+
```
131+
132+
# Endpoints and addresses:
133+
134+
- Program address: [`PytERJFhAKuNNuaiXkApLfWzwNwSNDACpigT3LwQfou`](https://solscan.io/address/PytERJFhAKuNNuaiXkApLfWzwNwSNDACpigT3LwQfou)
135+
- Auction Server Endpoint: [`https://per-mainnet.dourolabs.app/`](https://per-staging.dourolabs.app/)
136+
- Chain id: `solana`
137+
138+
You can test your service using real tokens like USDC and USDT.
139+
140+
# Links and references:
141+
142+
JS SDK: https://www.npmjs.com/package/@pythnetwork/express-relay-js/
143+
144+
Python SDK: https://pypi.org/project/express-relay/
145+
146+
Rust Client SDK: https://crates.io/crates/express-relay-client
147+
148+
Rust API Types: https://crates.io/crates/express-relay-api-types/
149+
150+
Api References: https://per-mainnet.dourolabs.app/docs
151+
152+
Testing Ui: You can use [Kamino Swap](https://swap.kamino.finance/) or Jupiter to test

0 commit comments

Comments
 (0)