Claude-skill-registry crane-natspec

This skill should be used when the user asks about "natspec", "documentation", "include-tag", "selector", "cast", "@custom:signature", "@custom:selector", "@custom:topiczero", "@custom:interfaceid", "AsciiDoc", or needs guidance on documenting Crane contracts with NatSpec and AsciiDoc include-tags.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/crane-natspec" ~/.claude/skills/majiayu000-claude-skill-registry-crane-natspec && rm -rf "$T"
manifest: skills/data/crane-natspec/SKILL.md
source content

Crane NatSpec & Documentation Standards

Crane uses NatSpec combined with AsciiDoc include-tags for accurate, extractable documentation.

AsciiDoc Include-Tags

Wrap documented symbols with include-tags for documentation extraction:

// tag::MySymbol[]
/// @notice Description of the symbol
function myFunction() external { ... }
// end::MySymbol[]

Tag Format Rules

  • Tag markers must match exactly (no extra spaces inside
    []
    )
  • Tag name should match the symbol being documented
  • Use PascalCase for tag names matching type names
  • Use camelCase for function tag names

Example Usage

// tag::transfer[]
/// @notice Transfers tokens to a recipient
/// @param to_ The recipient address
/// @param amount_ The amount to transfer
/// @return success True if transfer succeeded
/// @custom:signature transfer(address,uint256)
/// @custom:selector 0xa9059cbb
function transfer(address to_, uint256 amount_) external returns (bool success);
// end::transfer[]

Custom NatSpec Tags

Functions

TagPurposeExample
@custom:signature
Canonical signature string
transfer(address,uint256)
@custom:selector
bytes4 selector
0xa9059cbb
/// @notice Transfers tokens
/// @custom:signature transfer(address,uint256)
/// @custom:selector 0xa9059cbb
function transfer(address to_, uint256 amount_) external returns (bool);

Errors

TagPurposeExample
@custom:signature
Canonical error signature
NotOwner(address)
@custom:selector
bytes4 selector
0x30cd7471
/// @notice Thrown when caller is not the owner
/// @custom:signature NotOwner(address)
/// @custom:selector 0x30cd7471
error NotOwner(address caller);

Events

TagPurposeExample
@custom:signature
Canonical event signature
Transfer(address,address,uint256)
@custom:topiczero
bytes32 topic0 hash
0xddf252ad...
/// @notice Emitted on token transfer
/// @custom:signature Transfer(address,address,uint256)
/// @custom:topiczero 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
event Transfer(address indexed from, address indexed to, uint256 amount);

Note: Events use

@custom:topiczero
(bytes32), not
@custom:selector
(bytes4).

ERC-165 Interfaces

TagPurposeExample
@custom:interfaceid
bytes4 interface ID
0x01ffc9a7
/// @title IERC165
/// @custom:interfaceid 0x01ffc9a7
interface IERC165 {
    function supportsInterface(bytes4 interfaceId) external view returns (bool);
}

Computing Values with
cast

Use Foundry's

cast
to compute selectors and hashes:

Function Selector (bytes4)

cast sig "transfer(address,uint256)"
# Output: 0xa9059cbb

Error Selector (bytes4)

cast sig "NotOwner(address)"
# Output: 0x30cd7471

Event Topic0 (bytes32)

cast keccak "Transfer(address,address,uint256)"
# Output: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef

Interface ID (bytes4)

For interface IDs, prefer computing in Solidity:

bytes4 interfaceId = type(IMyInterface).interfaceId;

Or compute manually by XOR-ing all function selectors:

# Get each selector
cast sig "func1(uint256)"  # 0x12345678
cast sig "func2(address)"  # 0x87654321

# XOR them in Solidity or manually
bytes4 interfaceId = 0x12345678 ^ 0x87654321;

Complete Documentation Example

// SPDX-License-Identifier: MIT
pragma solidity ^0.8.30;

// tag::IMyToken[]
/// @title IMyToken
/// @notice Interface for MyToken
/// @custom:interfaceid 0x36372b07
interface IMyToken {

    // tag::Transfer[]
    /// @notice Emitted when tokens are transferred
    /// @param from The sender address
    /// @param to The recipient address
    /// @param amount The amount transferred
    /// @custom:signature Transfer(address,address,uint256)
    /// @custom:topiczero 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
    event Transfer(address indexed from, address indexed to, uint256 amount);
    // end::Transfer[]

    // tag::InsufficientBalance[]
    /// @notice Thrown when transfer amount exceeds balance
    /// @param account The account with insufficient balance
    /// @param balance The current balance
    /// @param required The required amount
    /// @custom:signature InsufficientBalance(address,uint256,uint256)
    /// @custom:selector 0xcf479181
    error InsufficientBalance(address account, uint256 balance, uint256 required);
    // end::InsufficientBalance[]

    // tag::transfer[]
    /// @notice Transfers tokens to a recipient
    /// @param to_ The recipient address
    /// @param amount_ The amount to transfer
    /// @return success True if the transfer succeeded
    /// @custom:signature transfer(address,uint256)
    /// @custom:selector 0xa9059cbb
    function transfer(address to_, uint256 amount_) external returns (bool success);
    // end::transfer[]

    // tag::balanceOf[]
    /// @notice Returns the token balance of an account
    /// @param account_ The account to query
    /// @return balance The token balance
    /// @custom:signature balanceOf(address)
    /// @custom:selector 0x70a08231
    function balanceOf(address account_) external view returns (uint256 balance);
    // end::balanceOf[]
}
// end::IMyToken[]

Validation Checklist

For each documented symbol, verify:

  • Include-tags wrap the symbol correctly
  • Tag names match symbol names
  • @custom:signature
    matches actual signature exactly
  • @custom:selector
    computed correctly (functions/errors)
  • @custom:topiczero
    computed correctly (events)
  • @custom:interfaceid
    computed correctly (interfaces)
  • NatSpec params match function parameters

Additional Resources

Reference Files

  • references/natspec-examples.md
    - More complete examples

Quick Reference

Symbol TypeSelector TagHash Type
Function
@custom:selector
bytes4
Error
@custom:selector
bytes4
Event
@custom:topiczero
bytes32
Interface
@custom:interfaceid
bytes4 (XOR of selectors)