Skip to content
This repository has been archived by the owner on Nov 24, 2024. It is now read-only.

dimulski - A malicious actor can manipulate the distribution of points #85

Closed
sherlock-admin3 opened this issue May 24, 2024 · 28 comments
Closed
Labels
Escalation Resolved This issue's escalations have been approved/rejected Excluded Excluded by the judge without consulting the protocol or the senior Non-Reward This issue will not receive a payout

Comments

@sherlock-admin3
Copy link
Contributor

sherlock-admin3 commented May 24, 2024

dimulski

high

A malicious actor can manipulate the distribution of points

Summary

The SophoFarming.sol contract main purpose is to calculate users points based on their deposits, it is stated that it will support different pools, where users can deposit the corresponding asset and gain points. New pools can be added after the contract has been initialized by the owner via the add() function. A malicious actor can manipulate the points for a certain pool, by backrunning the pool creation transaction and depositing 1 WEI, then in the next block he can fronrun all other transactions and call the updatePool() function, afterwards each user that deposits in that pool will be receiving much more points, than users that deposit in other pools.

    function updatePool(uint256 _pid) public {
        PoolInfo storage pool = poolInfo[_pid];
        if (getBlockNumber() <= pool.lastRewardBlock) {
            return;
        }
        uint256 lpSupply = pool.amount;
        uint256 _pointsPerBlock = pointsPerBlock;
        uint256 _allocPoint = pool.allocPoint;
        if (lpSupply == 0 || _pointsPerBlock == 0 || _allocPoint == 0) {
            pool.lastRewardBlock = getBlockNumber();
            return;
        }
        uint256 blockMultiplier = _getBlockMultiplier(pool.lastRewardBlock, getBlockNumber());
        uint256 pointReward =
            blockMultiplier *
            _pointsPerBlock *
            _allocPoint /
            totalAllocPoint;

        pool.accPointsPerShare = pointReward /
            lpSupply +
            pool.accPointsPerShare;

        pool.lastRewardBlock = getBlockNumber();
    }

As we can see from the above code snippet pointReward = blockMultiplier * _pointsPerBlock * _allocPoint / totalAllocPoint;
Consider the following example:

  • blockMultiplier = 1e18
  • _pointsPerBlock = 25e18
  • _allocPoint = 20_000
  • totalAllocPoint = 60_000
  • pointReward = 8333333333333333333333333333333333333

If lpSupply is equal to 1 then accPointsPerShare will be 8333333333333333333333333333333333333 as 8333333333333333333333333333333333333 / 1 = 8333333333333333333333333333333333333 ≈ 8.3e37 .

However if the first deposit is 1e18 or more, the above calculations for accPointsPerShare will be as follows:
8333333333333333333333333333333333333 / 1e18 = 8333333333333333333 ≈ 8.3e18 which is a big difference.

Given the fact that the main purpose of the contract is to track points, I believe points manipulation is of high severity.

Vulnerability Detail

Gist
After following the steps in the above mentioned gist add the following test to the AuditorTests.t.sol file:

    function test_ManipulatePointDistribution() public {
        vm.startPrank(alice);
        dealWstETH(alice, 1e18);
        console2.log("Balance of alice in wstETH: ", wstETH.balanceOf(alice));
        wstETH.approve(address(sophonFarming), type(uint256).max);

        /// @notice malicious user back runs the transaction and deposits 1 WEI of the lpToken
        sophonFarming.deposit(1, 1, 0);
        SophonFarmingState.PoolInfo[] memory wstETHPool = sophonFarming.getPoolInfo();
        console2.log("Acc Points per share: ", wstETHPool[1].accPointsPerShare);

        /// @notice roll to the next block
        vm.roll(2);

        /// @notice the malicious actor can frontrun all other transactions in order to update the pool first
        sophonFarming.updatePool(1);
        SophonFarmingState.PoolInfo[] memory wstETHPool1 = sophonFarming.getPoolInfo();
        console2.log("Acc Points per share: ", wstETHPool1[1].accPointsPerShare);
        vm.stopPrank();
    }
Logs:
  Balance of alice in wstETH:  860332717133233384
  Acc Points per share:  0
  Acc Points per share:  8333333333333333333333333333333333333

To run the test use: forge test -vvv --mt test_ManipulatePointDistribution

