GameFi Energy/Stamina System Development
Energy system is mechanic limiting game activity. Player spends energy on actions (battles, farming, crafting), energy regenerates over time or via purchase. In Web2 it's just database counter. In Web3 it's on-chain resource, creating both opportunities (tradeable energy, verifiable regen) and challenges (gas per update, cheating prevention).
Correct architecture of energy system is key engineering task in GameFi. Incorrect implementation either makes game unplayable (too many on-chain ops) or opens exploits (free energy via manipulation).
Key Problem: Time-based Regen Without Constant On-chain Updates
Intuitive solution: store energy in mapping, update every second. Bad—infinite transactions.
Correct approach: lazy evaluation. Store not current energy but last change moment and value then. Current energy computed on-the-fly at each read:
contract EnergySystem {
struct EnergyState {
uint128 storedEnergy; // energy at lastUpdate
uint64 lastUpdateTime;
uint64 maxEnergy;
}
mapping(address => EnergyState) private energyStates;
uint256 public constant REGEN_RATE = 1e18; // 1 energy per second (18 decimals)
uint256 public constant MAX_ENERGY = 100e18;
// Compute current energy without storage write
function currentEnergy(address player) public view returns (uint256) {
EnergyState storage state = energyStates[player];
uint256 elapsed = block.timestamp - state.lastUpdateTime;
uint256 regenerated = elapsed * REGEN_RATE;
uint256 total = uint256(state.storedEnergy) + regenerated;
uint256 max = state.maxEnergy == 0 ? MAX_ENERGY : uint256(state.maxEnergy);
return total > max ? max : total;
}
// Update storage only on actual usage/change
function _updateEnergyState(address player) internal {
EnergyState storage state = energyStates[player];
state.storedEnergy = uint128(currentEnergy(player));
state.lastUpdateTime = uint64(block.timestamp);
}
function spendEnergy(address player, uint256 amount) internal {
uint256 current = currentEnergy(player);
require(current >= amount, "Insufficient energy");
_updateEnergyState(player);
energyStates[player].storedEnergy -= uint128(amount);
}
}
Key point: currentEnergy() is view function, costs no gas. Storage updates only on spendEnergy/addEnergy—real gameplay action. Significant gas savings: no separate regen transactions.
Binding to NFT: Character Energy
Energy tied to specific NFT, not EOA wallet. Important: player can own multiple characters with independent energy, trade characters with their current energy.
contract CharacterEnergySystem {
struct CharacterEnergy {
uint128 storedEnergy;
uint64 lastUpdate;
uint8 tier; // tier affects max energy and regen
}
mapping(uint256 => CharacterEnergy) public characterEnergy; // tokenId → energy
// Regen rate depends on character tier
function regenRateForTier(uint8 tier) public pure returns (uint256) {
if (tier == 3) return 3e18; // 3 units/sec for legendary
if (tier == 2) return 2e18; // 2 units/sec for rare
return 1e18; // 1 unit/sec for common
}
// Max energy depends on tier
function maxEnergyForTier(uint8 tier) public pure returns (uint256) {
return 100e18 + uint256(tier) * 50e18; // 100, 150, 200 for tier 1,2,3
}
function currentEnergy(uint256 tokenId) public view returns (uint256) {
CharacterEnergy storage ce = characterEnergy[tokenId];
uint8 tier = nftContract.getTier(tokenId);
uint256 elapsed = block.timestamp - ce.lastUpdate;
uint256 regen = elapsed * regenRateForTier(tier);
uint256 total = uint256(ce.storedEnergy) + regen;
uint256 max = maxEnergyForTier(tier);
return total > max ? max : total;
}
}
Economic Model: Sink and Source
Energy system works as economy regulator. Important to balance:
Sources (where energy comes from):
- Regen over time (free, limited by max)
- Purchase for game token (sink for token)
- Staking NFT higher tier → bonus regen
- Daily login reward (once per 24h)
Sinks (where energy goes):
- Combat actions
- Resource farming
- Item crafting
- PvP bets
| Parameter | Recommendations |
|---|---|
| Regen rate | Full refill from 0 to max in 8–12 hours |
| Max energy | 1–3 game sessions of 2–3 hours |
| Premium refill | Max 2–3 full refills per day |
| Tier multiplier | Max 2x–3x, not more |
Testing
// Foundry test for regen mechanics
function test_energyRegenOverTime() public {
uint256 tokenId = 1;
// Spend all energy
vm.prank(player);
game.spendAllEnergy(tokenId);
assertEq(energy.currentEnergy(tokenId), 0);
// Skip 50 seconds
vm.warp(block.timestamp + 50);
// Check regen (tier 1: 1 unit/sec)
assertEq(energy.currentEnergy(tokenId), 50e18);
// Skip 200 more seconds—should cap at max (100)
vm.warp(block.timestamp + 200);
assertEq(energy.currentEnergy(tokenId), 100e18);
}
Timeline
Basic system (lazy regen, spend on actions, cooldowns)—2–3 weeks. Full system (tier-based regen, ERC-20 energy token, DEX integration, anti-cheat signed actions, analytics dashboard)—5–7 weeks.







