Assessment reports>Programmable Derivatives>High findings>Distribution's ,claim, function does not update storage variables
Category: Coding Mistakes

Distribution's claim function does not update storage variables

High Severity
High Impact
High Likelihood

Description

In Distributor, the claim function does not work correctly. The poolInfo is not updated after the claim, so the amountToDistribute is not updated. Since the balance decreases while the amountToDistribute remains the same, once the first claim occurs, other users will not be able to make a claim.

function claim(address _pool) external whenNotPaused() nonReentrant() {
  require(_pool != address(0), UnsupportedPool());
  
  // ...

  PoolInfo memory poolInfo = poolInfos[_pool];

  // check if pool has enough *allocated* shares to distribute
  if (poolInfo.amountToDistribute < shares) {
    revert NotEnoughSharesToDistribute();
  }

  // check if the distributor has enough shares tokens as the amount to distribute
  if (IERC20(couponToken).balanceOf(address(this)) < poolInfo.amountToDistribute) {
    revert NotEnoughSharesToDistribute();
  }
  
  poolInfo.amountToDistribute -= shares;
  couponAmountsToDistribute[couponToken] -= shares;
  
  // ...
}

Impact

If there are no other pools sharing the coupon token, a revert will occur from the second claimant onward due to the unupdated amountToDistribute. If such pools exist, it will ultimately cause a balance mismatch, putting the protocol at risk.

The following proof-of-concept script demonstrates that the second claimant receives a revert by the unupdated amountToDistribute:

function testAuditClaimFailedByPoolInfoNotUpdated() public {
    Token sharesToken = Token(_pool.couponToken());

    vm.startPrank(minter);
    _pool.bondToken().mint(user1, 1*10**18);
    _pool.bondToken().mint(user2, 1*10**18);
    sharesToken.mint(address(_pool), 20004000000000000000000);
    vm.stopPrank();

    vm.startPrank(governance);
    fakeSucceededAuction(address(_pool), 0);

    vm.mockCall(
        address(0),
        abi.encodeWithSignature("state()"),
        abi.encode(uint256(1))
    );

    vm.warp(block.timestamp + params.distributionPeriod);
    _pool.distribute();
    vm.stopPrank();

    vm.startPrank(user1);
    distributor.claim(address(_pool));
    vm.stopPrank();

    vm.startPrank(user2);
    vm.expectRevert();
    distributor.claim(address(_pool));
    vm.stopPrank();
}

Recommendations

Use storage poolInfo to update the storage variable.

Remediation

This issue has been acknowledged by Plaza Finance, and a fix was implemented in commit 70eb9503.

Zellic © 2025Back to top ↑