The CCIP Liquidation Protector is a cross-chain solution designed to protect users from liquidation on lending protocols (e.g., Compound V2 on Ethereum) by pulling liquidity from another chain (e.g., Arbitrum via Aave V3). The system uses Chainlink CCIP (Cross-Chain Interoperability Protocol) to request and receive tokens across different networks securely.
- CCIP Liquidation Protector
CCIP Liquidation Protector provides an automated way to bring funds from a “liquidity chain” (Arbitrum with Aave V3) to a “debt chain” (Ethereum Mainnet with Compound V2) when a user’s position is at risk of liquidation.
-
On Ethereum (Debt Chain):
AMonitorCompoundV2
contract checks if the user’s Compound position is underwater. If it is, it sends a CCIP request to Arbitrum. -
On Arbitrum (Liquidity Chain):
AnLPSC
(liquidation protection smart contract) receives the request, ensures there are enough funds (pulling from an Aave vault if necessary), and sends the required amount back to Ethereum via CCIP. -
Result:
The user’s position on Compound can be topped up, preventing liquidation.
- Inherits:
CCIPReceiver
to handle incoming CCIP messages from Ethereum.LPSCVault
for vault interactions (withdraw from Aave).
- Responsibilities:
- Receives cross-chain message from the debt chain (MonitorCompoundV2).
- Checks the requested token and amount.
- If needed, calls
withdrawFromVault
(fromLPSCVault
) to make sure there’s enough liquidity. - Uses Chainlink CCIP again to reply back with tokens to the original sender (on Ethereum).
- Purpose:
Manages liquidity on Arbitrum, specifically integrates with Aave V3. - Key Functions:
withdrawFromVault(token, amount)
: Withdraws from Aave ifLPSC
has insufficient funds to meet a cross-chain request.
- Purpose:
Holds mappings of token addresses across different chains (e.g., the address of “ETHx” on Ethereum vs. Arbitrum). - Usage:
AllowsLPSC
to map an incoming token address from Ethereum to its corresponding address on Arbitrum, ensuring correct bridging.
- Inherits:
AutomationCompatibleInterface
(Chainlink Keepers) to automatically check user positions.CCIPReceiver
to handle the cross-chain reply fromLPSC
.
- Responsibilities:
- Monitors user’s Compound V2 position.
- If there’s a shortfall, it sends a CCIP request to
LPSC
on Arbitrum. - On receiving the reply (tokens to repay debt), it can repay the user’s position on Compound.
Ethereum (Debt Chain) Arbitrum (Liquidity Chain)
-------------------- --------------------------
[User + CompoundV2] [Aave V3]
| (Check shortfall) ^
v |
[MonitorCompoundV2] -- CCIP request --> [LPSC + LPSCVault]
^ |
| <-- CCIP reply -- |
----------------------------------------------
MonitorCompoundV2
detects shortfall and sends CCIP request.LPSC
receives request, withdraws fromLPSCVault
if needed, sends back tokens.MonitorCompoundV2
uses returned tokens to repay or top up the user’s Compound position.
- Node.js (v14 or higher recommended)
- Foundry (forge) or Hardhat (this example uses Foundry)
- Git for version control
- Accounts or private keys to fork & interact with mainnet/Arbitrum
-
Clone the Repository
git clone https://github.com/smartcontractkit/ccip-liquidation-protector.git cd ccip-liquidation-protector
-
Install Foundry (if not already installed)
curl -L https://foundry.paradigm.xyz | bash foundryup
-
Set Up Environment
Make sure you have environment variables or direct URLs set for your mainnet and Arbitrum RPC endpoints (e.g., Alchemy or Infura). -
Install Dependencies
Inside the project folder:forge install
Here’s the updated test instructions section of the README, rewritten to guide users to set up their .env
file for RPC URLs and run tests using Foundry. This section now explicitly references the .env
setup step and incorporates the provided .env.example
.
The codebase includes a suite of Foundry tests that simulate both on-chain logic and cross-chain flows using Chainlink's CCIPLocalSimulatorFork for local cross-chain testing.
Before running the tests, ensure you set up the required RPC URLs for Ethereum Mainnet and Arbitrum in a .env
file. Use the provided .env.example
as a template:
-
Copy the
.env.example
file to.env
:cp .env.example .env
-
Update the placeholders in the
.env
file with your Alchemy or Infura API keys:ARBITRUM_RPC_URL=https://arb-mainnet.g.alchemy.com/v2/YOUR_API_KEY ETH_RPC_URL=https://eth-mainnet.g.alchemy.com/v2/YOUR_API_KEY
Replace
YOUR_API_KEY
with your actual API keys for Alchemy or Infura. -
Export the
.env
variables so Foundry can access them:source .env
With the .env
variables set up(see .evv.example for referance), you can now run the test suite. These tests use the RPC URLs to create Ethereum and Arbitrum forks locally.
-
Run all tests:
forge test
-
Example Tests:
FullFlowTest.sol
: Simulates the entire cross-chain liquidation protection flow.MonitorTest.sol
: Verifies theMonitorCompoundV2
logic (checking shortfall, sending CCIP requests).CompoundBasicTest.sol
: Focuses on the core Compound mechanics like mint, borrow, and repay.LPSCTestWithSimulator.sol
: Tests the CCIP interaction between Ethereum and Arbitrum using a local simulator.
For more detailed logs during test execution, increase the verbosity with -vvvv
:
forge test --fork-url $ETH_RPC_URL --fork-url $ARBITRUM_RPC_URL -vvvv
This outputs additional details like transaction traces and state changes, making it easier to debug complex flows.
-
Run only a specific test file:
forge test -vv --match-path test/FullFlowTest.sol
-
Run a specific test function:
forge test -vv --match-test testMonitorLiquidationFlow
-
Deploying LPSC on Arbitrum:
- You can deploy it directly with your router address (Chainlink CCIP) and a reference to an Aave pool to manage liquidity.
-
Deploying MonitorCompoundV2 on Ethereum:
- Point it to the Compound Comptroller and the relevant cToken addresses.
- Provide your user’s address so it knows who to protect.
-
Configuring LPSCRegistry:
- Map any tokens you expect to handle cross-chain. For example, if your token is “ETHx” on mainnet and “ETHx” on Arbitrum, store that in the registry so LPSC can do lookups.
-
Performing a Mock Liquidation Check:
- Use the
MonitorCompoundV2.checkUpkeep()
function to see if your user is in shortfall. If yes, it callsperformUpkeep()
to send a CCIP message.
- Use the
-
Access Control
onlyRouterOrOwner
inLPSC
ensures only the Chainlink CCIP router or the contract owner can trigger certain functions.- Ensure you consider additional role-based restrictions if needed (e.g., pausing functionality).
-
Token Approvals
MonitorCompoundV2
andLPSC
both approve the CCIP router to spend tokens. This is essential for bridging but should be carefully managed.- Confirm that no arbitrary addresses get infinite approvals.
-
Reentrancy and Upgrades
- Consider adding reentrancy guards if your project expands.
- For production, you might use upgradeable contracts; ensure that your cross-chain logic is upgrade-safe.
-
Testing on Real Networks
- The local simulator is great for rapid iteration. However, always run tests on testnets (Goerli, Arbitrum Goerli, etc.) if possible, to ensure your CCIP flows and addresses are configured correctly.
This project is licensed under the MIT License. Feel free to use, modify, and distribute this code. If you make improvements, consider submitting a pull request to help the community!
If you have any questions or issues, please open an issue in the GitHub repository or reach out to the Chainlink community on Discord.