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_).pendingWithdrawalsAmountincreases by the total assets earmarked for redemption (including fees).A
Redemptionstruct is inserted into the Safety Module'sredemptionsmapping. The redemption has an associatedredemptionId_determined by a state variableredemptionCounterwhich gets incremented by 1 on each new redemption. TheredemptionId_
struct Redemption {
uint8 reservePoolId; // Reserve pool being redeemed.
uint216 receiptTokenAmount; // Receipt tokens burned when queuing.
IReceiptToken receiptToken; // The receipt token contract.
uint128 totalAssetAmount; // Gross reserve assets (including fees) cached at queue time.
address owner; // Owner of the burned receipt tokens.
uint40 queueTime; // Timestamp when the redemption was queued.
uint40 delay; // Redemption delay captured at queue time (0 if paused).
uint32 queuedAccISFsLength; // pendingRedemptionAccISFs length snapshot for scaling.
uint256 queuedAccISF; // Last pendingRedemptionAccISFs value at queue time.
}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.
/// @notice Combined redeem() and completeRedemption(). Designed to be used when SM is paused or when the withdraw
/// delay is 0.
/// @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 owner_ The address that owns the deposit receipt tokens.
/// @param receiver_ The address to receive the reserve assets.
function redeemAndComplete(
uint8 reservePoolId_,
uint256 depositReceiptTokenAmount_,
address owner_,
address receiver_
) external returns (uint64 redemptionId_, uint256 reserveAssetAmount_, uint256 redemptionFeeAmount_) {} /// @notice Completes the redemption request for the specified redemption ID. Owner-only path (no signature). Reserve
/// pool assets will be sent to `receiver_` if the reserve pool's assets are not tapped.
/// @param redemptionId_ The ID of the redemption to complete.
/// @param receiver_ The address to receive the redeemed assets.
function completeRedemption(uint64 redemptionId_, address receiver_)
external
returns (uint256 reserveAssetAmount_, uint256 redemptionFeeAmount_){}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
PAUSEDor at leastSafetyModule.delays().withdrawDelayamount of time has elapsed sinceRedemption.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, andSafetyModule.reservePool(reservePoolId_).depositAmountare decreased.The final redemption assets are transferred to the receiver.
A
Redemptionevent is emitted.
To complete a redemption on behalf of another user, you may use completeRedemptionBySig
/// @notice Returns the domain separator.
function domainSeparator() external view returns (bytes32);
/// @notice Tracks per-owner nonces for EIP-712 signed operations.
/// @param owner_ the owner of this nonce
/// @param actionKey_ typehash used for this call (i.e. COMPLETE_REDEMPTION_BY_SIG_TYPEHASH)
function eip712Nonces(address owner_, bytes32 actionKey_) external view returns (uint256);
bytes32 public constant COMPLETE_REDEMPTION_BY_SIG_TYPEHASH = keccak256(
"CompleteRedemptionBySig(address owner,uint64 redemptionId,address caller,address receiver,uint256 deadline)"
);
/// @notice Completes the redemption request for the specified redemption ID on behalf of the owner. Permit-style
/// path: owner signs off-chain; specific caller executes on-chain. Reserve pool assets will be sent to `receiver_` if
/// the reserve pool's assets are not tapped.
/// @dev Signature binds to (owner, redemptionId, caller=msg.sender, deadline).
/// @param redemptionId_ The ID of the redemption to complete.
/// @param owner_ The owner of the deposit receipt tokens.
/// @param receiver_ The address to receive the redeemed assets.
/// @param deadline_ The time at which the signature expires.
/// @param signature_ The owner's signature over the EIP-712 structured data.
function completeRedemptionBySig(
uint16 rewardPoolId_,
uint256 rewardAssetAmount_,
address owner_,
address receiver_,
uint256 deadline_,
bytes calldata signature_
) external {
bytes32 digest_ = keccak256(
abi.encodePacked(
"\x19\x01",
keccak256(
abi.encode(
domainSeparator()
)
),
keccak256(
abi.encode(COMPLETE_REDEMPTION_BY_SIG_TYPEHASH, owner_, redemptionId_, msg.sender, receiver_, deadline_)
)
)
);
if (!SignatureChecker.isValidSignatureNow(owner_, digest_, signature_)) revert InvalidSignature();
}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
completeRedemptionBySigfunction 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:
factor = 1 - raiseAmount / (reservePool.depositAmount)This scaling factor may compound if multiple taps occur before a pending redemption is completed.
Last updated