RedemptionGateway

Description:

Proxy contract enabling upgradeable smart contract patterns. Delegates calls to an implementation contract.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "smart-contracts-public/src/redemption/RedemptionGateway.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

import "@openzeppelin/contracts/access/manager/AccessManaged.sol";
import "@openzeppelin/contracts/utils/ReentrancyGuard.sol";
import "@openzeppelin/contracts/utils/Pausable.sol";
import "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import "./IInstantRedemption.sol";
import "./IWindowRedemption.sol";
import "../token/IShareToken.sol";
import "../compliance/IKYCRegistry.sol";

/**
 * @title RedemptionGateway
 * @notice Single entry-point for users to redeem shares. Routes to instant or window modules.
 * @dev Business rule: a redemption window, when open, has exclusive precedence over instant mode.
 *
 * **Responsibilities**
 * - Enforce KYC and basic argument checks at the gateway boundary.
 * - Delegate to {IInstantRedemption} or {IWindowRedemption} and bubble up reverts.
 * - Expose consolidated read-only views used by frontends and monitoring.
 *
 * **Security**
 * - Pausable and reentrancy-protected; administrative setters gated by {AccessManaged}.
 */
contract RedemptionGateway is AccessManaged, ReentrancyGuard, Pausable {
    // ============ Types ============
    enum RedemptionMode {
        NONE,
        INSTANT,
        WINDOW
    }

    // ============ Roles ============
    // Roles are now managed by AccessManager

    // ============ State ============
    IInstantRedemption public instantRedemption;
    IWindowRedemption public windowRedemption;
    IShareToken public immutable shareToken;
    IKYCRegistry public immutable kyc;

    // ============ Events ============
    event InstantRedemptionSet(address indexed module);
    event WindowRedemptionSet(address indexed module);
    event InstantRedemptionRouted(address indexed user, uint256 shares, uint256 payout);
    event WindowRedemptionRouted(address indexed user, address indexed module, uint256 amount);

    // ============ Errors ============
    error InvalidModule();
    error KYCRequired();
    error InvalidAmount();
    error WrongMode(uint8 expected, uint8 actual);

    // ============ Constructor ============

    /**
     * @param _accessManager Address of the AccessManager
     * @param _instantRedemption Address of the InstantRedemption module
     * @param _windowRedemption Address of the WindowRedemption module
     * @param _shareToken Address of the ShareToken
     * @param _kyc Address of the KYC registry
     */
    constructor(
        address _accessManager,
        address _instantRedemption,
        address _windowRedemption,
        address _shareToken,
        address _kyc
    ) AccessManaged(_accessManager) {
        // Validate addresses are not zero
        if (_accessManager == address(0)) revert InvalidModule();
        if (_instantRedemption == address(0)) revert InvalidModule();
        if (_windowRedemption == address(0)) revert InvalidModule();
        if (_shareToken == address(0)) revert InvalidModule();
        if (_kyc == address(0)) revert InvalidModule();

        // Store validated addresses
        instantRedemption = IInstantRedemption(_instantRedemption);
        windowRedemption = IWindowRedemption(_windowRedemption);
        shareToken = IShareToken(_shareToken);
        kyc = IKYCRegistry(_kyc);
    }

    // ============ Instant Redemption Functions ============

    /**
     * @notice Process an instant redemption through the gateway
     * @param shares Amount of shares to redeem
     * @param minPayout Minimum payout amount (slippage protection)
     */
    function redeemInstant(uint256 shares, uint256 minPayout) external nonReentrant whenNotPaused {
        // Enforce mutual exclusivity: block instant while a window is open
        RedemptionMode mode = currentMode();
        if (mode != RedemptionMode.INSTANT) {
            revert WrongMode(uint8(RedemptionMode.INSTANT), uint8(mode));
        }

        if (!kyc.isKYCApproved(msg.sender)) revert KYCRequired();
        if (shares == 0) revert InvalidAmount();

        // Instant redemption module will transfer shares directly from user
        // and will revert if allowance is insufficient
        instantRedemption.redeemFor(msg.sender, shares, minPayout);

        emit InstantRedemptionRouted(msg.sender, shares, minPayout);
    }

    /**
     * @notice Preview instant redemption
     * @param user User address
     * @param shares Amount of shares
     * @return grossPayout Gross payout amount
     * @return fee Fee amount
     * @return netPayout Net payout after fee
     * @return available Whether redemption would succeed
     */
    function previewInstantRedemption(address user, uint256 shares)
        external
        view
        returns (uint256 grossPayout, uint256 fee, uint256 netPayout, bool available)
    {
        return instantRedemption.previewRedemption(user, shares);
    }

    // ============ Window Redemption Functions ============

    /**
     * @notice Submit a window redemption request
     * @param shares Amount of shares to request for redemption
     */
    function submitWindowRequest(uint256 shares) external nonReentrant whenNotPaused {
        // Enforce mutual exclusivity: only allow during window mode
        RedemptionMode mode = currentMode();
        if (mode != RedemptionMode.WINDOW) {
            revert WrongMode(uint8(RedemptionMode.WINDOW), uint8(mode));
        }

        if (!kyc.isKYCApproved(msg.sender)) revert KYCRequired();
        if (shares == 0) revert InvalidAmount();

        // Window module will transfer shares directly from user
        // and will revert if allowance is insufficient
        windowRedemption.submitRequestFor(msg.sender, shares);

        emit WindowRedemptionRouted(msg.sender, address(windowRedemption), shares);
    }

    /**
     * @notice Adjust a window redemption request
     * @param newShares New amount of shares for the request
     */
    function adjustWindowRequest(uint256 newShares) external nonReentrant whenNotPaused {
        // Enforce mutual exclusivity: only allow during window mode
        RedemptionMode mode = currentMode();
        if (mode != RedemptionMode.WINDOW) {
            revert WrongMode(uint8(RedemptionMode.WINDOW), uint8(mode));
        }

        if (!kyc.isKYCApproved(msg.sender)) revert KYCRequired();

        // Window module will handle token transfers directly with user
        windowRedemption.adjustRequestFor(msg.sender, newShares);
    }

    /**
     * @notice Cancel a window redemption request
     */
    function cancelWindowRequest() external nonReentrant whenNotPaused {
        // Enforce mutual exclusivity: only allow during window mode
        RedemptionMode mode = currentMode();
        if (mode != RedemptionMode.WINDOW) {
            revert WrongMode(uint8(RedemptionMode.WINDOW), uint8(mode));
        }

        // Window module will return shares directly to user
        windowRedemption.cancelRequestFor(msg.sender);
    }

    /**
     * @notice Claim payout from a settled window
     * @param windowId The window ID to claim from
     */
    function claimWindowPayout(uint256 windowId) external nonReentrant whenNotPaused {
        if (!kyc.isKYCApproved(msg.sender)) revert KYCRequired();

        // Window module will handle payouts directly to user
        windowRedemption.claimFor(windowId, msg.sender);
    }

    /**
     * @notice Get user's window request
     * @param windowId Window ID
     * @param user User address
     * @return shares Amount of shares requested
     * @return claimed Amount already claimed
     */
    function getWindowRequest(uint256 windowId, address user)
        external
        view
        returns (uint256 shares, uint256 claimed)
    {
        IWindowRedemption.UserRequest memory request =
            windowRedemption.getUserRequest(windowId, user);
        return (request.shares, request.claimed);
    }

    // ============ Admin Functions ============

    /**
     * @notice Update instant redemption module
     * @param _instantRedemption New instant redemption module address
     */
    function setInstantRedemption(address _instantRedemption) external restricted {
        if (_instantRedemption == address(0)) revert InvalidModule();

        // Validate interface by calling a view function
        try IInstantRedemption(_instantRedemption).paused() returns (bool) {
            // Interface is valid
        } catch {
            revert InvalidModule();
        }

        instantRedemption = IInstantRedemption(_instantRedemption);
        emit InstantRedemptionSet(_instantRedemption);
    }

    /**
     * @notice Update window redemption module
     * @param _windowRedemption New window redemption module address
     */
    function setWindowRedemption(address _windowRedemption) external restricted {
        if (_windowRedemption == address(0)) revert InvalidModule();

        // Validate interface by calling a view function
        try IWindowRedemption(_windowRedemption).isWindowOpen() returns (bool) {
            // Interface is valid
        } catch {
            revert InvalidModule();
        }

        windowRedemption = IWindowRedemption(_windowRedemption);
        emit WindowRedemptionSet(_windowRedemption);
    }

    /**
     * @notice Pause all redemptions
     */
    function pause() external restricted {
        _pause();
    }

    /**
     * @notice Unpause all redemptions
     */
    function unpause() external restricted {
        _unpause();
    }

    // ============ View Functions ============

    /**
     * @notice Get current redemption mode
     * @dev Rules:
     * - Gateway-level pause overrides and returns `NONE`.
     * - Window has precedence from opening through settlement (including closed-but-unsettled).
     * - Instant is available only when no window is open and the current window is settled.
     * - If the selected module is paused, returns `NONE`.
     * @return Current mode (NONE=0, INSTANT=1, WINDOW=2)
     */
    function currentMode() public view returns (RedemptionMode) {
        // Gateway-level pause overrides everything
        if (paused()) return RedemptionMode.NONE;

        // Window takes priority while open OR until it is settled
        // This enforces mutual exclusivity from window open through settlement.
        if (windowRedemption.isWindowOpen()) {
            return windowRedemption.paused() ? RedemptionMode.NONE : RedemptionMode.WINDOW;
        }

        // If a window was opened and is not yet settled, remain in WINDOW mode
        {
            uint256 id = windowRedemption.currentWindowId();
            IWindowRedemption.Window memory w = windowRedemption.getWindow(id);
            if (w.openTime != 0 && !w.isSettled) {
                return windowRedemption.paused() ? RedemptionMode.NONE : RedemptionMode.WINDOW;
            }
        }

        // Default to instant when no window is open
        return instantRedemption.paused() ? RedemptionMode.NONE : RedemptionMode.INSTANT;
    }

    /**
     * @notice Check if window is open for requests
     * @return isOpen Whether window is currently open
     */
    function isWindowOpen() external view returns (bool) {
        return windowRedemption.isWindowOpen();
    }

    /**
     * @notice Get current window ID
     * @return windowId Current window ID
     */
    function currentWindowId() external view returns (uint256) {
        return windowRedemption.currentWindowId();
    }

    /**
     * @notice Get current daily redemption stats
     * @return day Current day index
     * @return globalSpending Global spending for the day
     */
    function getDailyRedemptions() external view returns (uint256 day, uint256 globalSpending) {
        return instantRedemption.getDailyRedemptions();
    }

    /**
     * @notice Get user's instant redemption spending for today
     * @param user User address
     * @return spending User's spending for current day
     */
    function getUserDailySpending(address user) external view returns (uint256) {
        return instantRedemption.getUserDailySpending(user);
    }
}
"
    },
    "smart-contracts-public/lib/openzeppelin-contracts/contracts/access/manager/AccessManaged.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/manager/AccessManaged.sol)

