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

# CALL (0xf1)

> External message call with value transfer and full state modification access

<Warning>
  **This page is a placeholder.** All examples on this page are currently AI-generated and are not correct. This documentation will be completed in the future with accurate, tested examples.
</Warning>

## Overview

**Opcode:** `0xf1`
**Introduced:** Frontier (EVM genesis)

CALL executes code from another account with specified gas, value, and calldata. The callee runs in its own context with `msg.sender` set to the caller and `msg.value` to the transferred amount. This is the primary mechanism for inter-contract communication.

## Specification

**Stack Input:**

```
gas       (max gas to forward)
address   (target account to call)
value     (wei to send)
inOffset  (calldata memory offset)
inLength  (calldata size)
outOffset (returndata memory offset)
outLength (returndata size)
```

**Stack Output:**

```
success   (1 if call succeeded, 0 if failed)
```

**Gas Cost:** 700 + value\_transfer + new\_account + cold\_access + memory\_expansion

**Operation:**

```
calldata = memory[inOffset:inOffset+inLength]
success = external_call(address, value, calldata, gas * 63/64)
memory[outOffset:outOffset+outLength] = returndata[0:min(outLength, returndata.length)]
push(success)
```

## Behavior

CALL performs a nested execution in the target account's context:

1. **Pop 7 stack arguments** in order: gas, address, value, inOffset, inLength, outOffset, outLength
2. **Validate static context:** Cannot transfer value in static call (EIP-214)
3. **Calculate gas cost:**
   * Base: 700 gas (Tangerine Whistle+)
   * Value transfer: +9,000 gas if value > 0
   * New account: +25,000 gas if recipient doesn't exist and value > 0
   * Cold access: +2,600 gas for first access (Berlin+)
   * Memory expansion for both input and output regions
4. **Read calldata** from memory at inOffset:inLength
5. **Forward gas:** Up to 63/64 of remaining gas after charging (EIP-150)
6. **Execute in callee context:**
   * msg.sender = caller address
   * msg.value = transferred value
   * Storage = callee's storage
   * Code = callee's code
7. **Transfer value:** Move ETH from caller to callee (if value > 0)
8. **Copy returndata** to memory at outOffset (up to min(outLength, returndata.length))
9. **Set return\_data** buffer to full returndata
10. **Push success flag** (1 if succeeded, 0 if reverted/failed)
11. **Refund unused gas** from child execution

**Key rules:**

* Callee stipend: +2,300 gas (free) if value > 0 for receive/fallback execution
* Cannot be called with value in static context (EIP-214)
* Success flag pushed even if call reverts (caller continues execution)
* Returndata accessible via RETURNDATASIZE/RETURNDATACOPY
* Call depth limited to 1,024 (pre-Tangerine Whistle enforcement)

## Examples

### Basic External Call

```typescript theme={null}
import { CALL } from '@tevm/voltaire/evm/system';
import { createFrame } from '@tevm/voltaire/evm/Frame';
import { Address } from '@tevm/voltaire/primitives';

const frame = createFrame({
  gasRemaining: 1000000n,
  address: Address("0x1234..."),
});

// Calldata: function selector for "transfer(address,uint256)"
const calldata = new Uint8Array([
  0xa9, 0x05, 0x9c, 0xbb,  // keccak256("transfer(address,uint256)")[:4]
  // ... ABI-encoded parameters
]);

// Write calldata to memory
for (let i = 0; i < calldata.length; i++) {
  frame.memory.set(i, calldata[i]);
}

// Stack: [gas=100000, address, value=0, inOffset=0, inLength=68, outOffset=0, outLength=32]
frame.stack.push(32n);                              // outLength
frame.stack.push(0n);                               // outOffset
frame.stack.push(68n);                              // inLength
frame.stack.push(0n);                               // inOffset
frame.stack.push(0n);                               // value
frame.stack.push(BigInt("0x742d35Cc..."));          // address
frame.stack.push(100000n);                          // gas

const err = CALL(frame);

console.log(err);                    // null (success)
console.log(frame.stack[0]);         // 1n (call succeeded) or 0n (call failed)
console.log(frame.return_data);      // Returndata from callee
```

