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: 24/58
Findings: 1
Award: $353.85
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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
353.8487 USDC - $353.85
Solmate won't check for contract existence. If a contract used as underlying ever gets deleted, calling PaprController.uniswapV3SwapCallback()
will result a silent failure.
Add a check for contract existence prior to executing the transfer, e.g. address(underlying).code.length > 0
.
If the signature for L68-L86 doesn't match, signerAddress
will be address(0). And if oracleSigner
is initialized with address(0), the validation on L88-L90 will pass.
The return of ecrecoever
should include a address(0) check, e.g.
if (signerAddress != address(0) && signerAddress != oracleSigner) { revert IncorrectOracleSigner(); }
For PaprController.buyAndReduceDebt()
, the external call underling.transfer()
is executed before state updates are made into _reduceDebt()
.
Follow the checks-effects-interactions and update state variables prior to external calls.
Adding an event into PaprController.setLiquidationLocked()
will facilitate offchain monitoring and indexing.
Downcasting block.timestamp to uint40 or uint48 will not be an issue for a long time. However, the project could use a bigger value (or use the safecast function) to improve the resilience of the protocol - since safecast is not being used, when the time comes, state variables like _lastUpdate
and info.lastestAuctionStartTime
will overflow. See this item marked as a finding for a previous code4rena QA report.
safeTransferFrom
can be replace with safeTransfer
papr.safeTransferFrom(address(this), to, amount)
can be replaced with papr.safeTransfer(to, amount)
Consider renaming underlying
on L73. It's being shadowed by UniswapOracleFundingRateController.underlying
.
Consider adding natspec on all external functions to improve documentation.
The following instance
uint160 initSqrtRatio; // initialize the pool at 1:1 if (token0IsUnderlying) { initSqrtRatio = UniswapHelpers.oneToOneSqrtRatio(underlyingONE, 10 ** 18); } else { initSqrtRatio = UniswapHelpers.oneToOneSqrtRatio(10 ** 18, underlyingONE); }
Can be replaced with
// initialize the pool at 1:1 uint160 initSqrtRatio = token0IsUnderlying ? UniswapHelpers.oneToOneSqrtRatio(underlyingONE, 10 ** 18) : UniswapHelpers.oneToOneSqrtRatio(10 ** 18, underlyingONE);
The protocol is mostly using custom errors. Consider refactoring old revert and require statements to use custom errors consistently throughout the whole codebase.
type(uint200).max
Some functions return named variables, others return explicit values.
Following function returns an explicit value.
Following function return returns a named variable.
Consider adopting the same approach throughout the codebase to improve the explicitness and readability of the code.
The solidity documentation recommends the following order for functions:
constructor receive function (if exists) fallback function (if exists) external public internal private
The instances bellow highlight public above external.
Statements used to validate input parameters should be executed before the function internal logic.
For PaprController.startLiquidationAuction()
, the validation on L311-L313 can be move to the top of the function, above L306-L308 and bellow L302-304.
Using scientific notation for large multiples of ten will improve code readability.
#0 - c4-judge
2022-12-25T14:59:23Z
trust1995 marked the issue as grade-a