pragma solidity ^0.8.20;

import {AuthorityUtils} from "./AuthorityUtils.sol";
import {IAccessManager} from "./IAccessManager.sol";
import {IAccessManaged} from "./IAccessManaged.sol";
import {Context} from "../../utils/Context.sol";

/**
 * @dev This contract module makes available a {restricted} modifier. Functions decorated with this modifier will be
 * permissioned according to an "authority": a contract like {AccessManager} that follows the {IAuthority} interface,
 * implementing a policy that allows certain callers to access certain functions.
 *
 * IMPORTANT: The `restricted` modifier should never be used on `internal` functions, judiciously used in `public`
 * functions, and ideally only used in `external` functions. See {restricted}.
 */
abstract contract AccessManaged is Context, IAccessManaged {
    address private _authority;

    bool private _consumingSchedule;

    /**
     * @dev Initializes the contract connected to an initial authority.
     */
    constructor(address initialAuthority) {
        _setAuthority(initialAuthority);
    }

    /**
     * @dev Restricts access to a function as defined by the connected Authority for this contract and the
     * caller and selector of the function that entered the contract.
     *
     * [IMPORTANT]
     * ====
     * In general, this modifier should only be used on `external` functions. It is okay to use it on `public`
     * functions that are used as external entry points and are not called internally. Unless you know what you're
     * doing, it should never be used on `internal` functions. Failure to follow these rules can have critical security
     * implications! This is because the permissions are determined by the function that entered the contract, i.e. the
     * function at the bottom of the call stack, and not the function where the modifier is visible in the source code.
     * ====
     *
     * [WARNING]
     * ====
     * Avoid adding this modifier to the https://docs.soliditylang.org/en/v0.8.20/contracts.html#receive-ether-function[`receive()`]
     * function or the https://docs.soliditylang.org/en/v0.8.20/contracts.html#fallback-function[`fallback()`]. These
     * functions are the only execution paths where a function selector cannot be unambiguously determined from the calldata
     * since the selector defaults to `0x00000000` in the `receive()` function and similarly in the `fallback()` function
     * if no calldata is provided. (See {_checkCanCall}).
     *
     * The `receive()` function will always panic whereas the `fallback()` may panic depending on the calldata length.
     * ====
     */
    modifier restricted() {
        _checkCanCall(_msgSender(), _msgData());
        _;
    }

    /// @inheritdoc IAccessManaged
    function authority() public view virtual returns (address) {
        return _authority;
    }

    /// @inheritdoc IAccessManaged
    function setAuthority(address newAuthority) public virtual {
        address caller = _msgSender();
        if (caller != authority()) {
            revert AccessManagedUnauthorized(caller);
        }
        if (newAuthority.code.length == 0) {
            revert AccessManagedInvalidAuthority(newAuthority);
        }
        _setAuthority(newAuthority);
    }

    /// @inheritdoc IAccessManaged
    function isConsumingScheduledOp() public view returns (bytes4) {
        return _consumingSchedule ? this.isConsumingScheduledOp.selector : bytes4(0);
    }

    /**
     * @dev Transfers control to a new authority. Internal function with no access restriction. Allows bypassing the
     * permissions set by the current authority.
     */
    function _setAuthority(address newAuthority) internal virtual {
        _authority = newAuthority;
        emit AuthorityUpdated(newAuthority);
    }

    /**
     * @dev Reverts if the caller is not allowed to call the function identified by a selector. Panics if the calldata
     * is less than 4 bytes long.
     */
    function _checkCanCall(address caller, bytes calldata data) internal virtual {
        (bool immediate, uint32 delay) = AuthorityUtils.canCallWithDelay(
            authority(),
            caller,
            address(this),
            bytes4(data[0:4])
        );
        if (!immediate) {
            if (delay > 0) {
                _consumingSchedule = true;
                IAccessManager(authority()).consumeScheduledOp(caller, data);
                _consumingSchedule = false;
            } else {
                revert AccessManagedUnauthorized(caller);
            }
        }
    }
}
"
    },
    "smart-contracts-public/lib/openzeppelin-contracts/contracts/utils/ReentrancyGuard.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (utils/ReentrancyGuard.sol)

