Skip to content

Commit 1c6ccc8

Browse files
committed
add integrate-as-protocol changes
1 parent 160cf57 commit 1c6ccc8

File tree

3 files changed

+87
-233
lines changed

3 files changed

+87
-233
lines changed
Lines changed: 2 additions & 233 deletions
Original file line numberDiff line numberDiff line change
@@ -1,236 +1,5 @@
1-
import { Callout, Tabs, Steps } from "nextra/components";
2-
31
# How to Integrate Express Relay as a Protocol
42

5-
This guide will explain how DeFi protocols can integrate Express Relay.
6-
7-
Integrating with Express Relay involves two main steps:
8-
9-
- **Update** your DeFi protocol's contract.
10-
- **Expose** opportunities to searchers for auction.
11-
12-
## Update your DeFi Protocol's Contract
13-
14-
To integrate with Express Relay, your protocol's contract must check if Express Relay has permissioned the current transaction.
15-
16-
<Steps>
17-
### Install the Express Relay SDK
18-
19-
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.
20-
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.
21-
22-
<Tabs items={['Hardhat', 'Foundry']}>
23-
<Tabs.Tab>
24-
If you are using Hardhat, you can install the SDK using npm:
25-
26-
```bash copy
27-
npm install @pythnetwork/express-relay-sdk-solidity
28-
```
29-
30-
</Tabs.Tab>
31-
<Tabs.Tab>
32-
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:
33-
34-
```bash copy
35-
npm init -y
36-
npm install @pythnetwork/express-relay-sdk-solidity
37-
```
38-
39-
Then add the following line to `remappings.txt` file:
40-
41-
```text copy
42-
@pythnetwork/express-relay-sdk-solidity/=node_modules/@pythnetwork/express-relay-sdk-solidity
43-
```
44-
45-
</Tabs.Tab>
46-
</Tabs>
47-
48-
### Modify the Protocol's Contract
49-
50-
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.
51-
52-
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.
53-
1. Implement the [`IExpressRelayFeeReceiver`](https://github.com/pyth-network/per/blob/8e311d3dce7a54865ff98b25e57c6af2dd984d1f/sdk/solidity/IExpressRelayFeeReceiver.sol#L4) interface to **receive** auction proceeds.
54-
55-
#### 1. Verify Permissioning
56-
57-
The `isPermissioned` function takes two arguments:
58-
59-
1. `protocolFeeReceiver`: The address to receive the protocol's share of auction proceeds.
60-
1. `permissionId`: A unique identifier for the opportunity.
61-
62-
```solidity copy
63-
import "@pythnetwork/express-relay-sdk-solidity/IExpressRelay.sol";
64-
65-
// Express Relay contract address on Optimism Sepolia
66-
//
67-
// Check https://docs.pyth.network/express-relay/contract-addresses/evm
68-
// for the address deployed on other networks
69-
address expressRelay = 0xD6e417287b875A3932c1Ff5dcB26D4D2C8b90B40;
70-
71-
require(
72-
IExpressRelay(expressRelay).isPermissioned(
73-
protocolFeeReceiver,
74-
permissionId
75-
),
76-
"not permissioned"
77-
);
78-
```
79-
80-
<Callout type="info" emoji="ℹ️">
81-
The `permissionId` represents a unique identifier of an opportunity. For a
82-
liquidation opportunity, the vault address or ID could be concatenated into
83-
`bytes` format. Consult
84-
[`Permissioning`](./how-express-relay-works/permissioning.mdx) for more
85-
information on generating permission IDs.
86-
</Callout>
87-
88-
#### 2. Set up Fee Receiver
89-
90-
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.
91-
92-
```solidity copy
93-
interface IExpressRelayFeeReceiver {
94-
function receiveAuctionProceedings(
95-
bytes calldata permissionKey
96-
) external payable;
97-
}
98-
99-
```
100-
101-
The following code snippet shows a sample Express Relay-integrated contract that performs liquidation.
102-
_Note: The highlighted lines show the contract's relevant additions for Express Relay integration._
103-
104-
```solidity showLineNumbers {1,2,12,14,21,45-50,65-69} copy
105-
import "@pythnetwork/express-relay-sdk-solidity/IExpressRelay.sol";
106-
import "@pythnetwork/express-relay-sdk-solidity/IExpressRelayFeeReceiver.sol";
107-
108-
struct Vault {
109-
address tokenCollateral;
110-
address tokenDebt;
111-
uint256 amountCollateral;
112-
uint256 amountDebt;
113-
uint256 minHealthRatio;
114-
}
115-
116-
contract EasyLend is IExpressRelayFeeReceiver {
117-
118-
address public immutable expressRelay;
119-
120-
constructor(
121-
address expressRelayAddress,
122-
bool allowUndercollateralized
123-
){
124-
_nVaults = 0;
125-
expressRelay = expressRelayAddress;
126-
_allowUndercollateralized = allowUndercollateralized;
127-
}
128-
129-
/**
130-
* @notice createVault function - creates a vault
131-
* @param vaultParams: params of the vault to be created
132-
*/
133-
function createVault(VaultParams memory vaultParams) public {
134-
..
135-
}
136-
137-
/**
138-
* @notice liquidate function - liquidates a vault
139-
* @param vaultID: ID of the vault to be liquidated
140-
*/
141-
function liquidate(uint256 vaultID) public {
142-
Vault memory vault = _vaults[vaultID];
143-
uint256 vaultHealth = _getVaultHealth(vault);
144-
// Proceed only if the vault health is below the minimum health ratio
145-
if (vaultHealth >= vault.minHealthRatio) {
146-
revert InvalidLiquidation();
147-
}
148-
149-
// Check if the liquidation is permissioned
150-
bool permissioned = IExpressRelay(payable(expressRelay)).isPermissioned(
151-
address(this),
152-
abi.encode(vaultID) // permissionId generated from the unique vault ID
153-
);
154-
require(permissioned, "invalid liquidation");
155-
156-
IERC20(vault.tokenDebt).transferFrom(msg.sender,address(this),vault.amountDebt);
157-
IERC20(vault.tokenCollateral).transfer(msg.sender,vault.amountCollateral);
158-
159-
_vaults[vaultID].amountCollateral = 0;
160-
_vaults[vaultID].amountDebt = 0;
161-
}
162-
163-
/**
164-
* @notice receiveAuctionProceedings function - receives the native token from express relay
165-
* You can use the permission key to distribute the received funds to users who got liquidated, LPs, etc...
166-
*
167-
* @param permissionKey: permission key that was used for the auction
168-
*/
169-
function receiveAuctionProceedings(
170-
bytes calldata permissionKey
171-
) external payable {
172-
emit VaultReceivedETH(msg.sender, msg.value, permissionKey);
173-
}
174-
175-
}
176-
```
177-
178-
</Steps>
179-
180-
## Expose Opportunities to Searchers
181-
182-
DeFi protocols should fetch opportunities and expose them to Express Relay for auction.
183-
184-
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.
185-
186-
The JSON payload should contain opportunities in the following format:
187-
188-
```bash copy
189-
{
190-
"target_calldata": "0xdeadbeef", // Calldata to execute the opportunity
191-
"chain_id": "op_sepolia",
192-
"target_contract": "0xcA11bde05977b3631167028862bE2a173976CA11", // Protocol contract address to call
193-
"permission_key": "0xcafebabe", // Unique identifier for the opportunity
194-
"target_call_value": "1", // Value (in Wei) to send to the protocol contract.
195-
"sell_tokens": [ // Tokens the protocol expects to receive
196-
{
197-
"amount": "900",
198-
"token": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599"
199-
}
200-
],
201-
"buy_tokens": [ // Tokens the protocol will send in return
202-
{
203-
"amount": "1000",
204-
"token": "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"
205-
}
206-
],
207-
"version": "v1" // Opportunity format version
208-
}
209-
```
210-
211-
Each protocol integrated with Express Relay must actively monitor for new opportunities.
212-
Protocols can do this by indexing the chain, listening to protocol events, or querying protocol state through an RPC provider.
213-
214-
Check the [`monitor.ts`](https://github.com/pyth-network/per/blob/8e311d3dce7a54865ff98b25e57c6af2dd984d1f/examples/easy_lend/src/monitor.ts) script,
215-
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.
216-
217-
## Additional Resources
218-
219-
You may find these additional resources helpful for integrating Express Relay as a protocol.
220-
221-
### Example Application
222-
223-
[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.
224-
This lending protocol contract is updated to use Express Relay.
225-
226-
### Contract Address
227-
228-
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.
229-
230-
### Error Codes
231-
232-
The [EVM](./errors/evm.mdx) and [SVM](./errors/svm.mdx) Error Codes pages list the error codes returned by Express Relay.
233-
234-
### API Reference
3+
This section covers how to integrate Express Relay for your use case. Please see the relevant section below for the use case of interest.
2354

236-
The [API Reference](https://per-staging.dourolabs.app/docs) provides detailed information on Express Relay APIs for submitting opportunities.
5+
- [Swaps](integrate-as-protocol/swaps)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"swaps": "Swaps"
3+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
import { Callout, Tabs, Steps } from "nextra/components";
2+
3+
# How to Integrate Express Relay Swaps
4+
5+
This guide will explain how frontends can integrate Express Relay to empower swapping.
6+
7+
<Steps>
8+
### Install the Express Relay SDK
9+
10+
Pyth provides a [Typescript SDK](https://www.npmjs.com/package/@pythnetwork/express-relay-js) to help developers integrate Express Relay into their frontends.
11+
12+
You can install the SDK via npm or yarn. You can invoke the SDK client as below:
13+
14+
```typescript
15+
import { Client } from "@pythnetwork/express-relay-js";
16+
17+
const client = new Client(
18+
{ baseUrl: "https://pyth-express-relay-mainnet.asymmetric.re" },
19+
undefined // Default WebSocket options
20+
);
21+
```
22+
23+
### Request a Quote
24+
25+
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.
26+
27+
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:
28+
29+
```typescript
30+
const userWallet = new PublicKey("<INPUT USER PUBKEY>");
31+
32+
const quoteRequest = {
33+
chainId: "solana",
34+
inputTokenMint: new PublicKey("EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v"), // USDC mint
35+
outputTokenMint: new PublicKey("So11111111111111111111111111111111111111112"), // WSOL mint
36+
specifiedTokenAmount: {
37+
side: "input",
38+
amount: 100_000_000,
39+
},
40+
userWallet,
41+
};
42+
43+
const quote = await client.getQuote(quoteRequest);
44+
```
45+
46+
`quote` will contain the full details, including the amount the searcher is quoting and the transaction that the user needs to sign. It will also contain 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`.
47+
48+
### Submit User Signature to the Express Relay Server
49+
50+
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.
51+
52+
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.
53+
54+
```typescript
55+
const submitQuote = {
56+
chainId: "solana",
57+
referenceId: quote.referenceId,
58+
userSignature: signature,
59+
};
60+
61+
const txSubmitted = await client.submitQuote(submitQuote);
62+
```
63+
64+
As can be seen, this will return the fully signed transaction that the server submitted to the RPC node to land on chain.
65+
66+
</Steps>
67+
68+
## Additional Resources
69+
70+
You may find these additional resources helpful for integrating Express Relay as a frontend.
71+
72+
### Contract Address
73+
74+
The [SVM](../contract-addresses/svm.mdx) Contract Addresses pages list the relevant addresses for Express Relay integration.
75+
76+
### Error Codes
77+
78+
The [SVM](../errors/svm.mdx) Error Codes page lists the error codes returned by Express Relay.
79+
80+
### API Reference
81+
82+
The [API Reference](https://pyth-express-relay-mainnet.asymmetric.re/docs) provides detailed information on Express Relay APIs.

0 commit comments

Comments
 (0)