#55025 [SC-Insight] corevault refund failure can permanently freeze overpaid nat on assetmanager
Report ID: #55025
Report Type: Smart Contract
Report severity: Insight
Target: https://github.com/flare-foundation/fassets/commit/7aa02b62285cd5313032103710c2e083b166bf60
Impacts: Permanent freezing of funds
Description
When an agent transfers underlying to the Core Vault via transferToCoreVault and overpays the required fee, the contract attempts to refund the difference using a 100k gas native transfer. If that refund fails (for example, the caller is a contract that reverts in receive() or requires more than 100k gas), the return value is ignored and the overpaid NAT remains stuck on the Asset Manager (diamond) contract with no recovery path.
Vulnerability Details
Relevant snippet from CoreVault:
// pay the transfer fee and return overpaid transfer fee when the difference is larger than gas use
// (all transfers are guarded by nonReentrant in the facet)
if (msg.value > transferFeeWei + Transfers.TRANSFER_GAS_ALLOWANCE * tx.gasprice) {
Transfers.transferNAT(state.nativeAddress, transferFeeWei);
Transfers.transferNATAllowFailure(payable(msg.sender), msg.value - transferFeeWei);
} else {
Transfers.transferNAT(state.nativeAddress, msg.value);
}Transfers.transferNATAllowFailure(address payable, uint256) returns bool to indicate success/failure of the 100k-gas NAT transfer, but the result is ignored here. If the recipient cannot accept the transfer (reverts in receive() or needs more than the fixed TRANSFER_GAS_ALLOWANCE = 100_000 gas), the function returns false. Because the return is ignored, the overpaid amount remains held by the asset manager contract and is never forwarded or burned.
For context: the threshold transferFeeWei + Transfers.TRANSFER_GAS_ALLOWANCE * tx.gasprice is correct and ensures wei-to-wei comparison. The bug is independent of that threshold — it’s about ignoring the refund failure.
A similar pattern is handled correctly in CollateralReservations.sol:
Preconditions for the issue:
Caller is the authorized agent vault owner but is a contract that reverts in
receiveor requires >100k gas to accept ETH.Caller overpays
msg.valueenough to enter the refund branch.
Impact
Overpaid NAT remains in the Asset Manager’s balance indefinitely, with no generic sweep or recovery function exposed to return it.
Recommended mitigation steps
Update CoreVault.transferToCoreVault to handle failed refunds the same way as CollateralReservations — check the return value of Transfers.transferNATAllowFailure(...) and, on failure, execute an alternative such as burning the fee or forwarding it to a recovery/burn address so funds are not permanently stuck.
(Reference to the function: https://github.com/flare-foundation/fassets/blob/59373cee12e6d2a9fa0a9cc8735bb486faa51b36/contracts/assetManager/library/CoreVault.sol#L45)
Proof of Concept
The PoC demonstrates the issue by calling transferToCoreVault from a contract whose receive() reverts, ensuring the 100k gas refund fails and the overpaid NAT stays in the AssetManager.
PoC helper contract:
Integration test (create file test/integration/fasset-simulation/15-CoreVault-RefundPoC.ts):
Run the test with:
yarn hardhat test test/integration/fasset-simulation/15-CoreVault-RefundPoC.ts
Logs from the PoC run:
Steps to reproduce (high-level)
Was this helpful?