Description:
Smart contract deployed on Ethereum with Factory features.
Blockchain: Ethereum
Source Code: View Code On The Blockchain
Solidity Source Code:
{{
"language": "Solidity",
"sources": {
"contracts/interfaces/modules/IWithdrawModule.sol": {
"content": "pragma solidity 0.8.21;\r
\r
interface IWithdrawModule {\r
function request(\r
address caller,\r
address receiver,\r
address owner,\r
uint256 shares\r
) external;\r
\r
function claim(address receiver, uint256 shares) external;\r
\r
function claimable(\r
address receiver\r
) external view returns (uint256, uint256);\r
\r
function settle(uint256 shares, uint256 rate) external;\r
}\r
"
},
"contracts/modules/WithdrawModule.sol": {
"content": "pragma solidity 0.8.21;\r
import {IWithdrawModule} from "../interfaces/modules/IWithdrawModule.sol";\r
\r
/// @title Batched withdrawal request/settlement module\r
/// @notice Queues per-user share withdrawal requests into epochs and settles them pro-rata at an assets-per-share rate.\r
/// @dev\r
/// - All state-changing functions are restricted to the configured Hook via onlyHook.\r
/// - Epochs are indexed by epochId. New requests are tagged with the current epochId and are settled\r
/// when the Hook calls settle(). After settlement, epochId is incremented to open a new epoch.\r
/// - Fulfillment is stored in basis points (BPS) and the settlement rate is stored as assets-per-share\r
/// scaled by 10**shareDecimal.\r
/// - This module does not move tokens. The Hook is responsible for redeeming shares, transferring assets,\r
/// and invoking claim() to decrement user requests accordingly.\r
/// @custom:security Only the Hook may call mutating functions. Use a trusted Hook.\r
contract WithdrawModule is IWithdrawModule {\r
/// @notice Per-user withdrawal request state.\r
struct Order {\r
/// @notice Total shares requested for withdrawal that are still outstanding.\r
uint256 shares;\r
/// @notice The epoch in which the latest request was made.\r
uint256 epochId;\r
}\r
\r
/// @notice Settlement data for an epoch.\r
struct EpochInfo {\r
/// @notice Fulfillment as BPS of requested shares settled in this epoch (10_000 = 100%).\r
uint256 fulfillment;\r
/// @notice Assets-per-share settlement rate scaled by 10**shareDecimal.\r
uint256 rate;\r
}\r
\r
/// @notice Current open epoch identifier (first epoch is 1).\r
uint256 public epochId = 1;\r
\r
/// @notice Total shares requested for withdrawal across all users for the current open epoch.\r
uint256 public totalWithdraw;\r
\r
/// @notice Basis points denominator (10_000 = 100%).\r
uint256 constant ONE_HUNDRED_PERCENT = 10000;\r
\r
/// @notice Decimals used to scale the assets-per-share rate (typically vault share decimals).\r
uint256 immutable shareDecimal;\r
\r
/// @notice Hook authorized to call this module.\r
address immutable hook;\r
\r
/// @notice Pending withdrawal orders by receiver.\r
mapping(address => Order) public orders;\r
\r
/// @notice Settlement info per epoch.\r
mapping(uint256 => EpochInfo) public epochs;\r
\r
/// @notice Initialize the withdraw module.\r
/// @param _shareDecimal Decimals used to scale assets-per-share rate (matches the share token decimals).\r
/// @param _hook Address of the Hook that controls this module.\r
constructor(uint256 _shareDecimal, address _hook) {\r
shareDecimal = _shareDecimal;\r
hook = _hook;\r
}\r
\r
/// @notice Restricts function access to the configured Hook.\r
modifier onlyHook() {\r
require(msg.sender == hook, "Not hook");\r
_;\r
}\r
\r
/// @notice Emitted when a receiver requests a withdrawal.\r
/// @param receiver Address for which the withdrawal was requested.\r
/// @param shares Amount of shares requested.\r
event WithdrawRequested(address indexed receiver, uint256 shares);\r
\r
/// @notice Emitted when an epoch is settled and a new epoch is opened.\r
/// @param epochId The newly opened epoch id (the previous epochId - 1 was just settled).\r
/// @param rate Assets-per-share settlement rate for the settled epoch, scaled by 10**shareDecimal.\r
event EpochSettled(uint256 indexed epochId, uint256 rate);\r
\r
/// @notice Emitted when a receiver's settled shares are claimed (accounting-only in this module).\r
/// @param receiver Address claiming settled shares.\r
/// @param shares Amount of shares accounted as claimed.\r
event WithdrawClaimed(address indexed receiver, uint256 shares);\r
\r
/// @notice Queue a withdrawal request for shares in the current epoch.\r
/// @param caller Original msg.sender initiating the withdrawal (informational).\r
/// @param receiver Address that will receive assets when claimed.\r
/// @param owner Share owner authorizing the withdrawal (informational).\r
/// @param shares Amount of shares to request.\r
/// @dev\r
/// - Accumulates requested shares in orders[receiver].\r
/// - Tags the order with the current epochId if it lags behind.\r
/// - Increments totalWithdraw for the current epoch.\r
/// - Token movements are handled by the Hook; this function updates accounting only.\r
function request(\r
address caller,\r
address receiver,\r
address owner,\r
uint256 shares\r
) external onlyHook {\r
// Custom logic to handle withdraw requests\r
orders[receiver].shares += shares;\r
if (orders[receiver].epochId < epochId) {\r
orders[receiver].epochId = epochId;\r
}\r
totalWithdraw += shares;\r
\r
emit WithdrawRequested(receiver, shares);\r
}\r
\r
/// @notice Account for a receiver's claim of settled shares.\r
/// @param receiver Address claiming.\r
/// @param shares Number of shares being marked as claimed.\r
/// @dev\r
/// - Reverts if attempting to claim more shares than requested.\r
/// - When a receiver's outstanding shares reach zero, clears their epochId.\r
/// - Asset transfer is performed by the Hook after computing claimable().\r
function claim(address receiver, uint256 shares) external onlyHook {\r
// Custom logic to process claims for the receiver\r
require(orders[receiver].shares >= shares, "Exceed requested shares");\r
orders[receiver].shares -= shares;\r
if (orders[receiver].shares == 0) {\r
orders[receiver].epochId = 0;\r
}\r
\r
emit WithdrawClaimed(receiver, shares);\r
}\r
\r
/// @notice Settle a portion of pending withdrawals for the current epoch with a given rate.\r
/// @param shares Total shares being settled in this epoch across all users.\r
/// @param rate Assets-per-share settlement rate scaled by 10**shareDecimal.\r
/// @dev\r
/// - Records fulfillment = shares / totalWithdraw (in BPS) for the current epoch.\r
/// - Stores the provided settlement rate.\r
/// - Reduces totalWithdraw by shares and increments epochId (opening a new epoch).\r
/// - Emits EpochSettled with the newly opened epoch id.\r
function settle(uint256 shares, uint256 rate) external onlyHook {\r
// Custom logic to settle withdraw requests at the end of an epoch\r
require(shares <= totalWithdraw, "Exceed total withdraw");\r
uint256 fulfillment = (shares * ONE_HUNDRED_PERCENT) / totalWithdraw;\r
epochs[epochId] = EpochInfo(fulfillment, rate);\r
totalWithdraw -= shares;\r
epochId++;\r
\r
emit EpochSettled(epochId, rate);\r
}\r
\r
/// @notice Compute claimable assets and shares for a receiver across all settled epochs.\r
/// @param receiver Beneficiary address to query.\r
/// @return assets Total claimable assets for the receiver.\r
/// @return shares Total shares that will be marked as claimed.\r
/// @dev\r
/// - If the receiver last requested in the current open epoch, nothing is claimable yet.\r
/// - Iterates from the receiver's last request epoch up to the most recently settled epoch\r
/// applying each epoch's fulfillment and rate.\r
/// - Assets are computed as settledShares * rate / 10**shareDecimal.\r
function claimable(\r
address receiver\r
) external view returns (uint256 assets, uint256 shares) {\r
// Custom logic to calculate claimable assets and shares for the receiver\r
if (orders[receiver].epochId == epochId) {\r
return (0, 0);\r
}\r
uint256 lastRequestId = orders[receiver].epochId;\r
\r
uint256 totalAssets;\r
uint256 remainShares = orders[receiver].shares;\r
\r
while (lastRequestId < epochId) {\r
uint256 settledShares = (remainShares *\r
epochs[lastRequestId].fulfillment) / ONE_HUNDRED_PERCENT;\r
uint256 settledAssets = (settledShares *\r
epochs[lastRequestId].rate) / 10 ** shareDecimal;\r
totalAssets += settledAssets;\r
remainShares -= settledShares;\r
lastRequestId++;\r
}\r
\r
return (totalAssets, orders[receiver].shares - remainShares);\r
}\r
}\r
"
}
},
"settings": {
"evmVersion": "paris",
"optimizer": {
"enabled": false,
"runs": 200
},
"outputSelection": {
"*": {
"*": [
"evm.bytecode",
"evm.deployedBytecode",
"devdoc",
"userdoc",
"metadata",
"abi"
]
}
},
"metadata": {
"useLiteralContent": true
}
}
}}
Submitted on: 2025-10-25 16:07:01
Comments
Log in to comment.
No comments yet.