Duplicate fees will be paid by `LibTransfer::transferFee` when transferring fee-on-transfer tokens with `EXTERNAL_INTERNAL` 'from' mode and `EXTERNAL` 'to' mode
Beanstalk utilizes an internal virtual balance system that significantly reduces transaction fees when using tokens that are intended to remain within the protocol. `LibTransfer` achieves this by managing every transfer between accounts, considering both the origin 'from' and destination 'to' modes of the in-flight funds. As a result, there are four types of transfers based on the source of the funds (from mode):\\nEXTERNAL: The sender will not use their internal balances for the operation.\\nINTERNAL: The sender will use their internal balances for the operation.\\nEXTERNAL_INTERNAL: The sender will attempt to utilize their internal balance to transfer all desired funds. If funds remain to be sent, their externally owned funds will be utilized to cover the difference.\\nINTERNAL_TOLERANT: The sender will utilize their internal balances for the operation. With insufficient internal balance, the operation will continue (without reverting) with this reduced amount. It is, therefore, imperative to always check the return value of LibTransfer functions to continue the execution of calling functions with the true utilized amount, especially in this internal tolerant case.\\nThe current implementation of `LibTransfer::transferToken` for `(from mode: EXTERNAL ; to mode: EXTERNAL)` ensures a safe transfer operation from the sender to the recipient:\\n```\\n// LibTransfer::transferToken\\nif (fromMode == From.EXTERNAL && toMode == To.EXTERNAL) {\\n    uint256 beforeBalance = token.balanceOf(recipient);\\n    token.safeTransferFrom(sender, recipient, amount);\\n    return token.balanceOf(recipient).sub(beforeBalance);\\n}\\namount = receiveToken(token, amount, sender, fromMode);\\nsendToken(token, amount, recipient, toMode);\\nreturn amount;\\n```\\n\\nPerforming this operation allows duplication of fee-on-transfer token fees to be avoided if funds are first transferred to the contract and then to the recipient; however, `LibTransfer::transferToken` balance will incur double the fee if this function is used for `(from mode: EXTERNAL_INTERNAL ; to mode: EXTERNAL)` when the internal balance is insufficient cover the full transfer amount, given that:\\nThe remaining token balance would first be transferred to the Beanstalk Diamond, incurring fees.\\nThe remaining token balance would then be transferred to the recipient, incurring fees again.
Add an internal function `LibTransfer::handleFromExternalInternalToExternalTransfer` to handle this case to avoid duplication of fees. For instance:\\n```\\nfunction handleFromExternalInternalToExternalTransfer(\\n    IERC20 token,\\n    address sender,\\n    address recipient,\\n    address amount\\n) internal {\\n    uint256 amountFromInternal = LibBalance.decreaseInternalBalance(\\n        sender,\\n        token,\\n        amount,\\n        true // allowPartial to avoid revert\\n    );\\n    uint256 pendingAmount = amount - amountFromInternal;\\n    if (pendingAmount != 0) {\\n        token.safeTransferFrom(sender, recipient, pendingAmount);\\n    }\\n    token.safeTransfer(sender, amountFromInternal);\\n}\\n```\\n\\nThen consider the use of this new function in LibTransfer::transferToken:\\n```\\n    function transferToken(\\n        IERC20 token,\\n        address sender,\\n        address recipient,\\n        uint256 amount,\\n        From fromMode,\\n        To toMode\\n    ) internal returns (uint256 transferredAmount) {\\n// Remove the line below\\n       if (fromMode == From.EXTERNAL && toMode == To.EXTERNAL) {\\n// Add the line below\\n       if (toMode == To.EXTERNAL) {\\n// Add the line below\\n           if (fromMode == From.EXTERNAL) {\\n                uint256 beforeBalance = token.balanceOf(recipient);\\n                token.safeTransferFrom(sender, recipient, amount);\\n                return token.balanceOf(recipient).sub(beforeBalance);\\n// Add the line below\\n           } else if (fromMode == From.EXTERNAL_INTERNAL) {\\n// Add the line below\\n               handleFromExternalInternalToExternalTransfer(token, sender, recipient, amount);\\n// Add the line below\\n               return amount;\\n// Add the line below\\n           }\\n        }\\n        amount = receiveToken(token, amount, sender, fromMode);\\n        sendToken(token, amount, recipient, toMode);\\n        return amount;\\n    }\\n```\\n
`LibTransfer::transferToken` will incur duplicate fees if this function is used for `(from mode: EXTERNAL_INTERNAL ; to mode: EXTERNAL)` with fee-on-transfer tokens if the internal balance is not sufficient to cover the full transfer amount.\\nEven though Beanstalk currently does not impose any fees on token transfers, USDT is associated with the protocol, and its contract has already introduced logic to implement a fee on token transfer mechanism if ever desired in the future. Considering that the duplication of fees implies a loss of funds, but also taking into account the low likelihood of this issue occurring, the severity assigned to this issue is MEDIUM.
```\\n// LibTransfer::transferToken\\nif (fromMode == From.EXTERNAL && toMode == To.EXTERNAL) {\\n    uint256 beforeBalance = token.balanceOf(recipient);\\n    token.safeTransferFrom(sender, recipient, amount);\\n    return token.balanceOf(recipient).sub(beforeBalance);\\n}\\namount = receiveToken(token, amount, sender, fromMode);\\nsendToken(token, amount, recipient, toMode);\\nreturn amount;\\n```\\n