MEDIUM severity: Investors transferring all their balances among their wallets or self-transferring on the same wallet causes to incorrectly decrement the investor counters causing DoS for other investors transfers. **Description:** `ComplianceServiceRegulated::recordTransfer()` is in charge of adjusting the investor counters when the receiver's investor is new, or the sender's investor is transferring all of its balance, but there are edge cases that cause the investor counters to be incorrectly...

Vulnerable code:
function compareInvestorBalance(
        address _who,
        uint256 _value,
        uint256 _compareTo
    ) internal view returns (bool) {
       //@audit => true when the sender is transferring all of its balance
        return (_value != 0 && getToken().balanceOfInvestor(getRegistryService().getInvestor(_who)) == _compareTo);
    }

    function recordTransfer(
        address _from,
        address _to,
        uint256 _value
    ) internal override returns (bool) {
//@audit-issue => Not checking if sender is the same as the receiver

        if (compareInvestorBalance(_to, _value, 0)) {
            adjustTransferCounts(_to, CommonUtils.IncDec.Increase);
        }

        if (compareInvestorBalance(_from, _value, _value)) {
//@audit=> When from's investor transfers all of its balance, investor counters are decremented.
            adjustTotalInvestorsCounts(_from, CommonUtils.IncDec.Decrease);
        }

        ...
    }

it.only('Mess up counters via transfers among wallets owned by the same investor', async function () {
      const [owner, wallet1Investor1, wallet2Investor1, walletInvestor2, walletInvestor3] = await hre.ethers.getSigners();
      const { dsToken, registryService, complianceConfigurationService, lockManager, complianceService } = await loadFixture(deployDSTokenRegulated);

      await complianceConfigurationService.setCountryCompliance(INVESTORS.Country.USA, INVESTORS.Compliance.US);
      await complianceConfigurationService.setUSInvestorsLimit(10);

      await registryService.registerInvestor(INVESTORS.INVESTOR_ID.US_INVESTOR_ID, '');
      await registryService.registerInvestor(INVESTORS.INVESTOR_ID.US_INVESTOR_ID_2, '');
      await registryService.registerInvestor(INVESTORS.INVESTOR_ID.US_INVESTOR_ID_3, '');

      await registryService.setCountry(INVESTORS.INVESTOR_ID.US_INVESTOR_ID, INVESTORS.Country.USA);
      await registryService.setCountry(INVESTORS.INVESTOR_ID.US_INVESTOR_ID_2, INVESTORS.Country.USA);
      await registryService.setCountry(INVESTORS.INVESTOR_ID.US_INVESTOR_ID_3, INVESTORS.Country.USA);

      await registryService.addWallet(wallet1Investor1, INVESTORS.INVESTOR_ID.US_INVESTOR_ID);//wallet1Investor1 => US_INVESTOR_ID
      await registryService.addWallet(wallet2Investor1, INVESTORS.INVESTOR_ID.US_INVESTOR_ID);//wallet2Investor1 -> US_INVESTOR_ID

      await registryService.addWallet(walletInvestor2, INVESTORS.INVESTOR_ID.US_INVESTOR_ID_2);//walletInvestor2 -> US_INVESTOR_ID_2
      await registryService.addWallet(walletInvestor3, INVESTORS.INVESTOR_ID.US_INVESTOR_ID_3);//walletInvestor3 -> US_INVESTOR_ID_3

      const currentTime = await time.latest();

      //@audit-info => Issue unlocked tokens to investors

      await dsToken.issueTokensCustom(wallet1Investor1, 10_000, currentTime, 0, 'TEST', 0);
      await dsToken.issueTokensCustom(walletInvestor2, 10_000, currentTime, 0, 'TEST', 0);
      await dsToken.issueTokensCustom(walletInvestor3, 10_000, currentTime, 0, 'TEST', 0);

      expect(await complianceService.getUSInvestorsCount()).equal(3);
      expect(await complianceService.getTotalInvestorsCount()).equal(3);

      //@audit-info => investor1 transfers all the balance among his wallets
      const dsTokenWallet1Investor1 = await dsToken.connect(wallet1Investor1);
      const dsTokenWallet2Investor1 = await dsToken.connect(wallet2Investor1);

      await dsTokenWallet1Investor1.transfer(wallet2Investor1, 10_000);
      await dsTokenWallet2Investor1.transfer(wallet1Investor1, 10_000);
      await dsTokenWallet1Investor1.transfer(wallet2Investor1, 10_000);

      //@audit-issue => Investor counters have been brought down to 0 while the 3 investors still have balances!
      expect(await complianceService.getUSInvestorsCount()).equal(0);
      expect(await complianceService.getTotalInvestorsCount()).equal(0);

      expect(await dsToken.balanceOfInvestor(INVESTORS.INVESTOR_ID.US_INVESTOR_ID)).equal(10_000);
      expect(await dsToken.balanceOfInvestor(INVESTORS.INVESTOR_ID.US_INVESTOR_ID_2)).equal(10_000);
      expect(await dsToken.balanceOfInvestor(INVESTORS.INVESTOR_ID.US_INVESTOR_ID_3)).equal(10_000);

      //@audit-info => Will revert because investor counter has been brought down to 0
      const dsTokenWalletInvestor2 = await dsToken.connect(walletInvestor2);
      await expect(dsTokenWalletInvestor2.transfer(walletInvestor3, 10_000)).to.be.revertedWith('Not enough investors');
    });

