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 tapped. 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_)
    external
    returns (uint64 redemptionId_, uint256 reserveAssetAmount_);

When a redemption is queued:

  • The owner's deposit receipt tokens are burned.

  • reservePools(reservePoolId_).pendingWithdrawalsAmount increases by the total assets earmarked for redemption (including fees).

  • 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_

If the Safety Module is PAUSED (or the withdraw delay is zero), redeemAndComplete can be called to queue and complete in a single transaction; otherwise a RedemptionPending event is emitted with the redemptionId_ needed for completion. Note that the struct no longer stores a receiver, the destination address is supplied later during completeRedemption or completeRedemptionBySig.

  • To complete a redemption for yourself, use completeRedemption.

  • 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 tapped (see Safety Module Raising).

  • 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.

To complete a redemption on behalf of another user, you may use completeRedemptionBySig

  • The owner must sign a digest that matches the above.

  • Fetch Domain Separator via public domainSeparator() getter shown above exposed in ISafetyModule and include them when constructing the EIP-712 typed data.

  • The above completeRedemptionBySig function is truncated to only show the relevant structure of the digest.

Queued vs final redemption assets

If the reserve pool assets have been tapped 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 are made available to process payouts for raises. This includes any pending redemption assets. So, the following scaling factor is retroactively applied to all pending redemptions on completion:

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

Last updated