### Value Transfer Call

```typescript theme={null}
import { Address } from '@tevm/voltaire/primitives';

// Send 1 ETH to address with empty calldata
const frame = createFrame({
  gasRemaining: 1000000n,
  address: Address("0x1234..."),
});

// Stack: [gas=50000, address, value=1 ETH, inOffset=0, inLength=0, outOffset=0, outLength=0]
frame.stack.push(0n);                               // outLength
frame.stack.push(0n);                               // outOffset
frame.stack.push(0n);                               // inLength
frame.stack.push(0n);                               // inOffset
frame.stack.push(1_000_000_000_000_000_000n);      // value (1 ETH)
frame.stack.push(BigInt("0x742d35Cc..."));          // recipient
frame.stack.push(50000n);                           // gas

const err = CALL(frame);

// Check success
if (frame.stack[0] === 1n) {
  console.log("Transfer succeeded");
} else {
  console.log("Transfer failed:", frame.return_data);
}
```

### Safe External Call Pattern

```solidity theme={null}
contract Caller {
    event CallResult(bool success, bytes returnData);

    // Safe external call with error handling
    function safeCall(
        address target,
        uint256 value,
        bytes memory data
    ) external returns (bool success, bytes memory returnData) {
        // Limit gas to prevent griefing (retain 1/64 for post-call logic)
        uint256 gasToForward = gasleft() * 63 / 64;

        assembly {
            // CALL(gas, address, value, inOffset, inLength, outOffset, outLength)
            success := call(
                gasToForward,
                target,
                value,
                add(data, 0x20),     // Skip length prefix
                mload(data),         // Data length
                0,                   // Don't copy to memory yet
                0
            )

            // Copy returndata
            let size := returndatasize()
            returnData := mload(0x40)  // Free memory pointer
            mstore(returnData, size)   // Store length
            returndatacopy(add(returnData, 0x20), 0, size)
            mstore(0x40, add(add(returnData, 0x20), size))  // Update free pointer
        }

        emit CallResult(success, returnData);
        return (success, returnData);
    }

    // Revert if call fails
    function strictCall(address target, bytes memory data) external payable {
        (bool success, bytes memory returnData) = target.call{value: msg.value}(data);

        if (!success) {
            // Bubble up revert reason
            assembly {
                revert(add(returnData, 32), mload(returnData))
            }
        }
    }
}
```

### Token Transfer Example

```solidity theme={null}
interface IERC20 {
    function transfer(address to, uint256 amount) external returns (bool);
}

contract TokenCaller {
    // Call ERC20.transfer using CALL opcode
    function transferTokens(
        address token,
        address recipient,
        uint256 amount
    ) external returns (bool) {
        // ABI encode: transfer(address,uint256)
        bytes memory callData = abi.encodeWithSelector(
            IERC20.transfer.selector,
            recipient,
            amount
        );

        bool success;
        bytes memory returnData;

        assembly {
            // CALL with no value transfer
            success := call(
                gas(),                    // Forward all gas
                token,                    // Target contract
                0,                        // No ETH sent
                add(callData, 0x20),     // Calldata location
                mload(callData),          // Calldata length
                0,                        // Return data location
                0                         // Return data length
            )

            // Copy return data
            let size := returndatasize()
            returnData := mload(0x40)
            mstore(returnData, size)
            returndatacopy(add(returnData, 0x20), 0, size)
            mstore(0x40, add(add(returnData, 0x20), size))
        }

        // Decode return value (bool)
        require(success && returnData.length >= 32, "Transfer failed");
        return abi.decode(returnData, (bool));
    }
}
```

## Gas Cost

