Install
$ agentstack add skill-omermaksutii-rugproof-solady-erc20-permit2-assumptions ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Solady ERC20 / permit / DN404 assumption detection
When this applies
Trigger on any of:
import {ERC20} from "solady/tokens/ERC20.sol";orERC2612,DN404,DN404Mirror,ERC4626from 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)
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)
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)
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)
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
- Read
DOMAIN_SEPARATOR()off the token instead of reconstructing it; never hardcode the version string. - Catch
bytes, notError(string)— match Solady custom-error selectors or use a genericcatch. - Detect DN404 (e.g. via
DN404Mirrorlinkage / interface) and route it through NFT-aware logic; budget gas and treat transfers as callback-bearing. - 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
- Source: omermaksutii/RugProof
- License: MIT
- Homepage: https://omermaksutii.github.io/RugProof
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.