# Solady Erc20 Permit2 Assumptions

> Detect unsafe assumptions about Solady's gas-optimized ERC20/ERC2612 permit and DN404 metadata. Solady's ERC20 uses custom storage slots, returns bools via assembly, exposes a virtual `_constantNameHash`/`_versionHash` for permit domain separation, and its DN404 mirror splits ERC20/ERC721 logic — integrators that assume OZ-style behavior, revert strings, or that `name()`/`decimals()` are always p…

- **Type:** Skill
- **Install:** `agentstack add skill-omermaksutii-rugproof-solady-erc20-permit2-assumptions`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [omermaksutii](https://agentstack.voostack.com/s/omermaksutii)
- **Installs:** 0
- **Category:** [Security](https://agentstack.voostack.com/c/security)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [omermaksutii](https://github.com/omermaksutii)
- **Source:** https://github.com/omermaksutii/RugProof/tree/main/rules/solady-gotchas/skills/solady-erc20-permit2-assumptions
- **Website:** https://omermaksutii.github.io/RugProof

## Install

```sh
agentstack add skill-omermaksutii-rugproof-solady-erc20-permit2-assumptions
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# Solady ERC20 / permit / DN404 assumption detection

## When this applies

Trigger on any of:

- `import {ERC20} from "solady/tokens/ERC20.sol";` or `ERC2612`, `DN404`, `DN404Mirror`, `ERC4626` from solady
- Integrations calling `permit(...)` and assuming a fixed EIP-712 domain / version string
- Code relying on Solady's revert *reasons* (Solady reverts with custom errors / 4-byte selectors, not strings)
- DN404 tokens treated as plain ERC20 (the NFT mirror has separate transfer semantics)
- Off-chain indexers decoding events / revert data assuming OZ layout
- Overriding `name()`, `symbol()`, `_constantNameHash`, `_versionHash`, or `_domainNameAndVersion`

## Detection patterns

### Hardcoded permit domain separator (MEDIUM)
```solidity
bytes32 DOMAIN = keccak256(abi.encode(
    TYPE_HASH, keccak256("MyToken"), keccak256("1"), block.chainid, token
));
// ← assumes version "1"; Solady ERC2612 default version is "1" but DN404/overrides may differ
```
Solady derives the domain via `_domainNameAndVersion()` (default version `"1"`). If the token overrides `_versionHash` or `name()`, a hand-rolled separator mismatches and every `permit` reverts `InvalidPermit`.
**Signal:** off-chain or on-chain code reconstructs the EIP-712 domain instead of reading `DOMAIN_SEPARATOR()` from the token.

### Relying on revert strings (MEDIUM)
```solidity
try token.transferFrom(a, b, amt) returns (bool) {}
catch Error(string memory reason) {           // ← Solady reverts with custom errors,
    if (keccak256(bytes(reason)) == ...) {}    //    NOT Error(string); this branch never hits
}
```
Solady reverts `InsufficientBalance()` / `InsufficientAllowance()` (4-byte). `catch Error(string)` won't match; only `catch (bytes memory)` / `catch` will.
**Signal:** `catch Error(string)` or string comparison used to branch on a Solady token failure.

### DN404 treated as vanilla ERC20 (MEDIUM)
```solidity
dn404Token.transfer(to, amt);   // also mints/burns NFTs in the mirror; gas + reentrancy surface
```
DN404 fractionalizes an NFT collection; an ERC20 transfer can mint/burn mirror NFTs and invoke `onERC721Received`-style hooks. Treating it as inert ERC20 ignores added gas and a callback surface.
**Signal:** DN404 token integrated through a path that assumes ERC20 transfers have no side effects / no callbacks.

### Assuming metadata always present (LOW-MEDIUM)
```solidity
uint8 d = IERC20(token).decimals();   // Solady ERC20 leaves name/symbol/decimals virtual
```
Solady's base ERC20 leaves `name()`/`symbol()` as `virtual` returning empty unless overridden; a minimal token may omit them.
**Signal:** unguarded `decimals()`/`name()` read on a Solady-derived token.

## Severity rubric

| Pattern | Severity | Notes |
|---|---|---|
| Hand-rolled permit domain mismatching token → permits brick | **Medium** | Liveness, not loss; per-token |
| Revert-string branching never executes | **Medium** | Silent wrong control flow |
| DN404 used as plain ERC20 (callback / gas surprise) | **Medium** | Reentrancy/gas surface, see [[reentrancy]] |
| Unguarded metadata read | **Low** | Reverts or returns empty |

## Remediation patterns

1. **Read `DOMAIN_SEPARATOR()`** off the token instead of reconstructing it; never hardcode the version string.
2. **Catch `bytes`, not `Error(string)`** — match Solady custom-error selectors or use a generic `catch`.
3. **Detect DN404** (e.g. via `DN404Mirror` linkage / interface) and route it through NFT-aware logic; budget gas and treat transfers as callback-bearing.
4. **Guard metadata** with try/catch defaulting decimals to 18.

## False-positive notes

- Reading `DOMAIN_SEPARATOR()` directly (not reconstructing it) is correct — no finding.
- A non-DN404 Solady ERC20 with metadata overridden is a normal token — only flag missing overrides.
- Generic `catch {}` already handles custom errors — downgrade.

## Related

- [[signature-malleability]] — permit signature handling
- [[token-compatibility]] — non-standard return / metadata
- [[reentrancy]] — DN404 mirror callbacks

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [omermaksutii](https://github.com/omermaksutii)
- **Source:** [omermaksutii/RugProof](https://github.com/omermaksutii/RugProof)
- **License:** MIT
- **Homepage:** https://omermaksutii.github.io/RugProof

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** no
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** no
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-omermaksutii-rugproof-solady-erc20-permit2-assumptions
- Seller: https://agentstack.voostack.com/s/omermaksutii
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
