|
| 1 | +# How to Integrate Express Relay as a Protocol |
| 2 | + |
| 3 | +This guide explains how to integrate Express Relay as a protocol. |
| 4 | +To integrate Express Relay as a protocol, you need to: |
| 5 | + |
| 6 | +1. Update the protocol's contract to permission Express Relay transactions. |
| 7 | +2. Write a script to post liquidatable positions/vaults to Searchers for auction. |
| 8 | + |
| 9 | +## Update the Protocol's Contract |
| 10 | + |
| 11 | +Express Relay requires the protocol's contract to permit Express Relay to access liquidation. |
| 12 | + |
| 13 | +### Install the Express Relay SDK |
| 14 | + |
| 15 | +Pyth provides a [Solidity SDK](https://www.npmjs.com/package/@pythnetwork/express-relay-sdk-solidity) to help you integrate Express Relay into your protocol. |
| 16 | +The SDK exposes `IExpressRelay` and `IExpressRelayFeeReceiver` interfaces to interact with Express Relay. |
| 17 | + |
| 18 | +#### Truffle/Hardhat |
| 19 | + |
| 20 | +If you are using Truffle or Hardhat, you can install the SDK using npm: |
| 21 | + |
| 22 | +```bash copy |
| 23 | +npm install @pythnetwork/express-relay-sdk-solidity |
| 24 | +``` |
| 25 | +#### Foundry |
| 26 | + |
| 27 | +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: |
| 28 | + |
| 29 | +```bash copy |
| 30 | +npm init -y |
| 31 | +npm install @pythnetwork/express-relay-sdk-solidity |
| 32 | +``` |
| 33 | + |
| 34 | +Then add the following line to `remappings.txt` file: |
| 35 | + |
| 36 | +```bash copy |
| 37 | +@pythnetwork/express-relay-sdk-solidity/=node_modules/@pythnetwork/express-relay-sdk-solidity |
| 38 | +``` |
| 39 | + |
| 40 | +### Modifying the Protocol's Contract |
| 41 | + |
| 42 | + |
| 43 | +`IExpressRelay` interface exposes the `isPermissioned` function to check if Express Relay can liquidate a position. |
| 44 | + |
| 45 | +The `isPermissioned` function takes two arguments: |
| 46 | +1. `protocolFeeReceiver`: The address of the protocol's contract. If the protocol's contract is the contract that receives fees from the Express Relay server, this should be the address of that contract. (IS IT WORTH MENTIONING HERE????) |
| 47 | +2. `permissionId`: A unique identifier for the liquidation opportunity. |
| 48 | + |
| 49 | + |
| 50 | +```solidity copy |
| 51 | +import "@pythnetwork/express-relay-sdk-solidity/IExpressRelay.sol"; |
| 52 | +
|
| 53 | +// Express Relay contract address on Optimism Sepolia |
| 54 | +// Check {INSERT CONTRACT ADDRESS PAGE LINK} for the address deployed on other networks |
| 55 | +address expressRelay = 0xD6e417287b875A3932c1Ff5dcB26D4D2C8b90B40; |
| 56 | +
|
| 57 | +require( |
| 58 | + IExpressRelay(expressRelay).isPermissioned( |
| 59 | + protocolFeeReceiver, |
| 60 | + permissionId |
| 61 | + ), |
| 62 | + "invalid liquidation" |
| 63 | +); |
| 64 | +``` |
| 65 | + |
| 66 | + |
| 67 | + |
| 68 | + |
| 69 | +The following code snippet shows a sample liquidation how to update the protocol's contract to permit Express Relay transactions: |
| 70 | + |
| 71 | +```solidity showLineNumbers {1,2,12,14,39-43} copy |
| 72 | +import "@pythnetwork/express-relay-sdk-solidity/IExpressRelay.sol"; |
| 73 | +import "@pythnetwork/express-relay-sdk-solidity/IExpressRelayFeeReceiver.sol"; |
| 74 | +
|
| 75 | +struct Vault { |
| 76 | + address tokenCollateral; |
| 77 | + address tokenDebt; |
| 78 | + uint256 amountCollateral; |
| 79 | + uint256 amountDebt; |
| 80 | + uint256 minHealthRatio; |
| 81 | +} |
| 82 | +
|
| 83 | +contract EasyLend is IExpressRelayFeeReceiver { |
| 84 | +
|
| 85 | + address public immutable expressRelay; |
| 86 | +
|
| 87 | + constructor( |
| 88 | + address expressRelayAddress, |
| 89 | + address oracleAddress, |
| 90 | + bool allowUndercollateralized |
| 91 | + ){ |
| 92 | + _nVaults = 0; |
| 93 | + expressRelay = expressRelayAddress; |
| 94 | + _oracle = oracleAddress; |
| 95 | + _allowUndercollateralized = allowUndercollateralized; |
| 96 | + } |
| 97 | +
|
| 98 | + /** |
| 99 | + * @notice liquidate function - liquidates a vault |
| 100 | + * @param vaultID: ID of the vault to be liquidated |
| 101 | + */ |
| 102 | + function liquidate(uint256 vaultID) public { |
| 103 | + Vault memory vault = _vaults[vaultID]; |
| 104 | + uint256 vaultHealth = _getVaultHealth(vault); |
| 105 | + if (vaultHealth >= vault.minHealthRatio) { |
| 106 | + revert InvalidLiquidation(); |
| 107 | + } |
| 108 | +
|
| 109 | + // Check if the liquidation is permissioned |
| 110 | + bool permissioned = IExpressRelay(payable(expressRelay)).isPermissioned( |
| 111 | + address(this), |
| 112 | + abi.encode(vaultID) // vault id uniquely represents the opportunity and can be used as permission id |
| 113 | + ); |
| 114 | + require(permissioned, "invalid liquidation"); |
| 115 | + |
| 116 | + IERC20(vault.tokenDebt).transferFrom(msg.sender,address(this),vault.amountDebt); |
| 117 | + IERC20(vault.tokenCollateral).transfer(msg.sender,vault.amountCollateral); |
| 118 | + |
| 119 | + _vaults[vaultID].amountCollateral = 0; |
| 120 | + _vaults[vaultID].amountDebt = 0; |
| 121 | + } |
| 122 | +
|
| 123 | +} |
| 124 | +``` |
0 commit comments