Euler Vault Integration
This guide explains how to integrate Cozy Safety Module into a Euler Vault as a bad debt backstop. Use it as a reference when deploying, wiring, or operating the integration. Euler vaults can atomically repay bad debt post-liquidation using eTokens tapped from a Safety Module. This provides an alternative mechanism to managing bad debt with bad debt socialization (the default option).
contract EulerTrancheRaiseStrategy is IRaiseStrategy {
/// @notice Converts asset needs to safety module specific raises using a tranching strategy which prioritizes
/// raising the fee share reserve pool prior to the lender share reserve pool.
/// @param originSafetyModule_ The safety module that triggered the raise
/// @param assetNeeds_ The asset needs to be converted to raises
/// @param data_ Encoded reserve pool ids: abi.encode(uint8 feeShareReservePoolId, uint8 lenderShareReservePoolId)
function calculateRaise(ISafetyModule originSafetyModule_, AssetNeed[] memory assetNeeds_, bytes calldata data_)
external
returns (SafetyModuleRaise[] memory);
}contract CozyLiquidatorManager is Ownable, ICozyLiquidatorManager {
/// @notice Deploys a new CozyLiquidator with the provided parameters.
/// @param safetyModule_ The associated SafetyModule.
/// @param eVault_ The associated Euler vault.
/// @param raiseStrategy_ The associated raise strategy.
/// @param feeShareReservePoolId_ The reserve pool ID to use for the fee share.
/// @param lenderShareReservePoolId_ The reserve pool ID to use for the lender share.
/// @param salt_ Used to compute the resulting address of the CozyLiquidator along with `msg.sender`.
/// @return cozyLiquidator_ The newly created CozyLiquidator.
function createCozyLiquidator(
ISafetyModule safetyModule_,
IEVault eVault_,
EulerTrancheRaiseStrategy raiseStrategy_,
uint8 feeShareReservePoolId_,
uint8 lenderShareReservePoolId_,
bytes32 salt_
) external returns (ICozyLiquidator cozyLiquidator_)
}contract FeeReceiver is Ownable {
/**
* @notice Constructor that sets all parameters for the fee receiver
* @param asset_ Euler vault token that this fee receiver manages
* @param safetyModule_ The SafetyModule to which fees are redirected
* @param safetyModuleReservePoolId_ The reserve pool ID in the safety module for fee shares
* @param safetyModuleShare_ Percentage of fees that should be redirected to the safety module represented as a ZOC
* (e.g. 5000 = 50%)
* @param owner_ Address of the owner who can claim any undirected fees
*/
constructor(
IERC20 asset_,
ISafetyModule safetyModule_,
uint8 safetyModuleReservePoolId_,
uint256 safetyModuleShare_,
address owner_
)
}Components
CozyLiquidatorManager.createCozyLiquidatordeploys minimal proxy liquidators that are parameterized for a specific safety module + vault.CozyLiquidatoris the controller that Euler vaults call into.CozyLiquidationHandler.handleCozyLiquidationis delegate called by the liquidator to execute the liquidation, and trigger + raise the Safety Module.EulerTrancheRaiseStrategyis a raise strategy which specifies that eTokens deposited by the vault governor (via the FeeReceiver) get tapped prior to eTokens deposited by lenders.FeeReceiverredirects a configurable portion of vault fees into the safety module’s fee share reserve pool.
Deployment & Configuration
Configure the safety module
Define reserve pools for the Euler vault eToken. Pool
0for fee shares and pool1for lender shares (both backed by the same eToken).Include
ControllerConfig({controller: ISafetyModuleController(cozyLiquidatorAddress), exists: true})inside theConfigUpdateCalldataParamssupplied to a queued config update. This registers the liquidator as a controller viaCozySafetyModuleManager.registerSafetyModuleController.
Wire the Euler vault
Vault governance must set the liquidator as the
liquidatehook target (IEVault.setHookConfig) and flipCFG_DONT_SOCIALIZE_DEBTso Euler does not spread bad debt across depositors.eVault.feeReceiver()should be set to theFeeReceivercontract soFeeReceiverreceives a portion of the governor's fees.
Liquidation & Raise Lifecycle
Handler executes Euler liquidation
The handler executes the real Euler liquidation via the EVC, emitting LiquidationExecuted. Since CozyLiquidator is set as the pre-hook on the vault, the eVault immediately calls back into CozyLiquidator.liquidate(), which returns a no-op and continues on to eVault.liquidate()
If residual debt remains, trigger the safety module
If collateral is insufficient and residual debt remains:
A deterministic
triggerEventIdis computed from the violator, remaining debt, timestamp, andtriggerEventIdNonce.The handler calls
SafetyModule.trigger(triggerEventId, validityDuration)which moves the module intoTRIGGEREDstate and incrementsnumPendingRaises.An
AssetNeedfor the Euler eToken shares is created by converting debt assets into eVault share amount.SafetyModule.requestRaiseis invoked with theEulerTrancheRaiseStrategyand pool IDs encoded indata.
Observability
/// SafetyModule
/// @dev Emitted when the SafetyModule is triggered.
event Triggered(ISafetyModuleController indexed controller_, bytes32 indexed triggerEventId_, uint256 expiresAt_);
event ReservePoolTapped(
ISafetyModuleController indexed safetyModuleController_,
bytes32 triggerEventId_,
address indexed receiver_,
uint8 indexed reservePoolId_,
uint256 assetAmount_
);
/// @dev Emitted when a safety module is tapped.
event SafetyModuleTapped(
ISafetyModuleController indexed safetyModuleController_, bytes32 indexed triggerEventId_, address indexed receiver_
);
/// @dev Emitted when the SafetyModule is requested to raise.
event RaiseRequested(
ISafetyModuleController indexed controller_,
bytes32 indexed triggerEventId_,
address indexed receiver_,
AssetNeed[] assetNeeds_,
IRaiseStrategy raiseStrategy_,
bytes data_
);/// CozyLiquidator
/// @notice Emitted on trigger event state snapshot.
event TriggerEventStateSnapshot(bytes32 indexed triggerEventId_, bytes triggerEventStateSnapshot_);/// CozyLiquidationHandler
/// @notice Emitted when a liquidation is executed.
event LiquidationExecuted(
address liquidator_, address violator_, address collateral_, uint256 repayAssets_, uint256 minYieldBalance_
);
/// @notice Emitted when bad debt is repaid.
event BadDebtRepaid(bytes32 indexed triggerEventId_, uint256 badDebtAmount_, uint256 badDebtRepaid_);Last updated