pragma solidity ^0.8.20;

/**
 * @dev Contract module that helps prevent reentrant calls to a function.
 *
 * Inheriting from `ReentrancyGuard` will make the {nonReentrant} modifier
 * available, which can be applied to functions to make sure there are no nested
 * (reentrant) calls to them.
 *
 * Note that because there is a single `nonReentrant` guard, functions marked as
 * `nonReentrant` may not call one another. This can be worked around by making
 * those functions `private`, and then adding `external` `nonReentrant` entry
 * points to them.
 *
 * TIP: If EIP-1153 (transient storage) is available on the chain you're deploying at,
 * consider using {ReentrancyGuardTransient} instead.
 *
 * TIP: If you would like to learn more about reentrancy and alternative ways
 * to protect against it, check out our blog post
 * https://blog.openzeppelin.com/reentrancy-after-istanbul/[Reentrancy After Istanbul].
 */
abstract contract ReentrancyGuard {
    // Booleans are more expensive than uint256 or any type that takes up a full
    // word because each write operation emits an extra SLOAD to first read the
    // slot's contents, replace the bits taken up by the boolean, and then write
    // back. This is the compiler's defense against contract upgrades and
    // pointer aliasing, and it cannot be disabled.

    // The values being non-zero value makes deployment a bit more expensive,
    // but in exchange the refund on every call to nonReentrant will be lower in
    // amount. Since refunds are capped to a percentage of the total
    // transaction's gas, it is best to keep them low in cases like this one, to
    // increase the likelihood of the full refund coming into effect.
    uint256 private constant NOT_ENTERED = 1;
    uint256 private constant ENTERED = 2;

    uint256 private _status;

    /**
     * @dev Unauthorized reentrant call.
     */
    error ReentrancyGuardReentrantCall();

    constructor() {
        _status = NOT_ENTERED;
    }

    /**
     * @dev Prevents a contract from calling itself, directly or indirectly.
     * Calling a `nonReentrant` function from another `nonReentrant`
     * function is not supported. It is possible to prevent this from happening
     * by making the `nonReentrant` function external, and making it call a
     * `private` function that does the actual work.
     */
    modifier nonReentrant() {
        _nonReentrantBefore();
        _;
        _nonReentrantAfter();
    }

    function _nonReentrantBefore() private {
        // On the first call to nonReentrant, _status will be NOT_ENTERED
        if (_status == ENTERED) {
            revert ReentrancyGuardReentrantCall();
        }

        // Any calls to nonReentrant after this point will fail
        _status = ENTERED;
    }

    function _nonReentrantAfter() private {
        // By storing the original value once again, a refund is triggered (see
        // https://eips.ethereum.org/EIPS/eip-2200)
        _status = NOT_ENTERED;
    }

    /**
     * @dev Returns true if the reentrancy guard is currently set to "entered", which indicates there is a
     * `nonReentrant` function in the call stack.
     */
    function _reentrancyGuardEntered() internal view returns (bool) {
        return _status == ENTERED;
    }
}
"
    },
    "smart-contracts-public/lib/openzeppelin-contracts/contracts/utils/Pausable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

