Core Contracts

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

Centralized Treasury Control Analysis

Description

After reviewing FeeCollector.sol, I can confirm the report is valid but requires more detailed technical analysis. The contract has critical centralization risks in its treasury management system.

Vulnerable Code

function emergencyWithdraw(address token) external override whenPaused {
if (!hasRole(EMERGENCY_ROLE, msg.sender)) revert UnauthorizedCaller();
if (token == address(0)) revert InvalidAddress();
uint256 balance;
if (token == address(raacToken)) {
balance = raacToken.balanceOf(address(this));
raacToken.safeTransfer(treasury, balance); // Direct transfer to treasury
}
// ...
}
function setTreasury(address newTreasury) external override {
if (!hasRole(DEFAULT_ADMIN_ROLE, msg.sender)) revert UnauthorizedCaller();
if (newTreasury == address(0)) revert InvalidAddress();
pendingTreasury = PendingUpdate({
newAddress: newTreasury,
effectiveTime: block.timestamp + TREASURY_UPDATE_DELAY
});
emit TreasuryUpdated(newTreasury);
}

Impact Analysis

  1. Emergency Control Concentration

    • Single EMERGENCY_ROLE can pause contract

    • Same role can then withdraw all tokens

    • No multi-signature requirement

  2. Treasury Update Risks

    // Attack Sequence
    1. Admin calls setTreasury(attackerAddress)
    2. Wait TREASURY_UPDATE_DELAY (1 day)
    3. Call applyTreasuryUpdate()
    4. Pause contract
    5. Call emergencyWithdraw() to malicious treasury
  3. Insufficient Access Controls

    • DEFAULT_ADMIN_ROLE can grant EMERGENCY_ROLE

    • No separation of concerns between roles

    • Single account can accumulate all privileges

Proof of Concept

contract TreasuryAttackTest {
FeeCollector feeCollector;
address attacker;
function testTreasuryDrain() public {
// Setup
vm.startPrank(admin);
feeCollector.grantRole(EMERGENCY_ROLE, attacker);
// Attack
feeCollector.setTreasury(attacker);
vm.warp(block.timestamp + 1 days);
feeCollector.applyTreasuryUpdate();
feeCollector.pause();
feeCollector.emergencyWithdraw(address(raacToken));
// Verify
assertEq(raacToken.balanceOf(attacker), initialBalance);
}
}

Recommended Mitigation

contract FeeCollector {
uint256 public constant MIN_TIMELOCK_DELAY = 2 days;
uint256 public constant EMERGENCY_THRESHOLD = 3;
mapping(bytes32 => uint256) public proposalApprovals;
mapping(address => bool) public emergencyCouncil;
modifier requiresMultiApproval(bytes32 proposalId) {
require(proposalApprovals[proposalId] >= EMERGENCY_THRESHOLD,
"Insufficient approvals");
_;
}
function setTreasury(address newTreasury) external {
bytes32 proposalId = keccak256(abi.encodePacked(
"setTreasury",
newTreasury,
block.timestamp
));
require(emergencyCouncil[msg.sender], "Not council member");
proposalApprovals[proposalId]++;
if(proposalApprovals[proposalId] >= EMERGENCY_THRESHOLD) {
pendingTreasury = PendingUpdate({
newAddress: newTreasury,
effectiveTime: block.timestamp + MIN_TIMELOCK_DELAY
});
emit TreasuryUpdateProposed(newTreasury);
}
}
function emergencyWithdraw(address token) external
whenPaused
requiresMultiApproval(keccak256(abi.encodePacked(
"emergencyWithdraw",
token,
block.timestamp
)))
{
// ... existing logic ...
}
}

Additional Recommendations

  1. Implement role separation:

    • Treasury management

    • Emergency controls

    • Fee distribution

  2. Add timelock delays:

    uint256 public constant EMERGENCY_DELAY = 24 hours;
    uint256 public constant TREASURY_DELAY = 48 hours;
  3. Require multiple signatures:

    • 3/5 for treasury changes

    • 4/5 for emergency actions

    • 5/7 for protocol upgrades

  4. Enhanced event logging:

    event EmergencyActionProposed(
    bytes32 indexed proposalId,
    address indexed proposer,
    uint256 timestamp
    );
Updates

Lead Judging Commences

inallhonesty Lead Judge 7 months ago
Submission Judgement Published
Invalidated
Reason: Non-acceptable severity

Support

FAQs

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

Give us feedback!