Kann Audits / Security Review

Manifest Finance

Manifest Finance RWA Security Review

Review of access management, sanctions controls, price-oracle, staking-vault, and USH token contracts.

SolidityERC-4626ChainalysisAugust 26, 2025
Download Report Open Report
Audit period
August 26, 2025
Researchers
2 listed
Scope
14 scoped paths
Technologies
Solidity, ERC-4626, Chainalysis
Findings
6 documented

Executive summary

What was reviewed

Review of access management, sanctions controls, price-oracle, staking-vault, and USH token contracts.

This page reflects only the scope and review context disclosed in the published report. Fields the report does not provide are omitted rather than inferred; the PDF remains the source of record for issue detail and limitations.

Security is contextual. This report does not guarantee that the protocol is free from vulnerabilities. It applies to the review context documented in the report.

Scope & record

Engagement dossier

Researchers
Kann, Radev_eth
Technologies
Solidity, ERC-4626, Chainalysis
Category
RWA

Files and paths in scope

  • /src/authmanager/AuthManager.sol
  • /src/authmanager/AuthManagerStorage.sol
  • /src/chainalysis/MockChainalysisOracle.sol
  • /src/chainalysis/SanctionsList.sol
  • /src/chainalysis/SanctionsListStorage.sol
  • /src/oracle/USHPriceOracle.sol
  • /src/oracle/USHPriceOracleStorage.sol
  • /src/sush/AdminControl.sol
  • /src/sush/Silo.sol
  • /src/sush/StakedUSH.sol
  • /src/sush/StakedUSHBase.sol
  • /src/ush/USAToken.sol
  • /src/ush/USHToken.sol
  • /src/ush/USHTokenStorage.sol

Findings overview

Severity distribution

The counts below are transcribed from the published report. Status and issue detail remain subject to that report’s exact terminology.

FINDINGS06Documented in the published report
Critical: 1High: 1Medium: 1Low: 2Informational: 1
Source note

The report summary records one Medium finding. Its detailed entry is headed Medium but also contains the line ‘Severity: High risk.’ Aggregate counts on this page follow the report summary table.

Published findings

Findings

Findings below are reproduced from the complete Kann Audits security review. View the full PDF for complete scope, methodology, assumptions, and audit context.

Critical

1 finding
4.1.1

First Deposit Can Result in Zero Shares Due to Direct Token Transfer

Critical
Description

The StakedUSH vault is vulnerable to zero share deposits when USH tokens are sent directly to the contract. Because the vault calculates shares based on totalSupply and totalAssets, a direct token transfer increases totalAssets without increasing totalSupply. As a result, legitimate depositor will receive 0 shares for a positive deposit, breaking fair share distribution and potentially locking user funds. The sUSH vault calculates shares using the formula: shares = (assets * (totalSupply + decimalsOffset)) / (totalAssets + 1) If a user directly transfers USH to the vault contract (bypassing deposit()), the vault’s totalAssets increases while totalSupply remains 0. When the next user calls deposit(), the formula becomes: (assets * (0 + 0)) / (totalAssets + 1) = 0 Thus, the user receives 0 shares (REVERTS), even though they tried depositing a positive amount of assets.

Resolution

Fixed 4.2 High

High

1 finding
4.2.1

FULL-Restricted Users Can Still Deposit

High
Description

The deposit flow does not fully enforce the FULL_RESTRICTED_STAKER_ROLE check. When a user with FULL restriction calls deposit(assets, receiver), the restriction logic fails to prevent the operation, allowing restricted users to bypass intended compliance rules. Deposit flow: deposit(ERC4626 logic) -> _deposit(override) where checks if both addresses have SOFT_RESTRICTED_STAKER_ROLE if true reverts then logic goes to _deposit(ERC4626) function _deposit(address caller, address receiver, uint256 assets, uint256 shares) internal virtual { SafeERC20.safeTransferFrom(IERC20(asset()), caller, address(this), assets); _mint(receiver, shares); emit Deposit(caller, receiver, assets, shares); } Where _mint is called.