import {Context} from "../utils/Context.sol";

/**
 * @dev Contract module which allows children to implement an emergency stop
 * mechanism that can be triggered by an authorized account.
 *
 * This module is used through inheritance. It will make available the
 * modifiers `whenNotPaused` and `whenPaused`, which can be applied to
 * the functions of your contract. Note that they will not be pausable by
 * simply including this module, only once the modifiers are put in place.
 */
abstract contract Pausable is Context {
    bool private _paused;

    /**
     * @dev Emitted when the pause is triggered by `account`.
     */
    event Paused(address account);

    /**
     * @dev Emitted when the pause is lifted by `account`.
     */
    event Unpaused(address account);

    /**
     * @dev The operation failed because the contract is paused.
     */
    error EnforcedPause();

    /**
     * @dev The operation failed because the contract is not paused.
     */
    error ExpectedPause();

    /**
     * @dev Modifier to make a function callable only when the contract is not paused.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    modifier whenNotPaused() {
        _requireNotPaused();
        _;
    }

    /**
     * @dev Modifier to make a function callable only when the contract is paused.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    modifier whenPaused() {
        _requirePaused();
        _;
    }

    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        return _paused;
    }

    /**
     * @dev Throws if the contract is paused.
     */
    function _requireNotPaused() internal view virtual {
        if (paused()) {
            revert EnforcedPause();
        }
    }

    /**
     * @dev Throws if the contract is not paused.
     */
    function _requirePaused() internal view virtual {
        if (!paused()) {
            revert ExpectedPause();
        }
    }

    /**
     * @dev Triggers stopped state.
     *
     * Requirements:
     *
     * - The contract must not be paused.
     */
    function _pause() internal virtual whenNotPaused {
        _paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        _paused = false;
        emit Unpaused(_msgSender());
    }
}
"
    },
    "smart-contracts-public/lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (token/ERC20/IERC20.sol)

pragma solidity >=0.4.16;

/**
 * @dev Interface of the ERC-20 standard as defined in the ERC.
 */
interface IERC20 {
    /**
     * @dev Emitted when `value` tokens are moved from one account (`from`) to
     * another (`to`).
     *
     * Note that `value` may be zero.
     */
    event Transfer(address indexed from, address indexed to, uint256 value);

    /**
     * @dev Emitted when the allowance of a `spender` for an `owner` is set by
     * a call to {approve}. `value` is the new allowance.
     */
    event Approval(address indexed owner, address indexed spender, uint256 value);

    /**
     * @dev Returns the value of tokens in existence.
     */
    function totalSupply() external view returns (uint256);

    /**
     * @dev Returns the value of tokens owned by `account`.
     */
    function balanceOf(address account) external view returns (uint256);

    /**
     * @dev Moves a `value` amount of tokens from the caller's account to `to`.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transfer(address to, uint256 value) external returns (bool);

    /**
     * @dev Returns the remaining number of tokens that `spender` will be
     * allowed to spend on behalf of `owner` through {transferFrom}. This is
     * zero by default.
     *
     * This value changes when {approve} or {transferFrom} are called.
     */
    function allowance(address owner, address spender) external view returns (uint256);

    /**
     * @dev Sets a `value` amount of tokens as the allowance of `spender` over the
     * caller's tokens.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * IMPORTANT: Beware that changing an allowance with this method brings the risk
     * that someone may use both the old and the new allowance by unfortunate
     * transaction ordering. One possible solution to mitigate this race
     * condition is to first reduce the spender's allowance to 0 and set the
     * desired value afterwards:
     * https://github.com/ethereum/EIPs/issues/20#issuecomment-263524729
     *
     * Emits an {Approval} event.
     */
    function approve(address spender, uint256 value) external returns (bool);

    /**
     * @dev Moves a `value` amount of tokens from `from` to `to` using the
     * allowance mechanism. `value` is then deducted from the caller's
     * allowance.
     *
     * Returns a boolean value indicating whether the operation succeeded.
     *
     * Emits a {Transfer} event.
     */
    function transferFrom(address from, address to, uint256 value) external returns (bool);
}
"
    },
    "smart-contracts-public/src/redemption/IInstantRedemption.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

