sherlock-audit / 2023-05-perennial-judging

12 stars 9 forks source link

nobody2018 - Reward tokens belonging to BalancedVault will be stuck in the Incentivizer contract #150

Closed sherlock-admin closed 1 year ago

sherlock-admin commented 1 year ago

nobody2018

high

Reward tokens belonging to BalancedVault will be stuck in the Incentivizer contract

Summary

A product can create multiple incentive programs. Each incentive program has a corresponding reward token. Every time Incentivizer.syncAccount is called, all reward tokens will be accumulated for the account. BalancedVault is actually the maker of the product, and it is a user for the product. Whenever BalancedVault.sync() is called by off-chain keeper, BalancedVault's reward tokens will be accumulated. The larger the position, the more reward tokens you will get. As the maker of the product, BalancedVault must be a user with a large position. However, BalancedVault did not implement the interface to claim these reward tokens, resulting in a large number of reward tokens being left in the Incentivizer contract.

Vulnerability Detail

Let's take a look at the flow of the BalancedVault.sync() function.

BalancedVault.sync()
  _rebalance(context, UFixed18Lib.ZERO)         //L148 in BalancedVault.sol
    _rebalancePosition(context, claimAmount);       //L433 in BalancedVault.sol
      _updateMakerPosition(markets(marketId).long, targetPosition); //L489 in BalancedVault.sol
      _updateMakerPosition(markets(marketId).short, targetPosition);    //L490 in BalancedVault.sol
        product.closeMake               //L508 in BalancedVault.sol
        product.openMake                //L517 in BalancedVault.sol

When the position needs to be adjusted, product.openMake/closeMake will trigger Product._settleAccount. The flow of the function is as follows:

Product._settleAccount      //L154 in Product.sol
  _controller.incentivizer().syncAccount(account, settleOracleVersion)      //L167 in Product.sol
    _products[product].syncAccount(product, account, currentOracleVersion)      //L130 in Incentivizer.sol

There is a [for](https://github.com/sherlock-audit/2023-05-perennial/blob/main/perennial-mono/packages/perennial/contracts/incentivizer/types/ProductManager.sol#L124-L131) loop in ProductManager.syncAccount that will accumulate all reward tokens for the account.

// Settle programs
        uint256[] memory activeProgramIds = self.activeProgramsFor[account].values();
        for (uint256 i; i < activeProgramIds.length; i++) {
            uint256 programId = activeProgramIds[i];
            Program storage program = self.programs[programId];
->          program.settle(product, self.programInfos[programId], account, currentOracleVersion);
            if (!self.activePrograms.contains(programId) && currentOracleVersion.version >= program.versionComplete) {
                self.activeProgramsFor[account].remove(programId);
            }
        }

The Incentivizer contract provides a claim interface for users to claim all reward tokens. However, there is no code in BalancedVault.sol to call Incentivizer.claim.

Impact

This will result in a large amount of reward tokens being stuck in the Incentivizer contract.

Code Snippet

https://github.com/sherlock-audit/2023-05-perennial/blob/main/perennial-mono/packages/perennial/contracts/incentivizer/Incentivizer.sol#L138-L143

https://github.com/sherlock-audit/2023-05-perennial/blob/main/perennial-mono/packages/perennial/contracts/incentivizer/Incentivizer.sol#L125-L131

Tool used

Manual Review

Recommendation

Add interface to BalancedVault to claim reward tokens.

Duplicate of #43