Impact

The distribution of points can be manipulated for a certain pool, and users who don't know about it will receive less rewards, for the same amount of deposits in different pools. The malicious actor is effectively stealing rewards from them. Additionally adding a new pool after some time has passed, and people have already deposited in other pools, if the attacker manipulates the point distribution in the new pool, all users who have already deposited in other pools up to that point in time will be in a big disadvantage.

Code Snippet

https://github.com/sherlock-audit/2024-05-sophon/blob/main/farming-contracts/contracts/farm/SophonFarming.sol#L411-L435

Tool used

Manual Review & Foundry

Recommendation

In the transaction that the contract is inititalized, or a new pool is added create a deposit with a 1e18 amount of the lpToken for the pool.

@github-actions github-actions bot added the Excluded Excluded by the judge without consulting the protocol or the senior label May 28, 2024
@sherlock-admin3
Copy link
Contributor Author

1 comment(s) were left on this issue during the judging contest.

0xmystery commented:

invalid because userAmount isn't updated when user.rewardSettled is assigned. userAmount is updated by the time user.rewardDebt is assigned though

@sherlock-admin3 sherlock-admin3 changed the title Wobbly Olive Squirrel - A malicious actor can manipulate the distribution of points dimulski - A malicious actor can manipulate the distribution of points Jun 1, 2024
@sherlock-admin3 sherlock-admin3 added the Non-Reward This issue will not receive a payout label Jun 1, 2024
@AtanasDimulski
Copy link

Escalate,
@mystery0x I don't understand the comment you have provided as to why this issue is invalid. I have provided a POC that demonstrates how pool.accPointsPerShare is set to a much larger number than it should be. Keep in mind this number can only grow, later when it comes to calculating the points users who deposited in that pool will have many more points than the users who deposited in other pools. As I have mentioned in the report, the whole purpose of this contract is to track points distribution thus this is a valid high issue. Please have another look. Thank you for your time.

@sherlock-admin3
Copy link
Contributor Author

Escalate,
@mystery0x I don't understand the comment you have provided as to why this issue is invalid. I have provided a POC that demonstrates how pool.accPointsPerShare is set to a much larger number than it should be. Keep in mind this number can only grow, later when it comes to calculating the points users who deposited in that pool will have many more points than the users who deposited in other pools. As I have mentioned in the report, the whole purpose of this contract is to track points distribution thus this is a valid high issue. Please have another look. Thank you for your time.

You've created a valid escalation!

To remove the escalation from consideration: Delete your comment.

You may delete or edit your escalation comment anytime before the 48-hour escalation window closes. After that, the escalation becomes final.

@sherlock-admin4 sherlock-admin4 added the Escalated This issue contains a pending escalation label Jun 1, 2024
@mystery0x
Copy link
Collaborator

When the malicious actor first deposits 1 wei, user.rewardSettled and user.rewardDebt will both be 0 because pool.accPointsPerShare is 0 at this point. Calling update() will have pool.accPointsPerShare and pool.lastRewardBlock updated, and undoubtedly a big pointReward / 1 due to division by 1. If he deposits again with a sizable amount, user.amount at this point is 1 wei still when user.rewardSettled is updated, how is he going to capitalize on it the inflated pool.accPointsPerShare? On the other hand, it's user.rewardDebt that will be assigned the inflated value in the same call because userAmount has been added the sizable amount (aka depositAmount). Calling update() the next round renders the exploit infeasible because by now lpSupply is no longer 1 wei but an already much bigger amount.

@0xSandyy
Copy link

0xSandyy commented Jun 2, 2024

Calling update() the next round renders the exploit infeasible because by now lpSupply is no longer 1 wei but an already much bigger amount.

@mystery0x The only thing first deposit does for this issue is it sets the pool.amount to 1 wei. Then when the second deposit occurs by other users(exploiter is only going to deposit once), pool.amount of first deposit i.e 1 wei is used to calculate the pool.accPointsPerShare in the update() function. It is because updatePool is called before pool.amount is updated. It is better to demonstrate this with a PoC I submitted in my similar issue:
#177 (comment)

Also, there is no concern for user.rewardSettled and user.rewardDebt as the exploiter is going to deposit 1 wei only once to get a large number of points.

@mystery0x
Copy link
Collaborator