/**
 * @title IInstantRedemption
 * @notice Interface for the "instant" redemption module invoked by {RedemptionGateway}.
 * @dev The concrete implementation should:
 * - Enforce KYC via an external registry.
 * - Enforce daily/global/user limits and fee logic.
 * - Transfer/burn shares and pull/withdraw payout tokens from the vault.
 *
 * All values are denominated as noted by the implementation; payout amounts are in payout-token units.
 */
interface IInstantRedemption {
    // Functions called by gateway
    function redeemFor(address user, uint256 shares, uint256 minPayout) external;
    function previewRedemption(address user, uint256 shares)
        external
        view
        returns (uint256 grossPayout, uint256 fee, uint256 netPayout, bool available);

    // View functions for status
    function paused() external view returns (bool);
    function getDailyRedemptions() external view returns (uint256 day, uint256 globalSpending);
    function getUserDailySpending(address user) external view returns (uint256);
}
"
    },
    "smart-contracts-public/src/redemption/IWindowRedemption.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

/**
 * @title IWindowRedemption
 * @notice Interface for the batched/windowed redemption module coordinated by {RedemptionGateway}.
 * @dev Windows collect share requests, later settle pro‑rata using snapshot prices. Provides
 * view methods for UI and off-chain settlement monitoring.
 */
interface IWindowRedemption {
    // Struct needed for return values
    struct Window {
        uint256 openTime;
        uint256 submissionDeadline;
        uint256 closeTime;
        uint256 totalSharesRequested;
        uint256 totalValueRequested;
        uint256 fundingAmount;
        uint256 fulfillmentRate;
        uint256 totalClaimed;
        bool isSettled;
        bool isFinalized;
        address payoutToken;
        uint256 sharePriceAtClose;
        uint256 payoutPriceAtSettlement;
    }

    struct UserRequest {
        uint256 shares;
        uint256 claimed;
    }

    // Functions called by gateway
    function submitRequestFor(address user, uint256 shares) external;
    function adjustRequestFor(address user, uint256 newShares) external;
    function cancelRequestFor(address user) external;
    function claimFor(uint256 windowId, address user) external;

    // View functions for status
    function paused() external view returns (bool);
    function isWindowOpen() external view returns (bool);
    function currentWindowId() external view returns (uint256);
    function getWindow(uint256 windowId) external view returns (Window memory);
    function getUserRequest(uint256 windowId, address user)
        external
        view
        returns (UserRequest memory);
}
"
    },
    "smart-contracts-public/src/token/IShareToken.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

/**
 * @title IShareToken
 * @notice Minimal ERC20-compatible interface expected by the protocol's deposit and redemption flows.
 * @dev Implemented by {ShareToken}. Functions are deliberately narrow to reduce coupling.
 * - All amounts use the token's own decimals.
 * - {mint} and {burn} are typically restricted via AccessManager roles in the implementation.
 */
interface IShareToken {
    function burn(address user, uint256 amount) external;
    function mint(address to, uint256 amount) external;
    function transferFrom(address from, address to, uint256 amount) external returns (bool);
    function transfer(address to, uint256 amount) external returns (bool);
    function balanceOf(address account) external view returns (uint256);
    function decimals() external view returns (uint8);
}
"
    },
    "smart-contracts-public/src/compliance/IKYCRegistry.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity 0.8.30;

interface IKYCRegistry {
    function isKYCApproved(address user) external view returns (bool);
}
"
    },
    "smart-contracts-public/lib/openzeppelin-contracts/contracts/access/manager/AuthorityUtils.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (access/manager/AuthorityUtils.sol)

pragma solidity ^0.8.20;

import {IAuthority} from "./IAuthority.sol";

library AuthorityUtils {
    /**
     * @dev Since `AccessManager` implements an extended IAuthority interface, invoking `canCall` with backwards compatibility
     * for the preexisting `IAuthority` interface requires special care to avoid reverting on insufficient return data.
     * This helper function takes care of invoking `canCall` in a backwards compatible way without reverting.
     */
    function canCallWithDelay(
        address authority,
        address caller,
        address target,
        bytes4 selector
    ) internal view returns (bool immediate, uint32 delay) {
        bytes memory data = abi.encodeCall(IAuthority.canCall, (caller, target, selector));

        assembly ("memory-safe") {
            mstore(0x00, 0x00)
            mstore(0x20, 0x00)

            if staticcall(gas(), authority, add(data, 0x20), mload(data), 0x00, 0x40) {
                immediate := mload(0x00)
                delay := mload(0x20)

                // If delay does not fit in a uint32, return 0 (no delay)
                // equivalent to: if gt(delay, 0xFFFFFFFF) { delay := 0 }
                delay := mul(delay, iszero(shr(32, delay)))
            }
        }
    }
}
"
    },
    "smart-contracts-public/lib/openzeppelin-contracts/contracts/access/manager/IAccessManager.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/manager/IAccessManager.sol)

pragma solidity >=0.8.4;

interface IAccessManager {
    /**
     * @dev A delayed operation was scheduled.
     */
    event OperationScheduled(
        bytes32 indexed operationId,
        uint32 indexed nonce,
        uint48 schedule,
        address caller,
        address target,
        bytes data
    );

    /**
     * @dev A scheduled operation was executed.
     */
    event OperationExecuted(bytes32 indexed operationId, uint32 indexed nonce);

    /**
     * @dev A scheduled operation was canceled.
     */
    event OperationCanceled(bytes32 indexed operationId, uint32 indexed nonce);

    /**
     * @dev Informational labelling for a roleId.
     */
    event RoleLabel(uint64 indexed roleId, string label);

