Skip to content

Commit 6901e48

Browse files
committed
Reorg searcher docs
1 parent c35641d commit 6901e48

File tree

5 files changed

+231
-264
lines changed

5 files changed

+231
-264
lines changed

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

Lines changed: 154 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,125 +1,233 @@
1-
import { Callout, Tabs, Steps } from 'nextra/components'
1+
import { Callout, Tabs, Steps } from "nextra/components";
22

33
# How to Integrate Express Relay as a Searcher
44

5-
Express Relay gives one place stop for on-chain opportunities. Searchers can access opportunities from various DeFi protocols via a simple and unified interface.
5+
Express Relay gives one place stop for on-chain opportunities. Searchers can access opportunities from various DeFi protocols via a simple and unified interface.
66

77
Searchers **bid** on opportunities exposed by the Express Relay server.
8-
The server exposes different endpoints for interaction, which can be used directly via HTTP, WebSocket, or one of the SDKS for convenience.
8+
The server exposes different endpoints for interaction, which can be used directly via HTTP, WebSocket, or one of the SDKs for convenience.
99

10+
Searchers can integrate with the Express Relay server in three steps:
1011

11-
Searchers can integrate with the Express Relay server in two steps:
12-
- Integrate with the auction server
13-
- Integrate with the opportunity adapter _(OPTIONAL)_
12+
1. Subscribe to new opportunities
13+
2. Evaluating the opportunity and constructing the bid
14+
3. Submitting the bid to the auction server
1415

16+
<Steps>
17+
18+
### Subscribe to new opportunities
1519

20+
Searchers can fetch available opportunities via HTTP or subscribe to them via WebSocket.
1621

22+
<Tabs items={['Typescript', 'Python', 'HTTP', 'Websocket']}>
1723

18-
## Integrate with the auction server
24+
<Tabs.Tab>
25+
Our JavaScript SDK provides a convenient way to subscribe to opportunities:
1926

20-
Defi protocols integrated with Express Relay expose liquidation opportunities via the auction server.
21-
Searchers can access these opportunities by querying the server directly or subscribing to WebSocket updates.
27+
```typescript
28+
import { Client, Opportunity } from "@pythnetwork/express-relay-evm-js";
2229

23-
To integrate with the auction server, follow these steps:
24-
1. Fetch opportunities from the auction server.
25-
1. Submit bids on opportunities to the auction server.
30+
const handleOpporunity = async (opportunity: Opportunity) => {
31+
// TODO: Implement your opportunity handler here
32+
};
2633

27-
<Steps>
34+
const client = new Client(
35+
{ baseUrl: "https://pyth-express-relay-mainnet.asymmetric.re" },
36+
undefined,
37+
handleOpporunity
38+
);
39+
await client.subscribeChains(["op_sepolia"]);
40+
```
41+
42+
</Tabs.Tab>
43+
<Tabs.Tab>
44+
Our Python SDK provides a convenient way to subscribe to opportunities:
2845

29-
### Fetch opportunities from the auction server
46+
```python
47+
from express_relay.client import (
48+
ExpressRelayClient,
49+
)
50+
from express_relay.express_relay_types import Opportunity
3051

31-
Searchers can request via HTTP or subscribe to WebSocket updates to fetch opportunities from the auction server.
3252

33-
<Tabs items={['HTTP', 'Websocket']}>
53+
def opportunity_callback(opportunity: Opportunity):
54+
# TODO: Implement your opportunity handler here
55+
pass
3456

57+
58+
client = ExpressRelayClient(
59+
'server_url',
60+
None,
61+
opportunity_callback,
62+
None,
63+
)
64+
await client.subscribe_chains(['op_sepolia'])
65+
```
66+
67+
</Tabs.Tab>
3568
<Tabs.Tab>
36-
Searchers can request opportunities through an HTTP GET call to the `/v1/opportunities` endpoint.
69+
Searchers can request opportunities through an HTTP GET call to the `/v1/opportunities` endpoint.
3770

3871
```bash copy
3972
curl -X 'GET' \
40-
'https://per-staging.dourolabs.app/v1/opportunities?chain_id=op_sepolia&mode=live&permission_key=0xdeadbeef&from_time=2024-05-23T21%3A26%3A57.329954Z'
73+
'https://pyth-express-relay-mainnet.asymmetric.re/v1/opportunities?chain_id=op_sepolia&mode=live'
4174
```
75+
76+
Opportunities are short-lived and will be executed in a matter of seconds. So it is possible for this endpoint to return an empty response.
77+
78+
You can fetch historical opportunities by setting the `mode` parameter to `historical`.
79+
4280
</Tabs.Tab>
4381
<Tabs.Tab>
4482
Searchers can connect to the server via WebSocket to reduce latency and subscribe to various events. The WebSocket endpoint relies at `/v1/ws`(e.g `wss://pyth-express-relay-mainnet.asymmetric.re/v1/ws`)
83+
Here is a sample json payload to subscribe to opportunities:
4584

