RateHopper Contracts is a smart contract system that enables users to automatically switch their borrowing positions between different DeFi lending protocols to take advantage of the best borrowing rates. This helps users optimize their borrowing costs by seamlessly moving their debt between protocols when better rates are available.
-
Multi-Protocol Support: Currently supports borrowing from:
- Aave V3
- Compound
- Morpho
- Moonwell
- Fluid
-
Flash Loan Integration: Uses Uniswap V3 flash loans to facilitate debt position transfers without requiring users to have the full repayment amount upfront.
-
Collateral Management: Handles multiple collateral assets across different protocols during debt transfers.
-
Paraswap Integration: Uses Paraswap for efficient token swaps when debt assets differ between protocols.
-
Protocol Fee: Configurable protocol fee system with a designated fee beneficiary.
-
Safe Module Integration: Supports Gnosis Safe integration through dedicated Safe modules.
-
Leveraged Positions: Enables creation of leveraged positions across supported protocols.
The system consists of several key components:
-
Governance & Access Control:
TimelockController: OpenZeppelin's timelock implementation with 2-day delay for critical operationsProtocolRegistry.sol: Central registry with hybrid access control:DEFAULT_ADMIN_ROLE: For routine operations (whitelist, token mappings) - immediate executionCRITICAL_ROLE: For critical operations (setParaswapV6, setOperator) - requires timelock
-
DebtSwap.sol: The main contract that orchestrates the debt switching process using flash loans.
-
Protocol Handlers: Individual handlers for each supported lending protocol:
-
In
contracts/protocols/directory:AaveV3Handler.sol: Handles interactions with Aave V3 protocolCompoundHandler.sol: Handles interactions with Compound protocolMorphoHandler.sol: Handles interactions with Morpho protocol
-
In
contracts/protocolsSafe/directory:MoonwellHandler.sol: Handles interactions with Moonwell protocolFluidSafeHandler.sol: Handles interactions with Fluid protocol through Safe
-
-
Safe Modules: Modules for Gnosis Safe integration:
SafeDebtManager.sol: Enables debt swaps through Gnosis Safe- Both operator-initiated and Safe owner-initiated transactions supported
-
LeveragedPosition.sol: Facilitates creation of leveraged positions across protocols.
-
Morpho Libraries: Supporting libraries for the Morpho protocol:
MathLib.sol: Provides fixed-point arithmetic operations for the Morpho protocolSharesMathLib.sol: Handles share-to-asset conversion with virtual shares to protect against share price manipulations
Aave V3:
- Approve aToken when switching from Aave
- Approve debt delegation when switching to Aave
- Extra data:
"0x"
Compound V3:
- Call
allow()to authorize DebtSwap contract - Extra data:
"0x"
Morpho:
- Call
setAuthorization(debtSwapContract, true) - Extra data: Encode
(MarketParams, borrowShares)- REQUIRED
Moonwell:
- No pre-approval required
- Extra data:
"0x"
Fluid:
- No pre-approval required
- Extra data: Encode
(vaultAddress, nftId, isFullRepay)- REQUIRED
- Flash Loans: Uses Uniswap V3 flash loans for atomic debt transfers
- Protocol Fee: Configurable fee (max 1%) taken from destination debt
- Slippage: Include
srcAmountwith slippage adjustment inParaswapParamsfor token swaps - Collateral: Automatically moved from source to destination protocol
- Amount: Use
MaxUint256for full debt repayment, or specify exact amount
executeDebtSwap: Main entry point for initiating a debt position transferuniswapV3FlashCallback: Handles the flash loan callback from Uniswap V3setProtocolFee: Sets the protocol fee percentage (basis points)setFeeBeneficiary: Sets the address that receives protocol feesgetHandler: Retrieves the handler address for a specific protocolemergencyWithdraw: Allows the owner to withdraw tokens in case of emergency
Each protocol handler implements the following key functions:
getDebtAmount: Retrieves current debt amount for a userswitchIn: Handles debt switching within the same protocolswitchFrom: Handles debt repayment on the original protocolswitchTo: Handles borrowing on the new protocolrepay: Handles repayment of remaining balances
To execute a debt swap, you'll need to provide the following parameters:
function executeDebtSwap(
address _flashloanPool, // Uniswap V3 pool address for flash loan
Protocol _fromProtocol, // Source protocol enum (COMPOUND, AAVE_V3, MORPHO, FLUID, MOONWELL)
Protocol _toProtocol, // Destination protocol enum
address _fromDebtAsset, // Debt asset address on source protocol
address _toDebtAsset, // Debt asset address on destination protocol
uint256 _amount, // Amount to swap (use type(uint256).max for full debt)
CollateralAsset[] calldata _collateralAssets, // Array of collateral assets
bytes calldata _fromExtraData, // Extra data for source protocol
bytes calldata _toExtraData, // Extra data for destination protocol
ParaswapParams calldata _paraswapParams // Paraswap parameters for token swaps
)struct CollateralAsset {
address asset; // Collateral asset address
uint256 amount; // Collateral amount
}struct ParaswapParams {
uint256 srcAmount; // Source amount with slippage adjustment (for token swaps)
bytes swapData; // Encoded swap data from Paraswap API
}Create a .env file with the following required variables (use .env.sample as a template):
ADMIN_ADDRESS=0x... # Initial admin and timelock proposer/executor
SAFE_OPERATOR_ADDRESS=0x... # Operator address for Safe interactions
PAUSER_ADDRESS=0x... # Address that can pause contracts
DEPLOYER_PRIVATE_KEY=... # Private key for deployment
EXPLORER_KEY=... # Block explorer API key for verification- Install dependencies:
yarn install- Compile contracts:
yarn compile- Run tests:
yarn testThe project uses:
- Solidity version 0.8.28
- Hardhat for development and testing
- Hardhat Ignition for deployments
- OpenZeppelin contracts for standard implementations
- Uniswap V3 for flash loans
- Paraswap for token swaps
Comprehensive tests are available in the /test directory covering:
- Individual protocol handlers
- Cross-protocol debt switching flows
- Multiple collateral asset scenarios
- Safe module integration
- Leveraged position creation
Run tests with:
yarn testThe contracts use Hardhat Ignition for declarative deployments. Make sure you complete the sections Environment Variables and Setup and Development and make sure all tests pass before deploying.
Deploy everything in one command:
yarn hardhat ignition deploy ignition/modules/SharedInfrastructure.ts --network base --verify --resetThis deploys:
- TimelockController (2-day delay)
- ProtocolRegistry (with timelock configured)
- All protocol handlers (Aave, Compound, Morpho, Fluid, Moonwell)
- Token mappings and whitelists
After infrastructure is deployed, deploy the Safe modules:
# Deploy SafeDebtManager
yarn hardhat ignition deploy ignition/modules/SafeDebtManager.ts --network base --verify
# Deploy LeveragedPosition
yarn hardhat ignition deploy ignition/modules/LeveragedPosition.ts --network base --verifyVerify individual contracts on block explorers:
# Verify TimelockController
TIMELOCK_ADDRESS=0x... yarn hardhat run scripts/verifyTimelock.ts --network base
# Verify ProtocolRegistry
yarn hardhat run scripts/verifyProtocolRegistry.ts --network base
# Verify SafeDebtManager
yarn hardhat run scripts/verifySafeDebtManager.ts --network base
# Verify LeveragedPosition
yarn hardhat run scripts/verifyLeveragedPosition.ts --network baseFor critical operations (updating Paraswap address):
# Schedule operation (requires proposer role)
TIMELOCK_ADDRESS=0x... PROTOCOL_REGISTRY_ADDRESS=0x... NEW_PARASWAP_ADDRESS=0x... \
yarn hardhat run scripts/timelock-update-paraswap.ts --network base
# Wait 2 days, then execute
EXECUTE=true TIMELOCK_ADDRESS=0x... PROTOCOL_REGISTRY_ADDRESS=0x... NEW_PARASWAP_ADDRESS=0x... \
yarn hardhat run scripts/timelock-update-paraswap.ts --network baseNote: Updating the operator address (
setOperator) also requires the timelock. Create a similar script based ontimelock-update-paraswap.tsif needed.
The contracts include several security features:
- Timelock Controller: 2-day delay for critical operations (Paraswap and operator updates)
- Hybrid Access Control:
DEFAULT_ADMIN_ROLE: For routine operations (immediate execution)CRITICAL_ROLE: For critical operations (requires timelock)
- Operator Authorization: Centralized operator management through ProtocolRegistry
- Both
SafeDebtManagerandLeveragedPositionread operator from registry - Supports both operator-initiated and Safe owner-initiated transactions
- Both
- Reentrancy Protection: All state-changing functions protected via OpenZeppelin's
ReentrancyGuard - Ownership Pattern: Uses OpenZeppelin's
Ownablefor administrative functions - Safe ERC20 Operations: Uses OpenZeppelin's
SafeERC20for secure token transfers - Flash Loan Validation: Validates Uniswap V3 pool callbacks via
CallbackValidation
- Authorization Check: Only authorized callers (operator or Safe itself) can execute operations
- Safe Multi-sig Support: Safe owners must use multi-sig process to manage positions
- No Individual Owner Calls: Individual Safe owners cannot call directly (prevents malicious contract exploits)
- Emergency Withdrawal: Owner can withdraw stuck tokens in emergency situations
- Protocol Fee Limits: Maximum fee capped at 1% (100 basis points)
- Pausable Contracts: Designated pauser can pause operations in emergency situations
- Input Validation: Comprehensive checks on all function parameters
- Whitelist System: Only whitelisted tokens can be used in the protocol
Business Source License 1.1 (BUSL-1.1)
Licensed under the Business Source License 1.1. After December 8, 2028 (4 years from initial release), the license converts to GPL-2.0-or-later.
See LICENSE for details.