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: 87/106
Findings: 1
Award: $103.92
🌟 Selected for report: 0
🚀 Solo Findings: 0
🌟 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
In the fallback of ParaProxy
, it is better to check the length of msg.data
to be >= 4 bytes
. For example, if a user sends 0xaabbcc
, the msg.sig
will be automatically changed to 0xaabbcc00
. And if this selector is available, the corresponding facet will be delegate-called. By having the data length check in fallback function, the callers will be noticed that your contract is used incorrectly.
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/libraries/paraspace-upgradeability/ParaProxy.sol#L41
When an NFT is used as collateral and the borrower's account health factor goes below 1, the NFT will be auctioned. If the borrower pays back the borrowed amount until the health factor goes above auctionRecoveryHealthFactor
(which is 1.5), the auction will be ended by calling endAuction
.
https://github.com/code-423n4/2022-11-paraspace/blob/c6820a279c64a299a783955749fdc977de8f0449/paraspace-core/contracts/protocol/pool/PoolCore.sol#L512
During ending the auction, the mapping erc721Data.auctions[tokenId]
will be deleted.
function executeEndAuction( MintableERC721Data storage erc721Data, IPool POOL, uint256 tokenId ) external { require( isAuctioned(erc721Data, POOL, tokenId), Errors.AUCTION_NOT_STARTED ); require( _exists(erc721Data, tokenId), "ERC721: endAuction for nonexistent token" ); delete erc721Data.auctions[tokenId]; }
So, the function isAuctioned
will return false:
function isAuctioned( MintableERC721Data storage erc721Data, IPool POOL, uint256 tokenId ) public view returns (bool) { return erc721Data.auctions[tokenId].startTime > POOL .getUserConfiguration(erc721Data.owners[tokenId]) .auctionValidityTime; }
There is another function that can end an auction without deleting the mapping, just by setting the validity time to the current timestamp.
function setAuctionValidityTime(address user) external virtual override nonReentrant { DataTypes.PoolStorage storage ps = poolStorage(); require(user != address(0), Errors.ZERO_ADDRESS_NOT_VALID); DataTypes.UserConfigurationMap storage userConfig = ps._usersConfig[ user ]; (, , , , , , uint256 erc721HealthFactor) = PoolLogic .executeGetUserAccountData( user, ps, ADDRESSES_PROVIDER.getPriceOracle() ); require( erc721HealthFactor > ps._auctionRecoveryHealthFactor, Errors.ERC721_HEALTH_FACTOR_NOT_ABOVE_THRESHOLD ); userConfig.auctionValidityTime = block.timestamp; }
This function says that any auction before the validity time, is not valid anymore. But, it does not delete the mentioned mapping.
So, there will be a situation that before calling the function endAuction
, someone calls the function setAuctionValidityTime
. So, the first function call endAuction
will be reverted. So, the auction start time will be smaller than validity time (results in returning false when the function isAuctioned
is called), but the mapping is not deleted.
It is better to delete the mapping erc721Data.auctions[tokenId]
in the function setAuctionValidityTime
if its start time is >0
.
#0 - c4-judge
2023-01-25T12:29:27Z
dmvt marked the issue as grade-b