You guys would need to trace the flow carefully/manually. First off, user.amount (0 for the second depositor making his/her first deposit) is cached as userAmount prior to having user.rewardSettled updated. user.amount and pool.amount are only updated by adding _depositAmount + _boostAmount prior to having user.rewardDebt updated. It's infeasible for the second depositor benefiting from the inflated pool.accPointsPerShare.

@WangSecurity
Copy link

@AtanasDimulski do you have the counterarguments?

@mystery0x could you please share, if theoretically it's infeasible, where's the mistake in the report's POC? Doesn't it prove that it's indeed possible?

@mystery0x
Copy link
Collaborator

mystery0x commented Jun 5, 2024

@WangSecurity I think I know what the watsons were trying to portray now. There's actually no exploit but rather an intended design entailed.

In the test provided in this report, it's only displaying the two different accPointsPerShare values in the span of a block. Likewise, in #177, Alice (the first depositor) was the only depositor in the first block followed by others in the next block.

The thing is, between block 1 and 2, if you were the only depositor, you would be entitled to accPointsPerShare for that particular delta of 1 block (in the context of the reports) regardless of the amount of your deposit (because user.amount over lpSupply gets you the same returned value of _pendingPoints()), which is justifiably fair enough. Unlike the 4626 exploit, the first depositor isn't incurring any losses to or stealing from subsequent depositors. The suggested mitigations would be unnecessary though (doing this would upset/discourage users from depositing in the very first block from the very beginning).

The sponsor already disputed #177. You may want to seek the sponsor's opinion on this report since it has not been reviewed by them.

@0xSandyy
Copy link

0xSandyy commented Jun 5, 2024

@mystery0x @WangSecurity Yeah, there is an oversight on our part regarding the impact. The inflated accPointsPerShare doesn't impact other depositors and Only the first depositor can get large number of points by depositing as low as 1 wei in the first block. But considering that the main purpose of the contract is to track points, the first depositor does have an unfair advantage over other depositors as he/she can get significantly more points than others depositing same amount.

Alice is the first depositor depositing 1 wei in the first block.
Other depositors start depositing 1 ether in the next block.
Alice again deposits 1 ether later.
Alice will get 8333333333333333333 i.e 8.3e18 more points than others depositing same amount.

There's actually no exploit but rather an intended design entailed.

There is no docs provided for this contest and readme also doesn't state any design choices. I believe this is more of a edge case.

Unlike the 4626 exploit, the first depositor isn't incurring any losses to or stealing from subsequent depositors.

Yes, but the first depositor is indeed getting an unfair advantage over other depositors.

The suggested mitigations would be unnecessary though (doing this would upset/discourage users from depositing in the very first block from the very beginning).

If depositors want to get upset/discouraged over fair distribution of points then that's on them.

To conclude, the severity is not as we mentioned in the report but the first depositor can still get 8.3e18 more points for exploiting this issue. Here is the updated PoC. But I leave the final judgement to the judges to decide the severity and impact.

@WangSecurity
Copy link

@0xSandyy

Alice is the first depositor depositing 1 wei in the first block.
Other depositors start depositing 1 ether in the next block.
Alice again deposits 1 ether later.
Alice will get 8333333333333333333 i.e 8.3e18 more points than others depositing same amount.

To clarify, how many points would other depositors get in that scenario?

@0xSandyy
Copy link

0xSandyy commented Jun 6, 2024

To clarify, how many points would other depositors get in that scenario?

@WangSecurity Points calculation depends on a lot of factors like total amount deposited, blockMultiplier, pointsPerBlock, etc. In the updated PoC I provided, all other values are default and total amount deposited is 5 ether(1 ether per person) + first deposit amount. There are 5 depositors including Alice. Alice gets 175000000000000133324 and all other get 166666666666666633336 points. The difference is 8.333333333×10¹⁸.

Note that the more the deposit amount increases, the points decreases. Thus, having a 8.3e18 points difference is huge when the total amount deposited in the pool is very high.

For example in the above example if we have a sixth depositor and the total deposited is 6 ether(1 ether per person) + first deposited amount. Alice will get 147222222222222337955 points and all others get 138888888888888865743.Notice that the points decreased than from the first example but the difference is still 8.333333333×10¹⁸. The difference never decreases, only the points calculated does. Here is the PoC for this example: PoC.

