Hooks.wtf

Slab Machine

SlabMachine.sol

[HIGH] Missing Zero Address Validation in shutdown()

The shutdown() function does not validate that _tokenRecipient and _usdcRecipient are not zero addresses. This could lead to permanent loss of funds and NFTs.

If the owner accidentally passes a zero address, all funds and NFTs will be permanently locked.

function shutdown(address _tokenRecipient, address _usdcRecipient) public onlyOwner {
  require(_tokenRecipient != address(0), "Invalid token recipient");
  require(_usdcRecipient != address(0), "Invalid USDC recipient");
  
  // ... rest of function
}

[HIGH] Missing Zero Address Validation in withdrawUSDC()

The withdrawUSDC() function does not validate that _usdcRecipient is not a zero address. Additionally, there is no balance check before the transfer.

Funds could be sent to a zero address or the function could fail silently if there is insufficient balance.

function withdrawUSDC(address _usdcRecipient, uint _amount) public onlyOwner {
  require(_usdcRecipient != address(0), "Invalid recipient");
  require(usdc.balanceOf(address(this)) >= _amount, "Insufficient balance");

  bool success = usdc.transfer(_usdcRecipient, _amount);
  if (!success) {
    revert PaymentFailed();
  }
}

[HIGH] No Refund for Failed Pulls

When a pull fails due to no cards remaining for a specific rarity, the user pays but doesn't receive a refund. The payment is taken upfront in pull(), but if the rarity is out of stock when the callback executes, the user loses their payment.

// In pull() - payment is taken upfront
usdc.transferFrom(msg.sender, address(this), _amount * _machineConfig.usdcPullPrice);

// Later in randomNumberCallback() - if out of stock, just continue
if (remainingRarityTokens == 0) {
  emit SlabPullFailed(rarity, request.creator);
  continue; // No refund!
}

Users lose their USDC payment if the machine runs out of a specific rarity between when they pay and when the callback executes. This is particularly problematic for rare rarities that may be in high demand.

Implement a refund mechanism for failed pulls:

function randomNumberCallback(uint _requestId, uint[] memory _randomNumber) public override {
  // ... existing validation ...

  // If no cards are remaining for the rarity, then we need to exit to prevent the
  // response from reverting.
  if (remainingRarityTokens == 0) {
    // Refund the user for the pull
    bool success = usdc.transfer(request.creator, _machineConfig.usdcPullPrice);
    if (!success) {
      revert PaymentFailed();
    }

    // Emit an event notification to notify of the failure
    emit SlabPullFailed(rarity, request.creator);
    continue;
  }

  // ... existing logic ...
}

[MEDIUM] Missing Zero Address Validation in setRarityCalculator()

The setRarityCalculator() function does not validate that _rarityCalculator is not a zero address.

function setRarityCalculator(IRarityCalculator _rarityCalculator) public onlyOwner {
  rarityCalculator = _rarityCalculator;
}

If set to a zero address, all pull() operations will revert when calling _determineRarity().

function setRarityCalculator(IRarityCalculator _rarityCalculator) public onlyOwner {
  require(address(_rarityCalculator) != address(0), "Invalid rarity calculator");
  rarityCalculator = _rarityCalculator;
}

[MEDIUM] Missing Validation in setRandomNumberGenerator()

While setRandomNumberGenerator() validates the zero address, it doesn't check if there are pending requests. Changing the RNG while requests are pending could cause issues.

Pending requests might not be fulfillable if the old RNG contract is removed or changed.

Consider adding a check for pending requests, or at least emit a warning event.

Previous
Getting started