Rain Disclosed Report

Dispute Fee Calculation Error

Company
Created date
hidden

Target

https://github.com/hackenproof-public/rain-contracts

Vulnerability Details

Summary

The openDispute() function calculates dispute collateral as 1% instead of 0.1% of pool liquidity. The multiplier is hardcoded to 10 when it should be 1, causing a 10× overcharge.

Vulnerability Details

Buggy Code

uint256 FEE_MAGNIFICATION = 10 ** 3;  // = 1000
uint256 disputeFee = (allFunds * 10) / FEE_MAGNIFICATION;
// = 1% (WRONG - should be 0.1%)

Whitepaper Requirement

"the disputer must put up collateral of 0.1% of the market volume or $1000, whichever is less."

Correct Code

uint256 disputeFee = (allFunds * 1) / FEE_MAGNIFICATION;  // = 0.1%

Impact

Per-Dispute Overcharge:

  • $10K market: $90 overcharge (should be $10, charged $100)
  • $50K market: $450 overcharge (should be $50, charged $500)

Validation steps

function test_DisputeFee_Magnification_BUG() external {
    // 1) Start sale and seed public pool so the disputer is a participant
    vm.warp(block.timestamp + 5 minutes);
    approveAndEnterOptionPublic(addr1, 1, 20000);              // 20,000 units (scaled by 1e6) [participant 1]
    address disputer = makeAddr("disputer");
    approveAndEnterOptionPublic(disputer, 2, 30000);           // 30,000 units (scaled by 1e6) [participant 2]

    // 2) Close and resolve to open the dispute window
    vm.warp(block.timestamp + 40 minutes);
    vm.prank(poolOwner);
    rainPoolPublic.closePool();
    vm.prank(resolverAI);
    rainPoolPublic.chooseWinner(1);

    // 3) Compute expected bonds: BUG (1%) vs correct (0.1%), apply hard cap = 1000 * 10^decimals
    uint256 all = rainPoolPublic.allFunds();
    uint256 bugBond = (all * 10) / 1000;        // 1% with FEE_MAGNIFICATION = 1000
    uint256 correctBond = (all * 1) / 1000;     // 0.1% with FEE_MAGNIFICATION = 1000
    uint256 cap = 1000 * (10 ** baseTokenDecimals);
    if (bugBond > cap) bugBond = cap;
    if (correctBond > cap) correctBond = cap;

    // 4) Fund and approve disputer, open dispute, and measure actual deduction
    deal(address(baseToken), disputer, bugBond * 2);
    vm.startPrank(disputer);
    baseToken.approve(address(rainPoolPublic), bugBond * 2);
    uint256 balBefore = baseToken.balanceOf(disputer);
    rainPoolPublic.openDispute();
    uint256 balAfter = baseToken.balanceOf(disputer);
    vm.stopPrank();

    // 5) Assert that the fee taken is the BUG amount (1%), not the correct 0.1%
    uint256 deducted = balBefore - balAfter;
    assertEq(deducted, bugBond, "dispute fee charged equals 1% (BUG)");
    assertTrue(deducted != correctBond, "dispute fee should not equal 0.1%");
}

Attachments

hidden
CommentsReport History
Comments on this report are hidden
Details
State
hidden
Severity
Medium
Bounty$8
Visibilitypartially
VulnerabilityBlockchain
Participants
hidden