STBL_YLD

Description:

Multi-signature wallet contract requiring multiple confirmations for transaction execution.

Blockchain: Ethereum

Source Code: View Code On The Blockchain

Solidity Source Code:

{{
  "language": "Solidity",
  "sources": {
    "contracts/token/STBL_YLD.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/metatx/ERC2771ContextUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol";

import "../lib/STBL_Structs.sol";
import "../lib/STBL_Errors.sol";
import "../interfaces/ISTBL_YLD.sol";

/**
 * @title STBL YLD - Yield Bearing NFT Token
 * @notice Implementation of a yield-bearing NFT token with comprehensive metadata support and meta-transaction capabilities
 * @dev Extends ERC721Pausable with role-based access control, ERC2771Context for meta-transactions, and custom metadata management
 * @author STBL Team
 * @custom:version 1.0.0
 * @custom:security-contact security@stbl.finance
 */
contract STBL_YLD is
    Initializable,
    iSTBL_YLD,
    AccessControlUpgradeable,
    ERC721PausableUpgradeable,
    ERC2771ContextUpgradeable,
    UUPSUpgradeable
{
    /** @notice Role identifier for minting and burning functionality */
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    /** @notice Role identifier for pause/unpause functionality */
    bytes32 public constant PAUSE_ROLE = keccak256("PAUSE_ROLE");
    /** @notice Role identifier for upgrade functionality */
    bytes32 public constant UPGRADER_ROLE = keccak256("UPGRADER_ROLE");

    /** @notice Version number of the contract implementation */
    uint256 private _version;

    /** @notice Base URI for token metadata */
    string private baseURI;
    /** @notice Counter for NFT token IDs, incremented for each new mint */
    uint256 public nftCtr;
    /** @notice Mapping of token IDs to their complete metadata structures */
    mapping(uint256 => YLD_Metadata) private nftMetaData;

    /** @notice Address of the trusted forwarder for meta-transactions */
    address private trustedForwarderAddress;

    /**
     * @dev Storage gap for future upgrades
     * @notice Reserved storage space to allow for layout changes in future versions
     */
    uint256[64] private __gap;

    /**
     * @notice Disables initializers to prevent implementation contract initialization
     * @dev This constructor is marked as unsafe for upgrades but is required for proper proxy pattern implementation
     * @custom:oz-upgrades-unsafe-allow constructor
     */
    constructor() ERC2771ContextUpgradeable(address(0)) {
        _disableInitializers();
    }

    /**
     * @notice Initializes the STBL YLD NFT contract
     * @dev Sets up roles, initializes counters, and configures base URI. Can only be called once during deployment
     * @param _uri Base URI for token metadata endpoints
     * @custom:event Emits various RoleGranted events during initialization
     */
    function initialize(string memory _uri) public initializer {
        __AccessControl_init();
        __ERC721_init("STBL_YLD - Yield Bearing NFT", "YLD");
        __ERC721Pausable_init();
        __Pausable_init();
        __UUPSUpgradeable_init();

        _grantRole(DEFAULT_ADMIN_ROLE, _msgSender());
        _grantRole(UPGRADER_ROLE, _msgSender());
        _setRoleAdmin(MINTER_ROLE, DEFAULT_ADMIN_ROLE);
        _setRoleAdmin(PAUSE_ROLE, DEFAULT_ADMIN_ROLE);
        _setRoleAdmin(UPGRADER_ROLE, DEFAULT_ADMIN_ROLE);

        baseURI = _uri;
        nftCtr = 0;
        trustedForwarderAddress = address(0);
    }

    /**
     * @notice Authorizes upgrades to the contract implementation
     * @dev Only callable by addresses with UPGRADER_ROLE. Increments version number on each upgrade
     * @param newImplementation Address of the new implementation contract
     * @custom:security This function controls contract upgrades and should be carefully managed
     */
    function _authorizeUpgrade(
        address newImplementation
    ) internal override onlyRole(UPGRADER_ROLE) {
        _version = _version + 1;
        emit ContractUpgraded(newImplementation);
    }

    /**
     * @notice Returns the current implementation version
     * @dev Useful for tracking upgrade versions and contract state
     * @return The version number of the current implementation
     */
    function version() external view returns (uint256) {
        return _version;
    }

    /**
     * @notice Updates the base URI for token metadata
     * @dev Changes the base URI used for constructing token metadata URLs. Only callable by addresses with DEFAULT_ADMIN_ROLE
     * @param _uri The new base URI to set for token metadata
     * @custom:event Does not emit an event - consider adding one for transparency
     */
    function setBaseURI(
        string memory _uri
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        baseURI = _uri;
    }

    /**
     * @notice Pauses all token transfers and operations
     * @dev Emergency function to halt all contract operations when security issues are detected. Only callable by addresses with PAUSE_ROLE
     * @custom:security Critical security function that should be used sparingly
     * @custom:event Emits Paused event from PausableUpgradeable
     */
    function pause() external onlyRole(PAUSE_ROLE) {
        _pause();
    }

    /**
     * @notice Unpauses token transfers and operations
     * @dev Resumes normal contract operations after security issues have been resolved. Only callable by addresses with PAUSE_ROLE
     * @custom:event Emits Unpaused event from PausableUpgradeable
     */
    function unpause() external onlyRole(PAUSE_ROLE) {
        _unpause();
    }

    /**
     * @notice Returns the base URI for token metadata
     * @dev Internal function that can be overridden by inheriting contracts
     * @return The base URI used for constructing token metadata URLs
     */
    function _baseURI() internal view virtual override returns (string memory) {
        return baseURI;
    }

    /**
     * @notice Returns the complete URI for a specific token's metadata
     * @dev Overrides the standard ERC721 tokenURI to return custom metadata URIs stored in nftMetaData mapping
     * @param _tokenID The ID of the token to query
     * @return The complete URI for the token's metadata endpoint
     * @custom:throws May revert if tokenID does not exist or metadata is not properly set
     */
    function tokenURI(
        uint256 _tokenID
    ) public view virtual override(ERC721Upgradeable) returns (string memory) {
        return string(abi.encodePacked(baseURI, _tokenID));
    }

    /**
     * @notice Retrieves the complete metadata structure for a specific NFT
     * @dev Public getter for accessing detailed NFT metadata including yield information and disabled status
     * @param _tokenID The ID of the token to query
     * @return The complete YLD_Metadata structure for the token
     * @custom:gas This function returns a struct which may be gas-intensive for large metadata
     */
    function getNFTData(
        uint256 _tokenID
    ) external view returns (YLD_Metadata memory) {
        return nftMetaData[_tokenID];
    }

    /**
     * @notice Mints a new yield-bearing NFT token
     * @dev Creates a new NFT with associated metadata and increments the token counter. Only callable when not paused by addresses with MINTER_ROLE
     * @param _to Address to mint the NFT to
     * @param _metadata Complete metadata structure associated with the NFT
     * @return The ID of the newly minted NFT
     * @custom:event Emits MintEvent with recipient, token ID, and metadata
     * @custom:security Ensure _metadata is properly validated before calling this function
     */
    function mint(
        address _to,
        YLD_Metadata memory _metadata
    ) external whenNotPaused onlyRole(MINTER_ROLE) returns (uint256) {
        unchecked {
            nftCtr += 1;
        }
        _safeMint(_to, nftCtr);

        nftMetaData[nftCtr] = _metadata;
        nftMetaData[nftCtr].uri = string(abi.encodePacked(baseURI, nftCtr));

        emit MintEvent(_to, nftCtr, nftMetaData[nftCtr]);
        return nftCtr;
    }

    /**
     * @notice Burns an NFT permanently
     * @dev Removes the NFT from circulation and marks its metadata as disabled. Only callable when not paused by addresses with MINTER_ROLE
     * @param _from Address that currently owns the NFT
     * @param _id Token ID to burn
     * @custom:event Emits BurnEvent with owner and token ID
     * @custom:throws Reverts with STBL_YLD_NotOwner if _from is not the owner of the token
     */
    function burn(
        address _from,
        uint256 _id
    ) external whenNotPaused onlyRole(MINTER_ROLE) {
        if (ownerOf(_id) != _from) revert STBL_YLD_NotOwner(_id);

        _update(address(0), _id, _from);

        nftMetaData[_id].isDisabled = true;

        emit BurnEvent(_from, _id);
    }

    /**
     * @notice Disables an NFT, preventing transfers while maintaining ownership
     * @dev Sets the disabled flag to prevent transfers without burning the token. Only callable when not paused by addresses with DEFAULT_ADMIN_ROLE
     * @param _id Token ID to disable
     * @custom:event Emits NFTDisabled event
     * @custom:throws Reverts with STBL_YLD_AlreadyDisabled if the NFT is already disabled
     */
    function disableNFT(
        uint256 _id
    ) external whenNotPaused onlyRole(DEFAULT_ADMIN_ROLE) {
        if (nftMetaData[_id].isDisabled != false)
            revert STBL_YLD_AlreadyDisabled(_id);
        nftMetaData[_id].isDisabled = true;
        emit NFTDisabled(_id);
    }

    /**
     * @notice Enables a previously disabled NFT, allowing transfers again
     * @dev Removes the disabled flag to restore normal transfer functionality. Only callable when not paused by addresses with DEFAULT_ADMIN_ROLE
     * @param _id Token ID to enable
     * @custom:event Emits NFTEnabled event
     * @custom:throws Reverts with STBL_YLD_AlreadyEnabled if the NFT is already enabled
     */
    function enableNFT(
        uint256 _id
    ) external whenNotPaused onlyRole(DEFAULT_ADMIN_ROLE) {
        if (nftMetaData[_id].isDisabled != true)
            revert STBL_YLD_AlreadyEnabled(_id);
        nftMetaData[_id].isDisabled = false;
        emit NFTEnabled(_id);
    }

    /**
     * @notice Checks if the contract supports a given interface
     * @dev Implementation of ERC165 to declare supported interfaces including AccessControl, ERC721, and ERC2771Context
     * @param interfaceId The interface identifier to check
     * @return True if the interface is supported, false otherwise
     */
    function supportsInterface(
        bytes4 interfaceId
    )
        public
        view
        virtual
        override(AccessControlUpgradeable, ERC721Upgradeable, IERC165)
        returns (bool)
    {
        return
            interfaceId == type(IAccessControl).interfaceId ||
            super.supportsInterface(interfaceId) ||
            interfaceId == type(ERC2771ContextUpgradeable).interfaceId;
    }

    /**
     * @notice Updates the trusted forwarder address for meta-transactions
     * @dev Changes the forwarder used for ERC2771 meta-transaction support. Only callable by addresses with DEFAULT_ADMIN_ROLE
     * @param _newForwarder Address of the new trusted forwarder contract
     * @custom:event Emits TrustedForwarderUpdated with previous and new forwarder addresses
     * @custom:security Ensure the new forwarder contract is trusted and properly audited
     */
    function updateTrustedForwarder(
        address _newForwarder
    ) external onlyRole(DEFAULT_ADMIN_ROLE) {
        address previousForwarder = trustedForwarderAddress;
        trustedForwarderAddress = _newForwarder;
        emit TrustedForwarderUpdated(previousForwarder, _newForwarder);
    }

    /**
     * @notice Returns the address of the trusted forwarder for meta-transactions
     * @dev Public getter for the trusted forwarder address used in ERC2771Context
     * @return The current trusted forwarder address
     */
    function trustedForwarder() public view virtual override returns (address) {
        return trustedForwarderAddress;
    }

    /**
     * @notice Returns the actual sender of the transaction, accounting for meta-transactions
     * @dev Overrides to resolve inheritance conflict between ERC2771Context and Context
     * @return The actual sender address, which may differ from msg.sender in meta-transactions
     * @custom:inheritance Resolves conflict between ContextUpgradeable and ERC2771ContextUpgradeable
     */
    function _msgSender()
        internal
        view
        override(ContextUpgradeable, ERC2771ContextUpgradeable)
        returns (address)
    {
        return ERC2771ContextUpgradeable._msgSender();
    }

    /**
     * @notice Returns the actual calldata of the transaction, accounting for meta-transactions
     * @dev Overrides to resolve inheritance conflict between ERC2771Context and Context
     * @return The actual transaction data, which may be modified in meta-transactions
     * @custom:inheritance Resolves conflict between ContextUpgradeable and ERC2771ContextUpgradeable
     */
    function _msgData()
        internal
        view
        override(ContextUpgradeable, ERC2771ContextUpgradeable)
        returns (bytes calldata)
    {
        return ERC2771ContextUpgradeable._msgData();
    }

    /**
     * @notice Returns the length of the context suffix for meta-transaction support
     * @dev Overrides to resolve inheritance conflict for ERC2771Context
     * @return The context suffix length used in meta-transaction processing
     * @custom:inheritance Resolves conflict between ContextUpgradeable and ERC2771ContextUpgradeable
     */
    function _contextSuffixLength()
        internal
        view
        override(ContextUpgradeable, ERC2771ContextUpgradeable)
        returns (uint256)
    {
        return ERC2771ContextUpgradeable._contextSuffixLength();
    }

    /**
     * @notice Extends standard ERC721 _update function with disabled NFT validation
     * @dev Prevents transfers of disabled NFTs while allowing burns. Only operates when contract is not paused
     * @param to Recipient address (address(0) for burns)
     * @param tokenId Token being transferred
     * @param auth Address authorized to perform the transfer
     * @return The previous owner of the token
     * @custom:throws Reverts with STBL_YLD_TransferDisabled if attempting to transfer a disabled NFT
     * @custom:security Burns are still allowed for disabled NFTs as they transfer to address(0)
     */
    function _update(
        address to,
        uint256 tokenId,
        address auth
    ) internal virtual override whenNotPaused returns (address) {
        if (nftMetaData[tokenId].isDisabled)
            revert STBL_YLD_TransferDisabled(tokenId);
        return super._update(to, tokenId, auth);
    }
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/access/AccessControlUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (access/AccessControl.sol)

pragma solidity ^0.8.20;

import {IAccessControl} from "@openzeppelin/contracts/access/IAccessControl.sol";
import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {IERC165} from "@openzeppelin/contracts/utils/introspection/IERC165.sol";
import {ERC165Upgradeable} from "../utils/introspection/ERC165Upgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Contract module that allows children to implement role-based access
 * control mechanisms. This is a lightweight version that doesn't allow enumerating role
 * members except through off-chain means by accessing the contract event logs. Some
 * applications may benefit from on-chain enumerability, for those cases see
 * {AccessControlEnumerable}.
 *
 * Roles are referred to by their `bytes32` identifier. These should be exposed
 * in the external API and be unique. The best way to achieve this is by
 * using `public constant` hash digests:
 *
 * ```solidity
 * bytes32 public constant MY_ROLE = keccak256("MY_ROLE");
 * ```
 *
 * Roles can be used to represent a set of permissions. To restrict access to a
 * function call, use {hasRole}:
 *
 * ```solidity
 * function foo() public {
 *     require(hasRole(MY_ROLE, msg.sender));
 *     ...
 * }
 * ```
 *
 * Roles can be granted and revoked dynamically via the {grantRole} and
 * {revokeRole} functions. Each role has an associated admin role, and only
 * accounts that have a role's admin role can call {grantRole} and {revokeRole}.
 *
 * By default, the admin role for all roles is `DEFAULT_ADMIN_ROLE`, which means
 * that only accounts with this role will be able to grant or revoke other
 * roles. More complex role relationships can be created by using
 * {_setRoleAdmin}.
 *
 * WARNING: The `DEFAULT_ADMIN_ROLE` is also its own admin: it has permission to
 * grant and revoke this role. Extra precautions should be taken to secure
 * accounts that have been granted it. We recommend using {AccessControlDefaultAdminRules}
 * to enforce additional security measures for this role.
 */
abstract contract AccessControlUpgradeable is Initializable, ContextUpgradeable, IAccessControl, ERC165Upgradeable {
    struct RoleData {
        mapping(address account => bool) hasRole;
        bytes32 adminRole;
    }

    bytes32 public constant DEFAULT_ADMIN_ROLE = 0x00;


    /// @custom:storage-location erc7201:openzeppelin.storage.AccessControl
    struct AccessControlStorage {
        mapping(bytes32 role => RoleData) _roles;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.AccessControl")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant AccessControlStorageLocation = 0x02dd7bc7dec4dceedda775e58dd541e08a116c6c53815c0bd028192f7b626800;

    function _getAccessControlStorage() private pure returns (AccessControlStorage storage $) {
        assembly {
            $.slot := AccessControlStorageLocation
        }
    }

    /**
     * @dev Modifier that checks that an account has a specific role. Reverts
     * with an {AccessControlUnauthorizedAccount} error including the required role.
     */
    modifier onlyRole(bytes32 role) {
        _checkRole(role);
        _;
    }

    function __AccessControl_init() internal onlyInitializing {
    }

    function __AccessControl_init_unchained() internal onlyInitializing {
    }
    /// @inheritdoc IERC165
    function supportsInterface(bytes4 interfaceId) public view virtual override returns (bool) {
        return interfaceId == type(IAccessControl).interfaceId || super.supportsInterface(interfaceId);
    }

    /**
     * @dev Returns `true` if `account` has been granted `role`.
     */
    function hasRole(bytes32 role, address account) public view virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].hasRole[account];
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `_msgSender()`
     * is missing `role`. Overriding this function changes the behavior of the {onlyRole} modifier.
     */
    function _checkRole(bytes32 role) internal view virtual {
        _checkRole(role, _msgSender());
    }

    /**
     * @dev Reverts with an {AccessControlUnauthorizedAccount} error if `account`
     * is missing `role`.
     */
    function _checkRole(bytes32 role, address account) internal view virtual {
        if (!hasRole(role, account)) {
            revert AccessControlUnauthorizedAccount(account, role);
        }
    }

    /**
     * @dev Returns the admin role that controls `role`. See {grantRole} and
     * {revokeRole}.
     *
     * To change a role's admin, use {_setRoleAdmin}.
     */
    function getRoleAdmin(bytes32 role) public view virtual returns (bytes32) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        return $._roles[role].adminRole;
    }

    /**
     * @dev Grants `role` to `account`.
     *
     * If `account` had not been already granted `role`, emits a {RoleGranted}
     * event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleGranted} event.
     */
    function grantRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _grantRole(role, account);
    }

    /**
     * @dev Revokes `role` from `account`.
     *
     * If `account` had been granted `role`, emits a {RoleRevoked} event.
     *
     * Requirements:
     *
     * - the caller must have ``role``'s admin role.
     *
     * May emit a {RoleRevoked} event.
     */
    function revokeRole(bytes32 role, address account) public virtual onlyRole(getRoleAdmin(role)) {
        _revokeRole(role, account);
    }

    /**
     * @dev Revokes `role` from the calling account.
     *
     * Roles are often managed via {grantRole} and {revokeRole}: this function's
     * purpose is to provide a mechanism for accounts to lose their privileges
     * if they are compromised (such as when a trusted device is misplaced).
     *
     * If the calling account had been revoked `role`, emits a {RoleRevoked}
     * event.
     *
     * Requirements:
     *
     * - the caller must be `callerConfirmation`.
     *
     * May emit a {RoleRevoked} event.
     */
    function renounceRole(bytes32 role, address callerConfirmation) public virtual {
        if (callerConfirmation != _msgSender()) {
            revert AccessControlBadConfirmation();
        }

        _revokeRole(role, callerConfirmation);
    }

    /**
     * @dev Sets `adminRole` as ``role``'s admin role.
     *
     * Emits a {RoleAdminChanged} event.
     */
    function _setRoleAdmin(bytes32 role, bytes32 adminRole) internal virtual {
        AccessControlStorage storage $ = _getAccessControlStorage();
        bytes32 previousAdminRole = getRoleAdmin(role);
        $._roles[role].adminRole = adminRole;
        emit RoleAdminChanged(role, previousAdminRole, adminRole);
    }

    /**
     * @dev Attempts to grant `role` to `account` and returns a boolean indicating if `role` was granted.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleGranted} event.
     */
    function _grantRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (!hasRole(role, account)) {
            $._roles[role].hasRole[account] = true;
            emit RoleGranted(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }

    /**
     * @dev Attempts to revoke `role` from `account` and returns a boolean indicating if `role` was revoked.
     *
     * Internal function without access restriction.
     *
     * May emit a {RoleRevoked} event.
     */
    function _revokeRole(bytes32 role, address account) internal virtual returns (bool) {
        AccessControlStorage storage $ = _getAccessControlStorage();
        if (hasRole(role, account)) {
            $._roles[role].hasRole[account] = false;
            emit RoleRevoked(role, account, _msgSender());
            return true;
        } else {
            return false;
        }
    }
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721PausableUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.1.0) (token/ERC721/extensions/ERC721Pausable.sol)

pragma solidity ^0.8.20;

import {ERC721Upgradeable} from "../ERC721Upgradeable.sol";
import {PausableUpgradeable} from "../../../utils/PausableUpgradeable.sol";
import {Initializable} from "../../../proxy/utils/Initializable.sol";

/**
 * @dev ERC-721 token with pausable token transfers, minting and burning.
 *
 * Useful for scenarios such as preventing trades until the end of an evaluation
 * period, or having an emergency switch for freezing all token transfers in the
 * event of a large bug.
 *
 * IMPORTANT: This contract does not include public pause and unpause functions. In
 * addition to inheriting this contract, you must define both functions, invoking the
 * {Pausable-_pause} and {Pausable-_unpause} internal functions, with appropriate
 * access control, e.g. using {AccessControl} or {Ownable}. Not doing so will
 * make the contract pause mechanism of the contract unreachable, and thus unusable.
 */
abstract contract ERC721PausableUpgradeable is Initializable, ERC721Upgradeable, PausableUpgradeable {
    function __ERC721Pausable_init() internal onlyInitializing {
    }

    function __ERC721Pausable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev See {ERC721-_update}.
     *
     * Requirements:
     *
     * - the contract must not be paused.
     */
    function _update(
        address to,
        uint256 tokenId,
        address auth
    ) internal virtual override whenNotPaused returns (address) {
        return super._update(to, tokenId, auth);
    }
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/utils/PausableUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (utils/Pausable.sol)

pragma solidity ^0.8.20;

import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.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 PausableUpgradeable is Initializable, ContextUpgradeable {
    /// @custom:storage-location erc7201:openzeppelin.storage.Pausable
    struct PausableStorage {
        bool _paused;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Pausable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant PausableStorageLocation = 0xcd5ed15c6e187e77e9aee88184c21f4f2182ab5827cb3b7e07fbedcd63f03300;

    function _getPausableStorage() private pure returns (PausableStorage storage $) {
        assembly {
            $.slot := PausableStorageLocation
        }
    }

    /**
     * @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();
        _;
    }

    function __Pausable_init() internal onlyInitializing {
    }

    function __Pausable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Returns true if the contract is paused, and false otherwise.
     */
    function paused() public view virtual returns (bool) {
        PausableStorage storage $ = _getPausableStorage();
        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 {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = true;
        emit Paused(_msgSender());
    }

    /**
     * @dev Returns to normal state.
     *
     * Requirements:
     *
     * - The contract must be paused.
     */
    function _unpause() internal virtual whenPaused {
        PausableStorage storage $ = _getPausableStorage();
        $._paused = false;
        emit Unpaused(_msgSender());
    }
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/metatx/ERC2771ContextUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.4.0) (metatx/ERC2771Context.sol)

pragma solidity ^0.8.20;

import {ContextUpgradeable} from "../utils/ContextUpgradeable.sol";
import {Initializable} from "../proxy/utils/Initializable.sol";

/**
 * @dev Context variant with ERC-2771 support.
 *
 * WARNING: Avoid using this pattern in contracts that rely in a specific calldata length as they'll
 * be affected by any forwarder whose `msg.data` is suffixed with the `from` address according to the ERC-2771
 * specification adding the address size in bytes (20) to the calldata size. An example of an unexpected
 * behavior could be an unintended fallback (or another function) invocation while trying to invoke the `receive`
 * function only accessible if `msg.data.length == 0`.
 *
 * WARNING: The usage of `delegatecall` in this contract is dangerous and may result in context corruption.
 * Any forwarded request to this contract triggering a `delegatecall` to itself will result in an invalid {_msgSender}
 * recovery.
 */
abstract contract ERC2771ContextUpgradeable is Initializable, ContextUpgradeable {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable _trustedForwarder;

    /**
     * @dev Initializes the contract with a trusted forwarder, which will be able to
     * invoke functions on this contract on behalf of other accounts.
     *
     * NOTE: The trusted forwarder can be replaced by overriding {trustedForwarder}.
     */
    /// @custom:oz-upgrades-unsafe-allow constructor
    constructor(address trustedForwarder_) {
        _trustedForwarder = trustedForwarder_;
    }

    /**
     * @dev Returns the address of the trusted forwarder.
     */
    function trustedForwarder() public view virtual returns (address) {
        return _trustedForwarder;
    }

    /**
     * @dev Indicates whether any particular address is the trusted forwarder.
     */
    function isTrustedForwarder(address forwarder) public view virtual returns (bool) {
        return forwarder == trustedForwarder();
    }

    /**
     * @dev Override for `msg.sender`. Defaults to the original `msg.sender` whenever
     * a call is not performed by the trusted forwarder or the calldata length is less than
     * 20 bytes (an address length).
     */
    function _msgSender() internal view virtual override returns (address) {
        uint256 calldataLength = msg.data.length;
        uint256 contextSuffixLength = _contextSuffixLength();
        if (calldataLength >= contextSuffixLength && isTrustedForwarder(msg.sender)) {
            unchecked {
                return address(bytes20(msg.data[calldataLength - contextSuffixLength:]));
            }
        } else {
            return super._msgSender();
        }
    }

    /**
     * @dev Override for `msg.data`. Defaults to the original `msg.data` whenever
     * a call is not performed by the trusted forwarder or the calldata length is less than
     * 20 bytes (an address length).
     */
    function _msgData() internal view virtual override returns (bytes calldata) {
        uint256 calldataLength = msg.data.length;
        uint256 contextSuffixLength = _contextSuffixLength();
        if (calldataLength >= contextSuffixLength && isTrustedForwarder(msg.sender)) {
            unchecked {
                return msg.data[:calldataLength - contextSuffixLength];
            }
        } else {
            return super._msgData();
        }
    }

    /**
     * @dev ERC-2771 specifies the context as being a single address (20 bytes).
     */
    function _contextSuffixLength() internal view virtual override returns (uint256) {
        return 20;
    }
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/Initializable.sol)

pragma solidity ^0.8.20;

/**
 * @dev This is a base contract to aid in writing upgradeable contracts, or any kind of contract that will be deployed
 * behind a proxy. Since proxied contracts do not make use of a constructor, it's common to move constructor logic to an
 * external initializer function, usually called `initialize`. It then becomes necessary to protect this initializer
 * function so it can only be called once. The {initializer} modifier provided by this contract will have this effect.
 *
 * The initialization functions use a version number. Once a version number is used, it is consumed and cannot be
 * reused. This mechanism prevents re-execution of each "step" but allows the creation of new initialization steps in
 * case an upgrade adds a module that needs to be initialized.
 *
 * For example:
 *
 * [.hljs-theme-light.nopadding]
 * ```solidity
 * contract MyToken is ERC20Upgradeable {
 *     function initialize() initializer public {
 *         __ERC20_init("MyToken", "MTK");
 *     }
 * }
 *
 * contract MyTokenV2 is MyToken, ERC20PermitUpgradeable {
 *     function initializeV2() reinitializer(2) public {
 *         __ERC20Permit_init("MyToken");
 *     }
 * }
 * ```
 *
 * TIP: To avoid leaving the proxy in an uninitialized state, the initializer function should be called as early as
 * possible by providing the encoded function call as the `_data` argument to {ERC1967Proxy-constructor}.
 *
 * CAUTION: When used with inheritance, manual care must be taken to not invoke a parent initializer twice, or to ensure
 * that all initializers are idempotent. This is not verified automatically as constructors are by Solidity.
 *
 * [CAUTION]
 * ====
 * Avoid leaving a contract uninitialized.
 *
 * An uninitialized contract can be taken over by an attacker. This applies to both a proxy and its implementation
 * contract, which may impact the proxy. To prevent the implementation contract from being used, you should invoke
 * the {_disableInitializers} function in the constructor to automatically lock it when it is deployed:
 *
 * [.hljs-theme-light.nopadding]
 * ```
 * /// @custom:oz-upgrades-unsafe-allow constructor
 * constructor() {
 *     _disableInitializers();
 * }
 * ```
 * ====
 */
abstract contract Initializable {
    /**
     * @dev Storage of the initializable contract.
     *
     * It's implemented on a custom ERC-7201 namespace to reduce the risk of storage collisions
     * when using with upgradeable contracts.
     *
     * @custom:storage-location erc7201:openzeppelin.storage.Initializable
     */
    struct InitializableStorage {
        /**
         * @dev Indicates that the contract has been initialized.
         */
        uint64 _initialized;
        /**
         * @dev Indicates that the contract is in the process of being initialized.
         */
        bool _initializing;
    }

    // keccak256(abi.encode(uint256(keccak256("openzeppelin.storage.Initializable")) - 1)) & ~bytes32(uint256(0xff))
    bytes32 private constant INITIALIZABLE_STORAGE = 0xf0c57e16840df040f15088dc2f81fe391c3923bec73e23a9662efc9c229c6a00;

    /**
     * @dev The contract is already initialized.
     */
    error InvalidInitialization();

    /**
     * @dev The contract is not initializing.
     */
    error NotInitializing();

    /**
     * @dev Triggered when the contract has been initialized or reinitialized.
     */
    event Initialized(uint64 version);

    /**
     * @dev A modifier that defines a protected initializer function that can be invoked at most once. In its scope,
     * `onlyInitializing` functions can be used to initialize parent contracts.
     *
     * Similar to `reinitializer(1)`, except that in the context of a constructor an `initializer` may be invoked any
     * number of times. This behavior in the constructor can be useful during testing and is not expected to be used in
     * production.
     *
     * Emits an {Initialized} event.
     */
    modifier initializer() {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        // Cache values to avoid duplicated sloads
        bool isTopLevelCall = !$._initializing;
        uint64 initialized = $._initialized;

        // Allowed calls:
        // - initialSetup: the contract is not in the initializing state and no previous version was
        //                 initialized
        // - construction: the contract is initialized at version 1 (no reinitialization) and the
        //                 current contract is just being deployed
        bool initialSetup = initialized == 0 && isTopLevelCall;
        bool construction = initialized == 1 && address(this).code.length == 0;

        if (!initialSetup && !construction) {
            revert InvalidInitialization();
        }
        $._initialized = 1;
        if (isTopLevelCall) {
            $._initializing = true;
        }
        _;
        if (isTopLevelCall) {
            $._initializing = false;
            emit Initialized(1);
        }
    }

    /**
     * @dev A modifier that defines a protected reinitializer function that can be invoked at most once, and only if the
     * contract hasn't been initialized to a greater version before. In its scope, `onlyInitializing` functions can be
     * used to initialize parent contracts.
     *
     * A reinitializer may be used after the original initialization step. This is essential to configure modules that
     * are added through upgrades and that require initialization.
     *
     * When `version` is 1, this modifier is similar to `initializer`, except that functions marked with `reinitializer`
     * cannot be nested. If one is invoked in the context of another, execution will revert.
     *
     * Note that versions can jump in increments greater than 1; this implies that if multiple reinitializers coexist in
     * a contract, executing them in the right order is up to the developer or operator.
     *
     * WARNING: Setting the version to 2**64 - 1 will prevent any future reinitialization.
     *
     * Emits an {Initialized} event.
     */
    modifier reinitializer(uint64 version) {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing || $._initialized >= version) {
            revert InvalidInitialization();
        }
        $._initialized = version;
        $._initializing = true;
        _;
        $._initializing = false;
        emit Initialized(version);
    }

    /**
     * @dev Modifier to protect an initialization function so that it can only be invoked by functions with the
     * {initializer} and {reinitializer} modifiers, directly or indirectly.
     */
    modifier onlyInitializing() {
        _checkInitializing();
        _;
    }

    /**
     * @dev Reverts if the contract is not in an initializing state. See {onlyInitializing}.
     */
    function _checkInitializing() internal view virtual {
        if (!_isInitializing()) {
            revert NotInitializing();
        }
    }

    /**
     * @dev Locks the contract, preventing any future reinitialization. This cannot be part of an initializer call.
     * Calling this in the constructor of a contract will prevent that contract from being initialized or reinitialized
     * to any version. It is recommended to use this to lock implementation contracts that are designed to be called
     * through proxies.
     *
     * Emits an {Initialized} event the first time it is successfully executed.
     */
    function _disableInitializers() internal virtual {
        // solhint-disable-next-line var-name-mixedcase
        InitializableStorage storage $ = _getInitializableStorage();

        if ($._initializing) {
            revert InvalidInitialization();
        }
        if ($._initialized != type(uint64).max) {
            $._initialized = type(uint64).max;
            emit Initialized(type(uint64).max);
        }
    }

    /**
     * @dev Returns the highest version that has been initialized. See {reinitializer}.
     */
    function _getInitializedVersion() internal view returns (uint64) {
        return _getInitializableStorage()._initialized;
    }

    /**
     * @dev Returns `true` if the contract is currently initializing. See {onlyInitializing}.
     */
    function _isInitializing() internal view returns (bool) {
        return _getInitializableStorage()._initializing;
    }

    /**
     * @dev Pointer to storage slot. Allows integrators to override it with a custom storage location.
     *
     * NOTE: Consider following the ERC-7201 formula to derive storage locations.
     */
    function _initializableStorageSlot() internal pure virtual returns (bytes32) {
        return INITIALIZABLE_STORAGE;
    }

    /**
     * @dev Returns a pointer to the storage namespace.
     */
    // solhint-disable-next-line var-name-mixedcase
    function _getInitializableStorage() private pure returns (InitializableStorage storage $) {
        bytes32 slot = _initializableStorageSlot();
        assembly {
            $.slot := slot
        }
    }
}
"
    },
    "node_modules/@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol": {
      "content": "// SPDX-License-Identifier: MIT
// OpenZeppelin Contracts (last updated v5.3.0) (proxy/utils/UUPSUpgradeable.sol)

pragma solidity ^0.8.22;

import {IERC1822Proxiable} from "@openzeppelin/contracts/interfaces/draft-IERC1822.sol";
import {ERC1967Utils} from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Utils.sol";
import {Initializable} from "./Initializable.sol";

/**
 * @dev An upgradeability mechanism designed for UUPS proxies. The functions included here can perform an upgrade of an
 * {ERC1967Proxy}, when this contract is set as the implementation behind such a proxy.
 *
 * A security mechanism ensures that an upgrade does not turn off upgradeability accidentally, although this risk is
 * reinstated if the upgrade retains upgradeability but removes the security mechanism, e.g. by replacing
 * `UUPSUpgradeable` with a custom implementation of upgrades.
 *
 * The {_authorizeUpgrade} function must be overridden to include access restriction to the upgrade mechanism.
 */
abstract contract UUPSUpgradeable is Initializable, IERC1822Proxiable {
    /// @custom:oz-upgrades-unsafe-allow state-variable-immutable
    address private immutable __self = address(this);

    /**
     * @dev The version of the upgrade interface of the contract. If this getter is missing, both `upgradeTo(address)`
     * and `upgradeToAndCall(address,bytes)` are present, and `upgradeTo` must be used if no function should be called,
     * while `upgradeToAndCall` will invoke the `receive` function if the second argument is the empty byte string.
     * If the getter returns `"5.0.0"`, only `upgradeToAndCall(address,bytes)` is present, and the second argument must
     * be the empty byte string if no function should be called, making it impossible to invoke the `receive` function
     * during an upgrade.
     */
    string public constant UPGRADE_INTERFACE_VERSION = "5.0.0";

    /**
     * @dev The call is from an unauthorized context.
     */
    error UUPSUnauthorizedCallContext();

    /**
     * @dev The storage `slot` is unsupported as a UUID.
     */
    error UUPSUnsupportedProxiableUUID(bytes32 slot);

    /**
     * @dev Check that the execution is being performed through a delegatecall call and that the execution context is
     * a proxy contract with an implementation (as defined in ERC-1967) pointing to self. This should only be the case
     * for UUPS and transparent proxies that are using the current contract as their implementation. Execution of a
     * function through ERC-1167 minimal proxies (clones) would not normally pass this test, but is not guaranteed to
     * fail.
     */
    modifier onlyProxy() {
        _checkProxy();
        _;
    }

    /**
     * @dev Check that the execution is not being performed through a delegate call. This allows a function to be
     * callable on the implementing contract but not through proxies.
     */
    modifier notDelegated() {
        _checkNotDelegated();
        _;
    }

    function __UUPSUpgradeable_init() internal onlyInitializing {
    }

    function __UUPSUpgradeable_init_unchained() internal onlyInitializing {
    }
    /**
     * @dev Implementation of the ERC-1822 {proxiableUUID} function. This returns the storage slot used by the
     * implementation. It is used to validate the implementation's compatibility when performing an upgrade.
     *
     * IMPORTANT: A proxy pointing at a proxiable contract should not be considered proxiable itself, because this risks
     * bricking a proxy that upgrades to it, by delegating to itself until out of gas. Thus it is critical that this
     * function revert if invoked through a proxy. This is guaranteed by the `notDelegated` modifier.
     */
    function proxiableUUID() external view virtual notDelegated returns (bytes32) {
        return ERC1967Utils.IMPLEMENTATION_SLOT;
    }

    /**
     * @dev Upgrade the implementation of the proxy to `newImplementation`, and subsequently execute the function call
     * encoded in `data`.
     *
     * Calls {_authorizeUpgrade}.
     *
     * Emits an {Upgraded} event.
     *
     * @custom:oz-upgrades-unsafe-allow-reachable delegatecall
     */
    function upgradeToAndCall(address newImplementation, bytes memory data) public payable virtual onlyProxy {
        _authorizeUpgrade(newImplementation);
        _upgradeToAndCallUUPS(newImplementation, data);
    }

    /**
     * @dev Reverts if the execution is not performed via delegatecall or the execution
     * context is not of a proxy with an ERC-1967 compliant implementation pointing to self.
     */
    function _checkProxy() internal view virtual {
        if (
            address(this) == __self || // Must be called through delegatecall
            ERC1967Utils.getImplementation() != __self // Must be called through an active proxy
        ) {
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Reverts if the execution is performed via delegatecall.
     * See {notDelegated}.
     */
    function _checkNotDelegated() internal view virtual {
        if (address(this) != __self) {
            // Must not be called through delegatecall
            revert UUPSUnauthorizedCallContext();
        }
    }

    /**
     * @dev Function that should revert when `msg.sender` is not authorized to upgrade the contract. Called by
     * {upgradeToAndCall}.
     *
     * Normally, this function will use an xref:access.adoc[access control] modifier such as {Ownable-onlyOwner}.
     *
     * ```solidity
     * function _authorizeUpgrade(address) internal onlyOwner {}
     * ```
     */
    function _authorizeUpgrade(address newImplementation) internal virtual;

    /**
     * @dev Performs an implementation upgrade with a security check for UUPS proxies, and additional setup call.
     *
     * As a security check, {proxiableUUID} is invoked in the new implementation, and the return value
     * is expected to be the implementation slot in ERC-1967.
     *
     * Emits an {IERC1967-Upgraded} event.
     */
    function _upgradeToAndCallUUPS(address newImplementation, bytes memory data) private {
        try IERC1822Proxiable(newImplementation).proxiableUUID() returns (bytes32 slot) {
            if (slot != ERC1967Utils.IMPLEMENTATION_SLOT) {
                revert UUPSUnsupportedProxiableUUID(slot);
            }
            ERC1967Utils.upgradeToAndCall(newImplementation, data);
        } catch {
            // The implementation is not UUPS
            revert ERC1967Utils.ERC1967InvalidImplementation(newImplementation);
        }
    }
}
"
    },
    "contracts/lib/STBL_Structs.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @title STBL Protocol Structs
 * @notice Core data structures used throughout the STBL Protocol
 * @dev This file contains all the essential data structures that define the protocol's architecture
 */

/**
 * @title YLD_Metadata
 * @notice Metadata structure for yield-bearing tokens
 * @dev Stores essential information about yield tokens including values, fees, and operational state
 */
struct YLD_Metadata {
    /** @notice Unique identifier for the asset */
    uint256 assetID;
    /** @notice URI pointing to token metadata following standard metadata format */
    string uri;
    /** @notice Value representing the amount of the asset deposited in native asset units */
    uint256 assetValue;
    /** @notice The total USD value of tokens deposited in the protocol before any adjustments */
    uint256 stableValueGross;
    /** @notice The net USD value of USP tokens minted by the protocol after applying haircuts and fees */
    uint256 stableValueNet;
    /** @notice Block Timestamp when the deposit was made for time-based calculations */
    uint256 depositTimestamp;
    /** @notice Absolute fee amount charged on asset deposits in wei */
    uint256 depositfeeAmount;
    /** @notice Absolute haircut amount taken in USD terms as protocol fee */
    uint256 haircutAmount;
    /** @notice Absolute haircut amount taken in asset terms as protocol fee */
    uint256 haircutAmountAssetValue;
    /** @notice Absolute fee amount charged on asset withdrawals in wei */
    uint256 withdrawfeeAmount;
    /** @notice Absolute fee amount allocated to insurance pool in wei */
    uint256 insurancefeeAmount;
    /** @notice Snapshot of fee structure at the time of deposit for consistency */
    FeeStruct Fees;
    /** @notice Additional data buffer for future extensibility and upgrades */
    bytes additionalBuffer;
    /** @notice Flag indicating if token is disabled and cannot be used */
    bool isDisabled;
}

/**
 * @title AssetStatus
 * @notice Enumeration defining the operational status of assets within the protocol
 * @dev Used to track asset lifecycle and control operational permissions
 * @param INACTIVE Asset is not yet configured or deployed
 * @param INITIALIZED Asset is configured but not yet active for operations
 * @param ENABLED Asset is fully operational and available for deposits/withdrawals
 * @param DISABLED Asset is temporarily disabled but can be reactivated
 * @param EMERGENCY_STOP Asset is in emergency state with all operations halted
 */
enum AssetStatus {
    INACTIVE,
    INITIALIZED,
    ENABLED,
    DISABLED,
    EMERGENCY_STOP
}

/**
 * @title AssetDefinition
 * @notice Structure defining comprehensive properties of an asset in the protocol
 * @dev Contains all configuration parameters, addresses, and operational settings for an asset
 */
struct AssetDefinition {
    /** @notice Unique identifier assigned to the asset within the protocol */
    uint256 id;
    /** @notice Human-readable name of the asset for display purposes */
    string name;
    /** @notice Detailed description of the asset and its characteristics */
    string description;
    /** @notice Type of contract (ERC20 = 0, ERC721 = 1, Custom = 2) */
    uint8 contractType;
    /** @notice Flag indicating if yields are aggregated for this asset across all deposits */
    bool isAggreagated;
    /** @notice Current operational status of the asset (INACTIVE, INITIALIZED, ENABLED, DISABLED) */
    AssetStatus status;
    /** @notice Haircut percentage taken by the protocol in basis points */
    uint256 cut;
    /** @notice Maximum allowable deposit amount for this asset in native units */
    uint256 limit;
    /** @notice Contract address of the underlying token */
    address token;
    /** @notice Address responsible for asset issuance and management */
    address issuer;
    /** @notice Address responsible for distributing rewards to token holders */
    address rewardDistributor;
    /** @notice Address of the oracle providing price feeds and market data */
    address oracle;
    /** @notice Address of the custodian vault holding the actual assets */
    address vault;
    /** @notice Fee percentage charged on deposits in basis points */
    uint256 depositFees;
    /** @notice Fee percentage charged on withdrawals in basis points */
    uint256 withdrawFees;
    /** @notice Fee percentage charged on yield generation in basis points */
    uint256 yieldFees;
    /** @notice Percentage of fees allocated to insurance pool in basis points */
    uint256 insuranceFees;
    /** @notice Time duration in seconds for which asset must remain locked post deposit */
    uint256 duration;
    /** @notice Time interval in seconds between yield distribution events */
    uint256 yieldDuration;
    /** @notice Additional data buffer for future extensibility and protocol upgrades */
    bytes additionalBuffer;
}

/**
 * @title stakingStruct
 * @notice Data structure tracking token staking details for protocol participants
 * @dev Used to manage individual staking positions, rewards, and distribution tracking
 * @custom:security Balance must never underflow, implement proper checks before withdrawals
 * @custom:security RewardIndex must be properly synchronized with global reward calculations
 */
struct stakingStruct {
    /** @notice Current staked balance of the participant in wei */
    uint256 balance;
    /** @notice Index tracking participant's position in yield distribution calculations */
    uint256 rewardIndex;
    /** @notice Total rewards earned but not yet claimed by the participant */
    uint256 earned;
    /** @notice Flag indicating if staking position is currently active and earning rewards */
    bool isActive;
}

/**
 * @title VaultStruct
 * @notice Structure tracking protocol vault metrics and cumulative financial data
 * @dev Comprehensive record of all fees, deposits, and protocol revenue streams
 * @custom:security All fee calculations must prevent overflow and maintain precision
 */
struct VaultStruct {
    /** @notice Total accumulated fees from asset deposits across all assets */
    uint256 depositFees;
    /** @notice Total accumulated fees from asset withdrawals across all assets */
    uint256 withdrawFees;
    /** @notice Total accumulated fees from insurance contributions */
    uint256 insuranceFees;
    /** @notice Total accumulated fees from yield generation activities */
    uint256 yieldFees;
    /** @notice Cumulative total of all haircut fees collected by protocol in USD */
    uint256 cumilativeHairCutValue;
    /** @notice Current USD value of all deposits held in the vault */
    uint256 depositValueUSD;
    /** @notice Total gross value of asset deposits before any deductions */
    uint256 assetDepositGross;
    /** @notice Net value of asset deposits after haircuts and fees are applied */
    uint256 assetDepositNet;
}

/**
 * @title FeeStruct
 * @notice Structure defining fee configuration for various protocol operations
 * @dev Contains granular fee settings and timing parameters for protocol actions
 * @custom:security All fee percentages should be validated to prevent excessive fees
 */
struct FeeStruct {
    /** @notice Fee percentage charged on asset deposits in basis points */
    uint256 depositFee;
    /** @notice Percentage of deposit value taken as protocol haircut in basis points */
    uint256 hairCut;
    /** @notice Fee percentage charged on asset withdrawals in basis points */
    uint256 withdrawFee;
    /** @notice Percentage of total fees allocated to insurance pool in basis points */
    uint256 insuranceFee;
    /** @notice Lock duration in seconds for deposited assets */
    uint256 duration;
    /** @notice Time interval in seconds between yield distribution events */
    uint256 yieldDuration;
}

uint256 constant FEES_CONSTANT = 10 ** 9;
"
    },
    "contracts/lib/STBL_Errors.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

/**
 * @notice Thrown when an unauthorized caller attempts to access a restricted function
 */
error STBL_UnauthorizedCaller();

/**
 * @notice Thrown when attempting to set up an asset with invalid parameters
 */
error STBL_InvalidAssetSetup();

/**
 * @notice Thrown when attempting to set up an asset with invalid parameters
 */
error STBL_InvalidAssetName();

/**
 * @notice Thrown when the setup has already been completed
 */
error STBL_SetupAlreadyDone();

/**
 * @notice Thrown when an asset is not active
 */
error STBL_AssetNotActive();

/**
 * @notice Thrown when an asset is active
 */
error STBL_AssetActive();

/**
 * @notice Thrown when an invalid address is provided
 */
error STBL_InvalidAddress();

/**
 * @notice Thrown when an invalid Yield Duration and duration are provided
 */
error STBL_InvalidDuration();

/**
 * @notice Thrown when an invalid treasury address is provided
 */
error STBL_InvalidTreasury();

/**
 * @notice Thrown when an unauthorized issuer attempts an operation
 */
error STBL_UnauthorizedIssuer();

/**
 * @notice Thrown when an invalid cut percentage is provided
 * @param cut The invalid cut percentage that was provided
 */
error STBL_InvalidCutPercentage(uint256 cut);

/**
 * @notice Thrown when a maximum limit has been reached
 */
error STBL_MaxLimitReached();

/**
 * @notice Thrown when YLD functionality is disabled
 * @param id The ID of the YLD that is disabled
 */
error STBL_YLDDisabled(uint256 id);

/**
 * @notice Thrown when an invalid flag value is provided
 * @param flag The invalid flag value that was provided
 */
error STBL_InvalidFlagValue(uint8 flag);

/**
 * @notice Thrown when attempting to disable an already disabled asset
 * @param id The ID of the asset that is already disabled
 */
error STBL_AssetDisabled(uint256 id);

/**
 * @notice Thrown when attempting to enable an already enabled asset
 * @param id The ID of the asset that is already enabled
 */
error STBL_AssetEnabled(uint256 id);

/**
 * @notice Thrown when an invalid fee percentage is provided
 * @param fee The invalid fee percentage that was provided
 */
error STBL_InvalidFeePercentage(uint256 fee);

/**
 * @notice Thrown when attempting to disable an already disabled insurance
 * @param id The ID of the insurance that is already disabled
 */
error STBL_IssuanceAlreadyDisabled(uint256 id);

/**
 * @notice Thrown when attempting to enable an already enabled insurance
 * @param id The ID of the insurance that is already enabled
 */
error STBL_IssuanceAlreadyEnabled(uint256 id);

/**
 * @notice Thrown when the caller is not the owner of the NFT
 * @param tokenId The ID of the token for which ownership is required
 */
error STBL_YLD_NotOwner(uint256 tokenId);

/**
 * @notice Thrown when attempting to enable an already enabled NFT
 * @param tokenId The ID of the token that is already enabled
 */
error STBL_YLD_AlreadyEnabled(uint256 tokenId);

/**
 * @notice Thrown when attempting to disable an already disabled NFT
 * @param tokenId The ID of the token that is already disabled
 */
error STBL_YLD_AlreadyDisabled(uint256 tokenId);

/**
 * @notice Thrown when attempting to transfer a token that has transfers disabled
 * @param tokenId The ID of the token that cannot be transferred
 */
error STBL_YLD_TransferDisabled(uint256 tokenId);

/**
 * @notice Thrown when the sender address is blacklisted
 * @param sender The blacklisted sender address
 */
error STBL_USST_SenderBlacklisted(address sender);

/**
 * @notice Thrown when the recipient address is blacklisted
 * @param recipient The blacklisted recipient address
 */
error STBL_USST_RecipientBlacklisted(address recipient);
"
    },
    "contracts/interfaces/ISTBL_YLD.sol": {
      "content": "// SPDX-License-Identifier: MIT
pragma solidity ^0.8.20;

import "@openzeppelin/contracts/access/IAccessControl.sol";
import "@openzeppelin/contracts/token/ERC721/IERC721.sol";

import "../lib/STBL_Structs.sol";

/**
 * @title iSTBL_YLD Interface
 * @notice Interface for the STBL YLD contract that manages yield-generating NFT tokens with lifecycle controls
 * @dev Extends IAccessControl and IERC721 to provide role-based permissions and standard NFT functionality
 * @author STBL Protocol Team
 */
interface iSTBL_YLD is IAccessControl, IERC721 {
    /**
     * @notice Emitted when the trusted forwarder address is updated
     * @param previousForwarder Address of the previous trusted forwarder
     * @param newForwarder Address of the new trusted forwarder
     * @dev Used for meta-transaction support and gasless transactions
     */
    event TrustedForwarderUpdated(
        address indexed previousForwarder,
        address indexed newForwarder
    );

    /**
     * @notice Emitted when an NFT is disabled
     * @param _id Token ID of the disabled NFT
     * @dev Disabled NFTs cannot be transferred or used until re-enabled
     */
    event NFTDisabled(uint256 indexed _id);

    /**
     * @notice Emitted when a disabled NFT is re-enabled
     * @param _id Token ID of the enabled NFT
     * @dev Re-enabled NFTs restore full functionality including transfers
     */
    event NFTEnabled(uint256 indexed _id);

    /**
     * @notice Emitted when a new NFT is minted
     * @param _addr Recipient address of the minted NFT
     * @param _id Token ID of the newly minted NFT
     * @param _nftMetadata Complete metadata structure for the NFT
     * @dev Contains all relevant data for the newly created yield-bearing token
     */
    event MintEvent(
        address indexed _addr,
        uint256 indexed _id,
        YLD_Metadata _nftMetadata
    );

    /**
     * @notice Emitted when an NFT is permanently burned
     * @param _from Address that previously owned the burned NFT
     * @param _id Token ID of the burned NFT
     * @dev Burning permanently removes the token from circulation
     */
    event BurnEvent(address indexed _from, uint256 indexed _id);

    /**
     * @notice Emitted when the contract implementation is upgraded
     * @dev Triggered during an upgrade of the contract to a new implementation
     * @param newImplementation Address of the new implementation contract
     */
    event ContractUpgraded(address newImplementation);

    /**
     * @notice Updates the base URI for token metadata
     * @dev Changes the base URI used for constructing token metadata URLs
     * @dev Only callable by addresses with DEFAULT_ADMIN_ROLE
     * @param _uri The new base URI to set for token metadata
     */
    function setBaseURI(string memory _uri) external;

    /**
     * @notice Pauses all contract functionality
     * @dev Only callable by PAUSER_ROLE. Pre

Tags:
ERC721, ERC165, Multisig, Non-Fungible, Staking, Yield, Upgradeable, Multi-Signature, Factory, Oracle|addr:0x1ad81cafe5b3be8798ce05e84ae930a38226e8b4|verified:true|block:23446245|tx:0x18a27337a2009e88000f47a1c3199494ecec2a636b9fd4882b2f75614450c57b|first_check:1758882846

Submitted on: 2025-09-26 12:34:09

Comments

Log in to comment.

No comments yet.