    /**
     * @dev Emitted when `account` is granted `roleId`.
     *
     * NOTE: The meaning of the `since` argument depends on the `newMember` argument.
     * If the role is granted to a new member, the `since` argument indicates when the account becomes a member of the role,
     * otherwise it indicates the execution delay for this account and roleId is updated.
     */
    event RoleGranted(uint64 indexed roleId, address indexed account, uint32 delay, uint48 since, bool newMember);

    /**
     * @dev Emitted when `account` membership or `roleId` is revoked. Unlike granting, revoking is instantaneous.
     */
    event RoleRevoked(uint64 indexed roleId, address indexed account);

    /**
     * @dev Role acting as admin over a given `roleId` is updated.
     */
    event RoleAdminChanged(uint64 indexed roleId, uint64 indexed admin);

    /**
     * @dev Role acting as guardian over a given `roleId` is updated.
     */
    event RoleGuardianChanged(uint64 indexed roleId, uint64 indexed guardian);

    /**
     * @dev Grant delay for a given `roleId` will be updated to `delay` when `since` is reached.
     */
    event RoleGrantDelayChanged(uint64 indexed roleId, uint32 delay, uint48 since);

    /**
     * @dev Target mode is updated (true = closed, false = open).
     */
    event TargetClosed(address indexed target, bool closed);

    /**
     * @dev Role required to invoke `selector` on `target` is updated to `roleId`.
     */
    event TargetFunctionRoleUpdated(address indexed target, bytes4 selector, uint64 indexed roleId);

    /**
     * @dev Admin delay for a given `target` will be updated to `delay` when `since` is reached.
     */
    event TargetAdminDelayUpdated(address indexed target, uint32 delay, uint48 since);

    error AccessManagerAlreadyScheduled(bytes32 operationId);
    error AccessManagerNotScheduled(bytes32 operationId);
    error AccessManagerNotReady(bytes32 operationId);
    error AccessManagerExpired(bytes32 operationId);
    error AccessManagerLockedRole(uint64 roleId);
    error AccessManagerBadConfirmation();
    error AccessManagerUnauthorizedAccount(address msgsender, uint64 roleId);
    error AccessManagerUnauthorizedCall(address caller, address target, bytes4 selector);
    error AccessManagerUnauthorizedConsume(address target);
    error AccessManagerUnauthorizedCancel(address msgsender, address caller, address target, bytes4 selector);
    error AccessManagerInvalidInitialAdmin(address initialAdmin);

    /**
     * @dev Check if an address (`caller`) is authorised to call a given function on a given contract directly (with
     * no restriction). Additionally, it returns the delay needed to perform the call indirectly through the {schedule}
     * & {execute} workflow.
     *
     * This function is usually called by the targeted contract to control immediate execution of restricted functions.
     * Therefore we only return true if the call can be performed without any delay. If the call is subject to a
     * previously set delay (not zero), then the function should return false and the caller should schedule the operation
     * for future execution.
     *
     * If `allowed` is true, the delay can be disregarded and the operation can be immediately executed, otherwise
     * the operation can be executed if and only if delay is greater than 0.
     *
     * NOTE: The IAuthority interface does not include the `uint32` delay. This is an extension of that interface that
     * is backward compatible. Some contracts may thus ignore the second return argument. In that case they will fail
     * to identify the indirect workflow, and will consider calls that require a delay to be forbidden.
     *
     * NOTE: This function does not report the permissions of the admin functions in the manager itself. These are defined by the
     * {AccessManager} documentation.
     */
    function canCall(
        address caller,
        address target,
        bytes4 selector
    ) external view returns (bool allowed, uint32 delay);

    /**
     * @dev Expiration delay for scheduled proposals. Defaults to 1 week.
     *
     * IMPORTANT: Avoid overriding the expiration with 0. Otherwise every contract proposal will be expired immediately,
     * disabling any scheduling usage.
     */
    function expiration() external view returns (uint32);

    /**
     * @dev Minimum setback for all delay updates, with the exception of execution delays. It
     * can be increased without setback (and reset via {revokeRole} in the case event of an
     * accidental increase). Defaults to 5 days.
     */
    function minSetback() external view returns (uint32);

    /**
     * @dev Get whether the contract is closed disabling any access. Otherwise role permissions are applied.
     *
     * NOTE: When the manager itself is closed, admin functions are still accessible to avoid locking the contract.
     */
    function isTargetClosed(address target) external view returns (bool);

    /**
     * @dev Get the role required to call a function.
     */
    function getTargetFunctionRole(address target, bytes4 selector) external view returns (uint64);

    /**
     * @dev Get the admin delay for a target contract. Changes to contract configuration are subject to this delay.
     */
    function getTargetAdminDelay(address target) external view returns (uint32);

    /**
     * @dev Get the id of the role that acts as an admin for the given role.
     *
     * The admin permission is required to grant the role, revoke the role and update the execution delay to execute
     * an operation that is restricted to this role.
     */
    function getRoleAdmin(uint64 roleId) external view returns (uint64);

    /**
     * @dev Get the role that acts as a guardian for a given role.
     *
     * The guardian permission allows canceling operations that have been scheduled under the role.
     */
    function getRoleGuardian(uint64 roleId) external view returns (uint64);

    /**
     * @dev Get the role current grant delay.
     *
     * Its value may change at any point without an event emitted following a call to {setGrantDelay}.
     * Changes to this value, including effect timepoint are notified in advance by the {RoleGrantDelayChanged} event.
     */
    function getRoleGrantDelay(uint64 roleId) external view returns (uint32);

