JPEG'd contest - Foundation's results

Bridging the gap between DeFi and NFTs.

General Information

Platform: Code4rena

Start Date: 07/04/2022

Pot Size: $100,000 USDC

Total HM: 20

Participants: 62

Period: 7 days

Judge: LSDan

Total Solo HM: 11

Id: 107

League: ETH

JPEG'd

Findings Distribution

Researcher Performance

Rank: 18/62

Findings: 3

Award: $720.05

🌟 Selected for report: 0

🚀 Solo Findings: 0

Findings Information

🌟 Selected for report: hickuphh3

Also found by: 0x1f8b, AuditsAreUS, Foundation, Kthere, Meta0xNull, WatchPug, rayn

Labels

bug
duplicate
3 (High Risk)

Awards

471.3531 USDC - $471.35

External Links

Lines of code

https://github.com/code-423n4/2022-04-jpegd/blob/main/contracts/vaults/NFTVault.sol#L375 https://github.com/code-423n4/2022-04-jpegd/blob/main/contracts/lock/JPEGLock.sol#L59

Vulnerability details

Impact

If a user calls NFTVault.finalizePendingNFTValueETH a second time without first calling JPEGLock.unlock to recover their previous lockup, their balance will be overwritten leaving the previous lockup balance unrecoverable.

Proof of Concept

POC by adding the following to NFTValue.ts line 640:

    // Repeat the previous few lines to set up another value change
    await nftVault
      .connect(dao)
      .setPendingNFTValueETH(index, units(50000));
    await jpeg.transfer(user.address, units(12000000));
    await jpeg.connect(user).approve(locker.address, units(12000000));
    await nftVault.connect(user).finalizePendingNFTValueETH(index);

    // Locker is holding both deposits
    expect(await jpeg.balanceOf(locker.address)).to.equal(units(12000000 * 2));

    // But only one is attributed to a user
    expect((await locker.positions(index)).lockAmount).to.eq(units(12000000))

Tools Used

Manual review and modifying existing tests.

JPEGLock's lockFor could require positions[_nftIndex].lockAmount == 0 to avoid this issue. If the scenario is desirable (allowing the same user to finalize a value change for the same NFT) then maybe you could find a reasonable way to merge them or otherwise account for the original value locked; such as only calling jpeg.safeTransferFrom for the delta amount (possibly refunding a surplus).

#0 - spaghettieth

2022-04-13T19:00:08Z

Duplicate of #10

Awards

152.5804 USDC - $152.58

Labels

bug
QA (Quality Assurance)
sponsor acknowledged

External Links

  • FlashEscrow constructor: Use OpenZeppelin Address to bubble up the underlying revert reason if found instead of just emitting a generic FlashEscrow: call_failed.
  • LPFarming: newEpoch, add, and set should emit events. This would make it easier for frontends integrating (or subgraphs) to monitor and message significant changes like these.

Awards

96.1225 USDC - $96.12

Labels

bug
G (Gas Optimization)
sponsor acknowledged

External Links

  • JPEGLock: pack owner + unlockAt saving ~21k gas. Could use up to 96 bits and still pack into 1 slot, 96 bits is way more than you need for a date.
  • JPEGStaking: This contract upgradeable but the JPEG token is not. It's not common, but with an upgradeable contract you can use immutable variables to save gas. Using a constructor and switching to IERC20Upgradeable public immutable jpeg saves ~2.1k per stake/unstake call since there is no SLOAD required for immutable variables (they have the gas efficiency of a constant, except for the constructor cost itself).
  • CryptoPunksHelper / EtherRocksHelper: These are the only use cases for NFTEscrow, suggesting that the nftAddress could be made immutable (similar to the point above), saving ~2.1k per call.
  • NFTEscrow: Separate getSalt from precompute to avoid unnecessary calculations anytime _executeTransfer is called.
  • JPEGStaking: line 46 _amount <= balanceOf(msg.sender) is redundant, the internal _burn call will validate that as well. Saving ~500 gas.
  • Consistency / small gas savings: Cache loop length consistently. e.g. in LPFarming, _massUpdatePools caches but claimAll does not.
AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter