+ );
+};
+
+export default AddressTable;
diff --git a/components/icons/CodeIcon.tsx b/components/icons/CodeIcon.tsx
new file mode 100644
index 00000000..a770769b
--- /dev/null
+++ b/components/icons/CodeIcon.tsx
@@ -0,0 +1,15 @@
+const CodeIcon = ({ className }: { className: string }) => {
+ return (
+
+ );
+};
+
+export default CodeIcon;
diff --git a/components/icons/ContractIcon.tsx b/components/icons/ContractIcon.tsx
new file mode 100644
index 00000000..f3df4589
--- /dev/null
+++ b/components/icons/ContractIcon.tsx
@@ -0,0 +1,20 @@
+const ContractIcon = ({ className }: { className: string }) => {
+ return (
+
+ );
+};
+
+export default ContractIcon;
diff --git a/components/icons/QuestionMarkIcon.tsx b/components/icons/QuestionMarkIcon.tsx
new file mode 100644
index 00000000..52d57f39
--- /dev/null
+++ b/components/icons/QuestionMarkIcon.tsx
@@ -0,0 +1,18 @@
+const QuestionMarkIcon = ({ className }: { className: string }) => {
+ return (
+
+ );
+};
+
+export default QuestionMarkIcon;
diff --git a/components/icons/SearchIcon.tsx b/components/icons/SearchIcon.tsx
new file mode 100644
index 00000000..bd85911c
--- /dev/null
+++ b/components/icons/SearchIcon.tsx
@@ -0,0 +1,20 @@
+const SearchIcon = ({ className }: { className: string }) => {
+ return (
+
+ );
+};
+
+export default SearchIcon;
diff --git a/images/express_relay/after_express.jpg b/images/express_relay/after_express.jpg
new file mode 100644
index 00000000..1481dee0
Binary files /dev/null and b/images/express_relay/after_express.jpg differ
diff --git a/images/express_relay/after_searcher.jpg b/images/express_relay/after_searcher.jpg
new file mode 100644
index 00000000..a3765986
Binary files /dev/null and b/images/express_relay/after_searcher.jpg differ
diff --git a/images/express_relay/before_express.jpg b/images/express_relay/before_express.jpg
new file mode 100644
index 00000000..9db37f9e
Binary files /dev/null and b/images/express_relay/before_express.jpg differ
diff --git a/images/express_relay/before_searcher.jpg b/images/express_relay/before_searcher.jpg
new file mode 100644
index 00000000..46a9024a
Binary files /dev/null and b/images/express_relay/before_searcher.jpg differ
diff --git a/images/express_relay/express_relay_schematic.svg b/images/express_relay/express_relay_schematic.svg
new file mode 100644
index 00000000..414ef8de
--- /dev/null
+++ b/images/express_relay/express_relay_schematic.svg
@@ -0,0 +1,21 @@
+
diff --git a/pages/_meta.json b/pages/_meta.json
index dbf3aba1..2518e2ca 100644
--- a/pages/_meta.json
+++ b/pages/_meta.json
@@ -14,6 +14,11 @@
"type": "page"
},
+ "express-relay": {
+ "title": "Express Relay",
+ "type": "page"
+ },
+
"entropy": {
"title": "Entropy",
"type": "page"
diff --git a/pages/express-relay/_meta.json b/pages/express-relay/_meta.json
new file mode 100644
index 00000000..8fd29aca
--- /dev/null
+++ b/pages/express-relay/_meta.json
@@ -0,0 +1,45 @@
+{
+ "documentation-home": {
+ "title": "← Documentation Home",
+ "href": "/home"
+ },
+
+ "-- Express Relay": {
+ "title": "Express Relay",
+ "type": "separator"
+ },
+ "index": "Introduction",
+
+ "-- How-to Guides": {
+ "title": "How-To Guides",
+ "type": "separator"
+ },
+
+ "integrate-as-protocol": "Integrate as a Protocol",
+ "integrate-as-searcher": "Integrate as a Searcher",
+
+ "-- Reference Material": {
+ "title": "Reference Material",
+ "type": "separator"
+ },
+
+ "api-reference": {
+ "title": "HTTP API Reference ↗",
+ "href": "https://pyth-express-relay-mainnet.asymmetric.re/redoc",
+ "newWindow": true
+ },
+ "websocket-api-reference": "Websocket API Reference",
+ "contract-addresses": "Contract Addresses",
+ "errors": "Error Codes",
+ "examples": {
+ "title": "Example Application ↗",
+ "href": "https://github.com/pyth-network/pyth-examples/tree/main/express-relay/easy_lend",
+ "newWindow": true
+ },
+ "-- Understand Express Relay": {
+ "title": "Understanding Express Relay",
+ "type": "separator"
+ },
+
+ "how-express-relay-works": "How Express Relay Works"
+}
diff --git a/pages/express-relay/contract-addresses.mdx b/pages/express-relay/contract-addresses.mdx
new file mode 100644
index 00000000..c2eeb634
--- /dev/null
+++ b/pages/express-relay/contract-addresses.mdx
@@ -0,0 +1,138 @@
+import { Tabs } from "nextra/components";
+import AddressTable from "../../components/AddressTable";
+
+Express Relay is currently deployed on the following networks:
+
+
+
+
+Auction Server endpoint: https://pyth-express-relay-mainnet.asymmetric.re/
+
+### Mode
+
+**Network Details:**
+
+
+
+This list contains the addresses of the commonly used assets present in opportunities on the Mode network:
+
+
+
+
+
+Auction Server endpoint: https://per-staging.dourolabs.app/
+
+### Optimism Sepolia
+
+**Network Details:**
+
+
+
+This list contains the addresses of the commonly used assets present in opportunities on the Optimism Sepolia network:
+
+
+
+
+
diff --git a/pages/express-relay/errors.mdx b/pages/express-relay/errors.mdx
new file mode 100644
index 00000000..c85bb60b
--- /dev/null
+++ b/pages/express-relay/errors.mdx
@@ -0,0 +1,33 @@
+# Error Codes
+
+The following table lists the error codes and their explanations for [`ExpressRelay`](https://github.com/pyth-network/per/blob/main/contracts/src/express-relay/ExpressRelay.sol) and [`OpportunityAdapter`](https://github.com/pyth-network/per/blob/main/contracts/src/opportunity-adapter/OpportunityAdapter.sol) contracts.
+They can be used to identify the cause of a failed transaction or bid.
+
+## ExpressRelay
+
+| Error | Selector | Explanation |
+| -------------------------------------------- | ------------ | ------------------------------------------------------------------------------------------- |
+| `Unauthorized()` | `0x82b42900` | This function is called by an unauthorized party. |
+| `InvalidMagicValue()` | `0x4ed848c1` | An upgrade was attempted to a contract that does not match the ExpressRelay specification. |
+| `InvalidPermission()` | `0x868a64de` | The provided permissionKey is invalid (too short). |
+| `InvalidFeeSplit()` | `0x0601f697` | The proposed fee split is invalid (fee is larger than feePrecision, 10\*\*18). |
+| `InvalidTargetContract()` | `0x5569851a` | The provided target contract is not allowed. (e.g. can not call the ExpressRelay contract). |
+| `DuplicateRelayerSubwallet()` | `0xb40d37c3` | The provided subwallet to add has already been added. |
+| `RelayerSubwalletNotFound()` | `0xac4d92b3` | The provided subwallet to delete does not exist in the store. |
+| `ExternalCallFailed(MulticallStatus status)` | `0x740d0306` | The external call failed with the following MulticallStatus output. |
+
+## OpportunityAdapter
+
+| Error | Selector | Explanation |
+| -------------------------------------- | ------------ | ---------------------------------------------------------------------------------------------------------------- |
+| `NotCalledByExpressRelay()` | `0xd5668c88` | The OpportunityAdapterFactory contract was not called by the ExpressRelay contract. |
+| `NotCalledByFactory()` | `0xb02436cc` | The OpportunityAdapter contract was not called by the OpportunityAdapterFactory contract. |
+| `AdapterOwnerMismatch()` | `0x446f3eeb` | The provided executor field does not match the owner of the called OpportunityAdapter contract. |
+| `InsufficientTokenReceived()` | `0x4af147aa` | The specified buyTokens were not received after calling the target contract. |
+| `InsufficientEthToSettleBid()` | `0x9caaa1d7` | The contract did not receive enough ETH to pay the specified bid. |
+| `InsufficientWethForTargetCallValue()` | `0x5e520cd4` | The contract did not receive enough Wrapped ETH to pay the targetCallValue to the targetContract. |
+| `TargetCallFailed(bytes returnData)` | `0xa932c97a` | The call to targetContract failed with the specified returnData. |
+| `DuplicateToken()` | `0x464e3f6a` | There is a duplicate token in either the sellTokens or buyTokens. |
+| `EthOrWethBalanceDecreased()` | `0x1979776d` | The ETH or WETH balance of the contract decreased as a result of the call to targetContract and the bid payment. |
+| `TargetContractNotAllowed()` | `0x9c86e59e` | The provided targetContract is not allowed. (e.g. can not call the Permit2 contract). |
+| `OnlyOwnerCanCall()` | `0x47a8ea58` | Only the owner of the contract can call this method. |
diff --git a/pages/express-relay/how-express-relay-works.mdx b/pages/express-relay/how-express-relay-works.mdx
new file mode 100644
index 00000000..5710f712
--- /dev/null
+++ b/pages/express-relay/how-express-relay-works.mdx
@@ -0,0 +1,42 @@
+# How Express Relay Works
+
+Express Relay allows protocols to eliminate [Maximal Extractable Value](https://www.ledger.com/academy/glossary/maximal-extractable-value-mev) (MEV).
+Many protocols generate MEV on a regular basis.
+For example, borrow-lending protocols provide bonuses to searchers for liquidating undercollateralized loans.
+Searchers compete for these bonuses by tipping the chain's miners or validators.
+The validators capture most of the value of the liquidation bonus via these tips, so the liquidation bonus is in essence a transfer of wealth from the protocol's users to the validators in the form of tips.
+
+Express Relay solves the problem of MEV by providing protocol developers with an [auction](./how-express-relay-works/auction.mdx) primitive that they can use to prioritize access to valuable protocol operations.
+Developers specify a set of operations in their protocol that must be accessed through Express Relay.
+Searchers then participate in an off-chain auction to access these operations.
+Their bids in the auction are used to determine the order in which their transactions will be executed.
+The winners' transactions are forwarded to the Express Relay smart contract. As part of the transaction, searchers must pay their specified bid.
+The auction profits are then split between the integrated protocol and other participants in Express Relay.
+
+
+
+
+The diagram above shows how Express Relay changes the MEV landscape for a liquidation.
+In the status quo (above), Searchers tip miners to guarantee that their liquidation transaction lands on-chain and that their transaction directly interacts with the protocol, exposing the liquidation opportunity.
+With Express Relay (below), Searchers submit bids for their transaction to the Express Relay auction.
+After the auction, the winning bids are relayed to the blockchain, where the Express Relay smart contract processes the transactions before being forwarded on to the integrated protocol.
+The Express Relay contract collects payment from the Searchers and forwards a share of the revenue back to the integrated protocol.
+
+## Which protocols can use Express Relay?
+
+Any protocol with valuable operations can use Express Relay.
+These operations generate MEV, as the validators control which searchers have the right to access them.
+Express Relay enables protocols to auction access instead of the validators.
+Lending, perps, and derivatives protocols with liquidation mechanisms are clear candidates that can benefit from integration with Express Relay.
+
+Aside from eliminating MEV, protocols that need a stable set of searchers would benefit from using Express Relay.
+Express Relay provides access to a robust network of searchers who are already active in the Express Relay ecosystem.
+
+## Participants in Express Relay
+
+There are four types of participants in the Express Relay protocol:
+
+- The Relayer runs the off-chain auction and forwards winning transactions onto the blockchain.
+- Protocol developers integrate their protocol with Express Relay in order to eliminate MEV and gain access to searchers.
+- Searchers participate in auctions to access on-chain opportunities such as liquidations.
+- The Pyth DAO owns and governs the Express Relay system.
diff --git a/pages/express-relay/how-express-relay-works/_meta.json b/pages/express-relay/how-express-relay-works/_meta.json
new file mode 100644
index 00000000..9dab3992
--- /dev/null
+++ b/pages/express-relay/how-express-relay-works/_meta.json
@@ -0,0 +1,5 @@
+{
+ "auction": "Auction",
+ "opportunities": "Opportunities",
+ "permissioning": "Permissioning"
+}
diff --git a/pages/express-relay/how-express-relay-works/auction.mdx b/pages/express-relay/how-express-relay-works/auction.mdx
new file mode 100644
index 00000000..bf81f6fc
--- /dev/null
+++ b/pages/express-relay/how-express-relay-works/auction.mdx
@@ -0,0 +1,21 @@
+# Auction
+
+The auction in Express Relay is held off-chain at the auction server.
+Bids arrive at the auction server and compete against other bids, vying for the same [permission key](./permissioning.mdx).
+A relayer selected by governance serves as the auctioneer and determines the auction in line with the criterion of maximizing the revenue shared back to the protocol that generated this opportunity. That means the auctioneer is expected to forward on-chain the subset of bids that maximizes the revenue back to the protocol.
+
+Thus, the Express Relay auction is analogous to a sealed-bid auction, i.e., participants in the auction will not have the contents of their bid disclosed publicly unless they win the auction and are forwarded on-chain.
+
+The forwarded subset of transactions is submitted on-chain and first processed by the [`ExpressRelay`](https://github.com/pyth-network/per/blob/main/contracts/src/express-relay/ExpressRelay.sol) contract before individual searchers' submissions are routed to their corresponding `targetContract`s.
+
+Generally, the auction server expects bids to execute successfully on-chain. Falback bids are also forwarded in case of execution failures for the predicted winners.
+
+The `ExpressRelay` contract extracts the payment of the specified bid amount only if the searcher's bid is successfully executed on-chain.
+Hence, the Express Relay auction can be seen as a generalization of a [first-price sealed-bid auction](https://en.wikipedia.org/wiki/First-price_sealed-bid_auction), in that multiple bids can win and pay their first price.
+
+The revenue from the auction is shared amongst relevant stakeholders in the Express Relay system. These stakeholders include:
+
+- the protocol that generates the relevant opportunity
+- the relayer, which handles running the off-chain components of the system
+
+The Express Relay contract enforces the exact revenue splits and is subject to change based on governance decisions.
diff --git a/pages/express-relay/how-express-relay-works/opportunities.mdx b/pages/express-relay/how-express-relay-works/opportunities.mdx
new file mode 100644
index 00000000..2bf6a5d2
--- /dev/null
+++ b/pages/express-relay/how-express-relay-works/opportunities.mdx
@@ -0,0 +1,55 @@
+# Opportunities
+
+In the context of Express Relay, an opportunity refers to a potential transaction that a searcher can execute on a protocol. Typically, the term "opportunity" is used for such transactions that are lucrative and therefore competed for by many searchers.
+
+In the pre-Express Relay world, opportunities corresponded to MEV: a protocol generated MEV when an opportunity appeared on that protocol and searchers bid up the right to execute the opportunity at the validator level.
+
+In the context of Express Relay, the value deriving from an opportunity no longer flows to the validator but instead is recaptured by the system and shared back with the protocol. In Express Relay, protocols expose opportunities to the network of integrated searchers, and the searchers then bid on priority to execute those opportunities. Critically, the auction has moved from being determined by validators to a lower level of the stack (the protocol level).
+
+## Oracle-agnostic
+
+Opportunities do not refer to only transactions that use an oracle. In truth, any transaction that is lucrative but limited (available to only the first user(s) who executes it) generates MEV. As a result, Express Relay and the opportunity schema have been designed to be oracle-agnostic.
+
+Examples of opportunities include:
+
+- liquidations
+- open trade offers
+- NFT mints
+- top-of-block DEX arbitrage
+
+## Opportunity Adapter
+
+The Opportunity Adapter contract enables searchers to engage with opportunities from different protocols without needing to do any bespoke integration work per protocol. Instead of exposing lower-level fields determined by protocols (e.g. `amountCollateral`, `addressBorrower`), the Opportunity Adapter abstracts away the semantics of the opportunity and instead [exposes the fundamental traits](https://github.com/pyth-network/per/blob/30c3fc695034f518225f8255ebe8423604e8aca3/contracts/src/opportunity-adapter/Structs.sol#L20-L23) of any opportunity:
+
+- the tokens sold by the searcher
+- the tokens bought by the searcher
+- the identity of the searcher executing this opportunity
+- the contract to call and the calldata and value to call with
+- the user's bid
+
+Thus, the Opportunity Adapter unifies the disparate interfaces of different protocols with respect to their different opportunities. This unification in a single interface is what makes Express Relay an opportunity hub connecting protocols and searchers.
+
+To use the Opportunity Adapter workflow, a searcher submits a bid with the target contract set to the `OpportunityAdapterFactory` contract. The factory contract then routes the searcher's transaction to their respective `OpportunityAdapter` contract. Each searcher has their own `OpportunityAdapter` contract per chain that is automatically created for them when they first interact with `OpportunityAdapterFactory`.
+
+### Permit2
+
+The `OpportunityAdapter` contract uses the [Permit2](https://github.com/Uniswap/permit2) token approval system, which handles the validation of a searcher's signature. Permit2 enables users to authorize token approvals for specific transaction data. In combination with Permit2, `OpportunityAdapter` allows a searcher to authorize use of their tokens only with a call to a particular contract with specified calldata and conditional on receipt of a set of specified `buyTokens`.
+
+## Opportunity Server
+
+Once on-chain opportunities arise, searchers can be notified of their existence via the opportunity server. Like the auction server, this is an off-chain piece of the Express Relay stack that is run by an infrastructure partner designated by the DAO. The opportunity server presents opportunities to searchers in a way that abstracts away bespoke integration work for the searcher: instead of worrying about crafting calldata specific to every new liquidation interface, searchers now view liquidation opportunities in terms of tokens they need to sell (debt they repay) in exchange for tokens they buy (collateral they liquidate). This simplified interface unifies different liquidation schemas and allows searchers to easily plug into more protocols.
+
+In addition to learning about present opportunities, searchers can also query the opportunity server for data about historical opportunities.
+
+
+
+
+The diagrams above illustrate the difference between the nature of integrations pre- and post-Express Relay. In the status quo, each searcher must connect to every new protocol with a custom integration. Similarly, protocols have to court and incentivize individual searchers. This creates a lot of integration overhead and friction for both parties.
+
+With Express Relay, the integration is abstracted away, and searchers can connect to all current and future Express Relay-integrated protocols by **integrating once** with Express Relay. On the other hand, protocols that integrate with Express Relay gain access to the network of searchers integrated with Express Relay.
+
+## Opportunity Monitor
+
+Many integrated protocols with Express Relay build out an opportunity monitor script to query executable opportunities on their protocol and expose them to searchers. This allows them to tap into all Express Relay-integrated searchers, who do not need to do any additional integration work beyond having access to the relevant tokens to execute opportunities on the protocol.
+
+In terms of existing infrastructure, the opportunity monitor script is most analogous to a protocol's liquidation scripts/bots. Typically, many lending protocols build liquidation bot libraries that they share publicly in hopes of encouraging adoption by liquidators. With minor modifications, these scripts can serve as the opportunity monitor that enables all Express Relay-integrated searchers to participate in this protocol's liquidations.
diff --git a/pages/express-relay/how-express-relay-works/permissioning.mdx b/pages/express-relay/how-express-relay-works/permissioning.mdx
new file mode 100644
index 00000000..97a162ba
--- /dev/null
+++ b/pages/express-relay/how-express-relay-works/permissioning.mdx
@@ -0,0 +1,28 @@
+# Permissioning
+
+`permissionId` is a `bytes` object that represents the unique identifier of a position within the protocol. `permissionId` allows the system to distinguish between bids competing on different opportunities and thereby run more scoped and efficient auctions.
+
+Each borrower has a unique position for some protocols, so the borrower address uniquely identifies a position.
+In other protocols, each borrower might have multiple positions, distinguished by the address of the collateral asset or by a `uint256` ID number.
+In those cases, the information set that uniquely identifies a position would include multiple fields.
+
+`permissionId` can be the concatenation of all these fields in bytes format. You can call `abi.encode(){:solidity}` to concatenate these fields together.
+
+For example, if a protocol featured a unique position per borrower, then it could form `permissionId` as
+
+```solidity
+bytes memory permissionId = abi.encode(borrowerAddress);
+```
+
+On the other hand, if a protocol allowed a borrower to open as many new positions as they wanted, denoted by an identifier `uint256 positionId{:solidity}`, then it could form `permissionId` as
+
+```solidity
+bytes memory permissionId = abi.encode(borrowerAddress, positionId);
+```
+
+The Express Relay contract uses the `permissionId` to toggle permissions for interacting with the protocol.
+This toggling is checked within the protocol's code to ensure that the current transaction is within the context of Express Relay so that the recaptured value can be returned to the protocol. In particular, the Express Relay contract checks the toggling of the `permissionKey`, which is the concatenation of the protocol address and the `permissionId`:
+
+```solidity
+bytes memory permissionKey = abi.encode(protocolAddress, permissionId);
+```
diff --git a/pages/express-relay/index.mdx b/pages/express-relay/index.mdx
new file mode 100644
index 00000000..857a2fdc
--- /dev/null
+++ b/pages/express-relay/index.mdx
@@ -0,0 +1,52 @@
+import Link from "next/link";
+import { Cards, Card } from "nextra/components";
+import SearchIcon from "../../components/icons/SearchIcon";
+import ContractIcon from "../../components/icons/ContractIcon";
+import QuestionMarkIcon from "../../components/icons/QuestionMarkIcon";
+import CodeIcon from "../../components/icons/CodeIcon";
+
+# Introduction
+
+Express Relay is a priority auction that enables protocols to eliminate [Maximal Extractable Value](https://www.ledger.com/academy/glossary/maximal-extractable-value-mev) (MEV).
+
+- **For Protocol Developers:** Express Relay allows protocols to recapture MEV and access a network of searchers.
+ With Express Relay, protocols don't need to spend time and energy bootstrapping a protocol-specific searcher network.
+
+- **For Searchers:** Express Relay provides easy and unified access by aggregating liquidation and other MEV opportunities across integrated DeFi protocols.
+ Searchers integrate once and gain access to all existing and future opportunities.
+
+## Integration
+
+To integrate with Express Relay, you can integrate as a protocol or as a searcher.
+
+
+}
+ title="Integrate with Express Relay as a Protocol"
+ href="./express-relay/integrate-as-protocol"
+/>
+
+}
+ title="Integrate with Express Relay as a Searcher"
+ href="./express-relay/integrate-as-searcher"
+/>
+
+
+## Learn
+
+To learn more about Express Relay, refer to the following resources:
+
+
+}
+ title="How Express Relay Works"
+ href="./express-relay/how-express-relay-works "
+/>
+
+}
+ title="Example Protocol Integration"
+ href="https://github.com/pyth-network/pyth-examples/tree/main/express-relay/easy_lend"
+/>
+
diff --git a/pages/express-relay/integrate-as-protocol.mdx b/pages/express-relay/integrate-as-protocol.mdx
new file mode 100644
index 00000000..bcf56f96
--- /dev/null
+++ b/pages/express-relay/integrate-as-protocol.mdx
@@ -0,0 +1,236 @@
+import { Callout, Tabs, Steps } from "nextra/components";
+
+# How to Integrate Express Relay as a Protocol
+
+This guide will explain how DeFi protocols can integrate Express Relay.
+
+Integrating with Express Relay involves two main steps:
+
+- **Update** your DeFi protocol's contract.
+- **Expose** opportunities to searchers for auction.
+
+## Update your DeFi Protocol's Contract
+
+To integrate with Express Relay, your protocol's contract must check if Express Relay has permissioned the current transaction.
+
+
+### Install the Express Relay SDK
+
+Pyth provides a [Solidity SDK](https://www.npmjs.com/package/@pythnetwork/express-relay-sdk-solidity) to help developers integrate Express Relay into their DeFi protocol.
+The SDK exposes [`IExpressRelay`](https://github.com/pyth-network/pyth-crosschain/blob/main/express_relay/sdk/solidity/IExpressRelay.sol) and [`IExpressRelayFeeReceiver`](https://github.com/pyth-network/pyth-crosschain/blob/main/express_relay/sdk/solidity/IExpressRelayFeeReceiver.sol) interfaces to interact with Express Relay.
+
+
+
+ If you are using Hardhat, you can install the SDK using npm:
+
+```bash copy
+npm install @pythnetwork/express-relay-sdk-solidity
+```
+
+
+
+ If you are using Foundry, you must create an NPM project if you don't already have one. From the root directory of your project, run:
+
+```bash copy
+npm init -y
+npm install @pythnetwork/express-relay-sdk-solidity
+```
+
+Then add the following line to `remappings.txt` file:
+
+```text copy
+@pythnetwork/express-relay-sdk-solidity/=node_modules/@pythnetwork/express-relay-sdk-solidity
+```
+
+
+
+
+### Modify the Protocol's Contract
+
+The following steps show how to modify your protocol's contract to verify if the current transaction is permissioned by Express Relay and to receive the auction proceeds.
+
+1. Call the [`isPermissioned`](https://github.com/pyth-network/pyth-crosschain/blob/main/express_relay/sdk/solidity/IExpressRelay.sol#L10C14-L10C28) method from `IExpressRelay` interface to make sure the current transaction is permissioned by Express Relay.
+1. Implement the [`IExpressRelayFeeReceiver`](https://github.com/pyth-network/pyth-crosschain/blob/main/express_relay/sdk/solidity/IExpressRelayFeeReceiver.sol#L4) interface to **receive** auction proceeds.
+
+#### 1. Verify Permissioning
+
+The `isPermissioned` function takes two arguments:
+
+1. `protocolFeeReceiver`: The address to receive the protocol's share of auction proceeds.
+1. `permissionId`: A unique identifier for the opportunity.
+
+```solidity copy
+import "@pythnetwork/express-relay-sdk-solidity/IExpressRelay.sol";
+
+// Express Relay contract address on Optimism Sepolia
+//
+// Check https://docs.pyth.network/express-relay/contract-addresses
+// for the address deployed on other networks
+address expressRelay = 0xD6e417287b875A3932c1Ff5dcB26D4D2C8b90B40;
+
+require(
+ IExpressRelay(expressRelay).isPermissioned(
+ protocolFeeReceiver,
+ permissionId
+ ),
+ "not permissioned"
+);
+```
+
+
+ The `permissionId` represents a unique identifier of an opportunity. For a
+ liquidation opportunity, the vault address or ID could be concatenated into
+ `bytes` format. Consult
+ [`Permissioning`](./how-express-relay-works/permissioning.mdx) for more
+ information on generating permission IDs.
+
+
+#### 2. Set up Fee Receiver
+
+Express Relay will call the `receiveAuctionProceedings` method present in `IExpressRelayFeeReceiver`. The call will transfer the protocol's share of the auction proceeds to the `protocolFeeReceiver` address.
+
+```solidity copy
+interface IExpressRelayFeeReceiver {
+ function receiveAuctionProceedings(
+ bytes calldata permissionKey
+ ) external payable;
+}
+
+```
+
+The following code snippet shows a sample Express Relay-integrated contract that performs liquidation.
+_Note: The highlighted lines show the contract's relevant additions for Express Relay integration._
+
+```solidity showLineNumbers {1,2,12,14,21,45-50,65-69} copy
+import "@pythnetwork/express-relay-sdk-solidity/IExpressRelay.sol";
+import "@pythnetwork/express-relay-sdk-solidity/IExpressRelayFeeReceiver.sol";
+
+struct Vault {
+ address tokenCollateral;
+ address tokenDebt;
+ uint256 amountCollateral;
+ uint256 amountDebt;
+ uint256 minHealthRatio;
+}
+
+contract EasyLend is IExpressRelayFeeReceiver {
+
+ address public immutable expressRelay;
+
+ constructor(
+ address expressRelayAddress,
+ bool allowUndercollateralized
+ ){
+ _nVaults = 0;
+ expressRelay = expressRelayAddress;
+ _allowUndercollateralized = allowUndercollateralized;
+ }
+
+ /**
+ * @notice createVault function - creates a vault
+ * @param vaultParams: params of the vault to be created
+ */
+ function createVault(VaultParams memory vaultParams) public {
+ ..
+ }
+
+ /**
+ * @notice liquidate function - liquidates a vault
+ * @param vaultID: ID of the vault to be liquidated
+ */
+ function liquidate(uint256 vaultID) public {
+ Vault memory vault = _vaults[vaultID];
+ uint256 vaultHealth = _getVaultHealth(vault);
+ // Proceed only if the vault health is below the minimum health ratio
+ if (vaultHealth >= vault.minHealthRatio) {
+ revert InvalidLiquidation();
+ }
+
+ // Check if the liquidation is permissioned
+ bool permissioned = IExpressRelay(payable(expressRelay)).isPermissioned(
+ address(this),
+ abi.encode(vaultID) // permissionId generated from the unique vault ID
+ );
+ require(permissioned, "invalid liquidation");
+
+ IERC20(vault.tokenDebt).transferFrom(msg.sender,address(this),vault.amountDebt);
+ IERC20(vault.tokenCollateral).transfer(msg.sender,vault.amountCollateral);
+
+ _vaults[vaultID].amountCollateral = 0;
+ _vaults[vaultID].amountDebt = 0;
+ }
+
+ /**
+ * @notice receiveAuctionProceedings function - receives the native token from express relay
+ * You can use the permission key to distribute the received funds to users who got liquidated, LPs, etc...
+ *
+ * @param permissionKey: permission key that was used for the auction
+ */
+ function receiveAuctionProceedings(
+ bytes calldata permissionKey
+ ) external payable {
+ emit VaultReceivedETH(msg.sender, msg.value, permissionKey);
+ }
+
+}
+```
+
+
+
+## Expose Opportunities to Searchers
+
+DeFi protocols should fetch opportunities and expose them to Express Relay for auction.
+
+Express Relay provides a **POST** method, [`/v1/opportunities`](https://per-staging.dourolabs.app/redoc#tag/opportunity/operation/post_opportunity), which accepts a JSON payload containing the details of the opportunity.
+
+The JSON payload should contain opportunities in the following format:
+
+```bash copy
+{
+ "target_calldata": "0xdeadbeef", // Calldata to execute the opportunity
+ "chain_id": "op_sepolia",
+ "target_contract": "0xcA11bde05977b3631167028862bE2a173976CA11", // Protocol contract address to call
+ "permission_key": "0xcafebabe", // Unique identifier for the opportunity
+ "target_call_value": "1", // Value (in Wei) to send to the protocol contract.
+ "sell_tokens": [ // Tokens the protocol expects to receive
+ {
+ "amount": "900",
+ "token": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"
+ }
+ ],
+ "buy_tokens": [ // Tokens the protocol will send in return
+ {
+ "amount": "1000",
+ "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
+ }
+ ],
+ "version": "v1" // Opportunity format version
+}
+```
+
+Each protocol integrated with Express Relay must actively monitor for new opportunities.
+Protocols can do this by indexing the chain, listening to protocol events, or querying protocol state through an RPC provider.
+
+Check the [`monitor.ts`](https://github.com/pyth-network/pyth-crosschain/blob/main/express_relay/examples/easy_lend/src/monitor.ts) script,
+which fetches opportunities for the below-mentioned [Easy Lend](https://github.com/pyth-network/pyth-crosschain/tree/main/express_relay/examples/easy_lend) example and exposes them to Express Relay for auction.
+
+## Additional Resources
+
+You may find these additional resources helpful for integrating Express Relay as a protocol.
+
+### Example Application
+
+[Easy Lend](https://github.com/pyth-network/pyth-crosschain/tree/main/express_relay/examples/easy_lend) is a dummy lending protocol contract that allows users to borrow and lend assets.
+This lending protocol contract is updated to use Express Relay.
+
+### Contract Address
+
+The [Contract Address](./contract-addresses.mdx) page lists the addresses of Express Relay deployed on various networks.
+
+### Error Codes
+
+The [Error Codes](./error-codes.mdx) page lists the error codes returned by Express Relay.
+
+### API Reference
+
+The [API Reference](https://per-staging.dourolabs.app/redoc/) provides detailed information on Express Relay APIs for submitting opportunities.
diff --git a/pages/express-relay/integrate-as-searcher.mdx b/pages/express-relay/integrate-as-searcher.mdx
new file mode 100644
index 00000000..4a7cffd9
--- /dev/null
+++ b/pages/express-relay/integrate-as-searcher.mdx
@@ -0,0 +1,263 @@
+import { Callout, Tabs, Steps } from "nextra/components";
+
+# How to Integrate Express Relay as a Searcher
+
+Express Relay allows searchers to integrate once and access all existing and future opportunities across integrated DeFi protocols.
+Searchers **bid** on these opportunities exposed by Express Relay.
+
+Express Relay exposes different endpoints for interaction, which can be used directly via HTTP, WebSocket, or one of the SDKs for convenience.
+
+Searchers can integrate with Express Relay in three steps:
+
+1. Subscribe to new opportunities
+2. Construct the bid
+3. Submit the bid to Express Relay.
+
+
+
+### Subscribe to New Opportunities
+
+Express Relay provides searchers with [Typescript](https://github.com/pyth-network/pyth-crosschain/tree/main/express_relay/sdk/js) and [Python](https://github.com/pyth-network/pyth-crosschain/tree/main/express_relay/sdk/python) SDKs to interact with Express Relay.
+Searchers can also directly fetch available opportunities via HTTP or subscribe to them via WebSocket.
+
+
+
+
+Pyth provides a Typescript SDK, which allows searchers to subscribe to opportunities:
+
+```typescript
+import { Client, Opportunity } from "@pythnetwork/express-relay-evm-js";
+
+const handleOpporunity = 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
+);
+await client.subscribeChains(["op_sepolia"]);
+```
+
+
+
+Pyth provides a Python SDK, which allows searchers to subscribe to opportunities:
+
+```python copy
+from express_relay.client import (
+ ExpressRelayClient,
+)
+from express_relay.express_relay_types import Opportunity
+
+def opportunity_callback(opportunity: Opportunity):
+ # Implement your opportunity handler here
+ pass
+
+client = ExpressRelayClient(
+ 'server_url',
+ None,
+ opportunity_callback,
+ None,
+)
+await client.subscribe_chains(['op_sepolia'])
+```
+
+
+
+Searchers can request opportunities through an HTTP **GET** call to the [`/v1/opportunities`](https://pyth-express-relay-mainnet.asymmetric.re/redoc#tag/opportunity/operation/get_opportunities) endpoint.
+
+```bash copy
+curl -X 'GET' \
+ 'https://pyth-express-relay-mainnet.asymmetric.re/v1/opportunities?chain_id=op_sepolia&mode=live'
+```
+
+Opportunities are short-lived and could be executed in a matter of seconds. So, the above endpoint could return an empty response.
+
+
+
+Searchers can connect to the server via WebSocket to reduce latency and subscribe to various events. The WebSocket endpoint lives at `/v1/ws`(e.g `wss://pyth-express-relay-mainnet.asymmetric.re/v1/ws`).
+Here is a sample JSON payload to subscribe to opportunities:
+
+```bash copy
+{
+ "id": "1",
+ "method": "subscribe",
+ "params": {
+ "chain_ids": ["op_sepolia"]
+ }
+}
+```
+
+Consult [`Websocket API reference`](./websocket-api-reference.mdx) for a complete list of methods and parameters.
+
+
+
+
+The server responds with opportunities in the following format:
+
+```bash copy
+{
+ "target_calldata": "0xdeadbeef", // Calldata to execute the opportunity
+ "chain_id": "op_sepolia",
+ "target_contract": "0xcA11bde05977b3631167028862bE2a173976CA11", // Protocol contract address to call
+ "permission_key": "0xcafebabe", // Unique identifier for the opportunity
+ "target_call_value": "1", // Value (in Wei) to send to the protocol contract.
+ "sell_tokens": [ // Tokens the protocol expects to receive
+ {
+ "amount": "900",
+ "token": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"
+ }
+ ],
+ "buy_tokens": [ // Tokens the protocol will send in return
+ {
+ "amount": "1000",
+ "token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
+ }
+ ],
+ "version": "v1" // Opportunity format version
+}
+```
+
+### Construct the Bid
+
+Searchers should construct a bid by evaluating the fetched opportunity.
+The SDKs provide an easy way to construct a bid using the [`OpportunityAdapter`](./integrate-as-searcher/opportunity-adapter.mdx) contract.
+The `OpportunityAdapter` contract handles asset transfers and ensures the opportunity is executed correctly.
+
+
+ Before constructing the bid, make sure your wallet has the required assets and
+ has made the necessary approvals. Refer to the [Prepare assets for Opportunity
+ Adapter](./integrate-as-searcher/opportunity-adapter.mdx) section for more
+ details.
+
+
+Here is an example of how to construct a bid object using `OpportunityAdapter` via the SDKs:
+
+
+
+```typescript copy
+const handleOpportunity = async (opportunity: Opportunity) => {
+ const nonce = BigInt(Math.floor(Math.random() * 2 ** 50));
+ const privateKey = `0x0000`; // Private key of the searcher
+ const bidAmount = BigInt(1000); // The bidAmount should be determined based on opportunity
+ const deadline = BigInt(Math.round(Date.now() / 1000 + 60)) // Bid is valid for a minute
+ const bid = await client.signBid(opportunity, {bidAmount, nonce, deadline}, privateKey)
+}
+```
+
+
+```python copy
+from datetime import datetime
+
+from express_relay.client import (
+ExpressRelayClient,
+sign_bid
+)
+from secrets import randbits
+from express_relay.express_relay_types import Opportunity, OpportunityBidParams
+
+def opportunity_callback(opportunity: Opportunity):
+nonce = randbits(64)
+deadline = datetime.utcnow().timestamp() + 60 # Bid is valid for a minute
+bid_amount = 1000 # The bid_amount should be determined based on the opportunity
+private_key = '0x00000' # Private key of the searcher
+signed_bid = sign_bid(opportunity,
+OpportunityBidParams(amount=bid_amount, deadline=int(deadline), nonce=nonce),
+private_key)
+
+````
+
+
+
+
+
+
+If you prefer to use your custom contracts instead of the `OpportunityAdapter`, please refer to the [custom contracts](./integrate-as-searcher/custom-contract.mdx) section.
+
+### Submit Bids on Opportunities to Express Relay
+
+Searchers can submit the constructed bids to Express Relay via the SDKs, an HTTP POST request, or a WebSocket connection.
+
+
+
+
+
+The code snippet below demonstrates how to submit a bid using the Typescript SDK:
+
+```typescript {4} copy
+const handleOpporunity = async (opportunity: Opportunity) => {
+ ...
+ const bid = await client.signBid(opportunity, {amount, nonce, deadline}, privateKey)
+ await client.submitBid(bid)
+}
+````
+
+
+
+
+The code snippet below demonstrates how to submit a bid using the Python SDK:
+
+```python {5} copy
+def opportunity_callback(opportunity: Opportunity):
+ signed_bid = sign_bid(opportunity,
+ OpportunityBidParams(amount=amount, deadline=int(deadline), nonce=nonce),
+ private_key)
+ await client.submit_bid(signed_bid, subscribe_to_updates=True)
+```
+
+
+
+Searchers can submit bids through an HTTP POST call to the [`/v1/bids`](https://pyth-express-relay-mainnet.asymmetric.re/redoc#tag/bid/operation/bid) endpoint. This endpoint accepts a JSON payload containing the details of the bid.
+
+```bash copy
+curl -X POST https://pyth-express-relay-mainnet.asymmetric.re/v1/bids \
+ -H "Content-Type: application/json" \
+ -d '{
+ "chain_id": "op_sepolia",
+ "permission_key": "0x000000000000000000000000",
+ "target_contract": "0x87ee27c5ae396b28a825968b277fece0720f5907",
+ "target_calldata": "0xeadb38050000000000000000000000000000000000000000000000000000000000000064",
+ "amount": "10"
+}'
+```
+
+
+
+
+Searchers can submit bids via Websocket to avoid additional network round-trips and get notified about changes to the bid status.
+
+```bash copy
+{
+ "id": "1",
+ "method": "post_bid",
+ "params": {
+ "bid": {
+ "chain_id": "op_sepolia",
+ "permission_key": "0x000000000000000000000000",
+ "target_contract": "0x87ee27c5ae396b28a825968b277fece0720f5907",
+ "target_calldata": "0xeadb38050000000000000000000000000000000000000000000000000000000000000064",
+ "amount": "10"
+ }
+ }
+}
+```
+
+A successful response to a bid submission has the following schema:
+
+```bash copy
+{
+ "id": "1", // Websocket request id
+ "status": "success",
+ "result": {
+ "id": "beedbeed-b346-4fa1-8fab-2541a9e1872d", // Bid id
+ "status": "OK"
+ }
+}
+```
+
+Consult [`Websocket API reference`](./websocket-api-reference.mdx) for more details.
+
+
+
+
diff --git a/pages/express-relay/integrate-as-searcher/_meta.json b/pages/express-relay/integrate-as-searcher/_meta.json
new file mode 100644
index 00000000..8b30349f
--- /dev/null
+++ b/pages/express-relay/integrate-as-searcher/_meta.json
@@ -0,0 +1,4 @@
+{
+ "opportunity-adapter": "Prepare Assets for Opportunity Adapter",
+ "custom-contract": "Use a Custom Contract"
+}
diff --git a/pages/express-relay/integrate-as-searcher/custom-contract.mdx b/pages/express-relay/integrate-as-searcher/custom-contract.mdx
new file mode 100644
index 00000000..fc26e5e1
--- /dev/null
+++ b/pages/express-relay/integrate-as-searcher/custom-contract.mdx
@@ -0,0 +1,40 @@
+import { Callout } from "nextra/components";
+
+# Custom Searcher Contract
+
+Searchers can use their in-house custom contracts to execute the opportunities Express Relay provides.
+In this case, they must construct the bids based on the opportunity details.
+
+Refer to the following example of a custom contract that executes liquidation transactions:
+
+```solidity copy
+...
+function callLiquidation(Opportunity memory opp){
+ for (uint i=0; i
+ Make sure to allow the [Express Relay contract](../contract-addresses.mdx) to
+ call the relevant methods in your custom contract.
+
diff --git a/pages/express-relay/integrate-as-searcher/opportunity-adapter.mdx b/pages/express-relay/integrate-as-searcher/opportunity-adapter.mdx
new file mode 100644
index 00000000..3a0442da
--- /dev/null
+++ b/pages/express-relay/integrate-as-searcher/opportunity-adapter.mdx
@@ -0,0 +1,46 @@
+import { Steps } from "nextra/components";
+
+# Prepare Assets for Opportunity Adapter
+
+The [`OpportunityAdapter`](https://github.com/pyth-network/per/blob/main/contracts/src/opportunity-adapter/OpportunityAdapter.sol) is a utility contract that can be used for easier integration with Express Relay while maintaining maximum security.
+In this integration, searchers use the [`Permit2`](https://github.com/Uniswap/permit2/) contract for safe asset management (`ERC20` tokens and Wrapped ETH) and the adapter contract to transfer bids, execute the opportunity, and ensure the assets are transferred correctly.
+Using the `OpportunityAdapter` means that a searcher does not need to deploy their own custom contract for inventory management and routing to the protocol contracts where liquidation takes place.
+
+In order to use `OpportunityAdapter`, you need to prepare your assets using the following steps. We use [`cast`](https://book.getfoundry.sh/cast/) as a simple tool to interact with the blockchain but you can use any other tool you prefer.
+
+
+### Transfer Tokens to your Wallet
+
+Searchers are expected to own all the tokens that they expect to provide as `sell_tokens` for different opportunities.
+They will also need to approve `WETH`, as the `OpportunityAdapter` will pay the bids by deducting the bid amounts from their wallet's `WETH` balance.
+
+You can find the `WETH` contract address on the [contract address page](../contract-addresses).
+Refer to the following command to deposit native ETH to `WETH`:
+
+```bash copy
+cast send \
+--private-key $PRIVATE_KEY \
+--rpc-url https://mainnet.mode.network/ \
+$WETH_ADDRESS \
+"deposit()" --value 0.1ether # Change the value to the amount you want to deposit
+```
+
+### Approve your Assets to Permit2
+
+Approve your tokens, including `WETH`, to the `Permit2` contract at `0x000000000022D473030F116dDEE9F6B43aC78BA3`.
+This step enables token spending with off-chain signatures.
+
+You can find the `Permit2` and `WETH` contract address on the [contract address page](../contract-addresses.mdx).
+
+Refer to the following command to approve a token to `Permit2`:
+
+```bash copy
+cast send \
+--private-key $PRIVATE_KEY \
+--rpc-url https://mainnet.mode.network/ \
+$TOKEN_ADDRESS \
+"approve(address spender, uint256 value)" \
+0x000000000022D473030F116dDEE9F6B43aC78BA3 100000000000000000000000000 # You can use a large amount to avoid approving multiple times
+```
+
+
diff --git a/pages/express-relay/websocket-api-reference.mdx b/pages/express-relay/websocket-api-reference.mdx
new file mode 100644
index 00000000..62b8be30
--- /dev/null
+++ b/pages/express-relay/websocket-api-reference.mdx
@@ -0,0 +1,197 @@
+import { Tabs } from "nextra/components";
+
+# WebSocket API Reference
+
+Searchers can connect to the server via WebSocket to reduce latency and subscribe to various events.
+The WebSocket endpoint lives at `/v1/ws`(e.g `wss://pyth-express-relay-mainnet.asymmetric.re/v1/ws`).
+
+## General format
+
+Each request sent to the server via WebSocket should be in the following JSON format:
+
+```json copy
+{
+ "id": "...", // used for uniquely identifying the response to this request
+ "method": "...", // name of the server method to invoke
+ "params": {...} // parameters necessary for the method
+}
+```
+
+The server responds using the same `id` specified in the request:
+
+```json
+{
+ "id": "...",
+ "status": "success",
+ "result": {}
+}
+```
+
+In case of error, the `status` field will be `error`, and the error message will be available in the `result` field as a string:
+
+```json
+{
+ "id": "...",
+ "status": "error",
+ "result": "..."
+}
+```
+
+## Subscribing to opportunities
+
+To subscribe to opportunities, you can send a request using the `chain_ids` parameter, which specifies the chains as an array.
+
+```json
+{
+ "id": "1",
+ "method": "subscribe",
+ "params": {
+ "chain_ids": ["op_sepolia"]
+ }
+}
+```
+
+After a successful subscription, you will receive new opportunities for the selected chains via the WebSocket in the following format:
+
+```json
+{
+ "type": "new_opportunity",
+ "opportunity": {...}
+}
+```
+
+The schema for the opportunity is similar to what’s returned in the [HTTP requests](https://pyth-express-relay-mainnet.asymmetric.re/redoc#tag/opportunity/operation/get_opportunities)
+
+To unsubscribe from a list of chains, you can send the following message:
+
+```json copy
+{
+ "id": "1",
+ "method": "unsubscribe",
+ "params": {
+ "chain_ids": ["op_sepolia"]
+ }
+}
+```
+
+## Submitting bids
+
+In addition to the HTTP methods, you can submit your bids via WebSocket in order to avoid additional network round trips and get notified about changes to your bid status.
+Here is an example JSON payload for submitting a new bid:
+
+```json copy
+{
+ "id": "1",
+ "method": "post_bid",
+ "params": {
+ "bid": {
+ "amount": "10",
+ "calldata": "0xdeadbeef",
+ "chain_id": "sepolia",
+ "contract": "0xcA11bde05977b3631167028862bE2a173976CA11",
+ "permission_key": "0xcafebabe"
+ }
+ }
+}
+```
+
+A successful response to a bid submission has the following schema:
+
+```json
+{
+ "id": "1", // WebSocket request id
+ "status": "success",
+ "result": {
+ "id": "beedbeed-b346-4fa1-8fab-2541a9e1872d", // bid id
+ "status": "OK"
+ }
+}
+```
+
+After submitting your bid via WebSocket, you will receive notifications about the bid status updates in JSON format.
+Refer to the four examples below, one for each of the status options (`pending`, `submitted`, `lost`, `won`):
+
+
+
+```json
+// pending
+// The temporary state, which means the auction for this bid is pending
+{
+ "type": "bid_status_update",
+ "status": {
+ "id": "1eaee2a4-01bf-4f6c-8a76-21fadb2c43b1",
+ "bid_status": {
+ "type": "pending"
+ }
+ }
+}
+```
+
+
+```json
+// submitted
+// The bid is submitted to the chain, which is placed at the given index of the transaction with the given hash
+// This state is temporary and will be updated to either lost or won after the conclusion of the auction
+{
+ "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 index of the bid calldata within the multicall bundle for the above transaction
+ "index": 0
+ }
+ }
+}
+```
+
+
+```json
+// lost
+// The bid lost the auction, which is concluded with the transaction with the given hash and index
+// The result will be None if the auction was concluded off-chain and no auction was submitted to the chain
+// The index will be None if the bid was not submitted to the chain and lost the auction by off-chain calculation
+// There are cases where the result is not none and the index is none.
+// It is because other bids were selected for submission to the chain, but not this one.
+{
+ "type": "bid_status_update",
+ "status": {
+ "id": "1eaee2a4-01bf-4f6c-8a76-21fadb2c43b1",
+ "bid_status": {
+ "type": "lost",
+ "result": "0x99c2bf411330ae997632f88abe8f86c0d1f4c448f7d5061319d23814a0fb1135"
+ }
+ }
+}
+```
+
+
+```json
+// won
+// The bid won the auction, which is concluded with the transaction with the given hash and index
+{
+ "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 index of the bid calldata within the multicall bundle for the above transaction
+ "index": 0
+ }
+ }
+}
+```
+
+
+
+## Connection Persistence
+
+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.
diff --git a/pages/home/_meta.json b/pages/home/_meta.json
index 1c5e58d5..fa7c78fb 100644
--- a/pages/home/_meta.json
+++ b/pages/home/_meta.json
@@ -16,6 +16,11 @@
"href": "/benchmarks"
},
+ "Express Relay": {
+ "title": "Express Relay →",
+ "href": "/express-relay"
+ },
+
"Entropy": {
"title": "Entropy →",
"href": "/entropy"
diff --git a/pages/home/index.mdx b/pages/home/index.mdx
index 0bdbe012..edc7353b 100644
--- a/pages/home/index.mdx
+++ b/pages/home/index.mdx
@@ -9,6 +9,7 @@ Pyth Network offers several products for developers:
- [Price Feeds](../price-feeds) provide real-time prices for 500+ assets on 55+ blockchain ecosystems, including Solana, many EVM chains,
Aptos, Sui, NEAR, and several Cosmos chains.
- [Benchmarks](../benchmarks) provides historical Pyth prices for both on- and off-chain use.
+- [Express Relay](../express-relay/) enables protocols to eliminate their MEV while gaining access to active searchers and liquidators.
- [Entropy](../entropy) allows developers to generate secure random numbers on the blockchain.
Please follow the links above to learn about each product and how to integrate them into your application.