Revest Finance Vulnerabilities: More than Re-entrancy

What’s the Revest Finance FNFT

The Financial Non-Fungible Token (FNFT) of Revest Finance makes the trustless transfer of the future rights to locked assets possible. The entry contract (Revest contract) provides three different interfaces to mint FNFT by locking underlying assets:

  • mintTimeLock: the underlying asset will be unlocked after a period of time.
  • mintValueLock: the underlying asset will be unlocked when its value rises above or falls below a prescribed value.
  • mintAddressLock: the underlying asset will be unlocked by a prescribed account.
  • FNFTHandler: inherited from the ERC-1155 token. It creates a new FNFT with the incrementing fnftId for every lock. The lock prescribes the total supply of the new FNFT at the creation. The FNFT can not be minted in another way but can be burned for unlocking underlying assets.
  • LockManager: records the unlocking conditions for each lock when creating and decides if the lock can be unlocked when unlocking.
  • TokenVault: receives and sends the underlying assets and records the metadata for each FNFT, such as the value of a specified FNFT.
// Now, we transfer to the token vault
if(fnft.asset != address(0)){
IERC20(fnft.asset).safeTransferFrom(_msgSender(), vault, quantity * amount);
}
ITokenVault(vault).handleMultipleDeposits(fnftId, newFNFTId, fnft.depositAmount + amount);emit FNFTAddionalDeposited(_msgSender(), newFNFTId, quantity, amount);

What’s the Re-entrancy vulnerability

In this part, we will illustrate how the re-entrancy attack works and discuss the root cause and the fix method.

function mint(
address account,
uint id,
uint amount,
bytes memory data
) external override onlyRevestController {
require(amount > 0, "Invalid amount");
require(supply[id] == 0, "Repeated mint for the same FNFT");
supply[id] += amount;
fnftsCreated += 1;
_mint(account, id, amount, data);
}

the New Zero-day Vulnerability

When analyzing the code of Revest Finance, the function handleMultipleDeposits in the TokenVault contract confuses us, the code of which is shown in below.

function handleMultipleDeposits(
uint fnftId,
uint newFNFTId,
uint amount
) external override onlyRevestController {
require(amount >= fnfts[fnftId].depositAmount, 'E003');
IRevest.FNFTConfig storage config = fnfts[fnftId];
config.depositAmount = amount;
mapFNFTToToken(fnftId, config);
if(newFNFTId != 0) {
mapFNFTToToken(newFNFTId, config);
}
}

The workaround to fix the vulnerability

This is a logic bug, and we recommend using the following code to fix it.

function handleMultipleDeposits(
uint fnftId,
uint newFNFTId,
uint amount
) external override onlyRevestController {
require(amount >= fnfts[fnftId].depositAmount, 'E003');
IRevest.FNFTConfig memory config = fnfts[fnftId];
config.depositAmount = amount;
if(newFNFTId != 0) {
mapFNFTToToken(newFNFTId, config);
} else {
mapFNFTToToken(fnftId, config);
}
}

Takeaway

Making a DeFi project secure is not an easy job. Besides the code audit, we think the community should take a proactive method to monitor the project status, and block the attack if possible.

--

--

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
BlockSec

BlockSec

The BlockSec Team focuses on the security of the blockchain ecosystem and the research of crypto hack monitoring and blocking, smart contract auditing.