Safety Module Raises

Triggering a Safety Module

Assets in a Safety Module can be tapped in the event that any of the Safety Module's configured controllers are used to trigger it. To trigger a Safety Module, SafetyModule.trigger(triggerEventId_) can be called permissionlessly with a controller that the Safety Module has been configured to use (see Create a Controller).

/// @notice Triggers the safety module by referencing one of the controllers configured for this safety module.
function trigger(bytes32 triggerEventId_) external;

Each controller can trigger a SafetyModule an unlimited number of times, given that each triggerEventId is unique.

At a high-level, when SafetyModule.trigger(triggerEventId_) is called successfully with a valid controller and a unique triggerEventId:

  • Protocol fees are dripped.

  • The raise state for the controller and triggerEventId is set to PENDING_RAISE triggerEventRaiseStates_[controller_][triggerEventId_] = TriggerEventRaiseState.PENDING_RAISE

  • SafetyModule.numPendingRaises is incremented by 1. An invariant of the protocol is that the Safety Module is triggered while SafetyModule.numPendingRaises > 0 && SafetyModule.safetyModuleState != SafetyModuleState.PAUSED (see Safety Module States).

  • If the safety module is part of a shared safety module, SafetyModule.numPendingSsmRaises is incremented by 1 and SharedSafetyModule.propagateTrigger(controller_,triggerEventId_) is called to initiate trigger events across all sibling safety modules. Otherwise, SafetyModule.controllerData(controller_).numPendingRaises is incremented by 1.

  • event Triggered(controller_, triggerEventId_) is emitted

  • SafetyModule.safetyModuleState() is set to triggered if the Safety Module is not paused.

Safety Module State Change

When SafetyModule.trigger is called with a valid controller and unique triggerEventId, the Safety Module's state becomes triggered if it is currently active and not paused (see Safety Module States).

Raising Safety Module Assets

Assets in a Safety Module can be raised either by an authorized controller or by the parent Shared Safety Module, provided the Safety Module is in the TRIGGERED state (SafetyModule.safetyModuleState() == TRIGGERED).

To raise assets, controllers can call SafetyModule.requestRaise():

interface IRaiseStrategy {
  /// @notice Converts asset needs to safety module specific raises
  /// @param originSafetyModule_ The safety module that triggered the raise
  /// @param assetNeeds_ The asset needs to be converted to raises
  function calculateRaise(ISafetyModule originSafetyModule_, AssetNeed[] memory assetNeeds_)
    external
    returns (SafetyModuleRaise[] memory);
}

struct AssetNeed {
  // Asset that needs to be tapped.
  IERC20 asset;
  // Amount of asset that needs to be tapped.
  uint256 amount;
}

struct Raise {
  // ID of the reserve pool.
  uint8 reservePoolId;
  // Asset amount that will be tapped from the reserve pool.
  uint256 amount;
}

  /// @notice Requests the raise for a given trigger event id, by calling the raise strategy with the given asset needs
  /// and then raising the SafetyModule.
  /// @dev If the SafetyModule is in a SharedSafetyModule, the request raise call is delegated to the
  /// SharedSafetyModule.
  /// @dev Controllers can call this function once for a given trigger event id.
  /// @param triggerEventId_ The trigger event id to raise for.
  /// @param receiver_ The address to receive the tapped assets.
  /// @param assetNeeds_ The asset needs for the trigger event
  /// @param raiseStrategy_ The raise strategy for the asset needs.
  function requestRaise(
    bytes32 triggerEventId_,
    address receiver_,
    AssetNeed[] memory assetNeeds_,
    IRaiseStrategy raiseStrategy_
  ) external

While Shared Safety modules can call sharedSafetyModuleRaise()

  /// @notice Used by the SharedSafetyModule to raise the SafetyModule.
  /// @dev Only the SharedSafetyModule can call this function.
  function sharedSafetyModuleRaise(
    bytes32 triggerEventId_,
    Raise[] memory raises_,
    address receiver_,
    ISafetyModuleController originController_
  ) external

At a high-level, when SafetyModule.requestRaise is called by a valid controller

  • SafetyModule.numPendingRaises() is decremented by 1.

  • If the safety module is part of a shared safety module, SafetyModule.numPendingSsmRaises is decremented by 1 and SharedSafetyModule.requestRaise(triggerEventId_, receiver_, assetNeeds_, raiseStrategy_, controller_) is called to initiate raise events across all sibling safety modules. Otherwise, SafetyModule.controllerData(controller_).numPendingRaises is decremented by 1.

  • Assets are tapped from each reserve pool according to the raises_ specified, and transferred to receiver_.

  • Internal asset accounting is updated for each reserve pool and underlying asset.

    • SafetyModule.reservePool(reservePoolId_).depositAmount is decreased by the amount tapped from the reserve pool.

    • SafetyModule.assetPools(asset_).amount is decreased by the amount of the underlying asset tapped.

  • If SafetyModule.numPendingRaises == 0, SafetyModule.safetyModuleState() is set to active.

  • For each reserve pool that is tapped, event ReservePoolTapped( ISafetyModuleController originController_, bytes32 triggerEventId_, address receiver_, uint8 reservePoolId_. uint256 assetAmount); is emitted.

  • triggerEventId_, receiver_, raise_.reservePoolId, raise_.amount

Last updated