Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Remaining dust from Ether deposits is not returned to users #455

Closed
code423n4 opened this issue Mar 29, 2023 · 6 comments
Closed

Remaining dust from Ether deposits is not returned to users #455

code423n4 opened this issue Mar 29, 2023 · 6 comments
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working duplicate-152 high quality report This report is of especially high quality satisfactory satisfies C4 submission criteria; eligible for awards sponsor disputed Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue

Comments

@code423n4
Copy link
Contributor

Lines of code

https://github.com/code-423n4/2023-03-asymmetry/blob/main/contracts/SafEth/SafEth.sol#L88

Vulnerability details

When users stake Ether, some dust is left from the calculations corresponding to the Ether that has to be deposited for each derivative. This is due to some precision loss when calculating the weighted amounts.

Impact

Users lose the Ether dust that is not being used for staking. The dust is locked on the SafEth contract.

Proof of Concept

There is some precision loss in line 88: uint256 ethAmount = (msg.value * weight) / totalWeight;

Each weighted ethAmount is deposited for each derivative, but the sum of all those amounts is less than the msg.value. That dust is left on the contract and not returned to the user.

File: File: contracts/SafEth.sol
63:     function stake() external payable {

82: 
83:         uint256 totalStakeValueEth = 0; // total amount of derivatives worth of ETH in system
84:         for (uint i = 0; i < derivativeCount; i++) {
85:             uint256 weight = weights[i];
86:             IDerivative derivative = derivatives[i];
87:             if (weight == 0) continue;
88:             uint256 ethAmount = (msg.value * weight) / totalWeight; // @audit precision loss
89: 
90:             // This is slightly less than ethAmount because slippage
91:             uint256 depositAmount = derivative.deposit{value: ethAmount}();
92:             uint derivativeReceivedEthValue = (derivative.ethPerDerivative(
93:                 depositAmount
94:             ) * depositAmount) / 10 ** 18;
95:             totalStakeValueEth += derivativeReceivedEthValue;
96:         }
97:         // mintAmount represents a percentage of the total assets in the system
98:         uint256 mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice;
99:         _mint(msg.sender, mintAmount);
100:         emit Staked(msg.sender, msg.value, mintAmount);
101:     }

Link to code

Test

Add the following test to the describe("Af Strategy") in test/SafEth.test.ts to prove that Ether dust is locked in the contract:

  describe("Dust", function () {
    it.only("keeps ether dust in contract", async function () {
      const accounts = await ethers.getSigners();
      const user = accounts[0];

      // Verify that the contract has no Ether before the stake
      let safEthBalance = await ethers.provider.getBalance(safEthProxy.address);
      expect(safEthBalance).eq(0);

      // Perform stake
      const depositAmount = ethers.utils.parseEther("1");
      await safEthProxy.connect(user).stake({ value: depositAmount });

      // Verify that the contract now has some extra dust locked
      safEthBalance = await ethers.provider.getBalance(safEthProxy.address);
      expect(safEthBalance).gt(0);
    });
  });

Tools Used

Manual review

Recommended Mitigation Steps

Return the Ether dust not used for staking to the user:

// File: contracts/SafEth.sol

     function stake() external payable {
        // ...

         uint256 totalStakeValueEth = 0; // total amount of derivatives worth of ETH in system
+        uint256 totalValueEth = 0;
         for (uint i = 0; i < derivativeCount; i++) {
             uint256 weight = weights[i];
             IDerivative derivative = derivatives[i];
             if (weight == 0) continue;
             uint256 ethAmount = (msg.value * weight) / totalWeight;
+            totalValueEth += ethAmount;
 
             // This is slightly less than ethAmount because slippage
             uint256 depositAmount = derivative.deposit{value: ethAmount}();
             uint derivativeReceivedEthValue = (derivative.ethPerDerivative(
                 depositAmount
             ) * depositAmount) / 10 ** 18;
             totalStakeValueEth += derivativeReceivedEthValue;
         }
         // mintAmount represents a percentage of the total assets in the system
         uint256 mintAmount = (totalStakeValueEth * 10 ** 18) / preDepositPrice;
-        _mint(msg.sender, mintAmount);
-        emit Staked(msg.sender, msg.value, mintAmount);
+        _mint(totalValueEth, mintAmount);
+        uint256 dust = msg.value - totalValueEth;
+        (bool sent, ) = address(msg.sender).call{value: dust}("");
+        require(sent, "Failed to send Ether");
+        emit Staked(msg.sender, totalValueEth, mintAmount);
     }
@code423n4 code423n4 added 2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working labels Mar 29, 2023
code423n4 added a commit that referenced this issue Mar 29, 2023
@c4-pre-sort c4-pre-sort added the high quality report This report is of especially high quality label Apr 3, 2023
@c4-pre-sort
Copy link

0xSorryNotSorry marked the issue as high quality report

@c4-pre-sort c4-pre-sort added the primary issue Highest quality submission among a set of duplicates label Apr 4, 2023
@c4-pre-sort
Copy link

0xSorryNotSorry marked the issue as primary issue

@toshiSat
Copy link

toshiSat commented Apr 7, 2023

This will result in more gas spent by user to recover dust therefore resulting in a lower ETH balance overall.

@c4-sponsor
Copy link

toshiSat marked the issue as sponsor disputed

@c4-sponsor c4-sponsor added the sponsor disputed Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue label Apr 7, 2023
@c4-judge c4-judge added duplicate-152 and removed primary issue Highest quality submission among a set of duplicates labels Apr 24, 2023
@c4-judge
Copy link
Contributor

Picodes marked issue #152 as primary and marked this issue as a duplicate of 152

@c4-judge
Copy link
Contributor

Picodes marked the issue as satisfactory

@c4-judge c4-judge added the satisfactory satisfies C4 submission criteria; eligible for awards label Apr 24, 2023
This issue was closed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
2 (Med Risk) Assets not at direct risk, but function/availability of the protocol could be impacted or leak value bug Something isn't working duplicate-152 high quality report This report is of especially high quality satisfactory satisfies C4 submission criteria; eligible for awards sponsor disputed Sponsor cannot duplicate the issue, or otherwise disagrees this is an issue
Projects
None yet
Development

No branches or pull requests

5 participants