# 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.

- Skill: `majiayu000/crane-natspec` (Agent Skill, multi-file: 2 files)
- Install (CLI): `npx skillmds add majiayu000/crane-natspec`
- Raw SKILL.md: https://api.skillmd.com/api/skills/majiayu000/crane-natspec/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Docs & Writing
- License: MIT
- Author: majiayu000 (https://skillmd.com/u/majiayu000)
- Updated: 2026-09-09
- Page: https://skillmd.com/skills/majiayu000/crane-natspec

---


# 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:

```solidity
// 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

```solidity
// 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

| Tag | Purpose | Example |
|-----|---------|---------|
| `@custom:signature` | Canonical signature string | `transfer(address,uint256)` |
| `@custom:selector` | bytes4 selector | `0xa9059cbb` |

```solidity
/// @notice Transfers tokens
/// @custom:signature transfer(address,uint256)
/// @custom:selector 0xa9059cbb
function transfer(address to_, uint256 amount_) external returns (bool);
```

### Errors

| Tag | Purpose | Example |
|-----|---------|---------|
| `@custom:signature` | Canonical error signature | `NotOwner(address)` |
| `@custom:selector` | bytes4 selector | `0x30cd7471` |

```solidity
/// @notice Thrown when caller is not the owner
/// @custom:signature NotOwner(address)
/// @custom:selector 0x30cd7471
error NotOwner(address caller);
```

### Events

| Tag | Purpose | Example |
|-----|---------|---------|
| `@custom:signature` | Canonical event signature | `Transfer(address,address,uint256)` |
| `@custom:topiczero` | bytes32 topic0 hash | `0xddf252ad...` |

```solidity
/// @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

| Tag | Purpose | Example |
|-----|---------|---------|
| `@custom:interfaceid` | bytes4 interface ID | `0x01ffc9a7` |

```solidity
/// @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)

```bash
cast sig "transfer(address,uint256)"
# Output: 0xa9059cbb
```

### Error Selector (bytes4)

```bash
cast sig "NotOwner(address)"
# Output: 0x30cd7471
```

### Event Topic0 (bytes32)

```bash
cast keccak "Transfer(address,address,uint256)"
# Output: 0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef
```

### Interface ID (bytes4)

For interface IDs, prefer computing in Solidity:

```solidity
bytes4 interfaceId = type(IMyInterface).interfaceId;
```

Or compute manually by XOR-ing all function selectors:

```bash
# 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

```solidity
// 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 Type | Selector Tag | Hash Type |
|-------------|--------------|-----------|
| Function | `@custom:selector` | bytes4 |
| Error | `@custom:selector` | bytes4 |
| Event | `@custom:topiczero` | bytes32 |
| Interface | `@custom:interfaceid` | bytes4 (XOR of selectors) |

