This document provides a comprehensive, itemized report of all findings identified in the C4 audit report (https://gist.github.com/itsmetechjay/15522c3a7102c6526f2c6711bacfabd6/) and details how each was addressed, either through an implemented code fix or an architectural justification for rejection.
- Status: Skipped (Already Fixed)
- Explanation: This vulnerability had already been identified and resolved by the sponsor in the underlying repository. In WERC7575Vault.sol, the caller identity is strictly checked against the
ownervariable, and_shareToken.spendAllowance(owner, msg.sender, shares)correctly decrements allowance to prevent unauthorized withdrawals.
- Status: Fixed
- Resolution: Removed the strict
balanceOf(vaultAddress) != 0check inunregisterVault()in both ShareTokenUpgradeable.sol and WERC7575ShareToken.sol. This prevents DoS attacks where a malicious user transfers dust to the vault before unregistration. The vault can now be safely unregistered as long as all pending and claimable records are zero.
[M-02] Stale Redemption Liabilities Lead to Leveraged Losses for Remaining Shareholders and Potential Denial of Service
- Status: Rejected (Invalid Finding)
- Justification: The auditor proposed that committed assets should not remain in the "investable" volatile pool. However, the SukukFI codebase already strictly segregates fulfilled assets from the investable pool, preventing any leveraged loss mathematically:
- Physical Fulfillment:
ERC7575VaultUpgradeable::fulfillRedeem(assets)reverts ifassets > totalAssets(). The manager cannot fulfill a redemption unless those volatile assets have already physically been converted to plain stable cash in the vault's balance. - Strict Segregation: Inside
totalAssets(), the vault subtracts$.totalClaimableRedeemAssetsfrom the balance. - Reinvestments Blocked:
investAssets(amount)checksamount > totalAssets(). BecausetotalAssets()statically excludes the committed funds, the manager is physically and mathematically prevented from taking the fulfilled user's segregated cash and risking it back into the market. Therefore, the fulfilled user does not share in future market losses because their cash is no longer exposed to the market.
- Physical Fulfillment:
- Status: Fixed
- Resolution: Added an explicit safety bounds check (
if (assets > totalAssets()) revert...) inERC7575VaultUpgradeable.sol#fulfillRedeem()to ensureassets <= totalAssets(). This prevents the manager from accidentally inflating the share price (PPS) through invalid or over-funded redemptions.
- Status: Fixed
- Resolution: In
ShareTokenUpgradeable.sol::unregisterVault(), added logic to securely revoke the investment ShareToken's ERC20 allowance upon vault unregistration (IERC20($.investmentShareToken).approve(vaultAddress, 0)).
[L-02] ShareTokenUpgradeable and WERC7575ShareToken are non-compliant with expected ERC-7575 Standard due to incorrect supportsInterface() implementation
- Status: Fixed
- Resolution: Updated both ShareTokenUpgradeable.sol and WERC7575ShareToken.sol to correctly include the
IERC7575Shareinterface ID (0xf815c03d) in theirsupportsInterfacedefinitions.
[L-03] WERC7575ShareToken::batchTransfers() / WERC7575ShareToken::rBatchTransfers() is missing pause check
- Status: Rejected (Invalid Finding)
- Justification: This recommendation demonstrates a misunderstanding of the protocol's access control architecture. The
whenNotPausedmodifier halts non-privileged users during an emergency. Conversely,batchTransfersandrBatchTransfersare strictly permissioned administrative tools (onlyValidator). If the protocol is paused due to an accounting error, the validators actually require the ability to run these settlement functions to resolve the balances. Restricting them withwhenNotPausedwould intentionally brick the protocol's emergency resolution mechanism.
- Status: Rejected (Invalid Finding)
- Justification: This finding assumes the project is using OpenZeppelin Contracts v4.x. In OpenZeppelin Contracts v5.0, the upgradeable and non-upgradeable utility contracts were consolidated. The standard
@openzeppelin/contracts/utils/ReentrancyGuard.solcontract now natively uses namespaced storage (ERC-7201) and is completely safe for upgradeable proxies.ReentrancyGuardUpgradeableno longer exists in v5.0.
- Status: Fixed
- Resolution: Applied the gas optimization by updating
accountsLength++;to++accountsLength;inside theconsolidateTransfers()loop in WERC7575ShareToken.sol.
- Status: Rejected (Design Requirement)
- Justification: The duplicate emission is an intentional architectural necessity. SukukFI implements a centralized delegation architecture where operators are approved at the ShareToken level, guaranteeing consistent authorization behavior across all associated vaults. However, the ERC7540 Specification explicitly states that setting an operator MUST emit the
OperatorSetevent from the vault contract being interacted with. Removing either event breaks either strict standard compliance for the vault or the centralized state tracking for the ShareToken.
- Status: Fixed
- Resolution: Swapped the
receiverandcontrollerparameter order in theDepositevent emissions within ERC7575VaultUpgradeable.sol'sdeposit()andmint()functions to correctly match the standard specifications.
- Status: Skipped (Already Mitigated)
- Resolution: Already addressed in the codebase prior to the review session.
- Status: Fixed
- Resolution: Added
ERC20InvalidReceiverzero-address checks forreceiverparameters inmint()anddeposit()inside ERC7575VaultUpgradeable.sol.
- Status: Fixed
- Resolution: Removed the inline logic validating
ownerBalance < assetsandownerShares < sharesinrequestDepositandrequestRedeeminside ERC7575VaultUpgradeable.sol. The validation is securely handled natively via OpenZeppelin's underlying generic transfer operations (safeTransferFrom,vaultTransferFromwhich leverages_transfer).
- Status: Fixed
- Resolution: Removed the strict bounds restriction
if (scalingFactor > type(uint64).max) revert ScalingFactorTooLarge();from ERC7575VaultUpgradeable.sol.
- Status: Fixed
- Resolution: Updated
maxDepositandmaxMintto dynamically check theisActivestate boolean, returning0if the vaultisActiveboolean is false in ERC7575VaultUpgradeable.sol.