@WangSecurity
Copy link

Thank you for that detailed explanation. With that, I agree that it's indeed a bug and allows the first depositor to get significantly more points than other depositors with only 1 wei.

Planning to accept the escalation and validate this issue with high severity. As I know the duplicate is #177, are there any additional duplicates?

@aslanbekaibimov
Copy link

aslanbekaibimov commented Jun 6, 2024

@WangSecurity

Each block the pool from the example receives 25e18 / 3 rewards, and distributes them between its stakers, proportionally to their stake. If by the end of the first block Alice was the only staker of the pool, why should she not receive 25e18 / 3 rewards? Similarly, if there's 6 users staking 1 ETH each, why should not they receive (25e18 / 3) / 6 rewards each?

@0xAadi
Copy link

0xAadi commented Jun 7, 2024

Thank you for that detailed explanation. With that, I agree that it's indeed a bug and allows the first depositor to get significantly more points than other depositors with only 1 wei.

Planning to accept the escalation and validate this issue with high severity. As I know the duplicate is #177, are there any additional duplicates?

@WangSecurity I respectfully disagree with this.

There are 5 depositors including Alice. Alice gets 175000000000000133324 and all other get 166666666666666633336 points. The difference is 8.333333333×10¹⁸

Since there are no other depositors in the first block besides Alice, she is entitled to the entire amount (8.333333333×10¹⁸) allocated for that block, regardless of how much she deposited. This means she receives 100% of the allocation of that block.

I believe this is the intended behavior. For example, if the protocol decided to distribute some tokens to depositors from block 1-10, the protocol calculates tokens distributed for each block using the points system. And this point is added to the each depositers points who has active deposits in that block. The basic idea is the protocol allocating a portion of the tokens to each block for distribution.

Here is a document shared by the sponsor for explaining the point calculation in the Discord channel: Staking Algorithm by RareSkills. This will explain the calculations in more detail. I agree that this document is not mentioned anywhere in the README.

@mystery0x
Copy link
Collaborator

mystery0x commented Jun 7, 2024

Thank you for that detailed explanation. With that, I agree that it's indeed a bug and allows the first depositor to get significantly more points than other depositors with only 1 wei.

Planning to accept the escalation and validate this issue with high severity. As I know the duplicate is #177, are there any additional duplicates?

#82, #111, and #172 should be duplicates too. However, I think the severity of this finding is low/informational and at best medium. The sponsor already disputed #177 and #172, signifying they're aware of it and wouldn't care much about it. The extra points (similarly of 18 decimals and linearly distributed too) gained isn't much (which is fully deserving for your early support); and, the first depositor depositing 1 wei will have to bet on no one is depositing for at least the first 12 seconds.

@0xSandyy
Copy link

0xSandyy commented Jun 7, 2024

#82, #111, and #172 should be duplicates too.

There are no more duplicates that I can find.

The extra points (similarly of 18 decimals and linearly distributed too) gained isn't much (which is fully deserving for your early support); and, the first depositor depositing 1 wei will have to bet on no one is depositing for at least the first 12 seconds.

I respectfully disagree with this. First 12 seconds do not count as early support. Also the extra points i.e 8.3e18 points difference remain constant throughout the deposit period and considering the points decrease as the deposit amount increases, these extra points difference become significantly high in comparison to other points.

@Autosaida
Copy link

Fully agree with the views of both @aslanbekaibimov and @0xAadi, this is completely normal behavior. If at any moment there is only one depositor, whether he deposit 1 wei or 100 eth, he should be able to get all the points generated by the blocks during that period, until someone else deposits and competes with them. This is entirely reasonable.

@WangSecurity
Copy link

@0xSandyy could you please share your counterarguments to this and this comments by @aslanbekaibimov @0xAadi?

As I understand what you mean, for example, there are 10 points in each block, so if Alice is the only one who deposits in the first block (regardless of the amount) she gets all the points for that block?
If she deposits 1 wei or 10 ETH, either way, she gets 10 points?
If there is a second depositor in that block, each of them gets 5 points, regardless of the deposit amount?

But what if one honest depositor is the only one in the first block and they deposit 1 ETH, they get 10 points. Then Alice deposits 1 wei in the second block and also gets 10 points?

