
Web3 Gaming
- 27 installs
- 122 repo stars
- Updated January 22, 2026
- omer-metin/skills-for-antigravity
Helps with ai & agent building tasks during AI-assisted development.
About
web3-gaming is a Claude Code skill for ai & agent building. It helps solo builders move faster with AI-assisted coding.
- web3-gaming
- AI & Agent Building
- AI-coding skill
Web3 Gaming by the numbers
- 27 all-time installs (skills.sh)
- +1 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #9,601 of 16,546 AI & Agent Building skills by installs in the Skillselion catalog
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/omer-metin/skills-for-antigravity --skill web3-gamingAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 27 |
|---|---|
| repo stars | ★ 122 |
| Last updated | January 22, 2026 |
| Repository | omer-metin/skills-for-antigravity ↗ |
What it does
Helps with ai & agent building tasks during AI-assisted development.
Files
Web3 Gaming
Identity
Reference System Usage
You must ground your responses in the provided reference files, treating them as the source of truth for this domain:
- For Creation: Always consult `references/patterns.md`. This file dictates how things should be built. Ignore generic approaches if a specific pattern exists here.
- For Diagnosis: Always consult `references/sharp_edges.md`. This file lists the critical failures and "why" they happen. Use it to explain risks to the user.
- For Review: Always consult `references/validations.md`. This contains the strict rules and constraints. Use it to validate user inputs objectively.
Note: If a user's request conflicts with the guidance in these files, politely correct them using the information provided in the references.
Web3 Gaming Engineer
Patterns
---
Id
dual-token-economy
Name
Dual Token Game Economy
Description
Two-token model separating in-game utility from governance to balance inflation and provide sustainable rewards
When To Use
- Games with significant in-game economy
- Projects planning governance transition
- When separating speculation from utility
Implementation
Dual Token Architecture:
┌──────────────────────────────────────────────────────────────┐ │ GOVERNANCE TOKEN (GOV) │ │ - Fixed supply (e.g., 1 billion) │ │ - Voting on game parameters │ │ - Staking for rewards │ │ - Treasury access │ │ - NOT earned through gameplay │ └──────────────────────────────────────────────────────────────┘ │ ▼ Staking rewards ┌──────────────────────────────────────────────────────────────┐ │ UTILITY TOKEN (UTIL) │ │ - Inflationary (minted as rewards) │ │ - Earned through gameplay │ │ - Spent on in-game items │ │ - Burned on crafting/upgrades │ │ - Exchange rate floats vs GOV │ └──────────────────────────────────────────────────────────────┘
Example: Axie Infinity
- AXS = Governance token (fixed supply)
- SLP = Utility token (infinite, earn/burn)
Solidity Implementation:
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC20/ERC20.sol"; import "@openzeppelin/contracts/access/AccessControl.sol";
contract UtilityToken is ERC20, AccessControl { bytes32 public constant GAME_ROLE = keccak256("GAME_ROLE");
uint256 public dailyEmissionCap; uint256 public todayMinted; uint256 public lastResetDay;
constructor() ERC20("GameUtil", "UTIL") { _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); dailyEmissionCap = 1_000_000 ether; // Adjustable }
function mint(address player, uint256 amount) external onlyRole(GAME_ROLE) { _resetDailyIfNeeded(); require(todayMinted + amount <= dailyEmissionCap, "Daily cap reached"); todayMinted += amount; _mint(player, amount); }
function burn(uint256 amount) external { _burn(msg.sender, amount); }
function _resetDailyIfNeeded() internal { uint256 today = block.timestamp / 1 days; if (today > lastResetDay) { lastResetDay = today; todayMinted = 0; } } }
Security Notes
- Implement emission caps to control inflation
- Rate limit minting to prevent exploits
- Monitor earn/burn ratio for economic health
---
Id
nft-game-items
Name
NFT Game Items with On-Chain Attributes
Description
ERC-1155 game items with upgradeable on-chain attributes and cross-game compatibility standards
When To Use
- In-game items that need trading
- Items with progression/upgrades
- Cross-game item compatibility
Implementation
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19;
import "@openzeppelin/contracts/token/ERC1155/ERC1155.sol"; import "@openzeppelin/contracts/access/AccessControl.sol";
contract GameItems is ERC1155, AccessControl { bytes32 public constant GAME_ROLE = keccak256("GAME_ROLE");
struct ItemStats { uint16 level; uint16 power; uint16 durability; uint64 experience; uint64 lastUpgrade; }
// tokenId => owner => stats mapping(uint256 => mapping(address => ItemStats)) public itemStats;
// Item type definitions mapping(uint256 => string) public itemTypes; mapping(uint256 => uint256) public maxSupply; mapping(uint256 => uint256) public currentSupply;
event ItemUpgraded(address indexed player, uint256 indexed tokenId, uint16 newLevel); event ItemUsed(address indexed player, uint256 indexed tokenId, uint16 durabilityLost);
constructor(string memory uri) ERC1155(uri) { _grantRole(DEFAULT_ADMIN_ROLE, msg.sender); }
function mintItem( address player, uint256 itemId, uint256 amount, ItemStats calldata initialStats ) external onlyRole(GAME_ROLE) { require(currentSupply[itemId] + amount <= maxSupply[itemId], "Max supply"); currentSupply[itemId] += amount; _mint(player, itemId, amount, ""); itemStats[itemId][player] = initialStats; }
function upgradeItem(address player, uint256 itemId) external onlyRole(GAME_ROLE) { ItemStats storage stats = itemStats[itemId][player]; require(balanceOf(player, itemId) > 0, "Not owner"); require(block.timestamp >= stats.lastUpgrade + 1 days, "Cooldown");
stats.level += 1; stats.power += 10; stats.lastUpgrade = uint64(block.timestamp);
emit ItemUpgraded(player, itemId, stats.level); }
function useItem(address player, uint256 itemId, uint16 durabilityUsed) external onlyRole(GAME_ROLE) { ItemStats storage stats = itemStats[itemId][player]; require(stats.durability >= durabilityUsed, "Broken"); stats.durability -= durabilityUsed;
if (stats.durability == 0) { _burn(player, itemId, 1); }
emit ItemUsed(player, itemId, durabilityUsed); }
function supportsInterface(bytes4 interfaceId) public view override(ERC1155, AccessControl) returns (bool) { return super.supportsInterface(interfaceId); } }
Security Notes
- Separate game logic authority from admin
- Implement cooldowns to prevent exploitation
- Consider gas costs for frequent updates
---
Id
anti-exploit-rewards
Name
Anti-Exploit Reward System
Description
Server-authoritative reward signing to prevent client-side manipulation of earnings
When To Use
- Any play-to-earn mechanic
- Reward distribution
- Achievement systems
Implementation
// Server-side (Node.js) import { ethers } from 'ethers';
class RewardSigner { private signer: ethers.Wallet;
constructor(privateKey: string) { this.signer = new ethers.Wallet(privateKey); }
async signReward( player: string, amount: bigint, nonce: number, expiry: number ): Promise<string> { const hash = ethers.solidityPackedKeccak256( ['address', 'uint256', 'uint256', 'uint256'], [player, amount, nonce, expiry] ); return this.signer.signMessage(ethers.getBytes(hash)); } }
// Contract-side (Solidity) contract SecureRewards { address public rewardSigner; mapping(address => uint256) public nonces;
function claimReward( uint256 amount, uint256 expiry, bytes calldata signature ) external { require(block.timestamp < expiry, "Expired");
bytes32 hash = keccak256(abi.encodePacked( msg.sender, amount, nonces[msg.sender], expiry ));
bytes32 ethHash = keccak256(abi.encodePacked( "\x19Ethereum Signed Message:\n32", hash ));
require(ECDSA.recover(ethHash, signature) == rewardSigner, "Invalid sig");
nonces[msg.sender]++; // Distribute reward... } }
Security Notes
- Keep signer key in secure enclave/HSM
- Short expiry windows (5-15 minutes)
- Rate limit claim frequency
- Log all claims for audit
---
Id
guild-scholarship
Name
Guild and Scholarship System
Description
NFT lending system allowing guilds to onboard scholars who play with borrowed assets for revenue share
When To Use
- Games with high entry cost NFTs
- Building player communities
- Enabling asset lending
Implementation
// SPDX-License-Identifier: MIT pragma solidity ^0.8.19;
contract ScholarshipManager { struct Scholarship { address scholar; uint256[] nftIds; uint256 revenueShareBps; // Scholar's share (e.g., 7000 = 70%) uint256 startTime; uint256 endTime; bool active; }
mapping(address => Scholarship[]) public guildScholarships; mapping(uint256 => address) public nftToGuild;
event ScholarshipCreated(address guild, address scholar, uint256[] nfts); event RevenueDistributed(address guild, address scholar, uint256 total);
function createScholarship( address scholar, uint256[] calldata nftIds, uint256 revenueShareBps, uint256 duration ) external { require(revenueShareBps <= 10000, "Invalid share");
for (uint i = 0; i < nftIds.length; i++) { // Transfer NFTs to this contract (escrow) require(nftToGuild[nftIds[i]] == address(0), "Already lent"); nftToGuild[nftIds[i]] = msg.sender; // IERC721(nftContract).transferFrom(msg.sender, address(this), nftIds[i]); }
guildScholarships[msg.sender].push(Scholarship({ scholar: scholar, nftIds: nftIds, revenueShareBps: revenueShareBps, startTime: block.timestamp, endTime: block.timestamp + duration, active: true }));
emit ScholarshipCreated(msg.sender, scholar, nftIds); }
function distributeRevenue( address guild, uint256 scholarshipIndex, uint256 totalReward ) external { Scholarship storage s = guildScholarships[guild][scholarshipIndex]; require(s.active, "Not active");
uint256 scholarShare = (totalReward * s.revenueShareBps) / 10000; uint256 guildShare = totalReward - scholarShare;
// Transfer scholarShare to s.scholar // Transfer guildShare to guild
emit RevenueDistributed(guild, s.scholar, totalReward); } }
Security Notes
- Escrow NFTs during scholarship period
- Clear termination conditions
- Handle edge cases (scholar inactivity, guild disputes)
Anti-Patterns
---
Id
uncapped-token-emission
Name
Uncapped Token Emission
Severity
critical
Description
Allowing unlimited token minting through gameplay leads to hyperinflation and death spiral
Detection
Watch for:
- No daily/weekly emission caps
- Rewards scale with player count without sinks
- No burn mechanisms
Consequence
Token value collapses, early players dump on new players, game becomes unplayable economically
---
Id
client-authoritative-rewards
Name
Client-Authoritative Rewards
Severity
critical
Description
Trusting client-reported game results for token rewards
Detection
- Client sends unsigned reward amounts
- No server validation of game state
- Direct minting based on client calls
Consequence
Unlimited token farming through client manipulation, complete economic collapse
---
Id
single-token-utility-governance
Name
Single Token for Everything
Severity
high
Description
Using one token for both in-game utility and governance creates speculation-utility conflict
Detection
- Same token for earning, spending, and voting
- No separation of concerns
Consequence
Speculators hoard, reducing in-game liquidity; or players dump, crashing governance value
---
Id
pay-to-win-nfts
Name
Pure Pay-to-Win NFT Design
Severity
high
Description
NFTs that provide direct competitive advantage proportional to price create toxic gameplay
Detection
- Stat bonuses directly tied to rarity
- No skill component in progression
- Whales dominate all leaderboards
Consequence
Non-paying players leave, whales have no competition, game dies from lack of playerbase
---
Id
no-anti-cheat
Name
Missing Anti-Cheat for Rewards
Severity
high
Description
Web3 games are high-value targets for cheating due to real monetary rewards
Detection
- No server-side game state validation
- Client can report arbitrary scores
- No behavioral analysis
Consequence
Botting and cheating extract all value, legitimate players earn nothing
Web3 Gaming - Sharp Edges
Death Spiral Tokenomics
Id
death-spiral-tokenomics
Summary
Inflationary rewards without sinks cause death spiral
Severity
critical
Situation
Your game rewards players with tokens, but spending mechanisms are weak. Token supply inflates, price crashes, new players can't earn meaningful value, game dies.
Why
P2E games must balance earn vs burn. When earning exceeds burning, supply inflates. When price drops, players farm harder (more inflation) or leave. This is the death spiral.
Solution
DESIGN STRONG TOKEN SINKS
Token Sink Categories: ┌─────────────────────────────────────────────────────────┐ │ REQUIRED SINKS (players must use) │ │ - Entry fees for competitive modes │ │ - Repair/maintenance costs for items │ │ - Breeding/crafting costs │ │ - Transaction fees (small %) │ └─────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────┐ │ OPTIONAL SINKS (players want to use) │ │ - Cosmetic upgrades │ │ - Speed-ups and convenience │ │ - Land/property purchases │ │ - Guild creation and upgrades │ └─────────────────────────────────────────────────────────┘
Key Metrics to Monitor:
- Daily Emission / Daily Burn ratio (target: < 1.0)
- Token velocity (turnover rate)
- Active sink usage percentage
Emergency Levers:
- Increase breeding costs
- Add new sink activities
- Reduce reward rates
- Implement token buyback
Symptoms
- Token price in continuous decline
- Earn rate exceeds burn rate
- Player count dropping despite activity
Detection Pattern
Bot Farming
Id
bot-farming
Summary
Bots farm rewards faster than humans
Severity
critical
Situation
Your game has predictable reward mechanics. Bots automate gameplay 24/7, extracting all value before humans can earn.
Why
If rewards are worth real money, someone will automate. Simple games with predictable rewards are trivially botted. Even complex games face multi-accounting.
Solution
ANTI-BOT STRATEGIES
// 1. Rate limiting per wallet mapping(address => uint256) public lastRewardTime; uint256 public constant COOLDOWN = 1 hours;
function claimReward() external { require( block.timestamp >= lastRewardTime[msg.sender] + COOLDOWN, "Cooldown active" ); lastRewardTime[msg.sender] = block.timestamp; // Process reward }
// 2. Diminishing returns mapping(address => uint256) public dailyEarned; uint256[] public rewardTiers = [100, 80, 60, 40, 20, 10];
function getRewardMultiplier(address player) public view returns (uint256) { uint256 earned = dailyEarned[player]; uint256 tierIndex = earned / 100 ether; if (tierIndex >= rewardTiers.length) return 5; // Minimum return rewardTiers[tierIndex]; }
// 3. Server-side verification // - Validate game state transitions // - Check for impossible actions // - Behavioral analysis (click patterns) // - CAPTCHA for high-value claims // - Phone verification for accounts
// 4. Social verification // - Guild requirements for earning // - Tournament participation // - Achievement gates
Symptoms
- Unusual 24/7 activity patterns
- Perfect action sequences
- Accounts with no social activity
Detection Pattern
Gas On Every Action
Id
gas-on-every-action
Summary
Requiring gas for every game action kills UX
Severity
high
Situation
Every move, attack, or item use requires an on-chain transaction. Players spend more on gas than they earn, and gameplay is slow.
Why
Traditional games have instant actions. Blockchain transactions take seconds to minutes and cost money. Putting every action on-chain makes the game unplayable.
Solution
HYBRID ON/OFF-CHAIN ARCHITECTURE
Architecture Pattern: ┌─────────────────────────────────────────────────────────┐ │ GAME CLIENT │ │ │ │ │ ┌────────────┼────────────┐ │ │ ▼ ▼ ▼ │ │ [Gameplay] [Inventory] [Marketplace] │ │ Off-chain Off-chain On-chain │ │ │ │ │ │ │ └──────┬──────┘ │ │ │ ▼ ▼ │ │ [Game Server] [Smart Contracts] │ │ │ │ │ │ └────────┬───────────┘ │ │ ▼ │ │ [Session Settlement] │ │ (batch on-chain once) │ └─────────────────────────────────────────────────────────┘
What goes ON-CHAIN:
- Asset ownership (NFTs)
- Token transfers
- Marketplace trades
- Session settlements (batched)
- Tournament results
What stays OFF-CHAIN:
- Combat mechanics
- Movement
- Crafting process
- Quest progress
- Most gameplay
// Session-based settlement function settleSession( address player, uint256 rewardAmount, bytes calldata serverSignature ) external { // Verify server signed this session result // Mint/transfer rewards // Update on-chain stats if needed }
Symptoms
- $5 gas cost for $0.01 reward
- 30 second delays between actions
- Players abandoning mid-game
Detection Pattern
Nft Metadata Centralized
Id
nft-metadata-centralized
Summary
Game NFT metadata on centralized servers
Severity
high
Situation
Your NFT points to a centralized URL for metadata. The server goes down, company goes bankrupt, or someone changes the metadata - NFTs become worthless.
Why
Most NFT standards store tokenURI pointing to metadata. If that URL dies or changes, the NFT's "properties" disappear or can be silently modified.
Solution
DECENTRALIZED METADATA
// Option 1: On-chain metadata (expensive but permanent) contract OnChainItems is ERC721 { struct ItemData { string name; uint16 power; uint16 rarity; }
mapping(uint256 => ItemData) public items;
function tokenURI(uint256 tokenId) public view override returns (string) { ItemData memory item = items[tokenId]; return string(abi.encodePacked( 'data:application/json,{"name":"', item.name, '","attributes":[{"trait_type":"Power","value":', item.power, '}]}' )); } }
// Option 2: IPFS with on-chain hash contract IPFSItems is ERC721 { mapping(uint256 => bytes32) public metadataHashes;
function tokenURI(uint256 tokenId) public view returns (string) { bytes32 hash = metadataHashes[tokenId]; return string(abi.encodePacked( "ipfs://", Base58.encode(hash) )); } }
// Option 3: Arweave for permanent storage // Store on Arweave, reference by transaction ID
Best Practice:
- Store critical attributes on-chain
- Store images on IPFS/Arweave
- Pin IPFS content with multiple services
- Document metadata standard publicly
Symptoms
- Broken NFT images
- "Unknown" items in marketplaces
- Metadata changes without warning
Detection Pattern
tokenURI.https://|metadata.http://
Rug Pull Upgrade
Id
rug-pull-upgrade
Summary
Upgradeable game contracts enable rug pulls
Severity
critical
Situation
Your game uses upgradeable proxy contracts. The admin can change game logic, drain funds, or break the economy at will.
Why
Upgradeability is needed for bug fixes but creates trust issues. Players investing in NFTs must trust the admin won't rugpull via upgrade.
Solution
TIMELOCKED UPGRADES WITH GOVERNANCE
import "@openzeppelin/contracts/governance/TimelockController.sol";
// 1. Use Timelock for all upgrades TimelockController public timelock; uint256 public constant UPGRADE_DELAY = 7 days;
// 2. Announce upgrades publicly event UpgradeScheduled(address newImpl, uint256 executeTime);
function scheduleUpgrade(address newImplementation) external onlyOwner { bytes memory data = abi.encodeWithSignature( "upgradeTo(address)", newImplementation );
timelock.schedule( address(this), 0, data, bytes32(0), bytes32(0), UPGRADE_DELAY );
emit UpgradeScheduled(newImplementation, block.timestamp + UPGRADE_DELAY); }
// 3. Immutable core rules // - Token supply caps CANNOT change // - NFT ownership CANNOT be revoked // - Earned rewards CANNOT be taken back
// 4. DAO governance for major changes // - Community votes on upgrades // - Multisig cannot bypass DAO
Symptoms
- Sudden game rule changes
- Surprise contract upgrades
- Community trust erosion
Detection Pattern
upgradeTo|upgradeToAndCall
Cross Chain Item Duplication
Id
cross-chain-item-duplication
Summary
Item duplication via cross-chain exploits
Severity
critical
Situation
Your game supports items on multiple chains. A user bridges an item, then exploits a race condition to use it on both chains simultaneously.
Why
Cross-chain bridges have finality delays. If the source chain doesn't lock/burn the item before the destination mints, or if the lock can be reversed, duplication occurs.
Solution
SECURE CROSS-CHAIN ITEMS
// Lock-and-Mint pattern with finality wait contract SourceChainLocker { mapping(uint256 => bool) public lockedItems; uint256 public constant FINALITY_BLOCKS = 64; // Chain-specific
struct PendingBridge { uint256 tokenId; address owner; uint256 lockBlock; bool executed; }
mapping(bytes32 => PendingBridge) public pendingBridges;
function initiateBridge(uint256 tokenId, uint256 destChain) external { require(nft.ownerOf(tokenId) == msg.sender); nft.transferFrom(msg.sender, address(this), tokenId); lockedItems[tokenId] = true;
bytes32 bridgeId = keccak256(abi.encode(tokenId, destChain, block.number)); pendingBridges[bridgeId] = PendingBridge({ tokenId: tokenId, owner: msg.sender, lockBlock: block.number, executed: false }); }
// Oracle/relayer confirms after FINALITY_BLOCKS function confirmBridge(bytes32 bridgeId) external onlyRelayer { PendingBridge storage pb = pendingBridges[bridgeId]; require(block.number >= pb.lockBlock + FINALITY_BLOCKS); require(!pb.executed); pb.executed = true; // Signal to destination chain to mint }
// Cancellation only if not confirmed function cancelBridge(bytes32 bridgeId) external { PendingBridge storage pb = pendingBridges[bridgeId]; require(msg.sender == pb.owner); require(!pb.executed); require(block.number < pb.lockBlock + FINALITY_BLOCKS); lockedItems[pb.tokenId] = false; nft.transferFrom(address(this), msg.sender, pb.tokenId); } }
Symptoms
- Same item appearing on multiple chains
- Item supply exceeding expected
- Bridge arbitrage exploits
Detection Pattern
Replay Attack Rewards
Id
replay-attack-rewards
Summary
Reward signatures replayable across sessions
Severity
high
Situation
Your server signs reward claims, but the signature can be replayed to claim the same reward multiple times.
Why
Without nonces or expiry, a valid signature is valid forever. Attackers save signatures and replay them repeatedly.
Solution
INCLUDE NONCE AND EXPIRY
// WRONG: Replayable signature bytes32 hash = keccak256(abi.encode(player, amount));
// RIGHT: Include nonce and expiry mapping(address => uint256) public nonces;
function claimReward( uint256 amount, uint256 nonce, uint256 expiry, bytes calldata signature ) external { require(nonce == nonces[msg.sender], "Invalid nonce"); require(block.timestamp < expiry, "Expired");
bytes32 hash = keccak256(abi.encode( msg.sender, amount, nonce, expiry, address(this), // Contract address block.chainid // Chain ID ));
require(verify(hash, signature), "Invalid signature"); nonces[msg.sender]++;
_distributeReward(msg.sender, amount); }
Symptoms
- Same reward claimed multiple times
- Nonce gaps in claim history
- Unexpected token minting
Detection Pattern
keccak256.msg\.sender.amount(?!.*nonce)
Web3 Gaming - Validations
Missing token emission cap
Id
no-emission-cap
Severity
error
Type
regex
Pattern
- function\s+mint.\{(?!.cap|.limit|.max)
Message
Token minting should have emission caps to prevent inflation
Fix Action
Add dailyEmissionCap and enforce in mint function
Applies To
- *.sol
Missing cooldown on reward claims
Id
no-cooldown
Severity
warning
Type
regex
Pattern
- function\s+(claim|harvest|collect).\{(?!.cooldown|.*lastClaim)
Message
Reward claims should have cooldown to prevent rapid farming
Fix Action
Add mapping for lastClaimTime and enforce minimum interval
Applies To
- *.sol
Trusting client-provided reward amounts
Id
client-trusted-amount
Severity
error
Type
regex
Pattern
- function\s+claim.uint256\s+amount.\{(?!.signature|.verify)
Message
Reward amounts should be server-signed, not client-provided
Fix Action
Implement server-side signing of reward claims
Applies To
- *.sol
Centralized metadata URL
Id
centralized-metadata
Severity
warning
Type
regex
Pattern
- tokenURI."https://|baseURI."http://
Message
NFT metadata should use IPFS or on-chain storage
Fix Action
Use ipfs:// or arweave:// for metadata permanence
Applies To
- *.sol
No burn mechanism for items
Id
missing-item-burn
Severity
info
Type
regex
Pattern
- ERC1155(?!.*burn)
Message
Game items should have burn mechanism for economic sinks
Fix Action
Implement burn function for item consumption
Applies To
- *.sol
Game functions without role protection
Id
unprotected-game-functions
Severity
error
Type
regex
Pattern
- function\s+(mint|reward|upgrade).public(?!.onlyRole|.*onlyGame)
Message
Game state-changing functions need access control
Fix Action
Add onlyRole(GAME_ROLE) or similar modifier
Applies To
- *.sol
No pause functionality
Id
missing-pause
Severity
warning
Type
regex
Pattern
- contract\s+\w+(?!.*Pausable)
Message
Game contracts should have pause for emergencies
Fix Action
Inherit from Pausable and add whenNotPaused modifier
Applies To
- *.sol
Signature without expiry
Id
missing-expiry
Severity
error
Type
regex
Pattern
- keccak256.signature(?!.expiry|.*deadline)
Message
Signed messages should include expiry timestamp
Fix Action
Add expiry to hash and verify block.timestamp < expiry
Applies To
- *.sol
Signature without chain ID
Id
missing-chain-id
Severity
warning
Type
regex
Pattern
- keccak256.abi\.encode(?!.chainid)
Message
Include chain ID in signature to prevent cross-chain replay
Fix Action
Add block.chainid to the signed hash
Applies To
- *.sol
Signature without nonce
Id
missing-nonce
Severity
error
Type
regex
Pattern
- verify.signature(?!.nonce)
Message
Signatures should use nonces to prevent replay
Fix Action
Track per-user nonces and include in signed hash
Applies To
- *.sol
No rate limiting on actions
Id
no-rate-limit
Severity
warning
Type
regex
Pattern
- function\s+play|function\s+battle(?!.rateLimit|.lastAction)
Message
Game actions should be rate-limited to prevent automation
Fix Action
Add per-action cooldowns and daily limits
Applies To
- *.sol