Eggstravaganza

First Flight #37
Beginner FriendlySolidity
100 EXP
View results
Submission Details
Severity: medium
Valid

Changing NFT Contract Address After Deposits

Description

The EggVault contract allows the owner (assumed to be a trusted admin or game operator) to call setEggNFT and arbitrarily update the reference to the NFT contract, even after players have deposited their eggs.

This creates a critical integrity risk: the vault's storedEggs and eggDepositors mappings are keyed off token IDs, but these IDs are meaningless without a fixed NFT contract address. If the vault later points to a different NFT contract:

  • The vault's stored tokenIds no longer represent the same assets.

  • Previously deposited NFTs remain locked in the original contract but eggNFT.transferFrom() will act on a different token registry.

  • Withdrawals may fail or send unrelated NFTs to the wrong users.

This behavior undermines the assumptions players make when interacting with the vault, especially if they expect their deposited eggs to remain secure and retrievable.

Impact

Medium

  • This is not necessarily exploitable for theft if the vault owner is trusted, but it enables bricking or invalidating user deposits.

  • In a live game, it can lead to loss of access, broken game mechanics, or potential user distrust.

  • Even well-intentioned updates (e.g., contract upgrades, bug fixes) can result in asset loss if not handled carefully.

In the context of EggHunt, this could disrupt in-game rewards, scoring systems, or vault-based mechanics tied to event progression.

The impact is mitigated somewhat if:

  • The owner is a multisig or DAO with strong governance,

  • All users understand the vault may change behavior mid-game.

However, it still breaks the immutability and trust guarantees expected in smart contract systems.

Proof of Concept (PoC)

Scenario:

  1. A player deposits Egg #1 (from NFT contract A) into the vault.

  2. The vault stores eggDepositors[1] = player, and marks the egg as deposited.

  3. Mid-game, the owner calls setEggNFT(address(B)), switching to a new EggstravaganzaNFT contract.

  4. When the player tries to withdraw Egg #1, the vault calls transferFrom(address(this), player, 1) on contract B — which has no knowledge of the original egg.

    • If tokenId 1 exists in contract B, the wrong token may be transferred.

    • If not, the withdrawal may fail permanently.

  5. The actual egg remains locked in contract A forever.

Recommended Mitigation

Option 1: Make the NFT address immutable after initialization

function setEggNFT(address _eggNFTAddress) external onlyOwner {
require(address(eggNFT) == address(0), "NFT address already set");
require(_eggNFTAddress != address(0), "Invalid NFT address");
eggNFT = EggstravaganzaNFT(_eggNFTAddress);
}

This ensures the NFT contract can only be set once, aligning with the assumption that the game operates on a single NFT registry per deployment.

Option 2: Add explicit constraints

  • Prevent changes to the NFT address if any eggs are currently deposited.

function setEggNFT(address _eggNFTAddress) external onlyOwner {
require(_eggNFTAddress != address(0), "Invalid NFT address");
require(noActiveDeposits(), "Cannot change NFT contract with active deposits");
eggNFT = EggstravaganzaNFT(_eggNFTAddress);
}
function noActiveDeposits() internal view returns (bool) {
// Implement logic to check if storedEggs mapping is empty or all false
}

Option 3: Use constructor-only assignment If the contract architecture allows, simply set the eggNFT address in the constructor and remove setEggNFT entirely.

Additional Notes

  • Trusting the owner to not abuse a function does not mean the function should be allowed if it creates unrecoverable failure modes.

  • Even accidental misuse or poor upgrade practices can trigger this issue.

  • In competitive or adversarial games, the ability to brick eggs (intentionally or by mistake) is a valid concern.

Updates

Lead Judging Commences

m3dython Lead Judge 3 months ago
Submission Judgement Published
Validated
Assigned finding tags:

State corruption

Changing the NFT contract address doesn't update the storedEggs and eggDepositors mappings

Support

FAQs

Can't find an answer? Chat with us on Discord, Twitter or Linkedin.