Assessment reports>Programmable Derivatives>High findings>Incorrect PreDeposit reward
Category: Coding Mistakes

Incorrect PreDeposit reward

High Severity
High Impact
High Likelihood

Description

In PreDeposit, each user should be able to claim a proportion of the deposit based on their individual contribution. However, the claim function does not work proportionally. This is because the userBondShare and userLeverageShare are calculated based on the contract's current balance, not on the initial bondAmount and leverageAmount, which represent the total amount of tokens at the start. As the contract's balance decreases with each user's claim, users who claim later receive fewer tokens.

function claim() external nonReentrant whenNotPaused {
  // ...

  uint256 userBondShare = (bondToken.balanceOf(address(this)) * userBalance) / reserveAmount;
  uint256 userLeverageShare = (leverageToken.balanceOf(address(this)) * userBalance) / reserveAmount;

  // ...

  if (userBondShare > 0) {
    bondToken.transfer(msg.sender, userBondShare);
  }
  // ...
}

Impact

From the second claimant onward, they receive a smaller, incorrect amount that does not match the partial calculation.

The following proof-of-concept script demonstrates that the second claimant receives a smaller amount than the first claimant:

function testAuditPreDepositClaimIncorrect() public {
    // Setup initial deposit
    vm.startPrank(user1);
    reserveToken.approve(address(preDeposit), DEPOSIT_AMOUNT);
    preDeposit.deposit(DEPOSIT_AMOUNT);
    vm.stopPrank();
    vm.startPrank(user2);
    reserveToken.approve(address(preDeposit), DEPOSIT_AMOUNT);
    preDeposit.deposit(DEPOSIT_AMOUNT);
    vm.stopPrank();

    // Create pool
    vm.startPrank(governance);
    preDeposit.setBondAndLeverageAmount(BOND_AMOUNT, LEVERAGE_AMOUNT);
    vm.warp(block.timestamp + 8 days); // After deposit period
    poolFactory.grantRole(poolFactory.GOV_ROLE(), address(preDeposit));
    preDeposit.createPool();
    vm.stopPrank();

    // Claim tokens
    address bondToken = address(Pool(preDeposit.pool()).bondToken());

    uint256 user1_preDeposit_balance = preDeposit.balances(user1);
    uint256 user2_preDeposit_balance = preDeposit.balances(user2);
    console.log("user1 preDeposit balance: ", user1_preDeposit_balance);
    console.log("user2 preDeposit balance: ", user2_preDeposit_balance);
    
    vm.prank(user1);
    preDeposit.claim();
    vm.prank(user2);
    preDeposit.claim();
    
    uint256 user1_bond_share = BondToken(bondToken).balanceOf(user1);
    uint256 user2_bond_share = BondToken(bondToken).balanceOf(user2);
    assertNotEq(user1_bond_share, user2_bond_share);
    console.log("user1 bond share: ", user1_bond_share);
    console.log("user2 bond share: ", user2_bond_share);
}

The following text is the result of the proof-of-concept script:

[PASS] testAuditPreDepositClaimIncorrect() (gas: 1771121)
Logs:
  user1 preDeposit balance:  10000000000000000000
  user2 preDeposit balance:  10000000000000000000
  user1 bond share:  25000000000000000000
  user2 bond share:  12500000000000000000

Recommendations

Use the initial balance for the share calculation, not the current contract balance.

Remediation

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

Zellic © 2025Back to top ↑