function _mint(address account, uint256 value) internal { if (account == address(0)) { revert ERC20InvalidReceiver(address(0)); } _update(address(0), account, value); } See how _mint sets first parameter for _update to be address 0. and since we have _update override it goes to function _update(address from, address to, uint256 value) internal override { if (hasRole(FULL_RESTRICTED_STAKER_ROLE, from) && to != address(0)) { revert OperationNotAllowed(); } if (hasRole(FULL_RESTRICTED_STAKER_ROLE, to)) { revert OperationNotAllowed(); } super._update(from, to, value); } Where it fails to check if the msg.sender is restricted role

Resolution

Fixed 4.3 Medium

Medium

1 finding
4.3.1

_currentDefaultAdmin Not set in SingleAdminAccessControl

Medium
Description

In StakedUshBase.sol, which imports and inherits AdminControl, the constructor calls: _grantRole(DEFAULT_ADMIN_ROLE, _owner); This sets the default admin role to _owner. However, the contract does not initialize the _currentDefaultAdmin variable, leaving it as address(0). Consequently, in a scenario where the current default admin attempts to transfer the admin role by calling transferAdmin, followed by _pendingDefaultAdmin executing acceptAdmin, the _grantRole override logic executes: _revokeRole(DEFAULT_ADMIN_ROLE, _currentDefaultAdmin); Since _currentDefaultAdmin is address(0), the call to _revokeRole effectively does nothing. The function then proceeds to grant the role to the new admin via: super._grantRole(role, account); The net effect is that both the original _owner and the new admin retain the DEFAULT_ADMIN_ROLE, resulting in two admins instead of a proper transfer. Thisissuearisesfromthelackofproperinitializationof_currentDefaultAdminduringcontractdeployment and highlights a gap in the role transfer logic that lead to unintended multi-admin privileges.

Resolution

Fixed

4.4 Low

Low

2 findings
4.4.1

Pause bypass for approvals via permit

Low
Description

USHToken gates approve() with whenNotPaused, but inherits permit() from Solmate without overriding. While paused, anyone can still set/refresh allowances via permit, pre-arming drains that execute the instant the token is unpaused.

4.4.2

Burner role blocked by Auth Manager checks

Low
Description

USHToken.burn(from, amount) calls _checkAuthTransfer(from, address(0)). If from has since become banned/sanctioned (via AuthManager), checkSanctioned/checkBanned will revert. This prevents the protocol (holder of BURNER_ROLE) from programmatically burning balances from blocked accounts a common compliance/control action.

Resolution

Fixed 4.5 Informational

Informational

1 finding
4.5.1

chainalysisOracleEnabled() function logic inverted

Informational
Description

The view function chainalysissOracleEnabled() returns true when the oracle is unset (i.e., disabled), andfalsewhentheoracleisactuallyconfigured. Thisbehaviorcontradictsthefunction name, which suggests it should return true when the oracle is enabled.

Recommendation

Fix logic: function chainalysisOracleEnabled() public view returns (bool) { return KycManagerStorageLib.getV1().chainalysisOracle != address(0); }

Methodology

How Kann Audits reviews code

Kann Audits reports describe independent researcher review followed by collaborative analysis of findings and attack paths. The standard review foundation includes:

  1. 01Architecture and trust-boundary analysis
  2. 02Independent manual review
  3. 03State-transition and invariant analysis
  4. 04Access-control and integration review
  5. 05Adversarial testing and attack-path analysis
  6. 06Fix verification and regression review

Audit team

Researchers listed in the report

KannRadev_eth

Final assessment

Documented outcome

All six reported findings were marked fixed.

The assessment applies only to the review context and limitations documented in the published report. Missing details are not inferred, and later changes require separate analysis.

Start a conversation

Planning your next release?

Share the system, fixed scope, and target date. Build enough time into the plan for review, remediation, and verification.