GMX Reward Router's claimForAccount() can be abused to incorrectly add WETH to tokensIn
When `claimFees()` is called, the Controller automatically adds WETH to the user's account. However, in the case where no fees have accrued yet, there will not be WETH withdrawn. In this case, the user will have WETH added as an asset in their account, while they won't actually have any WETH holdings.\\nWhen a user calls the GMX Reward Router's `claimFees()` function, the RewardRouterController confirms the validity of this call in the `canCallClaimFees()` function:\\n```\\nfunction canCallClaimFees()\\n    internal\\n    view\\n    returns (bool, address[] memory, address[] memory)\\n{\\n    return (true, WETH, new address[](0));\\n}\\n```\\n\\nThis function assumes that any user calling `claimFees()` will always receive `WETH`. However, this is only the case if their stake has been accruing.\\nImagine the following two actions are taken in the same block:\\nDeposit assets into GMX staking\\nCall claimFees()\\nThe result will be that `claimFees()` returns no `WETH`, but `WETH` is added to the account's asset list.\\nThe same is true if a user performs the following three actions:\\nCall claimFees()\\nWithdraw all ETH from the WETH contract\\nCall claimFees() again
The best way to solve this is actually not at the Controller level. It's to solve the issue of fake assets being added once and not have to worry about it on the Controller level in the future.\\nThis can be accomplished in `AccountManager.sol#_updateTokensIn()`. It should be updated to only add the token to the assets list if it has a positive balance, as follows:\\n```\\nfunction _updateTokensIn(address account, address[] memory tokensIn)\\n    internal\\n{\\n    uint tokensInLen = tokensIn.length;\\n    for(uint i; i < tokensInLen; // Add the line below\\n// Add the line below\\ni) {\\n// Remove the line below\\n        if (IAccount(account).hasAsset(tokensIn[i]) == false)\\n// Add the line below\\n        if (IAccount(account).hasAsset(tokensIn[i]) == false && IERC20(token).balanceOf(account) > 0)\\n            IAccount(account).addAsset(tokensIn[i]);\\n    }\\n}\\n```\\n\\nHowever, `_updateTokensIn()` is currently called before the function is executed in `exec()`, so that would need to be changed as well:\\n```\\nfunction exec(address account, address target, uint amt, bytes calldata data) external onlyOwner(account) {\\n    bool isAllowed;\\n    address[] memory tokensIn;\\n    address[] memory tokensOut;\\n    (isAllowed, tokensIn, tokensOut) = controller.canCall(target, (amt > 0), data);\\n    if (!isAllowed) revert Errors.FunctionCallRestricted();\\n// Remove the line below\\n    _updateTokensIn(account, tokensIn);\\n    (bool success,) = IAccount(account).exec(target, amt, data);\\n    if (!success)\\n        revert Errors.AccountInteractionFailure(account, target, amt, data);\\n// Add the line below\\n    _updateTokensIn(account, tokensIn);\\n    _updateTokensOut(account, tokensOut);\\n    if (!riskEngine.isAccountHealthy(account))\\n        revert Errors.RiskThresholdBreached();\\n}\\n```\\n\\nWhile this fix does require changing a core contract, it would negate the need to worry about edge cases causing incorrect accounting of tokens on any future integrations, which I think is a worthwhile trade off.\\nThis accuracy is especially important as Sentiment becomes better known and integrated into the Arbitrum ecosystem. While I know that having additional assets doesn't cause internal problems at present, it is hard to predict what issues inaccurate data will cause in the future. Seeing that Plutus is checking Sentiment contracts for their whitelist drove this point home — we need to ensure the data stays accurate, even in edge cases, or else there will be trickle down problems we can't currently predict.
A user can force their account into a state where it has `WETH` on the asset list, but doesn't actually hold any `WETH`.\\nThis specific Impact was judged as Medium for multiple issues in the previous contest:
```\\nfunction canCallClaimFees()\\n    internal\\n    view\\n    returns (bool, address[] memory, address[] memory)\\n{\\n    return (true, WETH, new address[](0));\\n}\\n```\\n