And in the new POC, if there are only two depositors in 2 blocks (1 depositor for each block), there would be no 8.333333333×10¹⁸ difference?

@0xAadi
Copy link

0xAadi commented Jun 7, 2024

As I understand what you mean, for example, there are 10 points in each block, so if Alice is the only one who deposits in the first block (regardless of the amount) she gets all the points for that block?
If she deposits 1 wei or 10 ETH, either way, she gets 10 points?

Correct

If there is a second depositor in that block, each of them gets 5 points, regardless of the deposit amount?

If there is a second depositor, then total of their deposits are used for calculating the point share for each. Therefore, it will not be a 50:50 split, and the depositor with fewer deposits will receive a smaller point share.

But what if one honest depositor is the only one in the first block and they deposit 1 ETH, they get 10 points. Then Alice deposits 1 wei in the second block and also gets 10 points?

In this situation, the honest depositor will receive the majority of the point share because the first depositor remains active in the second block alongside Alice, who has a significant deposit.

Please see the test result below, which mocks the above scenario:

[PASS] test_poc() (gas: 360839)
Logs:
  ----------------------------------------------------------------
  Bob Deposits 1 ETH in Block 1
  --------------------ROLL TO BLOCK 2 ----------------------------
  Bob entitled to get  : 8333333333333333333
  ----------------------------------------------------------------
  Alice Deposits 1001 Wei in Block 2
  --------------------ROLL TO BLOCK 3 ----------------------------
  Alice entitled to get: 8340
  Bob entitled to get  : 16666666666666658326
  ----------------------------------------------------------------

Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 13.85ms (1.60ms CPU time)

Notice that the honest depositor, Bob, received most of the point share (8.333333333×10¹⁸) in the second block, whereas Alice only received 8340.

@0xSandyy
Copy link

0xSandyy commented Jun 7, 2024

@0xSandyy could you please share your counterarguments to #85 (comment) and #85 (comment) comments by @aslanbekaibimov @0xAadi?

@WangSecurity They are right. Since Alice is the only depositor in the first block, she gets all the points. I am not denying that. Let me simplify again what is the issue I am trying to portray:

  1. Let's say we have 10 points per block given to the depositors.
  2. Only in the first block, someone can deposit as low as 1 wei to get those 10 points if they are the only depositor in the block.

Note that if for any reason, after the first deposit of 1 wei, other depositors do not deposit for let's say, 1min/10min/100mins etc, the points will still accumulate 5x for one mins, 50x for 10mins and 500x for 100 mins and so on causing a serious issue. And for a newly deployed protocol and pool with few depositors, the chance of this happening is high.

  1. But after the first deposit, others needs to pay significantly higher amount than first deposit to get those 10 points and they will also get 10 points if there are no other depositors in their block.

  2. Thus, someone can use first few blocks to accumulate some free points by depositing 1 wei and later deposit some significant amount like 1e18 to take advantage of those free points to get an higher edge over others depositing the same 1e18.

But as I see from the @0xSandyy POC, Alice remains that big part of the pot, or am I missing something?

This was to prove point 4, thus Alice deposits 1 wei first + 1e18 later and gets edge over other depositors who also deposited 1e18.

The 4 is the same thing I was trying to prove here:

Alice is the first depositor depositing 1 wei in the first block.
Other depositors start depositing 1 ether in the next block.
Alice again deposits 1 ether later.
Alice will get 8333333333333333333 i.e 8.3e18 more points than others depositing same amount.

@WangSecurity
Copy link

To clarify:

In this situation, the honest depositor will receive the majority of the point share because the first depositor remains active in the second block alongside Alice, who has a significant deposit.

You made a typo and it should be "... who has an insignificant deposit", correct? @0xAadi

Also can you please share the POC, it's the one used above, but with Bob depositing first?

And the last question, if Alice deposits 1 wei in the first block, then Bob deposits 10 ETH in the second block. I assume he should have the majority of the points, not Alice? But as I see from the @0xSandyy POC, Alice remains that big part of the pot, or am I missing something?

And @0xSandyy thank you for the response, don't think I have any questions about that specifically, only the ones above.

@0xSandyy
Copy link

0xSandyy commented Jun 7, 2024

