Core Contracts

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

Denial of Service (DoS) Risk in redeemFromMarket Function

Summary

The redeemFromMarket function lacks a balance check before transferring rewards, which can lead to a Denial of Service (DoS) attack. If the contract's balance of raacToken is insufficient, redemption transactions will fail, preventing users from reclaiming their deposits.

Vulnerability Details

  • The function calculates reward using calculateReward(marketId, amount).

  • It then attempts to transfer raacToken to the user without checking if the contract holds enough balance.

  • If the contract’s raacToken balance is insufficient, the safeTransfer call will revert, making redemption impossible.

  • This can occur if:

    • The contract does not receive enough reward tokens initially.

    • Rewards are miscalculated, leading to an overcommitment.

    • A malicious actor drains the contract’s raacToken balance before redemptions occur.

Test:

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.19;
import "contracts/core/collectors/FeeCollector.sol";
import "contracts/core/tokens/RAACToken.sol";
import "contracts/core/tokens/veRAACToken.sol";
import "forge-std/Test.sol";
import "contracts/mocks/core/oracles/TestRAACHousePriceOracle.sol";
import "contracts/mocks/core/tokens/crvUSDToken.sol";
import "contracts/mocks/core/tokens/MockUSDC.sol";
import "contracts/core/tokens/RToken.sol";
import "contracts/core/tokens/DebtToken.sol";
import "contracts/core/tokens/RAACNFT.sol";
import "contracts/core/primitives/RAACHousePrices.sol";
import "contracts/core/pools/LendingPool/LendingPool.sol";
import "forge-std/Console2.sol";
import "contracts/interfaces/core/pools/LendingPool/ILendingPool.sol";
import "contracts/core/pools/StabilityPool/StabilityPool.sol";
import "contracts/core/pools/StabilityPool/NFTLiquidator.sol";
import "contracts/core/pools/StabilityPool/MarketCreator.sol";
import "@openzeppelin/contracts/proxy/transparent/TransparentUpgradeableProxy.sol";
contract Pool2 is Test{
RAACToken public raccToken;
veRAACToken public veraacToken;
crvUSDToken public crv;
RToken public rToken;
DebtToken public debtToken;
RAACNFT public raccNFT;
TestRAACHousePriceOracle public oracle;
RAACHousePrices public housePrice;
MockUSDC public usdc;
LendingPool public pool;
NFTLiquidator nftLq;
StabilityPool sbPool;
TransparentUpgradeableProxy proxy;
MarketCreator public market;
uint256 NFTTokenId = 1;
address alice = address(0x1001);
address bob = address(0x1002);
address candy = address(0x1003);
function setUp() public {
crv = new crvUSDToken(address(this));
rToken = new RToken("rt","rt",address(this),address(crv));
debtToken = new DebtToken("db","db",address(this));
address router;
usdc = new MockUSDC(1_000_000e6);
housePrice = new RAACHousePrices(address(this));
oracle = new TestRAACHousePriceOracle(router,bytes32('1'),address(housePrice));
raccNFT = new RAACNFT(address(usdc),address(housePrice),address(this));
pool = new LendingPool(address(crv),address(rToken),address(debtToken),address(raccNFT),address(housePrice),1e26);
rToken.setReservePool(address(pool));
housePrice.setOracle(address(this));
debtToken.setReservePool(address(pool));
nftLq = new NFTLiquidator(address(crv),address(raccNFT),address(this),50);
sbPool = new StabilityPool(address(this));
//add proxy.
proxy = new TransparentUpgradeableProxy(address(sbPool),address(this),"");
raccToken = new RAACToken(address(this),1_000,1_000);
veraacToken = new veRAACToken(address(raccToken));
market = new MarketCreator(address(this),address(raccToken),address(crv));
}
function testRewardRunOut() public {
// Setup minter
raccToken.setMinter(address(this));
raccToken.whitelistAddress(address(market));
raccToken.mint(address(market),100e18);
//owner create market.
market.createMarket(address(crv), 1 days, 100e18);
//alice deposit.
crv.mint(alice,10e18);
vm.startPrank(alice);
crv.approve(address(market), 10e18);
market.participateInMarket(1,10e18);
skip(2 days);
//alice redeem
market.redeemFromMarket(1);
//bob deposit.
crv.mint(bob,10e18);
vm.startPrank(bob);
crv.approve(address(market), 10e18);
market.participateInMarket(1,10e18);
vm.stopPrank();
skip(2 days);
//bob redeem
vm.prank(bob);
market.redeemFromMarket(1);
}

Out:

│ ├─ [23380] crvUSDToken::transfer(0x0000000000000000000000000000000000001002, 10000000000000000000 [1e19])
│ │ ├─ emit Transfer(from: MarketCreator: [0x3D7Ebc40AF7092E3F1C81F2e996cbA5Cae2090d7], to: 0x0000000000000000000000000000000000001002, value: 10000000000000000000 [1e19])
│ │ └─ ← [Return] true
│ ├─ [4922] RAACToken::transfer(0x0000000000000000000000000000000000001002, 100000000000000000000 [1e20])
│ │ └─ ← [Revert] ERC20InsufficientBalance(0x3D7Ebc40AF7092E3F1C81F2e996cbA5Cae2090d7, 0, 10000000000000000000 [1e19])
│ └─ ← [Revert] ERC20InsufficientBalance(0x3D7Ebc40AF7092E3F1C81F2e996cbA5Cae2090d7, 0, 10000000000000000000 [1e19])
└─ ← [Revert] ERC20InsufficientBalance(0x3D7Ebc40AF7092E3F1C81F2e996cbA5Cae2090d7, 0, 10000000000000000000 [1e19])
Suite result: FAILED. 0 passed; 1 failed; 0 skipped; finished in 4.33ms (428.13µs CPU time)
Ran 1 test suite in 1.31s (4.33ms CPU time): 0 tests passed, 1 failed, 0 skipped (1 total tests)
Failing tests:
Encountered 1 failing test in tests/Pool2.t.sol:Pool2
[FAIL: ERC20InsufficientBalance(0x3D7Ebc40AF7092E3F1C81F2e996cbA5Cae2090d7, 0, 10000000000000000000 [1e19])] testRewardRunOut() (gas: 703209)
Encountered a total of 1 failing tests, 0 tests succeeded

Impact

Dos

Tools Used

Foundry

Recommendations

check reward assets balance before transfer to user

Updates

Lead Judging Commences

inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Out of scope
inallhonesty Lead Judge 10 months ago
Submission Judgement Published
Invalidated
Reason: Out of scope

Support

FAQs

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

Give us feedback!