Platform: Code4rena
Start Date: 16/12/2022
Pot Size: $60,500 USDC
Total HM: 12
Participants: 58
Period: 5 days
Judge: Trust
Total Solo HM: 4
Id: 196
League: ETH
Rank: 17/58
Findings: 3
Award: $476.06
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 Selected for report: HollaDieWaldfee
399.1233 USDC - $399.12
PaprController#buyAndReduceDebt double charges msg.sender if msg.sender != account. This leads to either loss of funds or the inability to reduce debt for other users
(uint256 amountOut, uint256 amountIn) = UniswapHelpers.swap( pool, account, //@audit account is the recipient of the swap token0IsUnderlying, params.amount, params.minOut, params.sqrtPriceLimitX96, abi.encode(msg.sender) //@audit msg.sender is payer ); if (hasFee) { underlying.transfer(params.swapFeeTo, amountIn * params.swapFeeBips / BIPS_ONE); } //@audit papr is burned from msg.sender even though the papr was sent to account _reduceDebt({account: account, asset: collateralAsset, burnFrom: msg.sender, amount: amountOut});
UniswapHelpers.swap
sends the papr
obtained from the swap to account
. Later when reducing the debt of account it burns papr
from msg.sender
rather than account
. This has two outcomes when a user is paying off debt for another user. The transaction will either revert because msg.sender
doesn't have enough papr
to burn or msg.sender
will be double charged. First they will be charge underlying when swapping then they will be charged again when the papr
is burned from them in the _reduceDebt
subcall.
Manual Review
The swap output should be sent to msg.sender
rather than account
:
(uint256 amountOut, uint256 amountIn) = UniswapHelpers.swap( pool, - account, + msg.sender, token0IsUnderlying, params.amount, params.minOut, params.sqrtPriceLimitX96, abi.encode(msg.sender) );
#0 - trust1995
2022-12-25T13:17:59Z
Would like sponsor to weigh in, is this intended functionality of buyAndReduceDebt()?
#1 - c4-judge
2022-12-25T13:18:12Z
trust1995 marked the issue as primary issue
#2 - c4-judge
2022-12-25T13:18:27Z
trust1995 marked the issue as satisfactory
#3 - c4-judge
2022-12-25T16:20:00Z
trust1995 changed the severity to 2 (Med Risk)
#4 - c4-sponsor
2023-01-03T18:49:34Z
wilsoncusack marked the issue as sponsor confirmed
#5 - wilsoncusack
2023-01-03T18:50:30Z
This is intended functionality but agree would be better as is suggested.
#6 - C4-Staff
2023-01-10T22:34:28Z
JeeberC4 marked the issue as duplicate of #187
🌟 Selected for report: Jeiwan
Also found by: 0x52, Franfran, HollaDieWaldfee, KingNFT, Saintcode_, bin2chen, evan, fs0c, noot, poirots, rvierdiiev, stealthyz, teawaterwire, unforgiven
33.3998 USDC - $33.40
PaprController#buyAndReduceDebt will either revert or steal underlying from contract when the swap has a fee
if (hasFee) { underlying.transfer(params.swapFeeTo, amountIn * params.swapFeeBips / BIPS_ONE); }
If there is a swap fee for PaprController#buyAndReduceDebt it will attempt to transfer the fee from the contract. This leads to one of two issues:
Manual Review
PaprController already handles the underlying transfer for the swap via uniswapV3SwapCallback
which means the user has already approved PaprController to transfer underlying. Use safeTransferFrom instead:
if (hasFee) { - underlying.transfer(params.swapFeeTo, amountIn * params.swapFeeBips / BIPS_ONE); + underlying.safeTransferFrom(msg.sender, params.swapFeeTo, amountIn * params.swapFeeBips / BIPS_ONE); }
#0 - c4-judge
2022-12-25T13:19:02Z
trust1995 marked the issue as duplicate of #20
#1 - c4-judge
2022-12-25T13:19:10Z
trust1995 marked the issue as satisfactory
#2 - c4-judge
2022-12-25T17:29:41Z
trust1995 changed the severity to 2 (Med Risk)
#3 - C4-Staff
2023-01-10T22:32:21Z
JeeberC4 marked the issue as duplicate of #196
🌟 Selected for report: yixxas
Also found by: 0x52, 0xAgro, 0xSmartContract, 0xhacksmithh, Aymen0909, Bnke0x0, Bobface, Breeje, Diana, Franfran, HE1M, HollaDieWaldfee, IllIllI, Jeiwan, RaymondFam, Rolezn, SaharDevep, Secureverse, SmartSek, ak1, bin2chen, brgltd, chrisdior4, gz627, imare, ladboy233, lukris02, oyc_109, rvierdiiev, shark, tnevler, unforgiven, wait
43.5439 USDC - $43.54
Users are unable to withdraw there collateral without an oracle signature even if they have no debt
Under every circumstance in which a user removes collateral from the contract they must provide an oracle signed price for the NFT. This includes users with no debt. Also the oracle is immutable so it can't be replaced. In the event that the oracle become unrecoverable, all user collateral will become permanently stuck in the contract.
Manual Review
Bypass the oracle check if a user doesn't have any debt and is removing their collateral. Since the user carries no debt, there is no reason to check the price
#0 - trust1995
2022-12-25T13:29:51Z
Good recommendation but cannot award more than L as trust in system oracle is assumed.
#1 - c4-judge
2022-12-25T13:30:01Z
trust1995 changed the severity to QA (Quality Assurance)
#2 - c4-judge
2022-12-25T17:22:18Z
trust1995 marked the issue as grade-b