Hi guys!
I have a question with __gap in upgradeable contracts.
I have an NFT that inherits Openzeppelin's upgradeable contracts.
It has the following logic:
 Code to reproduce
 Code to reproduce
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract LandV1 is
    Initializable,
    ContextUpgradeable,
    AccessControlEnumerableUpgradeable,
    ERC721EnumerableUpgradeable,
    ERC721BurnableUpgradeable,
    ERC721PausableUpgradeable
{
    using CountersUpgradeable for CountersUpgradeable.Counter;
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    CountersUpgradeable.Counter private _tokenIdTracker;
    string private _baseTokenURI;
    uint256 public version;
    mapping(uint256 => uint256) _tokenIdToVersion;
    struct InfoV1 {
        uint256 version;
        uint256 x;
        uint256 y;
        bool positive;
    }
    // tokenId => InfoV1
    mapping(uint256 => InfoV1) _infoV1;
    function initialize(
        string memory name,
        string memory symbol,
        string memory baseTokenURI
    ) public virtual initializer {
        __Land_init(name, symbol, baseTokenURI);
    }
    function __Land_init(
        string memory name,
        string memory symbol,
        string memory baseTokenURI
    ) internal initializer {
        __Context_init_unchained();
        __ERC165_init_unchained();
        __AccessControl_init_unchained();
        __AccessControlEnumerable_init_unchained();
        __ERC721_init_unchained(name, symbol);
        __ERC721Enumerable_init_unchained();
        __ERC721Burnable_init_unchained();
        __Pausable_init_unchained();
        __ERC721Pausable_init_unchained();
        _baseTokenURI = baseTokenURI;
        version = 1;
        _setupRole(DEFAULT_ADMIN_ROLE, _msgSender());
        _setupRole(MINTER_ROLE, _msgSender());
        _setupRole(PAUSER_ROLE, _msgSender());
    }
function mint(
        address to,
        uint256 x,
        uint256 y,
        bool positive
    ) public virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "Land: must have minter role to mint");
        _infoV1[_tokenIdTracker.current()] = InfoV1(version, x, y, positive);
        _tokenIdToVersion[_tokenIdTracker.current()] = version;
        _mint(to, _tokenIdTracker.current());
        _tokenIdTracker.increment();
    }
    // other functions
    //...
    
    uint256[48] private __gap;
}
The first thing I want to ask is what do the "private __gap" variables in contracts mean? And why do they have different sizes in contracts, such as uint256[44], uint256[46], uint256[48],...?
Next, after deploying LandV1 and working for a while, I want to add an attribute to the info of the NFT, which is the landType property, I have kept the variables of LandV1 and added the variables as follows:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract LandV2 is
    Initializable,
    ContextUpgradeable,
    AccessControlEnumerableUpgradeable,
    ERC721EnumerableUpgradeable,
    ERC721BurnableUpgradeable,
    ERC721PausableUpgradeable
{
    using CountersUpgradeable for CountersUpgradeable.Counter;
    // ------------------------------ Common storage for all version -----------------------
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    CountersUpgradeable.Counter private _tokenIdTracker;
    string private _baseTokenURI;
    uint256 public version;
    mapping(uint256 => uint256) _tokenIdToVersion;
    struct InfoV1 {
        uint256 version;
        uint256 x;
        uint256 y;
        bool positive;
    }
    // tokenId => InfoV1
    mapping(uint256 => InfoV1) _infoV1;
    //------------------------- V2 -------------------------
    enum LandType {
        AGRICULTUAL,
        CONSTRUCTION
    }
    struct InfoV2 {
        uint256 version;
        uint256 x;
        uint256 y;
        bool positive;
        LandType landType;
    }
    // tokenId => InfoV1
    mapping(uint256 => InfoV2) _infoV2;
    function upgrade() public {
        require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Land: must have admin role to upgrade");
        version = version + 1;
    }
    function mint(
        address to,
        uint256 x,
        uint256 y,
        bool positive,
        LandType landType
    ) public virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "Land: must have minter role to mint");
        _infoV2[_tokenIdTracker.current()] = InfoV2(version, x, y, positive, landType);
        _tokenIdToVersion[_tokenIdTracker.current()] = version;
        _mint(to, _tokenIdTracker.current());
        _tokenIdTracker.increment();
    }
    // other functions
    //... 
    uint256[48] private __gap;
}
I want to ask if I set the variables above "__gap" like that right or should it be like this:
// SPDX-License-Identifier: GPL-3.0
pragma solidity ^0.8.0;
import "@openzeppelin/contracts-upgradeable/token/ERC721/ERC721Upgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721EnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721BurnableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/token/ERC721/extensions/ERC721PausableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/access/AccessControlEnumerableUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/utils/CountersUpgradeable.sol";
import "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol";
contract LandV2 is
    Initializable,
    ContextUpgradeable,
    AccessControlEnumerableUpgradeable,
    ERC721EnumerableUpgradeable,
    ERC721BurnableUpgradeable,
    ERC721PausableUpgradeable
{
    using CountersUpgradeable for CountersUpgradeable.Counter;
    // ------------------------------ Common storage for all version -----------------------
    bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
    bytes32 public constant PAUSER_ROLE = keccak256("PAUSER_ROLE");
    CountersUpgradeable.Counter private _tokenIdTracker;
    string private _baseTokenURI;
    uint256 public version;
    mapping(uint256 => uint256) _tokenIdToVersion;
    struct InfoV1 {
        uint256 version;
        uint256 x;
        uint256 y;
        bool positive;
    }
    // tokenId => InfoV1
    mapping(uint256 => InfoV1) _infoV1;
    uint256[48] private __gap;
    //------------------------- V2 -------------------------
    enum LandType {
        AGRICULTUAL,
        CONSTRUCTION
    }
    struct InfoV2 {
        uint256 version;
        uint256 x;
        uint256 y;
        bool positive;
        LandType landType;
    }
    // tokenId => InfoV1
    mapping(uint256 => InfoV2) _infoV2;
    function upgrade() public {
        require(hasRole(DEFAULT_ADMIN_ROLE, _msgSender()), "Land: must have admin role to upgrade");
        version = version + 1;
    }
    function mint(
        address to,
        uint256 x,
        uint256 y,
        bool positive,
        LandType landType
    ) public virtual {
        require(hasRole(MINTER_ROLE, _msgSender()), "Land: must have minter role to mint");
        _infoV2[_tokenIdTracker.current()] = InfoV2(version, x, y, positive, landType);
        _tokenIdToVersion[_tokenIdTracker.current()] = version;
        _mint(to, _tokenIdTracker.current());
        _tokenIdTracker.increment();
    }
    // other functions
    //... 
    uint256[48] private __gap; // Does this place need to be declared in case there is a LandV3?
 Environment
 Environment
type or paste code here