# How CallFlags Control Smart Contract Method Invocation Permissions and Scope in Neo

> Learn how CallFlags control Neo smart contract invocation permissions and scope. Understand how this byte-sized bit-mask enum dictates execution and ensures security.

- Repository: [The Neo Project/neo](https://github.com/neo-project/neo)
- Tags: internals
- Published: 2026-03-08

---

**CallFlags are a byte-sized bit-mask enum that defines what operations a Neo smart contract may perform, with the runtime verifying that the caller's flags contain the required flags declared on the callee method before allowing execution to proceed.**

In the neo-project/neo repository, CallFlags serve as the fundamental permission mechanism governing smart contract execution. These flags determine whether a contract can read or write storage, invoke other contracts, or emit notifications. Understanding how CallFlags control smart contract method invocation permissions and scope is essential for developing secure Neo smart contracts and analyzing their behavior.

## Understanding the CallFlags Bit-Mask Enum

### Core Flag Definitions

The `CallFlags` enum is defined in [`src/Neo/SmartContract/CallFlags.cs`](https://github.com/neo-project/neo/blob/main/src/Neo/SmartContract/CallFlags.cs) as a `[Flags]` byte enumeration:

```csharp
[Flags]
public enum CallFlags : byte
{
    None          = 0,
    ReadStates    = 0b00000001,   // contract may read storage
    WriteStates   = 0b00000010,   // contract may write storage
    AllowCall     = 0b00000100,   // contract may invoke other contracts
    AllowNotify   = 0b00001000,   // contract may emit notifications
    States        = ReadStates | WriteStates,
    ReadOnly      = ReadStates | AllowCall,
    All           = States | AllowCall | AllowNotify
}

```

### Composite Flag Constants

The enum provides composite values to simplify common permission sets. **States** combines `ReadStates` and `WriteStates`, permitting both reading and writing to storage. **ReadOnly** combines `ReadStates` with `AllowCall`, allowing storage reads and inter-contract calls but explicitly prohibiting storage writes. **All** grants every capability, serving as the default context for unrestricted execution.

## Where CallFlags Are Stored and Propagated

### ExecutionContextState Storage

Each execution context stores its current flags in `ExecutionContextState`, defined in [`src/Neo/SmartContract/ExecutionContextState.cs`](https://github.com/neo-project/neo/blob/main/src/Neo/SmartContract/ExecutionContextState.cs):

```csharp
public CallFlags CallFlags { get; set; } = CallFlags.All;

```

This property defaults to `CallFlags.All`, meaning scripts can perform any operation unless explicitly restricted.

### Loading Scripts with Custom Flags

When loading a script, callers can override the default flags using the `configureState` parameter:

```csharp
engine.LoadScript(scriptBytes, configureState: p => p.CallFlags = CallFlags.None);

```

This pattern creates a sandboxed environment where the loaded script cannot perform any stateful operations, useful for verification scripts that must not alter global state.

## Declaring Required CallFlags on Native Contracts

### ContractMethodAttribute Usage

Native contract methods declare their required permissions using `ContractMethodAttribute`. For example, in [`src/Neo/SmartContract/Native/Treasury.cs`](https://github.com/neo-project/neo/blob/main/src/Neo/SmartContract/Native/Treasury.cs), the `Verify` method requires only read access:

```csharp
[ContractMethod(CpuFee = 1 << 5, RequiredCallFlags = CallFlags.ReadStates)]
private bool Verify(ApplicationEngine engine) => CheckCommittee(engine);

```

This declaration ensures that callers must possess the `ReadStates` flag to invoke this method.

### ContractMethodMetadata Runtime Reflection

The runtime extracts these requirements through `ContractMethodMetadata`, defined in [`src/Neo/SmartContract/Native/ContractMethodMetadata.cs`](https://github.com/neo-project/neo/blob/main/src/Neo/SmartContract/Native/ContractMethodMetadata.cs):

```csharp
public CallFlags RequiredCallFlags { get; }

```

This property is populated from the `ContractMethodAttribute` during contract initialization, making the permission requirements available for runtime validation.

## Runtime Permission Verification

### The Permission Check in NativeContract.Invoke

When a native contract method is invoked, the `Invoke` method in [`src/Neo/SmartContract/Native/NativeContract.cs`](https://github.com/neo-project/neo/blob/main/src/Neo/SmartContract/Native/NativeContract.cs) performs the critical permission check:

```csharp
if (!state.CallFlags.HasFlag(method.RequiredCallFlags))
    throw new InvalidOperationException($"Cannot call this method with the flag {state.CallFlags}.");

```

This validation ensures that the caller's current flags contain all bits required by the callee. If the check fails, the transaction aborts with an `InvalidOperationException`, preventing unauthorized state modifications or external calls.

### Inter-Contract Call Flag Propagation

When contracts invoke other contracts via `ApplicationEngine.CallContract`, the caller explicitly specifies the flags for the new execution context:

```csharp
engine.CallContract(targetHash, method, CallFlags.ReadOnly, args);

```

The engine creates a new execution context with the supplied `CallFlags`, allowing contracts to deliberately restrict the capabilities of contracts they invoke. This propagation mechanism enables fine-grained control over permission delegation across contract boundaries.

## Practical Code Examples

### Read-Only Contract Calls

The following example demonstrates invoking a contract method that only requires storage read permissions:

```csharp
// Engine setup (test environment)
var engine = ApplicationEngine.Create(
    TriggerType.Application, null, snapshotCache, null, ProtocolSettings.Default);

// Call a contract method that only reads storage, passing ReadOnly flag.
engine.CallContract(contractHash, "balanceOf",
    CallFlags.ReadOnly, new VM.Types.Array { accountHash });

```

Because `CallFlags.ReadOnly` includes `ReadStates` and `AllowCall`, this satisfies native methods requiring only `ReadStates` while preventing any storage modifications.

### Attempting Writes Without Proper Flags

Attempting to call a state-modifying method without `WriteStates` results in an exception:

```csharp
engine.CallContract(contractHash, "transfer",
    CallFlags.ReadOnly, new VM.Types.Array { from, to, amount });

```

If the `transfer` method declares `RequiredCallFlags = CallFlags.States` (which includes `WriteStates`), the runtime throws:

```

InvalidOperationException: Cannot call this method with the flag ReadOnly.

```

This prevents unauthorized state modifications even if the contract logic is otherwise valid.

### Sandboxing with CallFlags.None

To create a completely restricted execution environment, set `CallFlags.None` when loading a script:

```csharp
engine.LoadScript(scriptBytes, configureState: p => p.CallFlags = CallFlags.None);

```

Any subsequent native contract call will fail because `None` does not contain required flags (e.g., `ReadStates`). This is useful for sandboxing or for verification scripts that must not alter state.

### Emitting Events with AllowNotify

To permit a contract to emit notifications, explicitly include the `AllowNotify` flag:

```csharp
engine.CallContract(contractHash, "emitEvent",
    CallFlags.AllowNotify, new VM.Types.Array { "hello" });

```

Only contracts that declare `RequiredCallFlags` that include `AllowNotify` (or `All`) will accept this call.

## Key Source Files Reference

| File | Role | Link |
|------|------|------|
| [`CallFlags.cs`](https://github.com/neo-project/neo/blob/main/CallFlags.cs) | Defines the flag enum used throughout the VM. | [CallFlags.cs](https://github.com/neo-project/neo/blob/master-n3/src/Neo/SmartContract/CallFlags.cs) |
| [`ExecutionContextState.cs`](https://github.com/neo-project/neo/blob/main/ExecutionContextState.cs) | Holds the per‑call `CallFlags` value (defaults to `All`). | [ExecutionContextState.cs](https://github.com/neo-project/neo/blob/master-n3/src/Neo/SmartContract/ExecutionContextState.cs) |
| [`ContractMethodAttribute.cs`](https://github.com/neo-project/neo/blob/main/ContractMethodAttribute.cs) | Attribute used on native contract methods to declare required flags. | [ContractMethodAttribute.cs](https://github.com/neo-project/neo/blob/master-n3/src/Neo/SmartContract/Native/ContractMethodAttribute.cs) |
| [`ContractMethodMetadata.cs`](https://github.com/neo-project/neo/blob/main/ContractMethodMetadata.cs) | Reads the attribute and exposes `RequiredCallFlags` for runtime checks. | [ContractMethodMetadata.cs](https://github.com/neo-project/neo/blob/master-n3/src/Neo/SmartContract/Native/ContractMethodMetadata.cs) |
| [`NativeContract.cs`](https://github.com/neo-project/neo/blob/main/NativeContract.cs) | Core dispatcher that verifies `state.CallFlags` against `method.RequiredCallFlags`. | [NativeContract.cs – Invoke](https://github.com/neo-project/neo/blob/master-n3/src/Neo/SmartContract/Native/NativeContract.cs) |
| [`UT_InteropService.cs`](https://github.com/neo-project/neo/blob/main/UT_InteropService.cs) (unit test) | Demonstrates successful and failing calls based on flag mismatches. | [UT_InteropService.cs](https://github.com/neo-project/neo/blob/master-n3/tests/Neo.UnitTests/SmartContract/UT_InteropService.cs) |

## Summary

- **CallFlags** are a byte-sized bit-mask enum that defines what operations a Neo smart contract may perform during execution.
- Each execution context stores its current flags in `ExecutionContextState.CallFlags`, defaulting to `CallFlags.All` for unrestricted access.
- Native contract methods declare required permissions via `ContractMethodAttribute.RequiredCallFlags`, which `ContractMethodMetadata` exposes to the runtime.
- The `NativeContract.Invoke` method enforces permissions by verifying `state.CallFlags.HasFlag(method.RequiredCallFlags)`, throwing `InvalidOperationException` for violations.
- Inter-contract calls propagate flags explicitly through `ApplicationEngine.CallContract`, allowing callers to restrict callee capabilities.
- Practical patterns include using `CallFlags.ReadOnly` for safe queries, `CallFlags.None` for sandboxing, and explicit `AllowNotify` for event emission.

## Frequently Asked Questions

### What happens if a contract tries to call a method without the required CallFlags?

The runtime throws an `InvalidOperationException` with a message indicating that the method cannot be called with the current flag set. This occurs in `NativeContract.Invoke` after checking `state.CallFlags.HasFlag(method.RequiredCallFlags)`, immediately aborting the transaction and preventing unauthorized operations.

### Can a contract reduce its own permissions before calling another contract?

Yes. When invoking another contract via `ApplicationEngine.CallContract`, the caller explicitly specifies the `CallFlags` for the new execution context. By passing restrictive flags such as `CallFlags.ReadOnly` or `CallFlags.None`, a contract can sandbox the callee, ensuring it cannot modify state or invoke further contracts beyond the granted permissions.

### What is the difference between CallFlags.States and CallFlags.ReadOnly?

`CallFlags.States` combines `ReadStates` and `WriteStates`, permitting both reading and writing to storage. `CallFlags.ReadOnly` combines `ReadStates` with `AllowCall`, allowing storage reads and inter-contract calls but explicitly prohibiting storage writes. Use `States` for state-modifying operations and `ReadOnly` for safe queries that may need to invoke other contracts.

### How do I completely restrict a script from performing any state operations?

Load the script with `CallFlags.None` by providing a configuration lambda to `LoadScript`: `engine.LoadScript(scriptBytes, configureState: p => p.CallFlags = CallFlags.None);`. This sets the execution context's flags to zero, removing all permissions including `ReadStates`, `WriteStates`, `AllowCall`, and `AllowNotify`. Any attempt to invoke native contract methods will fail because the context lacks the required flags.