diff --git a/components/AddressSvmTable.tsx b/components/AddressSvmTable.tsx index 68d29434..6cae3cb8 100644 --- a/components/AddressSvmTable.tsx +++ b/components/AddressSvmTable.tsx @@ -1,3 +1,5 @@ +import copy from "copy-to-clipboard"; +import CopyIcon from "./icons/CopyIcon"; import { StyledTd } from "./Table"; const AddressSvmTable = ({ @@ -11,15 +13,6 @@ const AddressSvmTable = ({ {entries.map(({ name, value, link }) => { - const component = ( - - {value} - - ); const addLink = explorer.includes("$ADDRESS") && link; return ( @@ -28,16 +21,26 @@ const AddressSvmTable = ({ {addLink ? ( - {component} + + {value} + ) : ( - component + + {value} + )} + + ); diff --git a/pages/express-relay/_meta.json b/pages/express-relay/_meta.json index cb3ab5d1..681c4da6 100644 --- a/pages/express-relay/_meta.json +++ b/pages/express-relay/_meta.json @@ -31,11 +31,6 @@ "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/6cea7c9602c7949c6dacc94aec44c0fe78ccb128/express-relay/easy_lend", - "newWindow": true - }, "-- Understand Express Relay": { "title": "Understanding Express Relay", "type": "separator" diff --git a/pages/express-relay/contract-addresses.mdx b/pages/express-relay/contract-addresses.mdx deleted file mode 100644 index 7ff03495..00000000 --- a/pages/express-relay/contract-addresses.mdx +++ /dev/null @@ -1,6 +0,0 @@ -# Contract Addresses - -This section covers the contract addresses for Express Relay on different networks. Please navigate to the relevant section below. - -- [EVM](contract-addresses/evm) -- [SVM](contract-addresses/svm) diff --git a/pages/express-relay/contract-addresses/_meta.json b/pages/express-relay/contract-addresses/_meta.json deleted file mode 100644 index 882eb292..00000000 --- a/pages/express-relay/contract-addresses/_meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "evm": "EVM", - "svm": "SVM" -} diff --git a/pages/express-relay/contract-addresses/evm.mdx b/pages/express-relay/contract-addresses/evm.mdx deleted file mode 100644 index d4626b0b..00000000 --- a/pages/express-relay/contract-addresses/evm.mdx +++ /dev/null @@ -1,138 +0,0 @@ -import { Tabs } from "nextra/components"; -import AddressTable from "../../../components/AddressTable"; - -Express Relay is currently deployed on the following EVM 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/contract-addresses/svm.mdx b/pages/express-relay/contract-addresses/svm.mdx index 10437213..d982367a 100644 --- a/pages/express-relay/contract-addresses/svm.mdx +++ b/pages/express-relay/contract-addresses/svm.mdx @@ -1,9 +1,60 @@ +--- +title: SVM +--- + import { Tabs, Callout } from "nextra/components"; import AddressSvmTable from "../../../components/AddressSvmTable"; +# SVM Contract Addresses + Express Relay is currently deployed on the following SVM environments: - + + +Auction Server endpoint: https://pyth-express-relay-mainnet.asymmetric.re/ + +### Prod + +**Network Details:** + + + + + To simulate real performance conditions and activity, the staging/testing @@ -11,10 +62,9 @@ Express Relay is currently deployed on the following SVM environments: for production use and should only be used for testing. - Auction Server endpoint: https://per-staging.dourolabs.app/ -### Solana +### Testing **Network Details:** @@ -22,7 +72,12 @@ Auction Server endpoint: https://per-staging.dourolabs.app/ explorer={"https://solscan.io/account/$ADDRESS"} entries={[ { - name: "Express Relay Program", + name: "Express Relay Program (Swaps)", + value: "stag1NN9voD7436oFvKmy1kvRZYLLW8drKocSCt2W79", + link: true, + }, + { + name: "Express Relay Program (Limit Orders)", value: "PytERJFhAKuNNuaiXkApLfWzwNwSNDACpigT3LwQfou", link: true, }, diff --git a/pages/express-relay/errors.mdx b/pages/express-relay/errors.mdx deleted file mode 100644 index ba1a420c..00000000 --- a/pages/express-relay/errors.mdx +++ /dev/null @@ -1,6 +0,0 @@ -# Error Codes - -This section covers error codes one may run into during integration. Please see the relevant section below for the environment of interest. - -- [EVM](errors/evm) -- [SVM](errors/svm) diff --git a/pages/express-relay/errors/_meta.json b/pages/express-relay/errors/_meta.json deleted file mode 100644 index 882eb292..00000000 --- a/pages/express-relay/errors/_meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "evm": "EVM", - "svm": "SVM" -} diff --git a/pages/express-relay/errors/evm.mdx b/pages/express-relay/errors/evm.mdx deleted file mode 100644 index eb4a944b..00000000 --- a/pages/express-relay/errors/evm.mdx +++ /dev/null @@ -1,33 +0,0 @@ -# EVM Error Codes - -The following table lists the error codes and their explanations for [`ExpressRelay`](https://github.com/pyth-network/per/blob/fccac65b00cff1cfe5c278b333a582fe66bda0f8/contracts/evm/src/express-relay/ExpressRelay.sol) and [`OpportunityAdapter`](https://github.com/pyth-network/per/blob/fccac65b00cff1cfe5c278b333a582fe66bda0f8/contracts/evm/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/errors/svm.mdx b/pages/express-relay/errors/svm.mdx index 950eb25f..d8ec4f3f 100644 --- a/pages/express-relay/errors/svm.mdx +++ b/pages/express-relay/errors/svm.mdx @@ -1,17 +1,23 @@ +--- +title: SVM +--- + # SVM Error Codes -The following table lists the error codes and their explanations for the [`ExpressRelay`](https://github.com/pyth-network/per/tree/fccac65b00cff1cfe5c278b333a582fe66bda0f8/contracts/svm/programs/express_relay) programs. +The following table lists the error codes and their explanations for the [`ExpressRelay`](https://github.com/pyth-network/per/tree/358eedc1f9072cdfc3418fba309697580f2474f9/contracts/svm/programs/express_relay) programs. They can be used to identify the cause of a failed transaction or bid. ## ExpressRelay -| Error | Explanation | -| ----------------------------- | ------------------------------------------------------------------------------------------- | -| `FeeSplitLargerThanPrecision` | The proposed fee split is invalid (split is larger than `FEE_SPLIT_PRECISION`, 10000) | -| `FeesHigherThanBid` | The fees to pay out exceed the value of the bid amount. | -| `DeadlinePassed` | The bid is no longer valid, as the Unix time deadline has passed. | -| `InvalidCPISubmitBid` | The `SubmitBid` instruction should not be called via CPI. | -| `MissingPermission` | The transaction is missing a `SubmitBid` instruction with the matching permission key. | -| `MultiplePermissions` | The transaction should not contain more than one `SubmitBid` instruction. | -| `InsufficientSearcherFunds` | The searcher lacks the funds to pay the specified bid amount. | -| `InsufficientRent` | The fees splits received by different parties must be sufficient to cover the account rent. | +| Error | Explanation | +| ----------------------------- | -------------------------------------------------------------------------------------------------------- | +| `FeeSplitLargerThanPrecision` | The proposed fee split is invalid (split is larger than `FEE_SPLIT_PRECISION`, 10000) | +| `FeesHigherThanBid` | The fees to pay out exceed the value of the bid amount. | +| `DeadlinePassed` | The bid is no longer valid, as the Unix time deadline has passed. | +| `InvalidCPISubmitBid` | The `SubmitBid` instruction should not be called via CPI. | +| `MissingPermission` | The transaction is missing a `SubmitBid` instruction with the matching permission key. | +| `MultiplePermissions` | The transaction should not contain more than one `SubmitBid` instruction. | +| `InsufficientSearcherFunds` | The searcher lacks the funds to pay the specified amount. | +| `InsufficientRent` | The fee splits received by different parties must be sufficient to cover the account rent. | +| `InvalidReferralFee` | The provided referral fee in the swap instruction data is too high to be a valid number of basis points. | +| `InsufficientUserFunds` | The user lacks the funds to pay the specified amount in the swap instruction. | diff --git a/pages/express-relay/index.mdx b/pages/express-relay/index.mdx index 66a8df2a..bf027dc3 100644 --- a/pages/express-relay/index.mdx +++ b/pages/express-relay/index.mdx @@ -7,17 +7,16 @@ 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). +Express Relay is a priority auction which enables better orderflow mechanisms that 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 Protocol Developers:** Express Relay allows protocols to recapture MEV and access a network of searchers for more competitive pricing than on-chain sources provide. + With Express Relay, protocols get access to plug-and-play liquidity. -- **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. +- **For Searchers:** Express Relay provides easy and unified access to a range of orderflow opportunities across integrated DeFi protocols. ## Integration -To integrate with Express Relay, you can integrate as a protocol or as a searcher. +To integrate with Express Relay, you can integrate as a protocol (to power token swaps) or as a searcher. -} - 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/6cea7c9602c7949c6dacc94aec44c0fe78ccb128/express-relay/easy_lend" -/> + } + title="How Express Relay Works" + href="./express-relay/how-express-relay-works " + /> diff --git a/pages/express-relay/integrate-as-protocol.mdx b/pages/express-relay/integrate-as-protocol.mdx index 383ad9e1..a2e4aa40 100644 --- a/pages/express-relay/integrate-as-protocol.mdx +++ b/pages/express-relay/integrate-as-protocol.mdx @@ -1,236 +1,5 @@ -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/per/blob/8e311d3dce7a54865ff98b25e57c6af2dd984d1f/sdk/solidity/IExpressRelay.sol) and [`IExpressRelayFeeReceiver`](https://github.com/pyth-network/per/blob/8e311d3dce7a54865ff98b25e57c6af2dd984d1f/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/per/blob/8e311d3dce7a54865ff98b25e57c6af2dd984d1f/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/per/blob/8e311d3dce7a54865ff98b25e57c6af2dd984d1f/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/evm -// 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/docs#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/per/blob/8e311d3dce7a54865ff98b25e57c6af2dd984d1f/examples/easy_lend/src/monitor.ts) script, -which fetches opportunities for the below-mentioned [Easy Lend](https://github.com/pyth-network/per/tree/8e311d3dce7a54865ff98b25e57c6af2dd984d1f/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/per/tree/8e311d3dce7a54865ff98b25e57c6af2dd984d1f/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 [EVM](./contract-addresses/evm.mdx) and [SVM](./contract-addresses/svm.mdx) Contract Addresses pages list the addresses of Express Relay deployed on various networks. - -### Error Codes - -The [EVM](./errors/evm.mdx) and [SVM](./errors/svm.mdx) Error Codes pages list the error codes returned by Express Relay. - -### API Reference +This section covers how to integrate Express Relay for your use case. Please see the relevant section below for the use case of interest. -The [API Reference](https://per-staging.dourolabs.app/docs) provides detailed information on Express Relay APIs for submitting opportunities. +- [Swaps](integrate-as-protocol/swaps) diff --git a/pages/express-relay/integrate-as-protocol/_meta.json b/pages/express-relay/integrate-as-protocol/_meta.json new file mode 100644 index 00000000..c20e21be --- /dev/null +++ b/pages/express-relay/integrate-as-protocol/_meta.json @@ -0,0 +1,3 @@ +{ + "swaps": "Swaps" +} diff --git a/pages/express-relay/integrate-as-protocol/swaps.mdx b/pages/express-relay/integrate-as-protocol/swaps.mdx new file mode 100644 index 00000000..0c8d78bf --- /dev/null +++ b/pages/express-relay/integrate-as-protocol/swaps.mdx @@ -0,0 +1,82 @@ +import { Callout, Tabs, Steps } from "nextra/components"; + +# How to Integrate Express Relay Swaps + +This guide will explain how frontends can integrate Express Relay to empower swapping. + + +### Install the Express Relay SDK + +Pyth provides a [Typescript SDK](https://www.npmjs.com/package/@pythnetwork/express-relay-js) to help developers integrate Express Relay into their frontends. + +You can install the SDK via npm or yarn. You can invoke the SDK client as below: + +```typescript +import { Client } from "@pythnetwork/express-relay-js"; + +const client = new Client( + { baseUrl: "https://pyth-express-relay-mainnet.asymmetric.re" }, + undefined // Default WebSocket options +); +``` + +### Request a Quote + +You can request a quote by calling the [`getQuote`](https://github.com/pyth-network/per/blob/281de989db887aaf568fed39315a76acc16548fa/sdk/js/src/index.ts#L501-L506) SDK method. + +The example below shows how you can construct a quote request for a USDC -> WSOL swap, with 100 USDC provided as input by the user: + +```typescript +const userWallet = new PublicKey(""); + +const quoteRequest = { + chainId: "solana", + inputTokenMint: new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // USDC mint + outputTokenMint: new PublicKey("So11111111111111111111111111111111111111112"), // WSOL mint + specifiedTokenAmount: { + side: "input", + amount: 100_000_000, + }, + userWallet, +}; + +const quote = await client.getQuote(quoteRequest); +``` + +`quote` contains the full details, including the amount the searcher is quoting and the transaction that the user needs to sign. It also contains an `expirationTime`; after this time, the transaction will no longer succeed on chain, so you should request a new quote a few seconds before the `expirationTime`. + +### Submit User Signature to the Express Relay Server + +Once you show the quote to the user, the user should sign the transaction if they wish to engage in the swap. The frontend can pass this signature along to the Express Relay server, which will handle aggregating all the required signatures for the transaction and submitting it to the RPC node. + +Below is an example showing how the frontend can submit the signed quote transaction to the server using the [`submitQuote`](https://github.com/pyth-network/per/blob/358eedc1f9072cdfc3418fba309697580f2474f9/sdk/js/src/index.ts#L537-L542) method. The response from the `getQuote` method includes a field called `referenceId`, which the frontend should use in its submission of the user signature. + +```typescript +const submitQuote = { + chainId: "solana", + referenceId: quote.referenceId, + userSignature: signature, +}; + +const txSubmitted = await client.submitQuote(submitQuote); +``` + +`submitQuote` returns the fully signed transaction that the server submitted to the RPC node. + + + +## Additional Resources + +You may find these additional resources helpful for integrating Express Relay as a frontend. + +### Contract Addresses + +The [SVM](../contract-addresses/svm.mdx) Contract Addresses page lists the relevant addresses for Express Relay integration. + +### Error Codes + +The [SVM](../errors/svm.mdx) Error Codes page lists the error codes returned by Express Relay. + +### API Reference + +The [API Reference](https://pyth-express-relay-mainnet.asymmetric.re/docs) provides detailed information on Express Relay APIs. diff --git a/pages/express-relay/integrate-as-searcher.mdx b/pages/express-relay/integrate-as-searcher.mdx index 20326cd7..4d7f047f 100644 --- a/pages/express-relay/integrate-as-searcher.mdx +++ b/pages/express-relay/integrate-as-searcher.mdx @@ -14,18 +14,12 @@ Searchers can integrate with Express Relay in three steps: 2. Construct the bid 3. Submit the bid to Express Relay. -Searchers can integrate with Express Relay on Ethereum Virtual Machine (EVM) and Solana Virtual Machine (SVM) chains. +Searchers can integrate with Express Relay on Solana Virtual Machine (SVM) chains. -} - title="Integrate with Express Relay on EVM Chains" - href="/express-relay/integrate-as-searcher/evm" -/> - -} - title="Integrate with Express Relay on SVM Chains" - href="/express-relay/integrate-as-searcher/svm" -/> + } + title="Integrate with Express Relay on SVM Chains" + href="/express-relay/integrate-as-searcher/svm" + /> diff --git a/pages/express-relay/integrate-as-searcher/_meta.json b/pages/express-relay/integrate-as-searcher/_meta.json index 7c532053..6a8f72e7 100644 --- a/pages/express-relay/integrate-as-searcher/_meta.json +++ b/pages/express-relay/integrate-as-searcher/_meta.json @@ -1,4 +1,3 @@ { - "evm": "Integrate on EVM chains", "svm": "Integrate on SVM chains" } diff --git a/pages/express-relay/integrate-as-searcher/evm.mdx b/pages/express-relay/integrate-as-searcher/evm.mdx deleted file mode 100644 index d9620cb5..00000000 --- a/pages/express-relay/integrate-as-searcher/evm.mdx +++ /dev/null @@ -1,252 +0,0 @@ -import { Callout, Tabs, Steps } from "nextra/components"; - -# EVM Searcher Integration - - - -### Subscribe to New Opportunities - -Express Relay provides searchers with [Typescript](https://github.com/pyth-network/per/tree/8e311d3dce7a54865ff98b25e57c6af2dd984d1f/sdk/js) and [Python](https://github.com/pyth-network/per/tree/8e311d3dce7a54865ff98b25e57c6af2dd984d1f/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-js"; - -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 - handleOpportunity -); -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/docs#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`](https://github.com/pyth-network/per/blob/fccac65b00cff1cfe5c278b333a582fe66bda0f8/contracts/evm/src/opportunity-adapter/OpportunityAdapter.sol) 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 handleOpportunity = 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/docs#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/evm/_meta.json b/pages/express-relay/integrate-as-searcher/evm/_meta.json deleted file mode 100644 index 8b30349f..00000000 --- a/pages/express-relay/integrate-as-searcher/evm/_meta.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "opportunity-adapter": "Prepare Assets for Opportunity Adapter", - "custom-contract": "Use a Custom Contract" -} diff --git a/pages/express-relay/integrate-as-searcher/evm/custom-contract.mdx b/pages/express-relay/integrate-as-searcher/evm/custom-contract.mdx deleted file mode 100644 index 0d58950c..00000000 --- a/pages/express-relay/integrate-as-searcher/evm/custom-contract.mdx +++ /dev/null @@ -1,41 +0,0 @@ -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/evm.mdx) to call the relevant methods in - your custom contract. - diff --git a/pages/express-relay/integrate-as-searcher/evm/opportunity-adapter.mdx b/pages/express-relay/integrate-as-searcher/evm/opportunity-adapter.mdx deleted file mode 100644 index 2b83ca36..00000000 --- a/pages/express-relay/integrate-as-searcher/evm/opportunity-adapter.mdx +++ /dev/null @@ -1,46 +0,0 @@ -import { Steps } from "nextra/components"; - -# Prepare Assets for Opportunity Adapter - -The [`OpportunityAdapter`](https://github.com/pyth-network/per/blob/fccac65b00cff1cfe5c278b333a582fe66bda0f8/contracts/evm/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/evm). -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/evm.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/integrate-as-searcher/svm.mdx b/pages/express-relay/integrate-as-searcher/svm.mdx index 97410b10..ac897d59 100644 --- a/pages/express-relay/integrate-as-searcher/svm.mdx +++ b/pages/express-relay/integrate-as-searcher/svm.mdx @@ -8,7 +8,7 @@ SVM Express Relay searchers fulfill opportunities representing limit orders on t ### Subscribe to New Opportunities -Express Relay provides searchers with [Typescript](https://github.com/pyth-network/per/tree/4be711525948cf24c0ebd4ebab007dc7f51b7069/sdk/js) and [Python](https://github.com/pyth-network/per/tree/4be711525948cf24c0ebd4ebab007dc7f51b7069/sdk/python) SDKs to interact with Express Relay. +Express Relay provides searchers with [Typescript](https://github.com/pyth-network/per/tree/358eedc1f9072cdfc3418fba309697580f2474f9/sdk/js) and [Python](https://github.com/pyth-network/per/tree/358eedc1f9072cdfc3418fba309697580f2474f9/sdk/python) SDKs to interact with Express Relay. Searchers can also directly fetch available opportunities via HTTP or subscribe to them via WebSocket. diff --git a/pages/express-relay/websocket-api-reference.mdx b/pages/express-relay/websocket-api-reference.mdx index dc04f1bb..bc81e796 100644 --- a/pages/express-relay/websocket-api-reference.mdx +++ b/pages/express-relay/websocket-api-reference.mdx @@ -46,7 +46,7 @@ To subscribe to opportunities, you can send a request using the `chain_ids` para "id": "1", "method": "subscribe", "params": { - "chain_ids": ["op_sepolia"] + "chain_ids": ["development-solana"] } } ``` @@ -60,7 +60,7 @@ After a successful subscription, you will receive new opportunities for the sele } ``` -The schema for the opportunity is similar to what’s returned in the [HTTP requests](https://pyth-express-relay-mainnet.asymmetric.re/docs#tag/opportunity/operation/get_opportunities) +The schema for the opportunity is similar to what’s returned in the [HTTP requests](https://pyth-express-relay-mainnet.asymmetric.re/docs#tag/opportunity/operation/get_opportunities). To unsubscribe from a list of chains, you can send the following message: @@ -69,7 +69,7 @@ To unsubscribe from a list of chains, you can send the following message: "id": "1", "method": "unsubscribe", "params": { - "chain_ids": ["op_sepolia"] + "chain_ids": ["development-solana"] } } ``` @@ -79,29 +79,6 @@ To unsubscribe from a list of chains, you can send the following message: 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": "op_sepolia", - "contract": "0xcA11bde05977b3631167028862bE2a173976CA11", - "permission_key": "0xcafebabe" - } - } -} -``` - - - - ```json copy { "id": "1", @@ -115,9 +92,6 @@ Here is an example JSON payload for submitting a new bid: } ``` - - - A successful response to a bid submission has the following schema: ```json @@ -132,98 +106,14 @@ A successful response to a bid submission has the following schema: ``` After submitting your bid via WebSocket, you will receive notifications about the bid status updates in JSON format. -Refer to the examples below, one for each of the status options in EVM and SVM: - - - - - - - ```json - // pending - // The temporary state, which means the auction for this bid is pending - { - "type": "bid_status_update", - "status": { - "id": "beedbeed-0e42-400f-a8ef-d78aa5422252", - "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": "beedbeed-0e42-400f-a8ef-d78aa5422252", - "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 - } - } - } - ``` - - - +Refer to the examples below: - - + ```json // 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. + // It will be updated to Lost or Submitted after the auction takes place. { "type": "bid_status_update", "status": { @@ -238,8 +128,8 @@ Refer to the examples below, one for each of the status options in EVM and SVM: ```json // submitted - // 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. + // 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": { @@ -256,10 +146,10 @@ Refer to the examples below, one for each of the status options in EVM and SVM: ```json - // lost - // The bid lost the auction. - // This bid status will have a result field containing the signature of the transaction corresponding to the winning bid, - // unless the auction had no winner (because all bids were found to be invalid). + // lost + // The bid lost the auction. + // This bid status will have a result field containing the signature of the transaction corresponding to the winning bid, + // unless the auction had no winner (because all bids were found to be invalid). { "type": "bid_status_update", "status": { @@ -275,7 +165,7 @@ Refer to the examples below, one for each of the status options in EVM and SVM: ```json // won - // The bid won the auction and was included in a block successfully. + // The bid won the auction and was included in a block successfully. { "type": "bid_status_update", "status": { @@ -291,7 +181,7 @@ Refer to the examples below, one for each of the status options in EVM and SVM: ``` - + ```json // failed // The bid was submitted on-chain, was included in a block, but resulted in a failed transaction. @@ -328,9 +218,7 @@ Refer to the examples below, one for each of the status options in EVM and SVM: } ``` - - ## Connection Persistence