A malicious DAO Pool can create a token sale tier without actually transferring any DAO tokens
`TokenSaleProposalCreate::createTier` is called by a DAO Pool owner to create a new token sale tier. A fundamental prerequisite for creating a tier is that the DAO Pool owner must transfer the `totalTokenProvided` amount of DAO tokens to the `TokenSaleProposal`.\\nCurrent implementation implements a low-level call to transfer tokens from `msg.sender(GovPool)` to `TokenSaleProposal` contract. However, the implementation fails to validate the token balances after the transfer is successful. We notice a `dev` comment stating "return value is not checked intentionally" - even so, this vulnerability is not related to checking return `status` but to verifying the contract balances before & after the call.\\n```\\nfunction createTier(\\n        mapping(uint256 => ITokenSaleProposal.Tier) storage tiers,\\n        uint256 newTierId,\\n        ITokenSaleProposal.TierInitParams memory _tierInitParams\\n    ) external {\\n\\n       // rest of code.\\n         /// @dev return value is not checked intentionally\\n  >      tierInitParams.saleTokenAddress.call(\\n            abi.encodeWithSelector(\\n                IERC20.transferFrom.selector,\\n                msg.sender,\\n                address(this),\\n                totalTokenProvided\\n            )\\n        );  //@audit -> no check if the contract balance has increased proportional to the totalTokenProvided\\n   }\\n```\\n\\nSince a DAO Pool owner can use any ERC20 as a DAO token, it is possible for a malicious Gov Pool owner to implement a custom ERC20 implementation of a token that overrides the `transferFrom` function. This function can override the standard ERC20 `transferFrom` logic that fakes a successful transfer without actually transferring underlying tokens.
Calculate the contract balance before and after the low-level call and verify if the account balance increases by `totalTokenProvided`. Please be mindful that this check is only valid for non-fee-on-transfer tokens. For fee-on-transfer tokens, the balance increase needs to be further adjusted for the transfer fees. Example code for non-free-on-transfer-tokens:\\n```\\n        // transfer sale tokens to TokenSaleProposal and validate the transfer\\n        IERC20 saleToken = IERC20(_tierInitParams.saleTokenAddress);\\n\\n        // record balance before transfer in 18 decimals\\n        uint256 balanceBefore18 = saleToken.balanceOf(address(this)).to18(_tierInitParams.saleTokenAddress);\\n\\n        // perform the transfer\\n        saleToken.safeTransferFrom(\\n            msg.sender,\\n            address(this),\\n            _tierInitParams.totalTokenProvided.from18Safe(_tierInitParams.saleTokenAddress)\\n        );\\n\\n        // record balance after the transfer in 18 decimals\\n        uint256 balanceAfter18 = saleToken.balanceOf(address(this)).to18(_tierInitParams.saleTokenAddress);\\n\\n        // verify that the transfer has actually occured to protect users from malicious\\n        // sale tokens that don't actually send the tokens for the token sale\\n        require(balanceAfter18 - balanceBefore18 == _tierInitParams.totalTokenProvided,\\n                "TSP: token sale proposal creation received incorrect amount of tokens"\\n        );\\n```\\n
A fake tier can be created without the proportionate amount of DAO Pool token balance in the `TokenSaleProposal` contract. Naive users can participate in such a token sale assuming their DAO token claims will be honoured at a future date. Since the pool has insufficient token balance, any attempts to claim the DAO pool tokens can lead to a permanent DOS.
```\\nfunction createTier(\\n        mapping(uint256 => ITokenSaleProposal.Tier) storage tiers,\\n        uint256 newTierId,\\n        ITokenSaleProposal.TierInitParams memory _tierInitParams\\n    ) external {\\n\\n       // rest of code.\\n         /// @dev return value is not checked intentionally\\n  >      tierInitParams.saleTokenAddress.call(\\n            abi.encodeWithSelector(\\n                IERC20.transferFrom.selector,\\n                msg.sender,\\n                address(this),\\n                totalTokenProvided\\n            )\\n        );  //@audit -> no check if the contract balance has increased proportional to the totalTokenProvided\\n   }\\n```\\n