**Total cost:** 700 + value\_transfer + new\_account + cold\_access + memory\_expansion + forwarded\_gas

### Base Cost: 700 gas (Tangerine Whistle+)

**Pre-Tangerine Whistle:** 40 gas

### Value Transfer: +9,000 gas

Charged when `value > 0`:

```
if (value > 0) {
  cost += 9000  // CallValueTransferGas
}
```

**Stipend:** Callee receives additional +2,300 gas (free to caller) for receive/fallback execution.

### New Account: +25,000 gas

Charged when sending value to non-existent account:

```
if (value > 0 && !accountExists(address)) {
  cost += 25000  // CallNewAccountGas
}
```

**Account exists if:** balance > 0 OR code.length > 0 OR nonce > 0

### Cold Access: +2,600 gas (Berlin+)

**EIP-2929 (Berlin+):** First access to address in transaction:

```
if (firstAccess(address)) {
  cost += 2600  // ColdAccountAccess
} else {
  cost += 100   // WarmStorageRead
}
```

**Pre-Berlin:** No access list costs.

### Memory Expansion

Dynamic cost for both input and output regions:

```typescript theme={null}
const inEnd = inOffset + inLength;
const outEnd = outOffset + outLength;
const maxEnd = Math.max(inEnd, outEnd);

const words = Math.ceil(maxEnd / 32);
const expansionCost = words ** 2 / 512 + 3 * words;
```

### Gas Forwarding (EIP-150)

**Tangerine Whistle+:** Caller retains 1/64th, forwards up to 63/64:

```
remaining_after_charge = gas_remaining - base_cost
max_forwarded = remaining_after_charge - (remaining_after_charge / 64)
actual_forwarded = min(gas_parameter, max_forwarded)
```

**Pre-Tangerine Whistle:** Forward all remaining gas.

### Example Calculation

```typescript theme={null}
// Call external contract with value, cold access, memory expansion
const gasRemaining = 100000n;
const value = 1_000_000_000_000_000_000n;  // 1 ETH
const inLength = 68;  // Function call with parameters
const outLength = 32;  // Return value

// Base cost
let cost = 700n;  // Tangerine Whistle+

// Value transfer
cost += 9000n;  // CallValueTransferGas

// New account (assume account doesn't exist)
cost += 25000n;  // CallNewAccountGas

// Cold access (Berlin+, first access)
cost += 2600n;  // ColdAccountAccess

// Memory expansion (assume clean memory)
const maxEnd = Math.max(68, 32);  // 68 bytes
const words = Math.ceil(68 / 32);  // 3 words
const memCost = Math.floor(words ** 2 / 512) + 3 * words;  // 9 gas
cost += BigInt(memCost);

// Total charged: 37,309 gas

// Gas forwarding (63/64 rule)
const afterCharge = gasRemaining - cost;  // 62,691 gas
const maxForward = afterCharge - afterCharge / 64n;  // 61,711 gas
// Forward min(gas_param, max_forward)

// Total consumed: 37,309 + gas_actually_used_by_callee
```

## Common Usage

### Reentrancy Guard

```solidity theme={null}
contract ReentrancyGuard {
    uint256 private locked = 1;

    modifier nonReentrant() {
        require(locked == 1, "Reentrancy");
        locked = 2;
        _;
        locked = 1;
    }

    // Safe withdrawal with reentrancy protection
    function withdraw(uint256 amount) external nonReentrant {
        require(balances[msg.sender] >= amount);

        // Update state BEFORE external call
        balances[msg.sender] -= amount;

        // External call (potential reentrancy point)
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success, "Transfer failed");
    }
}
```

### Multicall Pattern

