Skip to content

chore: add failed to bid statuses #528

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Dec 6, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions pages/express-relay/integrate-as-searcher/evm.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,14 @@ Pyth provides a Typescript SDK, which allows searchers to subscribe to opportuni
```typescript
import { Client, Opportunity } from "@pythnetwork/express-relay-js";

const handleOpporunity = async (opportunity: Opportunity) => {
const handleOpportunity = async (opportunity: Opportunity) => {
// Implement your opportunity handler here
};

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

```typescript {4} copy
const handleOpporunity = async (opportunity: Opportunity) => {
const handleOpportunity = async (opportunity: Opportunity) => {
...
const bid = await client.signBid(opportunity, {amount, nonce, deadline}, privateKey)
await client.submitBid(bid)
Expand Down
129 changes: 80 additions & 49 deletions pages/express-relay/integrate-as-searcher/svm.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Callout, Tabs, Steps } from "nextra/components";

# SVM Searcher Integration

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

<Steps>

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

const handleOpporunity = async (opportunity: Opportunity) => {
const handleOpportunity = async (opportunity: Opportunity) => {
console.log("Received opportunity");
// Implement your opportunity handler here
};

const client = new Client(
{ baseUrl: "https://pyth-express-relay-mainnet.asymmetric.re" },
undefined, // Default WebSocket options
handleOpporunity
handleOpportunity
);
await client.subscribeChains(["development-solana"]);

async function main() {
await client.subscribeChains(["solana"]);
}

main();
```

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

```python copy
import asyncio
from express_relay.client import (
ExpressRelayClient,
)
from express_relay.express_relay_types import Opportunity
from express_relay.models import Opportunity

def opportunity_callback(opportunity: Opportunity):
async def opportunity_callback(opportunity: Opportunity):
print("Received opportunity")
# Implement your opportunity handler here
pass

client = ExpressRelayClient(
"https://per-staging.dourolabs.app",
"https://pyth-express-relay-mainnet.asymmetric.re",
None,
opportunity_callback,
None,
)
await client.subscribe_chains(['development-solana'])

async def main():
await client.subscribe_chains(["solana"])
task = await client.get_ws_loop()
await task

if __name__ == "__main__":
asyncio.run(main())
```

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

```bash copy
curl -X 'GET' \
'https://pyth-express-relay-mainnet.asymmetric.re/v1/opportunities?chain_id=development-solana&mode=live'
'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.
Expand All @@ -75,7 +89,7 @@ Here is a sample JSON payload to subscribe to opportunities:
"id": "1",
"method": "subscribe",
"params": {
"chain_ids": ["development-solana"]
"chain_ids": ["solana"]
}
}
```
Expand Down Expand Up @@ -125,32 +139,41 @@ import * as limo from "@kamino-finance/limo-sdk";
* @returns The generated bid object
*/
async generateBid(opportunity: OpportunitySvm): Promise<BidSvm> {
const order = opportunity.order;
const limoClient = new limo.LimoClient(
this.connectionSvm,
order.state.globalConfig
);

const ixsTakeOrder = await this.generateTakeOrderIxs(limoClient, order);
const txRaw = new anchor.web3.Transaction().add(...ixsTakeOrder);

const bidData = await this.getBidData(limoClient, order);

const bid = await this.client.constructSvmBid(
txRaw,
this.searcher.publicKey,
bidData.router,
order.address,
bidData.bidAmount,
new anchor.BN(Math.round(Date.now() / 1000 + DAY_IN_SECONDS)),
this.chainId,
bidData.relayerSigner,
bidData.relayerFeeReceiver
);

bid.transaction.recentBlockhash = this.recentBlockhash[this.chainId];
bid.transaction.sign(this.searcher);
return bid;
const order = opportunity.order;
const limoClient = new limo.LimoClient(
this.connectionSvm,
order.state.globalConfig
);

const ixsTakeOrder = await this.generateTakeOrderIxs(limoClient, order);
const feeInstruction = ComputeBudgetProgram.setComputeUnitPrice({
microLamports:
this.latestChainUpdate[this.chainId].latestPrioritizationFee,
});
const txRaw = new anchor.web3.Transaction().add(
feeInstruction,
...ixsTakeOrder
);

const bidAmount = await this.getBidAmount(order);

const config = await this.getExpressRelayConfig();
const bid = await this.client.constructSvmBid(
txRaw,
this.searcher.publicKey,
getPdaAuthority(limoClient.getProgramID(), order.state.globalConfig),
order.address,
bidAmount,
new anchor.BN(Math.round(Date.now() / 1000 + DAY_IN_SECONDS)),
this.chainId,
config.relayerSigner,
config.feeReceiverRelayer
);

bid.transaction.recentBlockhash =
this.latestChainUpdate[this.chainId].blockhash;
bid.transaction.sign(this.searcher);
return bid;
}
```

Expand Down Expand Up @@ -179,24 +202,32 @@ async def assess_opportunity(self, opp: OpportunitySvm) -> BidSvm | None:
A bid object if the opportunity is worth taking to be submitted to the Express Relay server, otherwise None.
"""
order: OrderStateAndAddress = {"address": opp.order_address, "state": opp.order}

ixs_take_order = await self.generate_take_order_ixs(order)
bid_data = await self.get_bid_data(order)
bid_amount = await self.get_bid_amount(order)
router = self.limo_client.get_pda_authority(
self.limo_client.get_program_id(), order["state"].global_config
)

submit_bid_ix = self.client.get_svm_submit_bid_instruction(
searcher=self.private_key.pubkey(),
router=bid_data.router,
router=router,
permission_key=order["address"],
bid_amount=bid_data.bid_amount,
bid_amount=bid_amount,
deadline=DEADLINE,
chain_id=self.chain_id,
fee_receiver_relayer=bid_data.relayer_fee_receiver,
relayer_signer=bid_data.relayer_signer,
fee_receiver_relayer=(await self.get_metadata()).fee_receiver_relayer,
relayer_signer=(await self.get_metadata()).relayer_signer,
)
latest_chain_update = self.latest_chain_update[self.chain_id]
fee_instruction = set_compute_unit_price(
latest_chain_update.latest_prioritization_fee
)
transaction = Transaction.new_with_payer(
[submit_bid_ix] + ixs_take_order, self.private_key.pubkey()
[fee_instruction, submit_bid_ix] + ixs_take_order, self.private_key.pubkey()
)
transaction.partial_sign(
[self.private_key], recent_blockhash=self.recent_blockhash[self.chain_id]
[self.private_key], recent_blockhash=latest_chain_update.blockhash
)
bid = BidSvm(transaction=transaction, chain_id=self.chain_id)
return bid
Expand Down Expand Up @@ -233,9 +264,9 @@ const generateBid = async (opportunity: OpportunitySvm, recentBlockhash: Blockha
...
}

const handleOpporunity = async (opportunity: Opportunity) => {
const handleOpportunity = async (opportunity: Opportunity) => {
...
const bid = await generateBid(opportunity as OpportunitySvm, this.recentBlockhash[this.chainId]);
const bid = await this.generateBid(opportunity as OpportunitySvm);
await client.submitBid(bid);
}
```
Expand All @@ -252,7 +283,7 @@ async def generate_bid(opp: OpportunitySvm) -> BidSvm:
...

def opportunity_callback(opportunity: Opportunity):
bid = generate_bid(typing.cast(OpportunitySvm, opportunity))
bid = await self.assess_opportunity(typing.cast(OpportunitySvm, opp))
await client.submit_bid(bid, subscribe_to_updates=True)
```

Expand All @@ -264,7 +295,7 @@ Searchers can submit bids through an HTTP POST call to the [`/v1/bids`](https://
curl -X POST https://pyth-express-relay-mainnet.asymmetric.re/v1/bids \
-H "Content-Type: application/json" \
-d '{
"chain_id": "development-solana",
"chain_id": "solana",
"transaction": "SGVsbG8sIFdvcmxkIQ=="
}'
```
Expand All @@ -280,7 +311,7 @@ Searchers can submit bids via Websocket to avoid additional network round-trips
"method": "post_bid",
"params": {
"bid": {
"chain_id": "development-solana",
"chain_id": "solana",
"transaction": "SGVsbG8sIFdvcmxkIQ=="
}
}
Expand Down
52 changes: 36 additions & 16 deletions pages/express-relay/websocket-api-reference.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,12 @@ Refer to the examples below, one for each of the status options in EVM and SVM:
</Tabs.Tab>

<Tabs.Tab>
<Tabs items={[`pending`, `submitted`, `lost`, `won`, `expired`]}>
<Tabs items={[`pending`, `submitted`, `lost`, `won`, `failed`, `expired`]}>
<Tabs.Tab>
```json
// pending
// The temporary state, which means the auction for this bid is pending
// The temporary state which means the auction for this bid is pending.
// It will be updated to Lost or Submitted after the auction takes place.
{
"type": "bid_status_update",
"status": {
Expand All @@ -237,17 +238,17 @@ Refer to the examples below, one for each of the status options in EVM and SVM:
<Tabs.Tab>
```json
// submitted
/// The bid is submitted to the chain, with the transaction with the signature.
/// This state is temporary and will be updated to either lost or won after conclusion of the auction.
// The bid won the auction and was submitted to the chain, with the signature of the corresponding transaction provided in the result field.
// 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.
{
"type": "bid_status_update",
"status": {
"id": "beedbeed-0e42-400f-a8ef-d78aa5422252",
"bid_status": {
// the enum for the bid_status
"type": "submitted",
// the hash of the transaction that the bid's calldata was included in
"result": "0xabc393b634fdf3eb45be8350fd16cd1b4add47b96059beacc1d8c20e51d75ec3"
// the signature of the transaction that contains the bid
"result": "Jb2urXPyEh4xiBgzYvwEFe4q1iMxG1DNxWGGQg94AmKgqFTwLAiTiHrYiYxwHUB4DV8u5ahNEVtMMDm3sNSRdTg"
}
}
}
Expand All @@ -256,17 +257,17 @@ Refer to the examples below, one for each of the status options in EVM and SVM:
<Tabs.Tab>
```json
// lost
/// The bid lost the auction.
/// The result will be None if the auction was concluded off-chain and no auction was submitted to the chain.
/// The result will be not None if another bid were selected for submission to the chain.
/// The signature of the transaction for the submitted bid is the result value.
// The bid lost the auction.
// The result will be None if the auction had no winner (because all bids were found to be invalid).
// The result will be Some if this bid lost to another bid.
// The signature of the transaction for the submitted bid is the result value.
{
"type": "bid_status_update",
"status": {
"id": "beedbeed-0e42-400f-a8ef-d78aa5422252",
"bid_status": {
"type": "lost",
"result": "0x99c2bf411330ae997632f88abe8f86c0d1f4c448f7d5061319d23814a0fb1135"
"result": "Jb2urXPyEh4xiBgzYvwEFe4q1iMxG1DNxWGGQg94AmKgqFTwLAiTiHrYiYxwHUB4DV8u5ahNEVtMMDm3sNSRdTg"
}
}
}
Expand All @@ -275,34 +276,53 @@ Refer to the examples below, one for each of the status options in EVM and SVM:
<Tabs.Tab>
```json
// won
/// The bid won the auction, with the transaction with the signature.
// The bid won the auction and was included in a block successfully.
{
"type": "bid_status_update",
"status": {
"id": "beedbeed-0e42-400f-a8ef-d78aa5422252",
"bid_status": {
// the enum for the bid_status
"type": "won",
// the hash of the transaction that the bid's calldata was included in
"result": "0xabc393b634fdf3eb45be8350fd16cd1b4add47b96059beacc1d8c20e51d75ec3"
// the signature of the transaction that included the bid
"result": "Jb2urXPyEh4xiBgzYvwEFe4q1iMxG1DNxWGGQg94AmKgqFTwLAiTiHrYiYxwHUB4DV8u5ahNEVtMMDm3sNSRdTg"
}
}
}
```
</Tabs.Tab>

<Tabs.Tab>
```json
// failed
// The bid was submitted on-chain, was included in a block, but resulted in a failed transaction.
{
"type": "bid_status_update",
"status": {
"id": "beedbeed-0e42-400f-a8ef-d78aa5422252",
"bid_status": {
// the enum for the bid_status
"type": "failed",
// the signature of the transaction that included the bid
"result": "Jb2urXPyEh4xiBgzYvwEFe4q1iMxG1DNxWGGQg94AmKgqFTwLAiTiHrYiYxwHUB4DV8u5ahNEVtMMDm3sNSRdTg"
}
}
}
```
</Tabs.Tab>

<Tabs.Tab>
```json
// expired
// /// The bid expired without being submitted on chain.
// The bid was submitted on-chain but expired before it was included in a block.
{
"type": "bid_status_update",
"status": {
"id": "beedbeed-0e42-400f-a8ef-d78aa5422252",
"bid_status": {
// the enum for the bid_status
"type": "expired",
// the hash of the transaction that the bid's calldata was included in
// the signature of the transaction that included the bid
"result": "Jb2urXPyEh4xiBgzYvwEFe4q1iMxG1DNxWGGQg94AmKgqFTwLAiTiHrYiYxwHUB4DV8u5ahNEVtMMDm3sNSRdTg",
}
}
Expand Down
Loading