    /**
     * @dev Get the access details for a given account for a given role. These details include the timepoint at which
     * membership becomes active, and the delay applied to all operation by this user that requires this permission
     * level.
     *
     * Returns:
     * [0] Timestamp at which the account membership becomes valid. 0 means role is not granted.
     * [1] Current execution delay for the account.
     * [2] Pending execution delay for the account.
     * [3] Timestamp at which the pending execution delay will become active. 0 means no delay update is scheduled.
     */
    function getAccess(
        uint64 roleId,
        address account
    ) external view returns (uint48 since, uint32 currentDelay, uint32 pendingDelay, uint48 effect);

    /**
     * @dev Check if a given account currently has the permission level corresponding to a given role. Note that this
     * permission might be associated with an execution delay. {getAccess} can provide more details.
     */
    function hasRole(uint64 roleId, address account) external view returns (bool isMember, uint32 executionDelay);

    /**
     * @dev Give a label to a role, for improved role discoverability by UIs.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {RoleLabel} event.
     */
    function labelRole(uint64 roleId, string calldata label) external;

    /**
     * @dev Add `account` to `roleId`, or change its execution delay.
     *
     * This gives the account the authorization to call any function that is restricted to this role. An optional
     * execution delay (in seconds) can be set. If that delay is non 0, the user is required to schedule any operation
     * that is restricted to members of this role. The user will only be able to execute the operation after the delay has
     * passed, before it has expired. During this period, admin and guardians can cancel the operation (see {cancel}).
     *
     * If the account has already been granted this role, the execution delay will be updated. This update is not
     * immediate and follows the delay rules. For example, if a user currently has a delay of 3 hours, and this is
     * called to reduce that delay to 1 hour, the new delay will take some time to take effect, enforcing that any
     * operation executed in the 3 hours that follows this update was indeed scheduled before this update.
     *
     * Requirements:
     *
     * - the caller must be an admin for the role (see {getRoleAdmin})
     * - granted role must not be the `PUBLIC_ROLE`
     *
     * Emits a {RoleGranted} event.
     */
    function grantRole(uint64 roleId, address account, uint32 executionDelay) external;

    /**
     * @dev Remove an account from a role, with immediate effect. If the account does not have the role, this call has
     * no effect.
     *
     * Requirements:
     *
     * - the caller must be an admin for the role (see {getRoleAdmin})
     * - revoked role must not be the `PUBLIC_ROLE`
     *
     * Emits a {RoleRevoked} event if the account had the role.
     */
    function revokeRole(uint64 roleId, address account) external;

    /**
     * @dev Renounce role permissions for the calling account with immediate effect. If the sender is not in
     * the role this call has no effect.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * Emits a {RoleRevoked} event if the account had the role.
     */
    function renounceRole(uint64 roleId, address callerConfirmation) external;

    /**
     * @dev Change admin role for a given role.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {RoleAdminChanged} event
     */
    function setRoleAdmin(uint64 roleId, uint64 admin) external;

    /**
     * @dev Change guardian role for a given role.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {RoleGuardianChanged} event
     */
    function setRoleGuardian(uint64 roleId, uint64 guardian) external;

    /**
     * @dev Update the delay for granting a `roleId`.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {RoleGrantDelayChanged} event.
     */
    function setGrantDelay(uint64 roleId, uint32 newDelay) external;

    /**
     * @dev Set the role required to call functions identified by the `selectors` in the `target` contract.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {TargetFunctionRoleUpdated} event per selector.
     */
    function setTargetFunctionRole(address target, bytes4[] calldata selectors, uint64 roleId) external;

    /**
     * @dev Set the delay for changing the configuration of a given target contract.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {TargetAdminDelayUpdated} event.
     */
    function setTargetAdminDelay(address target, uint32 newDelay) external;

    /**
     * @dev Set the closed flag for a contract.
     *
     * Closing the manager itself won't disable access to admin methods to avoid locking the contract.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     *
     * Emits a {TargetClosed} event.
     */
    function setTargetClosed(address target, bool closed) external;

    /**
     * @dev Return the timepoint at which a scheduled operation will be ready for execution. This returns 0 if the
     * operation is not yet scheduled, has expired, was executed, or was canceled.
     */
    function getSchedule(bytes32 id) external view returns (uint48);

    /**
     * @dev Return the nonce for the latest scheduled operation with a given id. Returns 0 if the operation has never
     * been scheduled.
     */
    function getNonce(bytes32 id) external view returns (uint32);

    /**
     * @dev Schedule a delayed operation for future execution, and return the operation identifier. It is possible to
     * choose the timestamp at which the operation becomes executable as long as it satisfies the execution delays
     * required for the caller. The special value zero will automatically set the earliest possible time.
     *
     * Returns the `operationId` that was scheduled. Since this value is a hash of the parameters, it can reoccur when
     * the same parameters are used; if this is relevant, the returned `nonce` can be used to uniquely identify this
     * scheduled operation from other occurrences of the same `operationId` in invocations of {execute} and {cancel}.
     *
     * Emits a {OperationScheduled} event.
     *
     * NOTE: It is not possible to concurrently schedule more than one operation with the same `target` and `data`. If
     * this is necessary, a random byte can be appended to `data` to act as a salt that will be ignored by the target
     * contract if it is using standard Solidity ABI encoding.
     */
    function schedule(
        address target,
        bytes calldata data,
        uint48 when
    ) external returns (bytes32 operationId, uint32 nonce);

    /**
     * @dev Execute a function that is delay restricted, provided it was properly scheduled beforehand, or the
     * execution delay is 0.
     *
     * Returns the nonce that identifies the previously scheduled operation that is executed, or 0 if the
     * operation wasn't previously scheduled (if the caller doesn't have an execution delay).
     *
     * Emits an {OperationExecuted} event only if the call was scheduled and delayed.
     */
    function execute(address target, bytes calldata data) external payable returns (uint32);