Hi, @WangSecurity I updated my last comment as this was the attack flow in my mind the whole time and I feel the escalation is going all over the place now.

And the last question, if Alice deposits 1 wei in the first block, then Bob deposits 10 ETH in the second block. I assume he should have the majority of the points, not Alice? But as I see from the @0xSandyy POC, Alice remains that big part of the pot, or am I missing something?

Also the updated comment will answer this too.

@Autosaida
Copy link

And the last question, if Alice deposits 1 wei in the first block, then Bob deposits 10 ETH in the second block. I assume he should have the majority of the points, not Alice? But as I see from the @0xSandyy POC, Alice remains that big part of the pot, or am I missing something?

@WangSecurity , In this scenario, it's evident that Alice receives all the points from the first block, let's say 10000 points. Then, in the second block, due to Bob's entry into competition with a larger deposit, he will receive most of the points from the second block, for example, the ratio of Bob to Alice being 9999:1. At the end of the second block, Alice still has more points than Bob, with a ratio of 10001:9999, because Bob only received a portion of the points from the second block, while Alice already claimed all the points from the first block. It's only from the third block onwards that Bob's score will surpass Alice's.

My understanding of what @0xSandyy is saying is this: in any situation where there are no current deposits, anyone can use a very small amount to get all the points from a block. Am I understanding this correctly?

But is this an issue? The process of get points inherently involves competition. If no one else is competing with me, wouldn't it be reasonable for me to use just 1 wei to get all the points? Anyone can attempt to deposit a very small amount when there are no other deposits, which essentially is a process of competition and game, isn't it? So, I fail to see why this is considered a valid issue.

@0xAadi
Copy link

0xAadi commented Jun 8, 2024

In this situation, the honest depositor will receive the majority of the point share because the first depositor remains active in the second block alongside Alice, who has a significant deposit.

You made a typo and it should be "... who has an insignificant deposit", correct? @0xAadi

Yes, what I mean is the honest depositor will receive the majority of the point share because they have a large deposit.

Also can you please share the POC, it's the one used above, but with Bob depositing first?

    function test_poc() public {
        uint256 depositOfAlice = 1001 wei;
        // 1001 wei is the limit set in MockStETH.sol:submit().
        uint256 depositOfBob = 1 ether;

        address alice = address(10);
        vm.deal(alice, depositOfAlice);
        address bob = address(20);
        vm.deal(bob, depositOfBob);
        
        uint256 poolId = sophonFarming.typeToId(
            SophonFarmingState.PredefinedPool.wstETH
        );

        console.log("----------------------------------------------------------------");
        console.log("Bob Deposits 1 ETH in Block",block.number);

        vm.startPrank(bob);
        sophonFarming.depositEth{value: depositOfBob}(
            0,
            SophonFarmingState.PredefinedPool.wstETH
        );
        vm.stopPrank();
        
        vm.roll(block.number + 1);  
        console.log("--------------------ROLL TO BLOCK",block.number,"----------------------------");      
        console.log("Bob entitled to get  :", sophonFarming.pendingPoints(poolId, bob));
        console.log("----------------------------------------------------------------");
        console.log("Alice Deposits 1001 wei in Block",block.number);

        vm.startPrank(alice);
        sophonFarming.depositEth{value: depositOfAlice}(
            0,
            SophonFarmingState.PredefinedPool.wstETH
        );
        vm.stopPrank();

        vm.roll(block.number + 1);
        console.log("--------------------ROLL TO BLOCK",block.number,"----------------------------");
        console.log("Alice entitled to get:", sophonFarming.pendingPoints(poolId, alice));
        console.log("Bob entitled to get  :", sophonFarming.pendingPoints(poolId, bob));
        console.log("----------------------------------------------------------------");
    }

@0xAadi
Copy link

0xAadi commented Jun 8, 2024

And the last question, if Alice deposits 1 wei in the first block, then Bob deposits 10 ETH in the second block. I assume he should have the majority of the points, not Alice? But as I see from the @0xSandyy POC, Alice remains that big part of the pot, or am I missing something?

