Hesty Disclosed Report

Users will lose extra fees paid during investment due to missing refund logic

Company
Created date
Mar 12 2025

Target

https://github.com/la-bomba-studio/hesty-contract/tree/29596447b9a06d4ad53360d4a15f349ebf9fa0d8

Vulnerability Details

Description:
The recoverFundsInvested function in TokenFactory contract do not refund the extra fees that users pay when purchasing tokens:

@>        uint256 amount         = userInvested[user][id];
        userInvested[user][id] = 0;
        rightForTokens[user][id] = 0;

@>        SafeERC20.safeTransfer(p.paymentToken, user, amount);

Here is the amount investors pay in total to buy tokens via buyTokens function:

        // Calculate the investment fee and then get the total investment cost
        uint256 fee    = boughtTokensPrice * platformFeeBasisPoints / BASIS_POINTS;
@>        uint256 total  = boughtTokensPrice + fee;

        // Charge investment cost from user
@>        SafeERC20.safeTransferFrom(p.paymentToken,msg.sender, address(this), total);

As a result, when a user requests a refund or when a property is canceled, only the principal investment is returned, while the extra fees remain stuck in the contract. There is no function to retrieve these fees, leading to an irreversible loss of funds for users. Since fees can be as high as 30% of the investment amount, investors stand to lose a significant portion of their funds due to this vulnerability.

Impact:

  • Affected party: All investors who request refunds or experience property cancellations.
  • Loss: Users lose up to 23% of their investment due to unrefunded fees, leading to significant financial losses.
  • Additional impact: The contract accumulates locked funds over time, making them inaccessible.

Mitigation:
Modify the refund logic in the recoverFundsInvested function to ensure that users receive their total refunded investment.

Validation steps

Exploit Scenario:

  1. A user invests in a property and pays an additional fee as part of the token purchase.
  2. Later, the user requests a refund via recoverFundsInvested or the property gets canceled via cancelProperty.
  3. The user receives only the principal investment amount, while the extra fees remain locked in the contract.
  4. Since there is no mechanism to recover these fees, users permanently lose this amount.

Add this code in TokenFactory.test.js file and run the test:

  describe("Fees not refunded", function () {
    beforeEach(async function () {
      //Not yet initialized so therefore address(0)
      expect(await tokenFactory.referralSystemCtr()).to.equal("0x0000000000000000000000000000000000000000");

      await tokenFactory.initialize(referral.address, issuance.address)

      await hestyAccessControlCtr.connect(addr2).approveUserKYC(propertyManager.address);

      await tokenFactory.addWhitelistedToken(token.address);

      await tokenFactory.connect(propertyManager).createProperty(1000000, 1000, 1000, 100000, token.address, token.address, "token", "TKN", hestyAccessControlCtr.address)

      expect(await tokenFactory.propertyCounter()).to.equal(1);

      await tokenFactory.approveProperty(0, 2937487238472834);

      // Approve owner kyc to allow him to buy property token
      await hestyAccessControlCtr.connect(addr2).approveUserKYC(owner.address);

      await token.approve(tokenFactory.address, 2000);

      await token.mint(owner.address, 1050);

      // User buy 1 Property Tokens with boughtTokensPrice = 1000
      // Fees = 30; Total Price = 1030
      await tokenFactory.buyTokens(owner.address, 0, 1, addr3.address);
    })

    it("FeesNotRefunded", async function () {
      await ethers.provider.send("evm_mine", [2937487238472844]);

      await tokenFactory.recoverFundsInvested(owner.address, 0);

      // User only get 1000 back as compared to 1030
      expect(await token.balanceof(owner.address)).to.equal(1020);

    });

  })

Attachments

hidden
CommentsReport History
Comments on this report are hidden
Details
Statedisclosed
Severity
Critical
Bounty$365
Visibilitypartially
VulnerabilityBlockchain
Participants (5)
company admin
company admin
author
company admin