|
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