Platform: Code4rena
Start Date: 30/10/2021
Pot Size: $35,000 ETH
Total HM: 2
Participants: 16
Period: 3 days
Judge: alcueca
Total Solo HM: 1
Id: 48
League: ETH
Rank: 1/16
Findings: 4
Award: $10,594.91
π Selected for report: 4
π Solo Findings: 1
π Selected for report: WatchPug
Also found by: daejunpark, gpersoon, hickuphh3, kenzo, pmerkleplant
588.8441 USDC - $588.84
kenzo
When toToken == nativeToken
, executeTrades
compares Executioner's starting nativeToken balance to Executioner's ending wrappedNativeToken balance.
executeTrades
: if there is extra ETH in Executioner contract, finalOutputAmount
will be calculated to be smaller than it really is, thereby sending less tokens than deserved to the user, or failing the >= finalAmountMin
check and erroneously failing the trade.Slingshot saves the initialBalance as:
uint256 initialBalance = _getTokenBalance(toToken);
https://github.com/code-423n4/2021-10-slingshot/blob/main/contracts/Slingshot.sol#L81
If toToken == nativeToken
, initialBalance will be Executioner's nativeToken (ETH) balance.
https://github.com/code-423n4/2021-10-slingshot/blob/main/contracts/Slingshot.sol#L158:#L159
Then after the trades, if toToken == nativeToken
, Slingshot sends wrappedNativeToken
to _getTokenBalance
.
https://github.com/code-423n4/2021-10-slingshot/blob/main/contracts/Slingshot.sol#L87:#L88
Therefore, finalBalance will be Executioner's wrappedNativeToken balance (WETH), unlike initialBalance (ETH).
This inconsistency manifests the two scenarios mentioned in Impact above.
Loss of user funds or DOS of executeTrades
- if there is extra ETH in Executioner, uint finalOutputAmount = finalBalance - initialBalance
gives a wrong result and might send user less tokens than deserved or fail the finalOutputAmount >= finalAmountMin
check.
https://github.com/code-423n4/2021-10-slingshot/blob/main/contracts/Slingshot.sol#L92
Skimming of accidently-sent WETH: If a user sends a trade where toToken == nativeToken
, he will transfer to himself all the extra wrappedNativeToken
in Executioner as initialBalance
is the Executioner's nativeToken balance, not wrappedNativeToken.
Manual analysis
In initialBalance
, instead of passing toToken to _getTokenBalance:
https://github.com/code-423n4/2021-10-slingshot/blob/main/contracts/Slingshot.sol#L81
Align it with the same mechanism that is used to check finalBalance
:
https://github.com/code-423n4/2021-10-slingshot/blob/main/contracts/Slingshot.sol#L87:#L91
#0 - tommyz7
2021-11-03T14:40:49Z
Duplicate of #18, medium risk since no user funds are at risk because of finalAmountMin
will guard it.
π Selected for report: kenzo
5983.2756 USDC - $5,983.28
kenzo
Slingshot's executeTrades
checks that the trade result amount (to be sent to the user) is bigger than finalAmountMin, and after that sends the user the amount. But if the token charges fee on transfer, the final transfer to the user will decrease the amount the user is getting, maybe below finalAmountMin.
Slingshot requires finalOutputAmount >= finalAmountMin
before sending the funds to the user:
https://github.com/code-423n4/2021-10-slingshot/blob/main/contracts/Slingshot.sol#L93:#L98
So if the token charges fees on transfer, the user will get less tokens than finalOutputAmount
. The check of finalOutputAmount
against finalAmountMin
is premature.
Manual analysis
Save the user's (not Executioner's) toToken balance in the beginning of executeTrades
after _transferFromOrWrap(fromToken, _msgSender(), fromAmount)
, and also in the very end, after executioner.sendFunds(toToken, _msgSender(), finalOutputAmount)
has been called. The subtraction of user's initial balance from ending balance should be bigger than finalAmountMin
.
https://github.com/code-423n4/2021-10-slingshot/blob/main/contracts/Slingshot.sol#L65:#L99
#0 - tommyz7
2021-11-03T15:10:32Z
Slingshot concern is to execute the trade as promised and make sure we are sending to the user what has been promised in trade estimation. If the token adds additional taxation on transfer, it is on the user side and users understand and accept this. We have seen this play out on production for the previous version of the contracts and we decided not to make that check. It seems the most practical decision.
Personally, I think this is non-critical.
#1 - alcueca
2021-11-06T05:56:52Z
The severity for the issue is right. The sponsor should add documentation to the fact that some tokens might not conform to common expectations.
π Selected for report: kenzo
1994.4252 USDC - $1,994.43
kenzo
IUniswapModule has a comment:
for now, we only supporting .swapExactTokensForTokens()
However, the actual function being called is swapExactTokensForTokensSupportingFeeOnTransferTokens
.
It is unclear whether the comment makes the distinction between "swapETH" and "swapTokens" functions or "swapExactTokensForTokens" and "swapExactTokensForTokensSupportingFeeOnTransferTokens" functions.
It is unclear whether the usage of swapExactTokensForTokensSupportingFeeOnTransferTokens
was properly tested as the comment mentions another literal function name.
I suggest clarifying the comment.
#0 - tommyz7
2021-11-04T17:15:02Z
Outdated comment, however I don't see where's a risk here. In my opinion it's non-critical.
#1 - alcueca
2021-11-06T05:52:41Z
Issues with comments are severity 1
π Selected for report: kenzo
1994.4252 USDC - $1,994.43
kenzo
There's a comment in CurveModule:
the only unique logic in this contract calculates post-trade balance because Curve's innovative design choice of not returning an output amount.
https://github.com/code-423n4/2021-10-slingshot/blob/main/contracts/module/CurveModule.sol#L26:#L27
However, there seems to be no such calculation in the module.
https://github.com/code-423n4/2021-10-slingshot/blob/main/contracts/module/CurveModule.sol#L57:#L59
It is unclear whether the check should be there, what it's purpose is, and if something is wrong as the check is missing. I suggest clarifying the comment.
#0 - tommyz7
2021-11-04T17:14:38Z
Outdated comment, however I don't see where's a risk here. In my opinion it's non-critical.
#1 - alcueca
2021-11-06T05:52:08Z
Issues with comments and classified as severity 1.
kenzo
ConcatStrings prependNumber
is not used in the project.
Unnecessary gas spent on deployment.
https://github.com/code-423n4/2021-10-slingshot/blob/main/contracts/lib/ConcatStrings.sol#L31
Manual analysis, search in files
Remove function from library.
#0 - tommyz7
2021-11-04T18:20:36Z
Duplicate of #100