|
1 |
| -import { Callout, Tabs, Steps } from "nextra/components"; |
2 |
| - |
3 | 1 | # How to Integrate Express Relay as a Protocol
|
4 | 2 |
|
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. |
235 | 4 |
|
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) |
0 commit comments