Platform: Code4rena
Start Date: 09/12/2022
Pot Size: $36,500 USDC
Total HM: 9
Participants: 69
Period: 3 days
Judge: Picodes
Total Solo HM: 2
Id: 190
League: ETH
Rank: 16/69
Findings: 2
Award: $524.62
π Selected for report: 1
π Solo Findings: 0
π Selected for report: trustindistrust
496.5001 USDC - $496.50
β
The TokenSender.send()
method is called during the course of users withdrawing or redeeming tokens from the protocol. The method is called via DepositHook.hook()
, RedeemHook.hook()
, and WithdrawHook.hook()
. These in turn are called in prePOMarket.redeem()
or Collateral.deposit()|.withdraw()
β
TokenSender.send()
contains some logic to return early without sending any of the "outputToken", such as if the price of the outputToken has fallen below an adjustable lower bound, or if the amount would be 0.
β
However, it also checks its own balance to see if it can cover the required amount. If it cannot, it simply doesn't send tokens. These tokens are intended to be a compensation for fees paid elsewhere in the process, and thus do represent a value loss.
β
function send(address recipient, uint256 unconvertedAmount) external override onlyAllowedMsgSenders { uint256 scaledPrice = (_price.get() * _priceMultiplier) / MULTIPLIER_DENOMINATOR; if (scaledPrice <= _scaledPriceLowerBound) return; uint256 outputAmount = (unconvertedAmount * _outputTokenDecimalsFactor) / scaledPrice; if (outputAmount == 0) return; if (outputAmount > _outputToken.balanceOf(address(this))) return; // don't send if not enough balance _outputToken.transfer(recipient, outputAmount); }
β
The documentation in ITokenSender.sol
states this is so the protocol doesn't halt the redeem and deposit/withdraw actions.
β
β The warden agrees that the protocol halting is generally undesirable. β However, there isn't any facility in the code for the user who triggered the overage amount to be able to later receive their tokens when the contract is topped up. They must rely upon governance to send them any owed tokens. This increases centralization risks and isn't necessary. β Since the contract makes no attempt to track the tokens that should have been sent, manually reviewing and verifying owed tokens becomes a non-trivial task if any more than a handful of users were affected. β Since the user did receive their underlying collateral in any case and the loss isn't necessarily permanent, medium seems to be the right severity for this issue. β β
β
Bob wants to redeem his long and short tokens via PrePOMarket.redeem()
. However, Alice's redemption prior to his significantly drained the TokenSender
contract of its tokens. As a result, Bob's redemption fails to benefit him in the amount of the outputToken he should have received in compensation for the fees paid.
Because the quantity of tokens paid to Bob is partially dependent upon the token's price at the time of redemption, the protocol might shoulder more downside loss (token price dropped compared to when Bob redeemed, must pay out more tokens) or Bob might suffer upside loss (price went up compared to time of redemption, Bob looses the difference). β Bob's recourse is to contact the project administrators and try to have his tokens sent to him manually. Agreeing to a value adds friction to the process. β
β
The TokenSender
contract should track users whose balance wasn't covered in a mapping, as well as a function for them to manually claim tokens later on if the contract's balance is topped up. Such a function might record the price at redemption time, or it might calculate it with the current price.
#0 - Picodes
2022-12-17T19:09:07Z
In my understanding, if the refunds are all used, they aren't owed anything and pay the regular fees. The design seems to be that there is a way to give temporary discounts or fee refunds but it is not mandatory
#1 - c4-judge
2022-12-17T21:07:26Z
Picodes marked the issue as primary issue
#2 - c4-sponsor
2022-12-19T23:20:40Z
ramenforbreakfast marked the issue as disagree with severity
#3 - ramenforbreakfast
2022-12-19T23:23:21Z
I believe this issue is a duplicate of #311, although this one is more fleshed out and would consider as the primary issue. I agree with lowering this to QA since there is no expectation of rewards, and the frontend would be able to reliably inform the user whether they would receive a rebate, nothing is being misrepresented on-chain. Token rewards are only a possible incentive, not an owed liability, to users.
#4 - c4-judge
2023-01-07T11:41:42Z
Picodes marked the issue as selected for report
#5 - Picodes
2023-01-07T11:45:04Z
The front-end cannot properly mitigate the possibility of front running and someone taking all the available rebates before a user transaction. Therefore, in my opinion, due to the lack of safety checks, medium severity is appropriate as a user could think he'd receive a rebate but won't receive it.
#6 - c4-judge
2023-01-07T11:47:57Z
Picodes marked the issue as satisfactory
π Selected for report: 0xSmartContract
Also found by: 0Kage, 0x52, 0xAgro, 0xNazgul, 0xTraub, 0xhacksmithh, Awesome, Aymen0909, Bnke0x0, Englave, Janio, Mukund, Parth, RaymondFam, Rolezn, SmartSek, Tointer, UdarTeam, Udsen, Zarf, caventa, chaduke, csanuragjain, deliriusz, gz627, idkwhatimdoing, izhelyazkov, joestakey, neumo, obront, oyc_109, rvierdiiev, shark, trustindistrust, wait, yongskiws
28.124 USDC - $28.12
https://github.com/prepo-io/prepo-monorepo/blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/Collateral.sol#L85 https://github.com/prepo-io/prepo-monorepo/blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/Collateral.sol#L102-L115 https://github.com/prepo-io/prepo-monorepo/blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/DepositHook.sol#L69-L71 https://github.com/prepo-io/prepo-monorepo/blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/DepositHook.sol#L77-L79 https://github.com/prepo-io/prepo-monorepo/blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/ManagerWithdrawHook.sol#L24 https://github.com/prepo-io/prepo-monorepo/blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/PrePOMarket.sol#L109-L117 https://github.com/prepo-io/prepo-monorepo/blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/RedeemHook.sol#L26 https://github.com/prepo-io/prepo-monorepo/blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/RedeemHook.sol#L30-L32 https://github.com/prepo-io/prepo-monorepo/blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/TokenSender.sol#L60 https://github.com/prepo-io/prepo-monorepo/blob/3541bc704ab185a969f300e96e2f744a572a3640/apps/smart-contracts/core/contracts/WithdrawHook.sol#L116-L122
The PrePO protocol contracts under review for this contest contain many functions with role-based access controls.
These roles can make potentially dangerous changes to the functioning of the protocol. The provided documentation did not lay out what types of accounts these role holders were, how they were secured, nor how much or little overlap there were between role holders.
The following is a list of particularly dangerous functions. Other role-based functions could disrupt the protocol but not cause major loss of value, already contain safeguards for the values they control, or require more than one role to be breached to be useful.
hook()
to be called and to drain tokenssend()
to be called and to drain tokensFor the set of more dangerous functions above, a process where more than one role (main role and a generic administrator) must sign off on such changes would be best. For example, one role adds the requested contract to a registry, and that registry is checked before the second role can change the contract address.
Assuming a sufficient amount of non-overlap in role holders, subverting more than one EOA to circumvent this process is much more difficult.
#0 - Picodes
2022-12-17T18:28:34Z
The code works as intended for all these functions, and you're not describing an escalation privilege. This falls within the safety check category, so downgrading to QA.
#1 - c4-judge
2022-12-17T18:28:48Z
Picodes changed the severity to QA (Quality Assurance)
#2 - c4-judge
2022-12-19T13:52:29Z
Picodes marked the issue as grade-b
#3 - c4-sponsor
2022-12-22T10:46:34Z
davidprepo marked the issue as sponsor disputed
#4 - ramenforbreakfast
2022-12-22T19:11:08Z
Disputing since this report just writes extremely brief descriptions of what someone holding a role can do, does not illustrate an example of unintended privilege escalation or bypass.
#5 - Picodes
2023-01-07T18:13:05Z
See https://github.com/code-423n4/2022-12-prepo-findings/issues/334#issuecomment-1374552267 for why I'll keep grade-b for this report