# Unchecked Calls

> Detect ignored external-call return values — silent failures from low-level call/delegatecall/staticcall, ignored ERC20 transfer return values, return-data length issues. Activate on `.call`, `.delegatecall`, `.staticcall`, `.send`, `transfer`/`transferFrom` (without SafeERC20), and any function returning `bool` whose return is discarded.

- **Type:** Skill
- **Install:** `agentstack add skill-omermaksutii-rugproof-unchecked-calls`
- **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/skills/unchecked-calls
- **Website:** https://omermaksutii.github.io/RugProof

## Install

```sh
agentstack add skill-omermaksutii-rugproof-unchecked-calls
```

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

## About

# Unchecked external calls detection

## When this applies

- Low-level `call`, `delegatecall`, `staticcall`, `send`
- ERC-20 `transfer` / `transferFrom` / `approve` (non-SafeERC20)
- Calls to user-provided addresses
- Multi-call patterns that don't propagate failures
- Try-catch swallowing all errors

## Detection patterns

### Ignored `.call` return (HIGH)
```solidity
(bool ok,) = target.call(data);   // ← ok unused
// or worse:
target.call(data);                 // ← Solidity ≥0.5 still allows this with warning
```
Always `require(ok, "call failed");` unless an intentional best-effort.

### ERC-20 without SafeERC20 (HIGH)
USDT and other non-conformant tokens don't return `bool`. Naive call:
```solidity
IERC20(usdt).transfer(to, amt);   // ← reverts on USDT due to ABI mismatch
```
Use OZ `SafeERC20`'s `safeTransfer` which handles missing return values.

### Return-data check missing (HIGH)
Even compliant ERC20 returning `false` instead of reverting is silently passed:
```solidity
bool ok = token.transfer(to, amt);   // ← ok unused, returns false on failure
```

### `try`/`catch` swallows everything (MEDIUM-HIGH)
```solidity
try external.call() { /* … */ }
catch { /* silently ignored */ }
```
Without inspecting `catch (bytes memory reason)`, you lose all info; transitioning a critical revert into a silent success is a bug.

### Address with no code (HIGH on `call`)
```solidity
(bool ok,) = target.call(data);
require(ok);
```
`ok = true` even if `target` is an EOA with no contract — `call` returns true. Add `target.code.length > 0` check.

### Permit followed by transferFrom — permit silently revertable (MEDIUM)
If `permit` reverts (because it was already used), the consuming transferFrom continues with a stale allowance. Use `try`/`catch` only to skip the permit reuse case.

### Multicall fail-skip (HIGH)
Some routers continue on per-call failure. If a treasury sweeps via multicall, a single failed call could drop revenue.

### `selfdestruct` / `transfer` no longer guaranteed on L2s with reduced gas
Forwarding 2300 gas is brittle on Berachain/Arbitrum/etc. — see [[dos-vectors]].

## Severity rubric

| Pattern | Severity |
|---|---|
| `.call` ignored, leads to silent fund loss | **Critical** |
| Non-SafeERC20 with USDT/non-conformant token | **High** |
| `target.call` without code-length check | **High** |
| Try-catch swallows revert without inspection | **High** |
| Multicall fail-skip on revenue path | **High** |
| `.transfer` (2300 gas) on L2 / to smart-wallet receivers | **Medium** |
| Best-effort fire-and-forget event hook | **Low** *(document intent)* |

## Remediation patterns

- Always check `(bool ok, bytes memory ret) = target.call(...)` and `require(ok, ...)`.
- For ERC-20: use OZ `SafeERC20` everywhere.
- After `.call` to dynamic addresses, check `target.code.length > 0` before relying on return.
- Replace `transfer` / `send` with `(bool ok,) = to.call{value: amt}("")` + check.
- For best-effort hooks (event-style), document with a comment and consider returning a status code rather than silently succeeding.

## False-positive notes

- A `.call` whose only side-effect-on-failure is a UX message can be best-effort — Info.
- Solady / Solmate sometimes use bare `call` with custom assembly checks — verify the surrounding bytes are inspected.

## Related

- [[token-compatibility]] — non-conformant ERC20s
- [[dos-vectors]]
- [[reentrancy]]

## 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-unchecked-calls
- 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%.
