Beanstalk Notion
Beanstalk Notion
/
🪲
Bug Reports
/
BIC Notes
/
📄
Report #35782
📄

Report #35782

Report Date
October 7, 2024
Status
Closed
Payout

Incorrect math in `transferTalk` causes user to lose his roots

‣
Report Info

Report ID

#35782

Report type

Smart Contract

Has PoC?

Yes

Target

https://etherscan.io/address/0xC1E088fC1323b20BCBee9bd1B9fC9546db5624C5

Impacts

Permanent freezing of unclaimed yield

Description

The transferStalk function calculates the number of roots to transfer when transferring stalk between accounts. The current rounding logic is implemented incorrectly.

https://github.com/BeanstalkFarms/Beanstalk/blob/a7727c877dd4ec297c364661869da4bcbaf5c579/protocol/contracts/libraries/Silo/LibSilo.sol#L318-L320

Notice that a wrong calculation also will impact in the logic for the user sop.

Vulnerability Details

I.e about the current formula:

roots = (stalk - 1) * 500 / 3 + 1;

This is intended to round up but instead produces incorrect results. For example, with stalk = 3 and division = 3, the calculation gives:

roots = (3 - 1) * 500 / 3 + 1 = 334

However, the correct rounding formula should be:

roots = (stalk * 500 - 1) / 3 + 1;

This would result in:

roots = (3 * 500 - 1) / 3 + 1 = 500

Impact Details

  • Loss of funds for the recipient. The main issue is that the transferStalk removes the silo assets from the sender and adds them to the recipient, right after the transfer of stalk is made with less stalk than it should send to the recipient.
  • Sender cannot send the right amount of funds for the recipient

https://github.com/BeanstalkFarms/Beanstalk/blob/a7727c877dd4ec297c364661869da4bcbaf5c579/protocol/contracts/beanstalk/silo/SiloFacet/TokenSilo.sol#L342-L364

Proof of concept

Add the following test on Silo.test.js

run npx hardhat test test/Silo.test.js

output:

 Silo
    Silo TransferStalk Rounding Issue
@> Incorrect roots calculated: 334
@> Correct roots calculated: 500
      ✔ should demonstrate the rounding issue in transferStalk

BIC Response

Thank you for your report. This was a known issue and is already fixed, though not deployed on mainnet yet.

You can find the updated logic in our public repository here: https://github.com/BeanstalkFarms/Beanstalk/tree/beanstalk-3

        roots = stalk == s.a[sender].s.stalk
            ? s.a[sender].roots
@>            : s.s.roots.sub(1).mul(stalk).div(s.s.stalk).add(1);

...
         s.a[sender].roots = s.a[sender].roots.sub(roots);

         // Rain roots cannot be transferred, burn them
@>        if (s.a[sender].sop.roots > s.a[sender].roots) {
            uint256 deltaRoots = s.a[sender].sop.roots.sub(s.a[sender].roots);
            s.a[sender].sop.roots = s.a[sender].roots;
            s.r.roots = s.r.roots.sub(deltaRoots);
        }
describe.only('Silo TransferStalk Rounding Issue', function () {
    it('should demonstrate the rounding issue in transferStalk', async function () {
        // Initialize example values for stalk and roots
        const stalk = toBN('500');  // 500 stalk to transfer
        const wrongStalkExample = toBN('3');  // 3 stalk as in the example

        // Simulate transferring stalk
        const senderStalkBefore = await this.siloGetters.balanceOfStalk(userAddress);
        const recipientStalkBefore = await this.siloGetters.balanceOfStalk(user2Address);

        // Apply incorrect formula
        const incorrectRoots = wrongStalkExample.sub(1).mul(stalk).div(toBN('3')).add(1);  // Incorrect rounding

        // Apply correct formula for comparison
        const correctRoots = wrongStalkExample.mul(stalk).sub(1).div(toBN('3')).add(1);  // Correct rounding

        console.log(`Incorrect roots calculated: ${incorrectRoots.toString()}`);
        console.log(`Correct roots calculated: ${correctRoots.toString()}`);
    });
});