@WangSecurity Please see the below POC

    function test_poc() public {
        uint256 depositOfAlice = 1001 wei;
        // 1001 wei is the limit set in MockStETH.sol:submit().
        uint256 depositOfBob = 10 ether;

        address alice = address(10);
        vm.deal(alice, depositOfAlice);
        address bob = address(20);
        vm.deal(bob, depositOfBob);
        
        uint256 poolId = sophonFarming.typeToId(
            SophonFarmingState.PredefinedPool.wstETH
        );

        console.log("----------------------------------------------------------------");
        console.log("Alice Deposits 1001 wei in Block",block.number);

        vm.startPrank(alice);
        sophonFarming.depositEth{value: depositOfAlice}(
            0,
            SophonFarmingState.PredefinedPool.wstETH
        );
        vm.stopPrank();

        vm.roll(block.number + 1);  
        console.log("Total Points of Alice:", sophonFarming.pendingPoints(poolId, alice),"Points");
        console.log("Total Points of Bob  :", sophonFarming.pendingPoints(poolId, bob),"Points");  
        console.log("--------------------ROLL TO BLOCK",block.number,"----------------------------");
        
        console.log("Bob Deposits 10 ETH in Block",block.number);

        vm.startPrank(bob);
        sophonFarming.depositEth{value: depositOfBob}(
            0,
            SophonFarmingState.PredefinedPool.wstETH
        );
        vm.stopPrank();
        
        vm.roll(block.number + 1);  
        console.log("Total Points of Alice:", sophonFarming.pendingPoints(poolId, alice),"Points");
        console.log("Total Points of Bob  :", sophonFarming.pendingPoints(poolId, bob),"Points");
        console.log("--------------------ROLL TO BLOCK",block.number,"----------------------------");    

        vm.roll(block.number + 1);  
        console.log("Total Points of Alice:", sophonFarming.pendingPoints(poolId, alice),"Points");
        console.log("Total Points of Bob  :", sophonFarming.pendingPoints(poolId, bob),"Points");  
        console.log("----------------------------------------------------------------");
    }
[PASS] test_poc() (gas: 376809)
Logs:
  ----------------------------------------------------------------
  Alice Deposits 1001 wei in Block 1
  Total Points of Alice: 8333333333333333333 Points
  Total Points of Bob  : 0 Points
  --------------------ROLL TO BLOCK 2 ----------------------------
  Bob Deposits 10 ETH in Block 2
  Total Points of Alice: 8333333333333334167 Points
  Total Points of Bob  : 8333333333333332499 Points
  --------------------ROLL TO BLOCK 3 ----------------------------
  Total Points of Alice: 8333333333333335001 Points
  Total Points of Bob  : 16666666666666664999 Points
  ----------------------------------------------------------------

Suite result: ok. 1 passed; 0 failed; 0 skipped; finished in 13.61ms (1.89ms CPU time)

Here we can see that Alice is accumulating an insignificant amount of shares from Block 2 onwards, while Bob is accumulating the major portion of the points.

Alice accumulated 834 points in Block 2 and 834 points in Block 3. On the other hand, Bob accumulated 8.333333333×10¹⁸ points in Block 2 and 8.333333333×10¹⁸ points in Block 3.

Also note that the protocol allows users to deposit right after creating the pool, but the point calculation only starts from the start block. This gives all users an equal opportunity to start depositing in any pool before the start block.

@WangSecurity
Copy link

Thanks to all the Watsons for these comments and for explaining it. I agree that it's a design of the points system and it works as it should be. The problem is that as the first depositor and the only one in the block, you indeed receive all the points, but with all the depositors after it, you will receive all the points as per your allocation inside the pool. It's not a security issue but rather a system to reward the first depositors, regardless of their deposit (be that 10000 ETH or 1 wei, you will receive all the points if you're the only depositor in the first block).

Hence, planning to reject the escalation and leave the issue as it is.

@WangSecurity
Copy link

Result:
Invalid
Unique

@sherlock-admin2 sherlock-admin2 removed the Escalated This issue contains a pending escalation label Jun 10, 2024
@sherlock-admin3 sherlock-admin3 added the Escalation Resolved This issue's escalations have been approved/rejected label Jun 10, 2024
@sherlock-admin4
Copy link
Contributor

Escalations have been resolved successfully!

Escalation status:

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Escalation Resolved This issue's escalations have been approved/rejected Excluded Excluded by the judge without consulting the protocol or the senior Non-Reward This issue will not receive a payout
Projects
None yet
Development

No branches or pull requests

10 participants