Safety Module Slashing
Last updated
Last updated
Assets in a Safety Module can be slashed in the event that any of the Safety Module's configured triggers are used to trigger it. To trigger a Safety Module, SafetyModule.trigger(trigger_)
can be called permissionlessly with a trigger in the triggered state that the Safety Module has been configured to use (see ).
Each trigger is allowed to be successfully used with SafetyModule.trigger(trigger_)
once.
At a high-level, when SafetyModule.trigger(trigger_)
is called with a valid trigger successfully:
Protocol fees are dripped.
SafetyModule.numPendingSlashes
is incremented by 1. An invariant of the protocol is that the Safety Module is triggered while SafetyModule.numPendingSlashes > 0 && SafetyModule.safetyModuleState != SafetyModuleState.PAUSED
(see ).
SafetyModule.triggerData(trigger_).triggered
is set to true, disallowing the trigger to be used to trigger the Safety Module again.
SafetyModule.payoutHandlerNumPendingSlashes(triggerData_.payoutHandler)
is incremented by 1 (see ).
event Triggered(trigger_)
is emitted
SafetyModule.safetyModuleState()
is set to triggered if the Safety Module is not paused.
When SafetyModule.trigger
is called with a valid trigger in the triggered state, the Safety Module's state becomes triggered if it is currently active and not paused (see ).
To determine the payout handler for a trigger configured for the Safety Module, integrators can use SafetyModule.triggerData(trigger_)
:
When a trigger is successfully triggers the Safety Module, the amount of times its assigned payout handler is allowed to slash assets is incremented by one. When the payout handler slashes assets, the amount of times they are allowed to slash assets is decremented by one.
To slash assets, payout handlers can call SafetyModule.slash(Slash[] memory slashes_, address receiver_)
:
At a high-level, when SafetyModule.slash
is called by a valid payout handler:
SafetyModule.numPendingSlashes()
is decremented by 1.
SafetyModule.payoutHandlerNumPendingSlashes(payoutHandler)
is decremented by 1.
Assets are slashed from each reserve pool according to the slashes_
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 slashed from the reserve pool.
SafetyModule.assetPools(asset_).amount
is decreased by the amount of the underlying asset slashed.
If SafetyModule.numPendingSlashes == 0
, SafetyModule.safetyModuleState()
is set to active.
For each reserve pool that is slashed, event Slashed( address indexed payoutHandler_, address indexed receiver_, uint256 indexed reservePoolId_, uint256 assetAmount_ );
is emitted.
Safety Modules require that each trigger is configured with a payout handler address, which is allowed to slash assets from each reserve pool in the Safety Module in the case that the trigger is used to successfully call SafetyModule.trigger(trigger_)
(see ).
To determine how many times a specific payout handler can slash assets at the current block (which requires a trigger has configured it as its payout handler and has been used to ), integrators can call SafetyModule.payoutHandlerNumPendingSlashes(triggerData_.payoutHandler)
.
Safety Module assets can only be slashed by valid , and the Safety Module must be in the triggered state (SafetyModule.safetyModuleState() == TRIGGERED
).
If any of the slash amounts exceeds SafetyModule.reservePool(reservePoolId_).maxSlashPercentage
, the transaction reverts (see ).
Assets pending withdrawal are also slashed (see ).