|
1 | 1 | # Integrate with auction server
|
2 | 2 |
|
3 |
| -## TODO |
| 3 | +## Submitting bids |
| 4 | + |
| 5 | +Searchers submit their bids on transactions they wish to execute to the off-chain auction server of the Express Relay. The auction server lives at (TODO: INSERT ENDPOINT), and its API documentation can be found here (TODO: INSERT SWAGGER DOCS). You can use the express relay [JavaScript SDK](https://www.npmjs.com/package/@pythnetwork/express-relay-evm-js) or [Python SDK](https://pypi.org/project/express-relay/) for a more native integration. |
| 6 | + |
| 7 | +To submit a bid to the auction server, a searcher can use either websocket (if they wish to subscribe to updates on the status of their bid) or the HTTP `/v1/bids` POST method. |
| 8 | + |
| 9 | +### HTTP |
| 10 | + |
| 11 | +The HTTP POST request should be submitted with a JSON request body representing the searcher's `Bid` object. An example is provided below: |
| 12 | + |
| 13 | +``` |
| 14 | +{ |
| 15 | + "chain_id": "op_sepolia", |
| 16 | + "permission_key": "0x00000000000000000000000087ee27c5ae396b28a825968b277fece0720f5907000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000", |
| 17 | + "target_contract": "0x87ee27c5ae396b28a825968b277fece0720f5907" |
| 18 | + "target_calldata": "0xeadb38050000000000000000000000000000000000000000000000000000000000000064", |
| 19 | + "amount": "10", |
| 20 | +} |
| 21 | +``` |
| 22 | + |
| 23 | +The fields of this object are described in more detail below: |
| 24 | +- `chain_id`: The `string` identifying the chain on which the searcher wishes to submit the transaction. |
| 25 | +- `permission_key`: The `bytes` (in the form of a 0x-prefixed hex string) that serve as the unique identifying information for a position within a protocol |
| 26 | +- `target_contract`: The `address` of the contract the searcher wishes to call from the `ExpressRelay` contract. This could be the searcher's own contract or the [`OpportunityAdapterFactory` contract](./integrate-as-searcher/opportunity-adapter). |
| 27 | +- `target_calldata`: The `bytes` (in the form of a 0x-prefixed hex string) of the calldata the searcher wishes to call the `targetContract` with. |
| 28 | +- `amount`: The amount of ETH (in wei) the searcher is bidding for their transaction's priority. |
| 29 | + |
| 30 | +### WebSocket |
| 31 | + |
| 32 | +The WebSocket version of the above bid is very similar: |
| 33 | + |
| 34 | +``` |
| 35 | +{ |
| 36 | + "id": "1", |
| 37 | + "method": "post_bid", |
| 38 | + "params": { |
| 39 | + "bid": { |
| 40 | + "chain_id": "op_sepolia", |
| 41 | + "permission_key": "0x00000000000000000000000087ee27c5ae396b28a825968b277fece0720f5907000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000", |
| 42 | + "target_contract": "0x87ee27c5ae396b28a825968b277fece0720f5907" |
| 43 | + "target_calldata": "0xeadb38050000000000000000000000000000000000000000000000000000000000000064", |
| 44 | + "amount": "10", |
| 45 | + } |
| 46 | + } |
| 47 | +} |
| 48 | +``` |
| 49 | + |
| 50 | +A successful response to bid submission has the following schema: |
| 51 | + |
| 52 | +``` |
| 53 | +{ |
| 54 | + "id": "1", // websocket request id |
| 55 | + "status": "success" |
| 56 | + "result": { |
| 57 | + "id": "beedbeed-b346-4fa1-8fab-2541a9e1872d", // bid id |
| 58 | + "status": "OK" |
| 59 | + } |
| 60 | +} |
| 61 | +``` |
| 62 | + |
| 63 | +From this point on, you will receive notifications about the bid status updates in JSON format. There are four types of bid status updates ("pending", "submitted", "lost", "won"), and you can find more details and examples in the `BidStatus` schema in the Schemas section of the API documentation (TODO: INSERT SWAGGER DOCS). |
| 64 | + |
| 65 | +#### WebSocket connection persistence |
| 66 | + |
| 67 | +The WebSocket server responds to ping messages according to WebSocket standards. Additionally, the server periodically sends a ping message to the client to ensure the connection is still active and expects a pong in return. |
| 68 | + |
| 69 | +## Discovering transactions to bid on |
| 70 | + |
| 71 | +In addition, the server has a set of opportunity endpoints that expose [opportunities](./how-express-relay-works/opportunities) as they become available. An `Opportunity` has the following structure: |
| 72 | + |
| 73 | +``` |
| 74 | +{ |
| 75 | + "chain_id": "op_sepolia", |
| 76 | + "permission_key": "0x00000000000000000000000087ee27c5ae396b28a825968b277fece0720f5907000000000000000000000000000000000000000000000000000000000000004000000000000000000000000000000000000000000000000000000000000000200000000000000000000000000000000000000000000000000000000000000000", |
| 77 | + "target_contract": "0x87ee27c5ae396b28a825968b277fece0720f5907", |
| 78 | + "target_calldata": "0xdeadbeef", |
| 79 | + "target_call_value": "1", |
| 80 | + "buy_tokens": [ |
| 81 | + { |
| 82 | + "amount": "3", |
| 83 | + "contract": "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2" |
| 84 | + } |
| 85 | + ], |
| 86 | + "sell_tokens": [ |
| 87 | + { |
| 88 | + "amount": "1", |
| 89 | + "contract": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599" |
| 90 | + } |
| 91 | + ], |
| 92 | + "version": "v1" |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +The fields of the `Opportunity` are described below: |
| 97 | +- `chain_id`: The chain id where the opportunity is available |
| 98 | +- `permission_key`: The permission key corresponding to the opportunity |
| 99 | +- `target_contract`: The protocol contract address where the opportunity is available |
| 100 | +- `target_calldata`: The calldata to be used to execute the opportunity |
| 101 | +- `target_call_value`: The value to be sent to the `target_contract` in wei |
| 102 | +- `buy_tokens`: The tokens to be received as a result of the execution of the opportunity |
| 103 | +- `sell_tokens`: The tokens to be spent when executing the opportunity |
| 104 | + |
| 105 | +Note that the `target_contract` and `target_calldata` fields of an `Opportunity` are not intended to be used for the fields in a `Bid`. Rather, this `target_contract` and `target_calldata` represent parameters for the call to the [`OpportunityAdapter` contract](./integrate-as-searcher/opportunity-adapter). |
| 106 | + |
| 107 | +To query current opportunities, searchers can either make a HTTP `/v1/opportunities` GET request or subscribe to WebSocket updates on opportunities. To subscribe via WebSocket, you can send a request with the chain_ids parameter which specifies the chains as an array: |
| 108 | + |
| 109 | +``` |
| 110 | +{ |
| 111 | + "id": "1", |
| 112 | + "method": "subscribe", |
| 113 | + "params": { |
| 114 | + "chain_ids": ["op_sepolia"] |
| 115 | + } |
| 116 | +} |
| 117 | +``` |
| 118 | + |
| 119 | +After a successful subscription you will receive the new opportunities for the selected chains via the websocket in the following format: |
| 120 | + |
| 121 | +``` |
| 122 | +{ |
| 123 | + "type": "new_opportunity", |
| 124 | + "opportunity": {...} |
| 125 | +} |
| 126 | +``` |
| 127 | + |
| 128 | +with the opportunity being presented in a schema similar to above. |
| 129 | + |
| 130 | +In order to unsubscribe from a list of chains, you can send the following message: |
| 131 | + |
| 132 | +``` |
| 133 | +{ |
| 134 | + "id": "1", |
| 135 | + "method": "unsubscribe", |
| 136 | + "params": { |
| 137 | + "chain_ids": ["op_sepolia"] |
| 138 | + } |
| 139 | +} |
| 140 | +``` |
| 141 | + |
| 142 | +For most searchers, it is recommended to source opportunities via the opportunity endpoints and construct transactions intended for the [`OpportunityAdapter`](./integrate-as-searcher/opportunity-adapter). The SDKs expose methods to craft the calldata for the `OpportunityAdapter` contract and construct and submit the `Bid`. |
| 143 | + |
| 144 | +If you prefer to use your own custom contracts rather than the `OpportunityAdpater` contract for executing opportunities, you can still use the server's opportunity endpoints to learn about opportunities on specific protocols and then craft a `Bid` based on an `Opportunity`. Here is a simplified example of a custom contract method that uses the `Opportunity` information to call a liquidation. |
| 145 | + |
| 146 | +```solidity |
| 147 | +... |
| 148 | +function callLiquidation(Opportunity memory opp){ |
| 149 | +
|
| 150 | + // this is a simplified version since sell and buy tokens are |
| 151 | + // an array and multiple assets may be necessary for the liquidation |
| 152 | + assert(opp.buy_tokens.length == 1); |
| 153 | + assert(opp.sell_tokens.length == 1); |
| 154 | +
|
| 155 | + IERC20(opp.sell_tokens[0].contract).approve(opp.contract, opp.sell_tokens[0].amount); |
| 156 | +
|
| 157 | + before = IERC20(opp.buy_tokens[0].contract).balanceOf(address(this)); |
| 158 | + opp.target_contract.call{value: opp.target_call_value}(opp.target_calldata); |
| 159 | +
|
| 160 | + after = IERC20(opp.buy_tokens[0].target_contract).balanceOf(address(this)); |
| 161 | +
|
| 162 | + assert(after == before + opportunity.buy_tokens[0].amount) |
| 163 | +} |
| 164 | +... |
| 165 | +``` |
| 166 | + |
| 167 | +Note that the protocol contract (`opp.contract`) expects the caller to have already approved the necessary amount from the `sell_tokens` to the protocol. Also note that the custom contract will be expected to pay the specified bid in ETH while it is called. |
0 commit comments