it.only('self transfer from the same wallet messes up investor counters', async function () {
      const [owner, wallet1Investor1, wallet2Investor1, walletInvestor2, walletInvestor3] = await hre.ethers.getSigners();
      const { dsToken, registryService, complianceConfigurationService, lockManager, complianceService } = await loadFixture(deployDSTokenRegulated);

      await complianceConfigurationService.setCountryCompliance(INVESTORS.Country.USA, INVESTORS.Compliance.US);
      await complianceConfigurationService.setUSInvestorsLimit(10);

      await registryService.registerInvestor(INVESTORS.INVESTOR_ID.US_INVESTOR_ID, '');
      await registryService.registerInvestor(INVESTORS.INVESTOR_ID.US_INVESTOR_ID_2, '');
      await registryService.registerInvestor(INVESTORS.INVESTOR_ID.US_INVESTOR_ID_3, '');

      await registryService.setCountry(INVESTORS.INVESTOR_ID.US_INVESTOR_ID, INVESTORS.Country.USA);
      await registryService.setCountry(INVESTORS.INVESTOR_ID.US_INVESTOR_ID_2, INVESTORS.Country.USA);
      await registryService.setCountry(INVESTORS.INVESTOR_ID.US_INVESTOR_ID_3, INVESTORS.Country.USA);

      await registryService.addWallet(wallet1Investor1, INVESTORS.INVESTOR_ID.US_INVESTOR_ID);//wallet1Investor1 => US_INVESTOR_ID

      await registryService.addWallet(walletInvestor2, INVESTORS.INVESTOR_ID.US_INVESTOR_ID_2);//walletInvestor2 -> US_INVESTOR_ID_2
      await registryService.addWallet(walletInvestor3, INVESTORS.INVESTOR_ID.US_INVESTOR_ID_3);//walletInvestor3 -> US_INVESTOR_ID_3

      const currentTime = await time.latest();

      //@audit-info => Issue unlocked tokens to investors

      await dsToken.issueTokensCustom(wallet1Investor1, 10_000, currentTime, 0, 'TEST', 0);
      await dsToken.issueTokensCustom(walletInvestor2, 10_000, currentTime, 0, 'TEST', 0);
      await dsToken.issueTokensCustom(walletInvestor3, 10_000, currentTime, 0, 'TEST', 0);

      expect(await complianceService.getUSInvestorsCount()).equal(3);
      expect(await complianceService.getTotalInvestorsCount()).equal(3);

      //@audit-info => investor1 transfers all the balance among his wallets
      const dsTokenWallet1Investor1 = await dsToken.connect(wallet1Investor1);

      await dsTokenWallet1Investor1.transfer(wallet1Investor1, 10_000);
      await dsTokenWallet1Investor1.transfer(wallet1Investor1, 10_000);
      await dsTokenWallet1Investor1.transfer(wallet1Investor1, 10_000);

      //@audit-issue => Investor counters have been brought down to 0 while the 3 investors still have balances!
      expect(await complianceService.getUSInvestorsCount()).equal(0);
      expect(await complianceService.getTotalInvestorsCount()).equal(0);

      expect(await dsToken.balanceOfInvestor(INVESTORS.INVESTOR_ID.US_INVESTOR_ID)).equal(10_000);
      expect(await dsToken.balanceOfInvestor(INVESTORS.INVESTOR_ID.US_INVESTOR_ID_2)).equal(10_000);
      expect(await dsToken.balanceOfInvestor(INVESTORS.INVESTOR_ID.US_INVESTOR_ID_3)).equal(10_000);

      //@audit-info => Will revert because investor counter has been brought down to 0
      const dsTokenWalletInvestor2 = await dsToken.connect(walletInvestor2);
      await expect(dsTokenWalletInvestor2.transfer(walletInvestor3, 10_000)).to.be.revertedWith('Not enough investors');
    });