```solidity theme={null}
contract Multicall {
    struct Call {
        address target;
        bytes callData;
    }

    struct Result {
        bool success;
        bytes returnData;
    }

    // Execute multiple calls in single transaction
    function aggregate(Call[] memory calls) external returns (Result[] memory results) {
        results = new Result[](calls.length);

        for (uint256 i = 0; i < calls.length; i++) {
            (bool success, bytes memory returnData) = calls[i].target.call(calls[i].callData);
            results[i] = Result(success, returnData);
        }
    }

    // Execute multiple calls, revert if any fails
    function tryAggregate(Call[] memory calls) external returns (bytes[] memory returnData) {
        returnData = new bytes[](calls.length);

        for (uint256 i = 0; i < calls.length; i++) {
            (bool success, bytes memory data) = calls[i].target.call(calls[i].callData);
            require(success, "Call failed");
            returnData[i] = data;
        }
    }
}
```

### Router Pattern

```solidity theme={null}
contract Router {
    // Route call to appropriate handler based on selector
    function route(address target, bytes memory data) external payable returns (bytes memory) {
        require(data.length >= 4, "Invalid calldata");

        // Extract function selector
        bytes4 selector;
        assembly {
            selector := mload(add(data, 32))
        }

        // Validate selector against whitelist
        require(isAllowed(selector), "Selector not allowed");

        // Forward call
        (bool success, bytes memory returnData) = target.call{value: msg.value}(data);

        if (!success) {
            assembly {
                revert(add(returnData, 32), mload(returnData))
            }
        }

        return returnData;
    }

    function isAllowed(bytes4 selector) internal pure returns (bool) {
        // Whitelist logic
        return true;
    }
}
```

## Security

### Reentrancy Attacks

CALL's primary security risk - external code can re-enter caller:

```solidity theme={null}
// VULNERABLE: DAO hack pattern
contract Vulnerable {
    mapping(address => uint256) public balances;

    function withdraw() external {
        uint256 amount = balances[msg.sender];
        require(amount > 0);

        // VULNERABILITY: External call before state update
        (bool success, ) = msg.sender.call{value: amount}("");
        require(success);

        balances[msg.sender] = 0;  // Too late! Already re-entered
    }
}

contract Attacker {
    Vulnerable victim;
    uint256 public attackCount;

    receive() external payable {
        // Re-enter during CALL
        if (attackCount < 10) {
            attackCount++;
            victim.withdraw();  // Drain funds
        }
    }
}
```

**Mitigation: Checks-Effects-Interactions pattern:**

```solidity theme={null}
// SAFE: State updates before external calls
function withdraw() external {
    uint256 amount = balances[msg.sender];
    require(amount > 0);

    // Update state FIRST
    balances[msg.sender] = 0;

    // External call LAST
    (bool success, ) = msg.sender.call{value: amount}("");
    require(success);
}
```

### Return Value Check

Must check success flag - call can fail silently:

```solidity theme={null}
// VULNERABLE: Ignoring return value
function unsafeTransfer(address to, uint256 amount) external {
    // Assumes success, but call might fail!
    to.call{value: amount}("");
}

// SAFE: Check return value
function safeTransfer(address to, uint256 amount) external {
    (bool success, ) = to.call{value: amount}("");
    require(success, "Transfer failed");
}
```

### Gas Griefing

Callee controls gas consumption:

```solidity theme={null}
// VULNERABLE: Unbounded gas consumption
function dangerousCall(address target, bytes memory data) external {
    // Forwards 63/64 of all remaining gas!
    target.call(data);  // Callee can consume all gas
}

// BETTER: Limit forwarded gas
function limitedCall(address target, bytes memory data) external {
    // Limit gas to specific amount
    target.call{gas: 100000}(data);
}
```

### Returndata Bomb

Large returndata can cause OOG when copying:

```solidity theme={null}
// VULNERABLE: Unbounded returndata copy
function unsafeCopy(address target) external returns (bytes memory) {
    (bool success, bytes memory data) = target.call("");
    return data;  // Might copy gigabytes!
}

// SAFE: Limit returndata size
function safeCopy(address target) external returns (bytes memory) {
    (bool success, ) = target.call("");
    require(success);

    // Manual copy with size limit
    uint256 size = min(returndatasize(), 1024);
    bytes memory data = new bytes(size);
    assembly {
        returndatacopy(add(data, 32), 0, size)
    }
    return data;
}
```