46-
```bash copy
85+
```json copy
4786
{
48-
"id": "1",
49-
"method": "subscribe", // Name of the server method to invoke
50-
"params": {
51-
"chain_ids": ["op_sepolia"]
52-
}
87+
"id": "1",
88+
"method": "subscribe",
89+
"params": {
90+
"chain_ids": ["op_sepolia"]
91+
}
5392
}
5493
```
5594

5695
Consult [`Websocket API reference`](./websocket-api-reference.mdx) for a complete list of methods and parameters.
96+
5797
</Tabs.Tab>
5898
</Tabs>
5999

60100
The server responds with opportunities in the following format:
61101

62102
```json copy
63103
{
64-
"target_calldata": "0xdeadbeef", // Calldata to perform liquidation
65-
"chain_id": "op_sepolia",
66-
"target_contract": "0xcA11bde05977b3631167028862bE2a173976CA11", // Protocol contract address to call for liquidation
67-
"permission_key": "0xcafebabe", // Unique identifier for the liquidation opportunity
68-
"target_call_value": "1", // Value(in Wei) to send with to the Protocol contract.
69-
"buy_tokens": [ // Tokens to buy (Collateral)
104+
"target_calldata": "0xdeadbeef", // Calldata to execute the opportunity
105+
"chain_id": "op_sepolia",
106+
"target_contract": "0xcA11bde05977b3631167028862bE2a173976CA11", // Protocol contract address to call for execution
107+
"permission_key": "0xcafebabe", // Permission key required for the liquidation opportunity
108+
"target_call_value": "1", // Value(in Wei) to send with to the Protocol contract.
109+
"buy_tokens": [
110+
// Tokens to buy
70111
{
71112
"amount": "1000",
72113
"token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
73114
}
74115
],
75-
"sell_tokens": [ // Tokens to sell (Oustadaing Debt)
116+
"sell_tokens": [
117+
// Tokens to sell
76118
{
77119
"amount": "900",
78120
"token": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"
79121
}
80122
],
81-
"version": "v1" // Opportunity format version
123+
"version": "v1" // Opportunity format version
82124
}
83125
```
84126

127+
### Evaluating the opportunity and constructing the bid
85128

129+
Based on the opportunity details, mainly the sell tokens and buy tokens, searchers can evaluate the opportunity and construct a bid.
130+
The SDKs provide an easy way to construct a bid using the `OpportunityAdapter` contract. The `OpportunityAdapter` contract handles asset transfers and ensures the opportunity is executed correctly.
131+
132+
You can learn more about the `OpportunityAdapter` contract and how to prepare your assets in the [Opportunity Adapter](./integrate-as-searcher/opportunity-adapter.mdx) section.
133+
If you have already developed an in-house searcher contract there is no need to integrate using the Opportunity Adapter contract.
134+
You can use our lower level APIs which allows higher flexibility for advanced users.
135+
Please refer to the [custom contracts](./custom-contract) section for more details.
136+
137+
<Tabs items={['Typescript', 'Python']}>
138+
<Tabs.Tab>
139+
```typescript copy
140+
const handleOpportunity = async (opportunity: Opportunity) => {
141+
const nonce = BigInt(Math.floor(Math.random() * 2 ** 50));
142+
const privateKey = `0x0000`; // This should be the private key of the searcher
143+
const amount = BigInt(1000); // This should be determined based on opportunity
144+
const deadline = BigInt(Math.round(Date.now() / 1000 + 60)) // bid is valid for a minute
145+
const bid = await client.signBid(opportunity, {amount, nonce, deadline},privateKey)
146+
}
147+
```
148+
</Tabs.Tab>
149+
<Tabs.Tab>
150+
```python copy
151+
from datetime import datetime
152+
153+
from express_relay.client import (
154+
ExpressRelayClient,
155+
sign_bid
156+
)
157+
from secrets import randbits
158+
from express_relay.express_relay_types import Opportunity, OpportunityBidParams
159+
160+
def opportunity_callback(opportunity: Opportunity):
161+
nonce = randbits(64)
162+
deadline = datetime.utcnow().timestamp() + 60 # bid is valid for a minute
163+
amount = 1000 # This should be determined based on opportunity
164+
private_key = '0x00000'
165+
signed_bid = sign_bid(opportunity,
166+
OpportunityBidParams(amount=amount, deadline=int(deadline), nonce=nonce),
167+
private_key)
168+
169+
````
170+
</Tabs.Tab>
171+
</Tabs>
86172

87173
### Submit bids on opportunities to the auction server.
88174

89-
Searchers can submit bids on opportunities to the auction server via an HTTP POST or through a WebSocket connection.
175+
Searchers can submit the constructed bids to the auction server via the SDKs, an HTTP POST request, or through a WebSocket connection.
90176

91-
<Tabs items={['HTTP', 'Websocket']}>
177+
<Tabs items={['Typescript', 'Python', 'HTTP', 'Websocket']}>
178+
179+
<Tabs.Tab>
180+
```typescript {4} copy
181+
const handleOpporunity = async (opportunity: Opportunity) => {
182+
...
183+
const bid = await client.signBid(opportunity, {amount, nonce, deadline},privateKey)
184+
await client.submitBid(bid)
185+
}
186+
````
92187

188+
</Tabs.Tab>
189+
<Tabs.Tab>
190+
```python {5} copy
191+
def opportunity_callback(opportunity: Opportunity):
192+
signed_bid = sign_bid(opportunity,
193+
OpportunityBidParams(amount=amount, deadline=int(deadline), nonce=nonce),
194+
private_key)
195+
await client.submit_bid(signed_bid, subscribe_to_updates=True)
196+
```
197+
</Tabs.Tab>
93198
<Tabs.Tab>
94199
Searchers can submit bids through an HTTP POST call to the `/v1/bids` endpoint. This endpoint accepts a JSON payload containing the details of the bid.
95200

96201
```bash copy
97-
{
202+
curl -X POST https://pyth-express-relay-mainnet.asymmetric.re/v1/bids \
203+
-H "Content-Type: application/json" \
204+
-d '{
98205
"chain_id": "op_sepolia",
99-
"permission_key": "0x00000000000000000000000087ee27c5ae396b28a825968b277fece0720f5907000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000",
100-
"target_contract": "0x87ee27c5ae396b28a825968b277fece0720f5907"
206+
"permission_key": "0x000000000000000000000000",
207+
"target_contract": "0x87ee27c5ae396b28a825968b277fece0720f5907",
101208
"target_calldata": "0xeadb38050000000000000000000000000000000000000000000000000000000000000064",
102-
"amount": "10",
103-
}
209+
"amount": "10"
210+
}'
104211
```
105212

106213
TODO: Explain the fields here.
214+
107215
</Tabs.Tab>
108216
<Tabs.Tab>
109217

110218
Searchers can submit bids via Websocket to avoid additional network round-trips and get notified about changes to the bid status.
111219

112-
```bash copy
220+
```json copy
113221
{
114222
"id": "1",
115223
"method": "post_bid",
116224
"params": {
117225
"bid": {
118-
"amount": "10",
119-
"calldata": "0xdeadbeef",
120-
"chain_id": "sepolia",
121-
"contract": "0xcA11bde05977b3631167028862bE2a173976CA11",
122-
"permission_key": "0xdeadbeefcafe"
226+
"chain_id": "op_sepolia",
227+
"permission_key": "0x000000000000000000000000",
228+
"target_contract": "0x87ee27c5ae396b28a825968b277fece0720f5907",
229+
"target_calldata": "0xeadb38050000000000000000000000000000000000000000000000000000000000000064",
230+
"amount": "10"
123231
}
124232
}
125233
}
@@ -139,44 +247,7 @@ A successful response to a bid submission has the following schema:
139247
```
140248

141249
Consult [`Websocket API reference`](./websocket-api-reference.mdx) for more details.
250+
142251
</Tabs.Tab>
143252
</Tabs>
144253
</Steps>
145-
146-
147-
Searchers are **recommended** to source opportunities via the opportunity endpoints and construct transactions intended for the `OpportunityAdapter`.
148-
However, searchers can use custom contracts to execute transactions if they prefer.
149-
If searchers use custom contracts, they can still use the Express Relay server to source opportunities and craft bids based on the opportunity details.
150-
151-
Check the following example of a custom contract that executes liquidation transactions:
152-
153-
```solidity copy
154-
...
155-
function callLiquidation(Opportunity memory opp){
156-
for (uint i=0; i<opp.sell_tokens.length; i++) {
157-
let token = opp.sell_tokens[i];
158-
IERC20(token.contract).approve(opp.contract, token.amount);
159-
}
160-
161-
uint256[] before = new uint256[](opp.buy_tokens.length);
162-
for (uint j=0; j<opp.buy_tokens.length; j++) {
163-
let token = opp.buy_tokens[j];
164-
before = IERC20(token.contract).balanceOf(address(this));
165-
}
166-
167-
opp.target_contract.call{value: opp.target_call_value}(opp.target_calldata);
168-
169-
uint256[] after = new uint256[](opp.buy_tokens.length);
170-
for (uint j=0; j<opp.buy_tokens.length; j++) {
171-
let token = opp.buy_tokens[j];
172-
after = IERC20(token.target_contract).balanceOf(address(this));
173-
174-
assert(after[j] == before[j] + token.amount);
175-
}
176-
}
177-
...
178-
```
179-
180-
<Callout type="info" emoji="ℹ️">
181-
Make sure to approve the `opp.contract` for the necessary amount of `sell_tokens`.
182-
</Callout>
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
11
{
2-
"auction-server": "Integrate with the auction server",
3-
"opportunity-adapter": "Use Opportunity Adapter"
2+
"opportunity-adapter": "Prepare assets for Opportunity Adapter"
43
}

0 commit comments

Comments
 (0)