Skip to content

Commit e386b6a

Browse files
authored
chore: add failed to bid statuses (#528)
* update new opportunity stuff * pass * add limo * add failed * go * clean * go
1 parent 38b781f commit e386b6a

File tree

3 files changed

+119
-69
lines changed

3 files changed

+119
-69
lines changed

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,14 @@ Pyth provides a Typescript SDK, which allows searchers to subscribe to opportuni
1717
```typescript
1818
import { Client, Opportunity } from "@pythnetwork/express-relay-js";
1919

20-
const handleOpporunity = async (opportunity: Opportunity) => {
20+
const handleOpportunity = async (opportunity: Opportunity) => {
2121
// Implement your opportunity handler here
2222
};
2323

2424
const client = new Client(
2525
{ baseUrl: "https://pyth-express-relay-mainnet.asymmetric.re" },
2626
undefined, // Default WebSocket options
27-
handleOpporunity
27+
handleOpportunity
2828
);
2929
await client.subscribeChains(["op_sepolia"]);
3030
```
@@ -175,7 +175,7 @@ Searchers can submit the constructed bids to Express Relay via the SDKs, an HTTP
175175
The code snippet below demonstrates how to submit a bid using the Typescript SDK:
176176

177177
```typescript {4} copy
178-
const handleOpporunity = async (opportunity: Opportunity) => {
178+
const handleOpportunity = async (opportunity: Opportunity) => {
179179
...
180180
const bid = await client.signBid(opportunity, {amount, nonce, deadline}, privateKey)
181181
await client.submitBid(bid)

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

Lines changed: 80 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { Callout, Tabs, Steps } from "nextra/components";
22

33
# SVM Searcher Integration
44

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

77
<Steps>
88

@@ -19,39 +19,53 @@ Pyth provides a Typescript SDK, which allows searchers to subscribe to opportuni
1919
```typescript
2020
import { Client, Opportunity } from "@pythnetwork/express-relay-js";
2121

22-
const handleOpporunity = async (opportunity: Opportunity) => {
22+
const handleOpportunity = async (opportunity: Opportunity) => {
23+
console.log("Received opportunity");
2324
// Implement your opportunity handler here
2425
};
2526

2627
const client = new Client(
2728
{ baseUrl: "https://pyth-express-relay-mainnet.asymmetric.re" },
2829
undefined, // Default WebSocket options
29-
handleOpporunity
30+
handleOpportunity
3031
);
31-
await client.subscribeChains(["development-solana"]);
32+
33+
async function main() {
34+
await client.subscribeChains(["solana"]);
35+
}
36+
37+
main();
3238
```
3339

3440
</Tabs.Tab>
3541
<Tabs.Tab>
3642
Pyth provides a Python SDK, which allows searchers to subscribe to opportunities:
3743

3844
```python copy
45+
import asyncio
3946
from express_relay.client import (
4047
ExpressRelayClient,
4148
)
42-
from express_relay.express_relay_types import Opportunity
49+
from express_relay.models import Opportunity
4350

44-
def opportunity_callback(opportunity: Opportunity):
51+
async def opportunity_callback(opportunity: Opportunity):
52+
print("Received opportunity")
4553
# Implement your opportunity handler here
46-
pass
4754

4855
client = ExpressRelayClient(
49-
"https://per-staging.dourolabs.app",
56+
"https://pyth-express-relay-mainnet.asymmetric.re",
5057
None,
5158
opportunity_callback,
5259
None,
5360
)
54-
await client.subscribe_chains(['development-solana'])
61+
62+
async def main():
63+
await client.subscribe_chains(["solana"])
64+
task = await client.get_ws_loop()
65+
await task
66+
67+
if __name__ == "__main__":
68+
asyncio.run(main())
5569
```
5670

5771
</Tabs.Tab>
@@ -60,7 +74,7 @@ Searchers can request opportunities through an HTTP **GET** call to the [`/v1/op
6074

6175
```bash copy
6276
curl -X 'GET' \
63-
'https://pyth-express-relay-mainnet.asymmetric.re/v1/opportunities?chain_id=development-solana&mode=live'
77+
'https://pyth-express-relay-mainnet.asymmetric.re/v1/opportunities?chain_id=solana&mode=live'
6478
```
6579

6680
Opportunities are short-lived and could be executed in a matter of seconds. So, the above endpoint could return an empty response.
@@ -75,7 +89,7 @@ Here is a sample JSON payload to subscribe to opportunities:
7589
"id": "1",
7690
"method": "subscribe",
7791
"params": {
78-
"chain_ids": ["development-solana"]
92+
"chain_ids": ["solana"]
7993
}
8094
}
8195
```
@@ -125,32 +139,41 @@ import * as limo from "@kamino-finance/limo-sdk";
125139
* @returns The generated bid object
126140
*/
127141
async generateBid(opportunity: OpportunitySvm): Promise<BidSvm> {
128-
const order = opportunity.order;
129-
const limoClient = new limo.LimoClient(
130-
this.connectionSvm,
131-
order.state.globalConfig
132-
);
133-
134-
const ixsTakeOrder = await this.generateTakeOrderIxs(limoClient, order);
135-
const txRaw = new anchor.web3.Transaction().add(...ixsTakeOrder);
136-
137-
const bidData = await this.getBidData(limoClient, order);
138-
139-
const bid = await this.client.constructSvmBid(
140-
txRaw,
141-
this.searcher.publicKey,
142-
bidData.router,
143-
order.address,
144-
bidData.bidAmount,
145-
new anchor.BN(Math.round(Date.now() / 1000 + DAY_IN_SECONDS)),
146-
this.chainId,
147-
bidData.relayerSigner,
148-
bidData.relayerFeeReceiver
149-
);
150-
151-
bid.transaction.recentBlockhash = this.recentBlockhash[this.chainId];
152-
bid.transaction.sign(this.searcher);
153-
return bid;
142+
const order = opportunity.order;
143+
const limoClient = new limo.LimoClient(
144+
this.connectionSvm,
145+
order.state.globalConfig
146+
);
147+
148+
const ixsTakeOrder = await this.generateTakeOrderIxs(limoClient, order);
149+
const feeInstruction = ComputeBudgetProgram.setComputeUnitPrice({
150+
microLamports:
151+
this.latestChainUpdate[this.chainId].latestPrioritizationFee,
152+
});
153+
const txRaw = new anchor.web3.Transaction().add(
154+
feeInstruction,
155+
...ixsTakeOrder
156+
);
157+
158+
const bidAmount = await this.getBidAmount(order);
159+
160+
const config = await this.getExpressRelayConfig();
161+
const bid = await this.client.constructSvmBid(
162+
txRaw,
163+
this.searcher.publicKey,
164+
getPdaAuthority(limoClient.getProgramID(), order.state.globalConfig),
165+
order.address,
166+
bidAmount,
167+
new anchor.BN(Math.round(Date.now() / 1000 + DAY_IN_SECONDS)),
168+
this.chainId,
169+
config.relayerSigner,
170+
config.feeReceiverRelayer
171+
);
172+
173+
bid.transaction.recentBlockhash =
174+
this.latestChainUpdate[this.chainId].blockhash;
175+
bid.transaction.sign(this.searcher);
176+
return bid;
154177
}
155178
```
156179

@@ -179,24 +202,32 @@ async def assess_opportunity(self, opp: OpportunitySvm) -> BidSvm | None:
179202
A bid object if the opportunity is worth taking to be submitted to the Express Relay server, otherwise None.
180203
"""
181204
order: OrderStateAndAddress = {"address": opp.order_address, "state": opp.order}
205+
182206
ixs_take_order = await self.generate_take_order_ixs(order)
183-
bid_data = await self.get_bid_data(order)
207+
bid_amount = await self.get_bid_amount(order)
208+
router = self.limo_client.get_pda_authority(
209+
self.limo_client.get_program_id(), order["state"].global_config
210+
)
184211
185212
submit_bid_ix = self.client.get_svm_submit_bid_instruction(
186213
searcher=self.private_key.pubkey(),
187-
router=bid_data.router,
214+
router=router,
188215
permission_key=order["address"],
189-
bid_amount=bid_data.bid_amount,
216+
bid_amount=bid_amount,
190217
deadline=DEADLINE,
191218
chain_id=self.chain_id,
192-
fee_receiver_relayer=bid_data.relayer_fee_receiver,
193-
relayer_signer=bid_data.relayer_signer,
219+
fee_receiver_relayer=(await self.get_metadata()).fee_receiver_relayer,
220+
relayer_signer=(await self.get_metadata()).relayer_signer,
221+
)
222+
latest_chain_update = self.latest_chain_update[self.chain_id]
223+
fee_instruction = set_compute_unit_price(
224+
latest_chain_update.latest_prioritization_fee
194225
)
195226
transaction = Transaction.new_with_payer(
196-
[submit_bid_ix] + ixs_take_order, self.private_key.pubkey()
227+
[fee_instruction, submit_bid_ix] + ixs_take_order, self.private_key.pubkey()
197228
)
198229
transaction.partial_sign(
199-
[self.private_key], recent_blockhash=self.recent_blockhash[self.chain_id]
230+
[self.private_key], recent_blockhash=latest_chain_update.blockhash
200231
)
201232
bid = BidSvm(transaction=transaction, chain_id=self.chain_id)
202233
return bid
@@ -233,9 +264,9 @@ const generateBid = async (opportunity: OpportunitySvm, recentBlockhash: Blockha
233264
...
234265
}
235266
236-
const handleOpporunity = async (opportunity: Opportunity) => {
267+
const handleOpportunity = async (opportunity: Opportunity) => {
237268
...
238-
const bid = await generateBid(opportunity as OpportunitySvm, this.recentBlockhash[this.chainId]);
269+
const bid = await this.generateBid(opportunity as OpportunitySvm);
239270
await client.submitBid(bid);
240271
}
241272
```
@@ -252,7 +283,7 @@ async def generate_bid(opp: OpportunitySvm) -> BidSvm:
252283
...
253284
254285
def opportunity_callback(opportunity: Opportunity):
255-
bid = generate_bid(typing.cast(OpportunitySvm, opportunity))
286+
bid = await self.assess_opportunity(typing.cast(OpportunitySvm, opp))
256287
await client.submit_bid(bid, subscribe_to_updates=True)
257288
```
258289

@@ -264,7 +295,7 @@ Searchers can submit bids through an HTTP POST call to the [`/v1/bids`](https://
264295
curl -X POST https://pyth-express-relay-mainnet.asymmetric.re/v1/bids \
265296
-H "Content-Type: application/json" \
266297
-d '{
267-
"chain_id": "development-solana",
298+
"chain_id": "solana",
268299
"transaction": "SGVsbG8sIFdvcmxkIQ=="
269300
}'
270301
```
@@ -280,7 +311,7 @@ Searchers can submit bids via Websocket to avoid additional network round-trips
280311
"method": "post_bid",
281312
"params": {
282313
"bid": {
283-
"chain_id": "development-solana",
314+
"chain_id": "solana",
284315
"transaction": "SGVsbG8sIFdvcmxkIQ=="
285316
}
286317
}

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

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,12 @@ Refer to the examples below, one for each of the status options in EVM and SVM:
218218
</Tabs.Tab>
219219

220220
<Tabs.Tab>
221-
<Tabs items={[`pending`, `submitted`, `lost`, `won`, `expired`]}>
221+
<Tabs items={[`pending`, `submitted`, `lost`, `won`, `failed`, `expired`]}>
222222
<Tabs.Tab>
223223
```json
224224
// pending
225-
// The temporary state, which means the auction for this bid is pending
225+
// The temporary state which means the auction for this bid is pending.
226+
// It will be updated to Lost or Submitted after the auction takes place.
226227
{
227228
"type": "bid_status_update",
228229
"status": {
@@ -237,36 +238,35 @@ Refer to the examples below, one for each of the status options in EVM and SVM:
237238
<Tabs.Tab>
238239
```json
239240
// submitted
240-
/// The bid is submitted to the chain, with the transaction with the signature.
241-
/// This state is temporary and will be updated to either lost or won after conclusion of the auction.
241+
// The bid won the auction and was submitted to the chain, with the signature of the corresponding transaction provided in the result field.
242+
// This state is temporary and will be updated to either Won or Failed after the transaction is included in a block, or Expired if the transaction expires before it is included.
242243
{
243244
"type": "bid_status_update",
244245
"status": {
245246
"id": "beedbeed-0e42-400f-a8ef-d78aa5422252",
246247
"bid_status": {
247248
// the enum for the bid_status
248249
"type": "submitted",
249-
// the hash of the transaction that the bid's calldata was included in
250-
"result": "0xabc393b634fdf3eb45be8350fd16cd1b4add47b96059beacc1d8c20e51d75ec3"
250+
// the signature of the transaction that contains the bid
251+
"result": "Jb2urXPyEh4xiBgzYvwEFe4q1iMxG1DNxWGGQg94AmKgqFTwLAiTiHrYiYxwHUB4DV8u5ahNEVtMMDm3sNSRdTg"
251252
}
252253
}
253254
}
254255
```
255256
</Tabs.Tab>
256257
<Tabs.Tab>
257258
```json
258-
// lost
259-
/// The bid lost the auction.
260-
/// The result will be None if the auction was concluded off-chain and no auction was submitted to the chain.
261-
/// The result will be not None if another bid were selected for submission to the chain.
262-
/// The signature of the transaction for the submitted bid is the result value.
259+
// lost
260+
// The bid lost the auction.
261+
// This bid status will have a result field containing the signature of the transaction corresponding to the winning bid,
262+
// unless the auction had no winner (because all bids were found to be invalid).
263263
{
264264
"type": "bid_status_update",
265265
"status": {
266266
"id": "beedbeed-0e42-400f-a8ef-d78aa5422252",
267267
"bid_status": {
268268
"type": "lost",
269-
"result": "0x99c2bf411330ae997632f88abe8f86c0d1f4c448f7d5061319d23814a0fb1135"
269+
"result": "Jb2urXPyEh4xiBgzYvwEFe4q1iMxG1DNxWGGQg94AmKgqFTwLAiTiHrYiYxwHUB4DV8u5ahNEVtMMDm3sNSRdTg"
270270
}
271271
}
272272
}
@@ -275,34 +275,53 @@ Refer to the examples below, one for each of the status options in EVM and SVM:
275275
<Tabs.Tab>
276276
```json
277277
// won
278-
/// The bid won the auction, with the transaction with the signature.
278+
// The bid won the auction and was included in a block successfully.
279279
{
280280
"type": "bid_status_update",
281281
"status": {
282282
"id": "beedbeed-0e42-400f-a8ef-d78aa5422252",
283283
"bid_status": {
284284
// the enum for the bid_status
285285
"type": "won",
286-
// the hash of the transaction that the bid's calldata was included in
287-
"result": "0xabc393b634fdf3eb45be8350fd16cd1b4add47b96059beacc1d8c20e51d75ec3"
286+
// the signature of the transaction that included the bid
287+
"result": "Jb2urXPyEh4xiBgzYvwEFe4q1iMxG1DNxWGGQg94AmKgqFTwLAiTiHrYiYxwHUB4DV8u5ahNEVtMMDm3sNSRdTg"
288288
}
289289
}
290290
}
291291
```
292292
</Tabs.Tab>
293293

294+
<Tabs.Tab>
295+
```json
296+
// failed
297+
// The bid was submitted on-chain, was included in a block, but resulted in a failed transaction.
298+
{
299+
"type": "bid_status_update",
300+
"status": {
301+
"id": "beedbeed-0e42-400f-a8ef-d78aa5422252",
302+
"bid_status": {
303+
// the enum for the bid_status
304+
"type": "failed",
305+
// the signature of the transaction that included the bid
306+
"result": "Jb2urXPyEh4xiBgzYvwEFe4q1iMxG1DNxWGGQg94AmKgqFTwLAiTiHrYiYxwHUB4DV8u5ahNEVtMMDm3sNSRdTg"
307+
}
308+
}
309+
}
310+
```
311+
</Tabs.Tab>
312+
294313
<Tabs.Tab>
295314
```json
296315
// expired
297-
// /// The bid expired without being submitted on chain.
316+
// The bid was submitted on-chain but expired before it was included in a block.
298317
{
299318
"type": "bid_status_update",
300319
"status": {
301320
"id": "beedbeed-0e42-400f-a8ef-d78aa5422252",
302321
"bid_status": {
303322
// the enum for the bid_status
304323
"type": "expired",
305-
// the hash of the transaction that the bid's calldata was included in
324+
// the signature of the transaction that included the bid
306325
"result": "Jb2urXPyEh4xiBgzYvwEFe4q1iMxG1DNxWGGQg94AmKgqFTwLAiTiHrYiYxwHUB4DV8u5ahNEVtMMDm3sNSRdTg",
307326
}
308327
}

0 commit comments

Comments
 (0)