Safety Module Redemptions / Withdrawals

Depositors are able to redeem deposit receipt tokens / withdraw assets from Safety Modules. Redemptions follow a two-step process in which the redemption is queued and then completed after a delay (see Withdraw Delay). Note: redemptions cannot be queued or completed when the Safety Module is triggered (see Safety Module States).

SafetyModule.redeem is used to queue a redemption:

/// @notice Queues a redemption by burning `depositReceiptTokenAmount_` of `reservePoolId_` reserve pool deposit
/// tokens.
/// When the redemption is completed, `reserveAssetAmount_` of `reservePoolId_` reserve pool assets will be sent
/// to `receiver_` if the reserve pool's assets are not slashed. If the SafetyModule is paused, the redemption
/// will be completed instantly.
/// @dev Assumes that user has approved the SafetyModule to spend its deposit tokens.
/// @param reservePoolId_ The ID of the reserve pool to redeem from.
/// @param depositReceiptTokenAmount_ The amount of deposit receipt tokens to redeem.
/// @param receiver_ The address to receive the reserve assets.
/// @param owner_ The address that owns the deposit receipt tokens.
function redeem(uint8 reservePoolId_, uint256 depositReceiptTokenAmount_, address receiver_, address owner_)
    returns (uint64 redemptionId_, uint256 reserveAssetAmount_);

When a redemption is queued:

  • The user's deposit receipt tokens are burned.

  • The Safety Module's internal accounting for the reserve pool is updated - SafetyModule.reservePools(reservePoolId_).pendingWithdrawalsAmount gets incremented.

  • A Redemption struct is inserted into the Safety Module's redemptions mapping. The redemption has an associated redemptionId_ determined by a state variable redemptionCounter which gets incremented by 1 on each new redemption. The redemptionId_

struct Redemption {
  uint40 queueTime; // Timestamp at which the redemption was requested.
  uint40 delay; // Protocol redemption delay at the time of request.
  uint176 shares; // Shares burned to queue the redemption.
  uint128 assets; // Amount of assets that will be paid out upon completion of the redemption.
  address owner; // Owner of the shares.
  address receiver; // Receiver of redeemed assets.
  uint32 queuedAccISFsLength; // Length of pendingRedemptionAccISFs at queue time.
  uint256 queuedAccISF; // Last pendingRedemptionAccISFs value at queue time.
  • If the Safety Module is PAUSED, the redemption gets immediately completed. Else, a RedemptionPending event is emitted. This event includes the redemptionId_ necessary to complete the redemption.

Once a redemption is queued, anyone can complete the redemption on behalf of the user. The completion relies on the cached Redemption.receiver to know where to send the redemption assets. When the redemption is completed:

  • The queued redemption data is obtained from the mapping, redemptions[redemptionId_]. It is then deleted from the mapping to ensure that redemption cannot be completed again.

  • The contract checks that either the Safety Module is PAUSED or at least SafetyModule.delays().withdrawDelay amount of time has elapsed since Redemption.queueTime.

  • The final redemption assets gets computed, which may have decreased if reserve pool assets were slashed (see Safety Module Slashing).

  • SafetyModule.assetPool(asset_).amount , SafetyModule.reservePool(reservePoolId_).pendingWithdrawalsAmount , and SafetyModule.reservePool(reservePoolId_).depositAmount are decreased.

  • The final redemption assets are transferred to the receiver.

  • A Redemption event is emitted.

Queued vs final redemption assets

If the reserve pool assets have been slashed since a redemption was queued, the final redemption assets a user receives on completion may be smaller than the queued redemption assets.

All assets in the reserve pool, up to the maxSlashPercentage, are made available to process payouts for slashes. This includes any pending redemption assets. So, the following scaling factor is retroactively applied to all pending redemptions on completion:

factor = 1 - slashAmount / (reservePool.depositAmount)

This scaling factor may compound if multiple slashes occur before a pending redemption is completed.

Last updated