    /**
     * @dev Cancel a scheduled (delayed) operation. Returns the nonce that identifies the previously scheduled
     * operation that is cancelled.
     *
     * Requirements:
     *
     * - the caller must be the proposer, a guardian of the targeted function, or a global admin
     *
     * Emits a {OperationCanceled} event.
     */
    function cancel(address caller, address target, bytes calldata data) external returns (uint32);

    /**
     * @dev Consume a scheduled operation targeting the caller. If such an operation exists, mark it as consumed
     * (emit an {OperationExecuted} event and clean the state). Otherwise, throw an error.
     *
     * This is useful for contract that want to enforce that calls targeting them were scheduled on the manager,
     * with all the verifications that it implies.
     *
     * Emit a {OperationExecuted} event.
     */
    function consumeScheduledOp(address caller, bytes calldata data) external;

    /**
     * @dev Hashing function for delayed operations.
     */
    function hashOperation(address caller, address target, bytes calldata data) external view returns (bytes32);

    /**
     * @dev Changes the authority of a target managed by this manager instance.
     *
     * Requirements:
     *
     * - the caller must be a global admin
     */
    function updateAuthority(address target, address newAuthority) external;
}
"
    },
    "smart-contracts-public/lib/openzeppelin-contracts/contracts/access/manager/IAccessManaged.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/manager/IAccessManaged.sol)

pragma solidity >=0.8.4;

interface IAccessManaged {
    /**
     * @dev Authority that manages this contract was updated.
     */
    event AuthorityUpdated(address authority);

    error AccessManagedUnauthorized(address caller);
    error AccessManagedRequiredDelay(address caller, uint32 delay);
    error AccessManagedInvalidAuthority(address authority);

    /**
     * @dev Returns the current authority.
     */
    function authority() external view returns (address);

    /**
     * @dev Transfers control to a new authority. The caller must be the current authority.
     */
    function setAuthority(address) external;

    /**
     * @dev Returns true only in the context of a delayed restricted call, at the moment that the scheduled operation is
     * being consumed. Prevents denial of service for delayed restricted calls in the case that the contract performs
     * attacker controlled calls.
     */
    function isConsumingScheduledOp() external view returns (bytes4);
}
"
    },
    "smart-contracts-public/lib/openzeppelin-contracts/contracts/utils/Context.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.0.1) (utils/Context.sol)

pragma solidity ^0.8.20;

/**
 * @dev Provides information about the current execution context, including the
 * sender of the transaction and its data. While these are generally available
 * via msg.sender and msg.data, they should not be accessed in such a direct
 * manner, since when dealing with meta-transactions the account sending and
 * paying for execution may not be the actual sender (as far as an application
 * is concerned).
 *
 * This contract is only required for intermediate, library-like contracts.
 */
abstract contract Context {
    function _msgSender() internal view virtual returns (address) {
        return msg.sender;
    }

    function _msgData() internal view virtual returns (bytes calldata) {
        return msg.data;
    }

    function _contextSuffixLength() internal view virtual returns (uint256) {
        return 0;
    }
}
"
    },
    "smart-contracts-public/lib/openzeppelin-contracts/contracts/access/manager/IAuthority.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/manager/IAuthority.sol)

pragma solidity >=0.4.16;

/**
 * @dev Standard interface for permissioning originally defined in Dappsys.
 */
interface IAuthority {
    /**
     * @dev Returns true if the caller can invoke on a target the function identified by a function selector.
     */
    function canCall(address caller, address target, bytes4 selector) external view returns (bool allowed);
}
"
    }
  },
  "settings": {
    "remappings": [
      "@chainlink/=smart-contracts-public/lib/chainlink/",
      "@openzeppelin/contracts/=smart-contracts-public/lib/openzeppelin-contracts/contracts/",
      "@openzeppelin/contracts-upgradeable/=smart-contracts-public/lib/openzeppelin-contracts-upgradeable/contracts/",
      "forge-std/=smart-contracts-public/lib/forge-std/src/",
      "openzeppelin-foundry-upgrades/=lib/openzeppelin-foundry-upgrades/src/",
      "chainlink/=smart-contracts-public/lib/chainlink/",
      "erc4626-tests/=smart-contracts-public/lib/openzeppelin-contracts-upgradeable/lib/erc4626-tests/",
      "halmos-cheatcodes/=smart-contracts-public/lib/openzeppelin-contracts-upgradeable/lib/halmos-cheatcodes/src/",
      "openzeppelin-contracts-upgradeable/=smart-contracts-public/lib/openzeppelin-contracts-upgradeable/",
      "openzeppelin-contracts/=smart-contracts-public/lib/openzeppelin-contracts/"
    ],
    "optimizer": {
      "enabled": true,
      "runs": 500
    },
    "metadata": {
      "useLiteralContent": false,
      "bytecodeHash": "ipfs",
      "appendCBOR": true
    },
    "outputSelection": {
      "*": {
        "*": [
          "evm.bytecode",
          "evm.deployedBytecode",
          "devdoc",
          "userdoc",
          "metadata",
          "abi"
        ]
      }
    },
    "evmVersion": "cancun",
    "viaIR": true
  }
}}

Tags:
ERC20, Proxy, Mintable, Burnable, Pausable, Voting, Upgradeable, Factory, Oracle|addr:0x8aeb9453ef22cb38abc7a3af9c208f65c1bfe31e|verified:true|block:23479682|tx:0x2cb98572f2c3c9d7d55b1cddd27bba0c35a04e5501120565bac4da186bd0eaeb|first_check:1759318520

Submitted on: 2025-10-01 13:35:20

Comments

Log in to comment.

No comments yet.