### Value Transfer Validation

Ensure sufficient balance before value transfer:

```solidity theme={null}
// VULNERABLE: No balance check
function unsafeSend(address to, uint256 amount) external {
    to.call{value: amount}("");  // Reverts if insufficient balance
}

// SAFE: Explicit balance validation
function safeSend(address to, uint256 amount) external {
    require(address(this).balance >= amount, "Insufficient balance");
    (bool success, ) = to.call{value: amount}("");
    require(success, "Transfer failed");
}
```

## Implementation

<Tabs>
  <Tab title="TypeScript">
    ```typescript theme={null}
    /**
     * CALL opcode (0xf1) - Message call into an account
     */
    export function call(frame: FrameType): EvmError | null {
      // Pop 7 arguments
      const gas = popStack(frame);
      const address = popStack(frame);
      const value = popStack(frame);
      const inOffset = popStack(frame);
      const inLength = popStack(frame);
      const outOffset = popStack(frame);
      const outLength = popStack(frame);

      // EIP-214: Cannot transfer value in static context
      if (frame.isStatic && value > 0n) {
        return { type: "WriteProtection" };
      }

      // Calculate gas cost
      let gasCost = 700n;  // Base (Tangerine Whistle+)

      // Value transfer
      if (value > 0n) {
        gasCost += 9000n;  // CallValueTransferGas

        // Check if account exists
        const exists = accountExists(address);
        if (!exists) {
          gasCost += 25000n;  // CallNewAccountGas
        }
      }

      // Cold access cost (Berlin+)
      const accessCost = getAccessCost(address);
      gasCost += accessCost;

      // Memory expansion
      const inEnd = inLength > 0 ? inOffset + inLength : 0;
      const outEnd = outLength > 0 ? outOffset + outLength : 0;
      const maxEnd = Math.max(inEnd, outEnd);

      if (maxEnd > 0) {
        gasCost += memoryExpansionCost(frame, maxEnd);
        updateMemorySize(frame, maxEnd);
      }

      // Calculate forwarded gas (63/64 rule)
      const afterCharge = frame.gasRemaining - gasCost;
      const maxForward = afterCharge - afterCharge / 64n;
      const forwardedGas = min(gas, maxForward);

      // Charge total cost
      consumeGas(frame, gasCost + forwardedGas);

      // Read calldata
      const calldata = readMemory(frame, inOffset, inLength);

      // Execute call
      const result = executeCall({
        target: address,
        value: value,
        data: calldata,
        gas: forwardedGas
      });

      // Refund unused gas
      frame.gasRemaining += result.gasLeft;

      // Copy returndata to memory
      const copySize = min(outLength, result.returnData.length);
      writeMemory(frame, outOffset, result.returnData.slice(0, copySize));

      // Set return_data buffer
      frame.returnData = result.returnData;

      // Push success flag
      pushStack(frame, result.success ? 1n : 0n);

      frame.pc += 1;
      return null;
    }
    ```
  </Tab>
</Tabs>

## References

* **[Yellow Paper](https://ethereum.github.io/yellowpaper/paper.pdf)** - Section 9.4.4 (Message Call)
* **[EIP-150](https://eips.ethereum.org/EIPS/eip-150)** - Gas cost changes (63/64 rule)
* **[EIP-214](https://eips.ethereum.org/EIPS/eip-214)** - STATICCALL and static context
* **[EIP-2929](https://eips.ethereum.org/EIPS/eip-2929)** - Access list gas costs
* **[evm.codes - CALL](https://www.evm.codes/#f1)** - Interactive reference
* **[Consensys Best Practices](https://consensys.github.io/smart-contract-best-practices/)** - Reentrancy patterns
