> ## Documentation Index
> Fetch the complete documentation index at: https://docs.webacy.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Unchecked Low Level Calls

> Detect unchecked low-level call vulnerabilities in Solidity where ignored return values cause silent failures, lost funds, and corrupted contract state.

## Overview

Unchecked low-level calls occur when Solidity's low-level functions (`call`, `delegatecall`, `staticcall`) are used without properly verifying their return values.

<Warning>
  Failed low-level calls return `false` instead of reverting. If not checked, transactions appear successful while silently failing.
</Warning>

## The Risk

Low-level calls in Solidity don't automatically revert on failure. If the return value isn't checked:

* Funds can be "sent" but never arrive
* State changes occur based on failed operations
* Users lose assets without error messages

## Vulnerable Patterns

### Unchecked Call

```solidity theme={null}
// VULNERABLE: Return value ignored
function withdraw(uint256 amount) public {
    balances[msg.sender] -= amount;
    payable(msg.sender).call{value: amount}("");  // Silent failure!
}
```

### Unchecked Delegatecall

```solidity theme={null}
// VULNERABLE: Delegatecall without check
function upgrade(address impl) public onlyOwner {
    impl.delegatecall(abi.encodeWithSignature("initialize()"));
}
```

### Unchecked Transfer

```solidity theme={null}
// VULNERABLE: ERC20 transfer without check
function distribute(address token, address[] memory recipients) public {
    for (uint i = 0; i < recipients.length; i++) {
        IERC20(token).transfer(recipients[i], 100);  // May fail silently
    }
}
```

## Safe Patterns

### Check Return Value

```solidity theme={null}
// SAFE: Explicit check
function withdraw(uint256 amount) public {
    balances[msg.sender] -= amount;
    (bool success, ) = payable(msg.sender).call{value: amount}("");
    require(success, "Transfer failed");
}
```

### Use SafeERC20

```solidity theme={null}
// SAFE: SafeERC20 library
import "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol";

using SafeERC20 for IERC20;

function distribute(address token, address[] memory recipients) public {
    for (uint i = 0; i < recipients.length; i++) {
        IERC20(token).safeTransfer(recipients[i], 100);  // Reverts on failure
    }
}
```

### Use Transfer/Send (Limited)

```solidity theme={null}
// SAFE: transfer() reverts on failure (but limited gas)
function withdraw(uint256 amount) public {
    balances[msg.sender] -= amount;
    payable(msg.sender).transfer(amount);  // Reverts if fails
}
```

## Detection Tags

| Tag                      | Description                                |
| ------------------------ | ------------------------------------------ |
| `unchecked_call`         | `call()` return value not verified         |
| `unchecked_delegatecall` | `delegatecall()` return value not verified |
| `unchecked_send`         | `send()` return value not verified         |
| `unchecked_transfer`     | ERC20 `transfer()` return not verified     |

## API Response Example

```json theme={null}
{
  "issues": [
    {
      "tag": "unchecked_call",
      "severity": "high",
      "description": "Low-level call without return value check",
      "location": "withdraw(uint256):L45"
    }
  ]
}
```

## Why Low-Level Calls?

Despite risks, low-level calls are sometimes necessary:

| Method       | Gas Forwarded | Reverts on Failure |
| ------------ | ------------- | ------------------ |
| `transfer()` | 2300          | Yes                |
| `send()`     | 2300          | No (returns bool)  |
| `call()`     | All available | No (returns bool)  |

<Note>
  `transfer()` and `send()` forward only 2300 gas, which may not be enough for receiving contracts with complex fallback functions.
</Note>
