
Fluton Confidential DeFi Security Review
Review of Fluton’s confidential DeFi and cross-chain infrastructure, including FHE token flows, bridge integrations, and a Diamond-based Aave V3 adapter.
Kann Audits / Security Review
Review of borrowing, repayment, asset deposit and withdrawal, and swap logic documented in the RWA report.
Executive summary
Review of borrowing, repayment, asset deposit and withdrawal, and swap logic documented in the RWA report.
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
The published report does not enumerate individual paths. Consult the complete PDF for its documented scope and limitations.
Findings overview
The counts below are transcribed from the published report. Status and issue detail remain subject to that report’s exact terminology.
Published findings
Findings below are reproduced from the complete Kann Audits security review. View the full PDF for complete scope, methodology, assumptions, and audit context.
High-1
Cross-chain Replay: If an User borrows assets on Chain A, they could reuse the same signature (generated from the same data) on Chain B, assuming the signature is valid for the same borrow hash structure on both chains.
Proof of Concept Admin signs signature with his private key with information about the collateral address, borrow address, collateral amount, borrow amount, address of the User to use signature and nonce. Chain A User calls borrowAsset() borrows assets. Chain B User reuses the same signedHash to borrow assets.
bytes32 _borrowHash = keccak256( abi.encode( collaterals, borrows, collateralAmounts, borrowsAmounts, msg.sender, nonceOf[msg.sender], block.chainid // Include the chainId in the hash to make it unique per chain ) );
High-2
The depositAsset function is vulnerable to the first depositor bug, where the first depositor intentionally gain an unfair advantage due to how shares (_share) are calculated for initial deposits. Example: Bob wants to deposit 10 WETH Alice sees Bob's transaction in the mempool and frontruns it (Alice becomes the first depositor). She deposits just 1 wei. Bob deposits the 10 WETH but due to rounding issues he receives 0 shares. Alice calls removeAsset() and gets 10 WETH + 1 wei withdrawed to her since he owns the 100% of the shares.
Medium-1
Some actions in financial contracts are time-sensitive, and allowing a signature to be valid indefinitely could break expected business rules. For instance, if the contract terms change or the collateral value fluctuates, an old signature might lead to unfair borrowing conditions or outdated agreements.
Proof of Concept Example: A user signs a borrowing agreement for certain collateral amounts and borrowing amounts today. Later, the market conditions change, and collateral values drop or borrowing values gets too high , making the original agreement unfair for the contract owner. By adding a time limit (e.g., valid for 1 hour), the signature would become invalid once the time expires, ensuring that the agreement can only be executed within the intended window
bytes32 _borrowHash = keccak256( abi.encode( collaterals, borrows, collateralAmounts, borrowsAmounts, msg.sender, nonceOf[msg.sender], deadline // Add expiry ) ); // Check if the signature is expired if (block.timestamp > deadline) { revert SignatureExpired(); }
Medium-2
In borrowAsset() when user is trying to send collateral it will fail since there is missing approval. Same applies for in repayLoan().
Add approve for contract to use the tokens of the user.
Medium-3
In borrowAssets() when contract is trying to send borrows to user it incorrectly uses transferFrom instead of transfer when transferring borrow tokens from the contract to the user. This implementation error will cause the function to revert due to missing allowances. same applies for in repayLoan() when trying to send collateral back to user.
token.safeTransfer(msg.sender, _amount); //use this when transfering to user.
Medium-4
Some ERC-20 tokens will return on failure instead of reverting a transaction, Some tokens will even not return any value. not all tokens adhere to the standart of ERC-20
Consider adding OpenZeppelin SafeERC20.
Informational-1
The lengths of the collaterals and collateralAmounts arrays are not explicitly checked within the function. If their lengths differ, this can lead to mismatched indexing, potentially causing unexpected behavior or errors when processing collateral addresses and their corresponding amounts. This issue assumes the off-chain logic ensures both arrays are correctly populated. However, without a validation check in the contract, there's no guarantee of alignment, which could result in incorrect collateral handling.
if (collaterals.lenght != collateralAmounts.lenght) revert
Informational-2
Summary: The processed[_borrowHash] check in the borrowAsset function is redundant because the nonceOf[msg.sender] mechanism already prevents signature reuse. Reusing a signature with an outdated
4 / 5 nonce will fail the isAdmin verification, as the recomputed _borrowHash will not match the original signed hash.
Consider removing the processed[_borrowHash] mapping and its associated logic to simplify the code and reduce gas costs.
Informational-3
In the removeAsset function, if the _share of msg.sender is zero, the function still proceeds to execute and consumes unnecessary gas before returning. Instead of allowing the transaction to continue and waste gas, it would be more efficient to revert the transaction early when _share is found to be zero. This prevents further execution and optimizes gas usage.
if(_share == 0) revert
Informational-4
In the removeAsset function, if a user attempts to withdraw more shares than they hold, the transaction will fail, but the reason for failure may not be immediately clear to the user. This is due to the absence of a check to verify if shareToWithdraw exceeds the balance of shareOf[msg.sender][token]. Since shareOf[address] [token] is a uint256 and cannot be negative (with a minimum value of 0), the transaction fails when the withdrawal amount is too large, but the failure message is not informative. Adding a condition to explicitly check and revert the transaction with a meaningful message would improve clarity and user experience.
if (shareToWithdraw > shareOf[msg.sender][token]) revert
Informational-5
5 / 5 In the Mooniswap swap function, users can specify a minAmount parameter to protect against slippage, ensuring that the output of the trade meets a minimum threshold. However, the function does not include a deadline parameter, which means users can set a high minAmount and wait indefinitely for the market to meet favorable conditions. This behavior could lead to inefficiencies in the protocol, as transactions could be left pending for extended periods without completing.
Consider adding deadline for swap
Methodology
Kann Audits reports describe independent researcher review followed by collaborative analysis of findings and attack paths. The standard review foundation includes:
Audit team
The public report does not list individual researcher names.
Final assessment
The report documents 11 findings: two high, four medium, and five informational; remediation statuses are not stated.
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
Share the system, fixed scope, and target date. Build enough time into the plan for review, remediation, and verification.