Platform: Code4rena
Start Date: 28/11/2022
Pot Size: $192,500 USDC
Total HM: 33
Participants: 106
Period: 11 days
Judge: LSDan
Total Solo HM: 15
Id: 186
League: ETH
Rank: 16/106
Findings: 5
Award: $1,671.95
🌟 Selected for report: 1
🚀 Solo Findings: 0
🌟 Selected for report: hyh
Also found by: Jeiwan, brgltd, gzeon, minhquanym
592.7549 USDC - $592.75
When adding a asset to NFTFloorOracle, it set its index to assetFeederMap
for easier lookup.
assetFeederMap[_asset].index = uint8(assets.length - 1);
The problem is that the cast to uint8 will overflow silently and the wrong feeder will be removed when you try to remove asset.
function _removeAsset(address _asset) internal onlyWhenAssetExisted(_asset) { uint8 assetIndex = assetFeederMap[_asset].index; delete assets[assetIndex]; delete assetPriceMap[_asset]; delete assetFeederMap[_asset]; emit AssetRemoved(_asset); }
Check and limit the length of feeder array or use uint256 for FeederRegistrar.index
#0 - c4-judge
2022-12-20T16:34:11Z
dmvt marked the issue as duplicate of #209
#1 - c4-judge
2023-01-23T20:13:25Z
dmvt marked the issue as partial-50
951.3351 USDC - $951.34
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/marketplaces/LooksRareAdapter.sol#L73-L94 https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/marketplaces/SeaportAdapter.sol#L93-L107 https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/misc/marketplaces/X2Y2Adapter.sol#L88-L102
matchAskWithTakerBid is payable, it call functionCallWithValue
with the value parameter. There are no check to make sure
msg.value == value`, if excess value is sent user will not receive a refund.
function matchAskWithTakerBid( address marketplace, bytes calldata params, uint256 value ) external payable override returns (bytes memory) { bytes4 selector; if (value == 0) { selector = ILooksRareExchange.matchAskWithTakerBid.selector; } else { selector = ILooksRareExchange .matchAskWithTakerBidUsingETHAndWETH .selector; } bytes memory data = abi.encodePacked(selector, params); return Address.functionCallWithValue( marketplace, data, value, Errors.CALL_MARKETPLACE_FAILED ); }
Same code exists in LooksRareAdapter, SeaportAdapter and X2Y2Adapter
Check msg.value == value
if the function is only supposed to be delegate called into, consider add a check to prevent direct call.
#0 - c4-judge
2022-12-20T17:08:35Z
dmvt marked the issue as duplicate of #34
#1 - c4-judge
2023-01-09T15:29:08Z
dmvt marked the issue as selected for report
#2 - c4-judge
2023-01-23T20:46:51Z
dmvt marked the issue as satisfactory
#3 - C4-Staff
2023-02-01T19:09:57Z
captainmangoC4 marked the issue as selected for report
🌟 Selected for report: IllIllI
Also found by: 0x52, 0xNazgul, Franfran, IllIllI, Jeiwan, Lambda, RaymondFam, Rolezn, Trust, __141345__, codecustard, erictee, gzeon, hansfriese, imare, rbserver, rvierdiiev, seyni, skinz, ujamal_
18.3064 USDC - $18.31
Chainlink latestAnswer()
is deprecated. It might return stale data or incomplete round answer.
* - Use of Chainlink Aggregators as first source of price
price = uint256(source.latestAnswer());
Check for stale price and round completeness using latestRoundData()
#0 - JeffCX
2022-12-18T03:28:49Z
#1 - c4-judge
2022-12-20T17:44:43Z
dmvt marked the issue as duplicate of #5
#2 - c4-judge
2023-01-09T16:32:41Z
dmvt marked the issue as partial-50
#3 - c4-judge
2023-01-23T15:57:12Z
dmvt marked the issue as satisfactory
🌟 Selected for report: IllIllI
Also found by: 0x4non, 0x52, 0xAgro, 0xNazgul, 0xSmartContract, 0xackermann, 9svR6w, Awesome, Aymen0909, B2, BRONZEDISC, Bnke0x0, Deekshith99, Deivitto, Diana, Dravee, HE1M, Jeiwan, Kaiziron, KingNFT, Lambda, Mukund, PaludoX0, RaymondFam, Rolezn, Sathish9098, Secureverse, SmartSek, __141345__, ahmedov, ayeslick, brgltd, cccz, ch0bu, chrisdior4, cryptonue, cryptostellar5, csanuragjain, datapunk, delfin454000, erictee, gz627, gzeon, helios, i_got_hacked, ignacio, imare, jadezti, jayphbee, joestakey, kankodu, ksk2345, ladboy233, martin, nadin, nicobevi, oyc_109, pashov, pavankv, pedr02b2, pzeus, rbserver, ronnyx2017, rvierdiiev, shark, unforgiven, xiaoming90, yjrwkk
103.9175 USDC - $103.92
It does not make sense to allow LTV above 100%, although it is currently not possible as a consequence of other invariant, it is recommended to set MAX_VALID_LTV to PERCENTAGE_FACTOR.
uint256 internal constant MAX_VALID_LTV = 65535;
Currently, it is not possible to set ltv > PERCENTAGE_FACTOR because the following code make sure ltv <= liquidationThreshold <= PERCENTAGE_FACTOR
require(ltv <= liquidationThreshold, Errors.INVALID_RESERVE_PARAMS); DataTypes.ReserveConfigurationMap memory currentConfig = _pool .getConfiguration(asset); if (liquidationThreshold != 0) { //liquidation bonus must be bigger than 100.00%, otherwise the liquidator would receive less //collateral than needed to cover the debt require( liquidationBonus >= PercentageMath.PERCENTAGE_FACTOR, Errors.INVALID_RESERVE_PARAMS ); //if threshold * bonus is less than PERCENTAGE_FACTOR, it's guaranteed that at the moment //a loan is taken there is enough collateral available to cover the liquidation bonus require( liquidationThreshold.percentMul(liquidationBonus) <= PercentageMath.PERCENTAGE_FACTOR, Errors.INVALID_RESERVE_PARAMS );
ParaSpaceOracle revert if both the primary and fallback oracle return price == 0. This might have undesired consequence, for instance, if an asset price literally drop to 0, the protocol will not be able to liquidate the said asset.
require(price != 0, Errors.ORACLE_PRICE_NOT_READY);
The pool delegate call into the adapter logics to execute certain action, project admin / governance need to be very careful when approval new marketplace / adapter since it might brick the protocol if it modify storage slot / call selfdestruct.
Address.functionDelegateCall( params.marketplace.adapter,
For example, liquidationProtocolFee
and liquidationProtocolFeePercentage
are used interchangeably where it is unclear it represent the amount or %.
vars.liquidationProtocolFeePercentage = collateralReserve .configuration .getLiquidationProtocolFee();
string public constant CALLER_NOT_BRIDGE = "6"; // 'The caller of the function is not a bridge'
ReserveConfiguration used a bit masking appoach to pack storage. While no issue is found at the moment it is a risky design. Consider using a packed struct instead.
contracts/mocks/tokens/Moonbirds.sol:483:OpenSea's example code. TODO: it's likely that the above mapping can be changed contracts/misc/UniswapV3OracleWrapper.sol:238: // TODO using bit shifting for the 2^96 contracts/misc/marketplaces/LooksRareAdapter.sol:59: makerAsk.price, // TODO: take minPercentageToAsk into account contracts/ui/UiIncentiveDataProvider.sol:68: // TODO: check that this is deployed correctly on contract and remove casting contracts/protocol/libraries/logic/MarketplaceLogic.sol:442: // TODO: support PToken contracts/interfaces/INToken.sol:97: // TODO are we using the Treasury at all? Can we remove?
#0 - c4-judge
2023-01-25T16:21:45Z
dmvt marked the issue as grade-b