Core Contracts

Regnum Aurum Acquisition Corp
HardhatReal World AssetsNFT
77,280 USDC
View results
Submission Details
Severity: high
Invalid

wrong recipient when retrieving funds from curveVault

Summary

withdrawals from vaults are being deposited into the lending pool instead of the Rtoken this will result in buffer not being maintained as we generally transfer underlying assets from and to the Rtoken when depositing and withdrawing also we will not be able to ensure liquidity when withdrawal requests are being made.

Vulnerability Details

when depositing funds are transferred to the Rtoken contract and pulled from when withdrawal requests are made :

// Transfer asset from caller to the RToken contract
IERC20(reserve.reserveAssetAddress).safeTransferFrom(
msg.sender, // from
reserve.reserveRTokenAddress, // to
amount // amount
);

When rebalancing and ensuring liquidity during deposits and withdrawals as well as borrows we send funds to the vault when there are excess liquidity(greater than the buffer ratio) but when we're below the ratio we're pulling funds from and sending to the pool rather than the Rtoken contract :

function _withdrawFromVault(uint256 amount) internal {
curveVault.withdraw(
amount,
address(this), // this is referring to the pool
msg.sender,
0,
new address[](0)
);
totalVaultDeposits -= amount;
}

When withdrawal requests are made it's then being pulled from the Rtoken contract after liquidity has been ensured because the Rtoken : burn function is being called.

Impact

  • this will result in liquidity not being technically ensured and potentially making withdrawals impossible if Rtoken contract doesn't have enough to cover the request

  • This will result in buffer ratio not being maintained and funds being lost in the lending pool

  • this will also result in the accidental transfer of funds sent to the pool being considered dust because All balance, that is not tied to rToken are dust can be donated to one lucky recipient

function calculateDustAmount() public view returns (uint256) {
// Calculate the actual balance of the underlying asset held by this contract
uint256 contractBalance = IERC20(_assetAddress)
.balanceOf(address(this))
.rayDiv(ILendingPool(_reservePool).getNormalizedIncome());
// Calculate the total real obligations to the token holders
uint256 currentTotalSupply = totalSupply();
// Calculate the total real balance equivalent to the total supply
//@audit scaled twice by the way ... so dust totalBalance will always be greater
uint256 totalRealBalance = currentTotalSupply.rayMul(
ILendingPool(_reservePool).getNormalizedIncome()
);
// All balance, that is not tied to rToken are dust (can be donated or is the rest of exponential vs linear)
return
contractBalance <= totalRealBalance
? 0
: contractBalance - totalRealBalance;
}
function transferAccruedDust(
address recipient,
uint256 amount
) external onlyReservePool {
if (recipient == address(0)) revert InvalidAddress();
uint256 poolDustBalance = calculateDustAmount();
if (poolDustBalance == 0) revert NoDust();
// Cap the transfer amount to the actual dust balance
uint256 transferAmount = (amount < poolDustBalance)
? amount
: poolDustBalance;
// Transfer the amount to the recipient
IERC20(_assetAddress).safeTransfer(recipient, transferAmount);
emit DustTransferred(recipient, transferAmount);
}

Recommendations

  • Consider making the reciever and distributor the lending pool contract

  • Consider making the recipient the Rtoken contract :

    curveVault.withdraw(
    amount,
    IRToken(reserve.reserveRTokenAddress),
    msg.sender,
    0,
    new address[](0)
    );
    totalVaultDeposits -= amount;
    }
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Incorrect statement

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.

Give us feedback!