We integrate NFT mechanics for games using a hybrid on-chain/off-chain architecture that cuts gas costs by 90% compared to full on-chain logging. For example, an MMO-RPG with 10,000 active users each performing 500 actions per hour would require 5 million transactions per hour — gas costs could exceed $200,000 monthly. Our hybrid approach reduces that to under $20,000. With over 50 implementations in games with their own tokenomics, our engineers audit contracts and optimize logic. We guarantee a free project evaluation and detailed audit report — contact us for a consultation on your game's architecture.
On-chain vs off-chain: what to store where
On-chain is verifiable and permanent. In the contract, we store:
- Ownership via ERC-721
- Core stats — level, class, rarity (affecting trade value)
- Earned traits — achievements confirmed by settlement
- Resource balances — accumulated resources
Off-chain (game server or L3) handles:
- Real-time positions and movements
- Combat calculations and temporary effects
- Event queues and intermediate results
- Current HP/MP
| Feature | On-chain | Off-chain |
|---|---|---|
| Verification | Full (anyone can verify) | None (trust in server) |
| Gas cost | High (per transaction) | Zero |
| Speed | ~12 seconds (Ethereum) | Instant |
| Example data | Ownership, final stats | Intermediate game events |
Periodically (daily settlement or on significant events), aggregated results are recorded on-chain. This hybrid architecture is 5x cheaper than full on-chain logging for high-frequency games.
How on-chain settlement works
Full on-chain logging of every click leads to costs comparable to contract deployment. Settlement consolidates thousands of events into one transaction, recording only the final stat changes. For instance, a character may complete 1000 PvE battles in a day, but only the final level and acquired items are written to the blockchain. This saves >99% gas for active players.
How to implement dynamic NFTs?
A dynamic NFT is an ERC-721 whose metadata changes based on in-game events. We use the ERC-4906 standard to notify marketplaces of updates without re-minting the token. Example contract for a character with on-chain stats:
contract GameCharacter is ERC721, AccessControl {
bytes32 public constant GAME_SERVER_ROLE = keccak256("GAME_SERVER_ROLE");
struct CharacterStats {
uint16 level;
uint32 experience;
uint8 strength;
uint8 agility;
uint8 intelligence;
uint64 lastSettled;
}
mapping(uint256 => CharacterStats) public stats;
mapping(uint256 => uint256) public achievementFlags;
function settleExperience(
uint256 tokenId,
uint32 expGained,
uint256 newAchievements
) external onlyRole(GAME_SERVER_ROLE) {
CharacterStats storage char = stats[tokenId];
char.experience += expGained;
while (char.experience >= expForNextLevel(char.level)) {
char.experience -= expForNextLevel(char.level);
char.level++;
_applyLevelUpBonus(tokenId, char.level);
}
achievementFlags[tokenId] |= newAchievements;
char.lastSettled = uint64(block.timestamp);
// ERC-4906: notify marketplaces of metadata update
emit MetadataUpdate(tokenId);
}
function tokenURI(uint256 tokenId) public view override returns (string memory) {
// Generate dynamic URI based on current stats
return string(abi.encodePacked(BASE_URI, tokenId.toString(), '?level=', stats[tokenId].level.toString()));
}
}
Item crafting and composability: ERC-1155 and equip
ERC-1155 suits fungible/semi-fungible items: 1000 iron swords are identical, each legendary is unique. We implement crafting with recipes and material burning:
contract GameItems is ERC1155, AccessControl {
bytes32 public constant MINTER_ROLE = keccak256("MINTER_ROLE");
struct CraftingRecipe {
uint256[] inputIds;
uint256[] inputAmounts;
uint256 outputId;
uint256 outputAmount;
}
mapping(uint256 => CraftingRecipe) public recipes;
function craft(uint256 recipeId) external {
CraftingRecipe storage recipe = recipes[recipeId];
_burnBatch(msg.sender, recipe.inputIds, recipe.inputAmounts);
_mint(msg.sender, recipe.outputId, recipe.outputAmount, "");
emit ItemCrafted(msg.sender, recipeId, recipe.outputId);
}
}
An equip system locks the item when equipped — it cannot be transferred until unequipped. We ensure security by overriding _update.
Seaport zone for buyer protection
Problem: a player lists a level-50 character, but during listing the level drops. Solution — a custom Seaport zone that checks minimum stats at the time of the deal:
contract CharacterStatsZone is ZoneInterface {
function validateOrder(ZoneParameters calldata zoneParameters) external view override returns (bytes4 validOrderMagicValue) {
(uint256 tokenId, uint16 minLevel) = abi.decode(zoneParameters.extraData, (uint256, uint16));
CharacterStats memory current = characterContract.stats(tokenId);
require(current.level >= minLevel, "Character level too low");
return ZoneInterface.validateOrder.selector;
}
}
This trust-minimized solution reduces buyer risk and increases NFT liquidity.
Why Chainlink VRF is the standard?
Loot boxes, critical hits, drops — all require verifiable randomness. Chainlink VRF V2 Plus provides provably fair randomness: the player can verify the result. Oracle calls cost gas, but we optimize by batching requests.
contract LootSystem is VRFConsumerBaseV2Plus {
function openLootBox(uint256 boxTokenId) external {
require(lootBoxContract.ownerOf(boxTokenId) == msg.sender, "Not owner");
lootBoxContract.burn(boxTokenId);
uint256 requestId = s_vrfCoordinator.requestRandomWords(...);
requestToPlayer[requestId] = msg.sender;
}
function fulfillRandomWords(uint256 requestId, uint256[] calldata randomWords) internal override {
address player = requestToPlayer[requestId];
uint256 rand = randomWords[0];
uint256 rarityRoll = rand % 10_000;
// 0.5% legendary, 4.5% epic, 15% rare, 80% common
ItemRarity rarity;
if (rarityRoll < 50) rarity = ItemRarity.Legendary;
else if (rarityRoll < 500) rarity = ItemRarity.Epic;
else if (rarityRoll < 2000) rarity = ItemRarity.Rare;
else rarity = ItemRarity.Common;
uint256 itemId = _mintRandomItem(player, rarity, rand);
emit LootBoxOpened(player, itemId, rarity);
}
}
Integration process
- Requirements analysis — data flow diagrams, selection of standards (ERC-721/1155, ERC-4906, ERC-4626 if needed).
- Contract development — full test coverage with Foundry/Hardhat, gas profiling.
- Frontend integration — wagmi, viem, RainbowKit for wallet interaction.
- Testing and audit — mandatory before mainnet, includes Slither/Mythril/Echidna.
- Deployment and monitoring — via Tenderly, alert configuration.
Our engineers have 7+ years of blockchain development experience and have delivered over 50 projects with NFT integration. We guarantee all contracts are rigorously tested and audited by our certified engineers. Order a smart contract audit before launch — project evaluation for free.
What's included in the work
Within the NFT mechanics integration, we provide:
- Audit of existing architecture and gas optimization recommendations.
- Smart contract development in Solidity (ERC-721/1155, ERC-4906, ERC-4626 if needed).
- Frontend integration (wagmi, viem, RainbowKit).
- Technical documentation and API specifications.
- Team training on contract operations and settlement processes.
- Post-launch support and monitoring via Tenderly.
Timeline estimates
Click to expand timeline
| Stage | Duration |
|---|---|
| Basic integration (ERC-721 with level, settlement, VRF) | 4–6 weeks |
| Full system (dynamic metadata, equipment, crafting, custom zone) | 8–12 weeks |
| Smart contract audit (mandatory before mainnet) | 3–5 weeks |
Contact us to discuss your project. Book a consultation on architecture — we'll calculate the gas savings for your load.







