Agent skill
nft-protocol-security
Protocol Type Trigger nft (detected when ERC721/ERC1155 with marketplace, minting, staking, or collateral logic found) - Inject Into Breadth agents, depth-token-flow, depth-edge...
Install this agent skill to your Project
npx add-skill https://github.com/PlamenTSV/plamen/tree/main/agents/skills/injectable/nft-protocol-security
SKILL.md
Injectable Skill: NFT Protocol Security
Protocol Type Trigger:
nft(detected when ERC721/ERC1155 with marketplace, minting, staking, or collateral logic found) Inject Into: Breadth agents, depth-token-flow, depth-edge-case Language: EVM only (Solana/Move NFT models use different mechanisms without callbacks or enumeration) Finding prefix:[NFT-N]
Orchestrator Decomposition Guide
When decomposing this skill into depth agent investigation questions, map sections to domains:
- Sections 1, 2: depth-token-flow (callback flows, approval/transfer paths)
- Section 3: depth-edge-case (enumeration invariants, boundary states)
- Section 4: depth-state-trace (ownership state consistency, metadata integrity)
When This Skill Activates
Recon detects NFT protocol patterns: ERC721/ERC1155 with state-modifying logic beyond simple transfer (marketplace listing, staking, collateral, minting with conditions, royalty enforcement, batch operations).
Pure ERC721/ERC1155 token implementations without protocol logic do NOT trigger this skill.
1. Callback Reentrancy Surface
For each function that triggers NFT callbacks:
1a. Safe Transfer Callback Inventory
Enumerate all code paths that invoke _safeMint, _safeTransfer, safeTransferFrom, or onERC1155Received/onERC1155BatchReceived:
| # | Function | Callback Triggered | State Modified BEFORE Callback | State Modified AFTER Callback | Reentrancy Guard? |
|---|
For each entry:
- Is all critical state updated BEFORE the callback? (checks-effects-interactions)
- Can the callback recipient re-enter the calling contract?
- Can the callback recipient REVERT selectively to reject unwanted outcomes and retry until a desired outcome occurs?
- Pattern:
_safeMint(to, tokenId)→onERC721Receivedcallback → recipient reverts if assigned token has undesirable properties → retry until desired properties assigned.
1b. Batch Callback Completeness
For contracts implementing ERC1155:
- Is
onERC1155Receivedimplemented for single transfers? - Is
onERC1155BatchReceivedimplemented for batch transfers? - If batch callback is MISSING:
safeBatchTransferFromwill revert, blocking batch settlement/distribution. - Do both callbacks return the correct selector?
Tag: [TRACE:_safeMint → onERC721Received callback → state_before={list} → reentrant_path={YES/NO}]
2. Approval and Transfer Path Analysis
2a. Approval Scope
For each approval mechanism:
approve(address, tokenId): per-token approval. Is approval cleared on transfer?setApprovalForAll(address, bool): blanket approval. Can an approved operator transfer ANY token?- Is there a mechanism to revoke approvals? Can approval persist across ownership changes?
2b. Transfer Authorization
For each transfer function:
- Who can transfer? (owner, approved, operator)
- Is authorization checked for ALL transfer code paths? (direct, marketplace, staking, collateral seizure)
- Pattern: protocol custody contract has
setApprovalForAllfrom users → contract can transfer any user's NFTs → compromise of contract = compromise of all approved NFTs.
2c. Royalty Bypass
If royalties are enforced:
- Can transfers occur through paths that bypass royalty checks? (direct
transferFromvs marketplaceexecuteSale) - Are royalties enforced at the token level (ERC2981) or marketplace level?
- If marketplace-level only: direct transfer bypasses royalties.
Tag: [TRACE:transfer_path={function} → auth_check={method} → royalty_enforced={YES/NO}]
3. Enumeration and Index Integrity
For contracts using ERC721Enumerable or custom enumeration:
3a. Index Structure Consistency
- What data structures track token ownership enumeration? (
_ownedTokens,_allTokens, index mappings) - For every state-changing operation (mint, burn, transfer): are ALL index structures updated atomically?
- Pattern: override
_beforeTokenTransfer(OZ v4) or_update(OZ v5) without callingsuper→ index structures become stale.
3b. Burn and Transfer Edge Cases
- After burn: does
tokenOfOwnerByIndexstill return correct values? IstotalSupplydecremented? - After transfer: does old owner's index shrink and new owner's index grow?
- At boundary: single-token owner burns their only token → empty enumeration handles correctly?
3c. Batch Operation Atomicity
For batch mint/burn/transfer:
- Are indices updated for EACH token in the batch, or only once at the end?
- Can a partial failure in a batch leave indices in an inconsistent state?
- Gas cost at maximum batch size: does it exceed block gas limit?
Tag: [BOUNDARY:burn_last_token → _ownedTokens[owner].length={0} → tokenOfOwnerByIndex={result}]
4. Metadata and State Consistency
4a. Token URI Integrity
If tokenURI or uri returns dynamic content:
- For ERC1155: does
uri(uint256 id)return a template with literal{id}placeholder per spec? Or a fully resolved URL? (clients expect to substitute the zero-padded hex ID client-side) - Can metadata be changed after mint? By whom? Does change emit event?
- Is metadata stored on-chain or off-chain? If off-chain: what happens if the URI host is unreachable?
4b. Token Property Assignment
If tokens have properties assigned at mint time (rarity, type, attributes):
- Is the assignment deterministic or random?
- If random: is the randomness source manipulable? (block.timestamp, block.prevrandao, weak PRNG)
- Can the minter influence which properties are assigned? (selective minting via callback revert)
- Cross-reference FLASH_LOAN_INTERACTION if properties affect economic value.
4c. Ownership State During Custody
When tokens are deposited into protocol custody (staking, collateral, escrow):
- Does
ownerOf(tokenId)return the protocol address or the depositor? - Are user rights (claim, withdraw, liquidate) tracked correctly in protocol state?
- If the protocol is compromised: can deposited NFTs be recovered?
Tag: [TRACE:mint → property_assignment={method} → randomness_source={source} → manipulable={YES/NO}]
Key Questions (must answer all)
- For each
_safeMint/_safeTransfer: is critical state updated BEFORE the callback? - For ERC1155: are BOTH single and batch callbacks implemented with correct selectors?
- For enumerable tokens: do burn/transfer operations update ALL index structures?
- For metadata: does
uri()follow the spec for the token standard used? - For custody protocols: is depositor ownership tracked independently from
ownerOf?
Common False Positives
- Non-reentrant safe transfers:
_safeMintwithinnonReentrantmodifier → callback reentrancy blocked - Standard OZ implementation: Inherits
ERC721EnumerableorERC1155without overriding internal hooks → indices maintained by parent - Immutable metadata: Token URI set at mint and never changeable → no metadata manipulation
- Trusted recipients only: Safe transfers only to protocol-controlled addresses (not user-supplied) → callback revert/reentrancy not user-exploitable
Step Execution Checklist (MANDATORY)
| Section | Required | Completed? | Notes |
|---|---|---|---|
| 1. Callback Reentrancy Surface | IF safe mint/transfer used | Callback inventory, batch completeness | |
| 2. Approval and Transfer Paths | YES | Authorization, scope, royalty bypass | |
| 3. Enumeration and Index Integrity | IF enumerable | Index consistency across operations | |
| 4. Metadata and State Consistency | YES | URI spec, property assignment, custody |
Recommended Agent Skills
Expand your agent's capabilities with these related and highly-rated skills.
integration-hazard-research
Protocol Type Trigger NAMED_EXTERNAL_PROTOCOL (detected when recon finds import/interface for an identifiable external protocol — not standard libraries). Researches known integration hazards of the target protocol.
outcome-determinism
Protocol Type Trigger outcome_determinism - detected when EITHER of these code patterns are present - - Selection from finite depletable pool with fallback behavior (while(full)...
governance-attack-vectors
Protocol Type Trigger governance (detected when Governor, Timelock, voting, proposal, quorum, delegate patterns found) - Inject Into Breadth agents, depth-external, depth-edge-case
vault-accounting
Protocol Type Trigger vault (detected in recon TASK 0 Step 1) - Inject Into Core state agent OR economic design agent (merge via M4 hierarchy)
lending-protocol-security
Protocol Type Trigger lending (detected when recon finds liquidate|borrow|repay|collateral|lend|loan|LTV|healthFactor|interestRate|debtToken) - Inject Into Breadth agents, depth...
dex-integration-security
Protocol Type Trigger dex_integration (detected when recon finds swap|addLiquidity|removeLiquidity|IUniswapV2Router|ISwapRouter|amountOutMin|amountOutMinimum|slippage - AND the...
Didn't find tool you were looking for?