> For the complete documentation index, see [llms.txt](/llms.txt)

# Call Types

## What is a Call

We say that a smart contract is called when one of its functions is invoked and its code is run. This means there'll be:

* a caller
* arguments
* return values
* a call status (successful or failed)

There are multiple types of calls, and some of the naming can make things **very** confusing. This page lists the different call types and execution modes, pointing out key differences between them.

<!-- -->

A key property of Aztec calls is that contracts can call each other privately, keeping even the call stack itself private. This two-minute explainer covers the idea before we get into the details (find more on the [video lessons](/developers/docs/resources/video_lessons.md) page):

[What is Private Composability? An Aztec Explainer](https://www.youtube-nocookie.com/embed/idxRuGQnQKs)

## Ethereum Call Types

Aztec's design is heavily influenced by Ethereum, and many APIs and concepts are similar. This section provides background on Ethereum call types for context. If you're already familiar with Ethereum, you can skip to [Aztec Call Types](#aztec-call-types).

Ethereum background (click to expand)

Broadly speaking, Ethereum contracts can be thought of as executing as a result of three different things: running certain EVM opcodes, running Solidity code (which compiles to EVM opcodes), or via the node JSON-RPC interface (e.g. when executing transactions).

### EVM

Certain opcodes allow contracts to make calls to other contracts, each with different semantics. We're particularly interested in `CALL` and `STATICCALL`, and how those relate to contract programming languages and client APIs.

#### `CALL`

This is the most common and basic type of call. It grants execution control to the caller until it eventually returns. No special semantics are in play here. Most Ethereum transactions spend the majority of their time in `CALL` contexts.

#### `STATICCALL`

This behaves almost exactly the same as `CALL`, with one key difference: any state-changing operations are forbidden and will immediately cause the call to fail. This includes writing to storage, emitting logs, or deploying new contracts. This call is used to query state on an external contract, e.g. to get data from a price oracle, check for access control permissions, etc.

#### Others

The `CREATE` and `CREATE2` opcodes (for contract deployment) also result in something similar to a `CALL` context, but all that's special about them has to do with how deployments work. `DELEGATECALL` (and `CALLCODE`) are somewhat complicated to understand but don't have any Aztec equivalents, so they are not worth covering.

### Solidity

Solidity (and other contract programming languages such as Vyper) compile down to EVM opcodes, but it is useful to understand how they map language concepts to the different call types.

#### Mutating External Functions

These are functions marked `payable` (which can receive ETH, which is a state change) or with no mutability declaration (sometimes called `nonpayable`). When one of these functions is called on a contract, the `CALL` opcode is emitted, meaning the callee can perform state changes, make further `CALL`s, etc.

It is also possible to call such a function with `STATICCALL` manually (e.g. using assembly), but the execution will fail as soon as a state-changing opcode is executed.

#### `view`

An external function marked `view` will not be able to mutate state (write to storage, etc.), it can only *view* the state. Solidity will emit the `STATICCALL` opcode when calling these functions, since its restrictions provide added safety to the caller (e.g. no risk of reentrancy).

Note that it is entirely possible to use `CALL` to call a `view` function, and the result will be the exact same as if `STATICCALL` had been used. The reason why `STATICCALL` exists is so that *untrusted or unknown* contracts can be called while still being able to reason about correctness. From the [EIP](https://eips.ethereum.org/EIPS/eip-214):

> '`STATICCALL` adds a way to call other contracts and restrict what they can do in the simplest way. It can be safely assumed that the state of all accounts is the same before and after a static call.'

### JSON-RPC

From outside the EVM, calls to contracts are made via [JSON-RPC](https://ethereum.org/en/developers/docs/apis/json-rpc/) methods, typically from some client library that is aware of contract ABIs, such as [ethers.js](https://docs.ethers.org/v5) or [viem](https://viem.sh/).

#### `eth_sendTransaction`

This method is how transactions are sent to a node to get them to be broadcast and eventually included in a block. The specified `to` address will be called in a `CALL` context, with some notable properties:

* there are no return values, even if the contract function invoked does return some data
* there is no explicit caller: it is instead derived from a provided signature

Some client libraries choose to automatically issue `eth_sendTransaction` when calling functions from a contract ABI that are not marked as `view` - [ethers is a good example](https://docs.ethers.org/v5/getting-started/#getting-started--writing). Notably, this means that any return value is lost and not available to the calling client - the library typically returns a transaction receipt instead. If the return value is required, the only option is to simulate the call using `eth_call`.

Note that it is possible to call non state-changing functions (i.e. `view`) with `eth_sendTransaction` - this is always meaningless. What transactions do is change the blockchain state, so all calling such a function achieves is for the caller to lose funds by paying for gas fees. The sole purpose of a `view` function is to return data, and `eth_sendTransaction` does not make the return value available.

#### `eth_call`

This method is the largest culprit of confusion around calls, but unfortunately requires understanding of all previous concepts in order to be explained. Its name is also quite unhelpful.

What `eth_call` does is simulate a transaction (a call to a contract) given the current blockchain state. The behavior will be the exact same as `eth_sendTransaction`, except:

* no actual transaction will be created
* while gas *will* be measured, there'll be no transaction fees of any kind
* no signature is required: the `from` address is passed directly, and can be set to any value (even if the private key is unknown, or if they are contract addresses!)
* the return value of the called contract is available

`eth_call` is typically used for one of the following:

* query blockchain data, e.g. read token balances
* preview the state changes produced by a transaction, e.g. the transaction cost, token balance changes, etc

Because some libraries ([such as ethers](https://docs.ethers.org/v5/getting-started/#getting-started--reading)) automatically use `eth_call` for `view` functions (which when called via Solidity result in the `STATICCALL` opcode), these concepts can be hard to tell apart. The following bears repeating: **an `eth_call`'s call context is the same as `eth_sendTransaction`, and it is a `CALL` context, not `STATICCALL`.**

## Aztec Call Types

While Ethereum contracts are defined by bytecode that runs on the EVM, Aztec contracts have multiple modes of execution depending on the function that is invoked. This section covers the main ways contracts can be interacted with, drawing analogies to Ethereum call types where applicable.

### Quick Reference

| Execution Mode | Annotation               | Runs On         | State Access          | Use Case                                     |
| -------------- | ------------------------ | --------------- | --------------------- | -------------------------------------------- |
| **Private**    | `#[external("private")]` | User's device   | Private state (notes) | Confidential transactions, private transfers |
| **Public**     | `#[external("public")]`  | Sequencer       | Public state          | Token balances, access control checks        |
| **Utility**    | `#[external("utility")]` | Offchain client | Both (unconstrained)  | Read-only queries, frontend data fetching    |

### Private Execution

Contract functions marked with `#[external("private")]` can only be called privately, and as such 'run' in the user's device. Since they're circuits, their 'execution' is actually the generation of a zk-SNARK proof that'll later be sent to the sequencer for verification.

#### Private Calls

Private functions from other contracts can be called either regularly or statically by using `self.call()` and `self.view()`. They will also be 'executed' (i.e. proved) in the user's device, and `self.view()` will fail if any state changes are attempted (like the EVM's `STATICCALL`).

```rust
let _ = self.call(Token::at(stable_coin).burn_private(from, amount, authwit_nonce));
```

> [Source code: noir-projects/noir-contracts/contracts/app/lending\_contract/src/main.nr#L231-L233](https://github.com/aztec-labs-eng/aztec-node/blob/v6.0.0/noir-projects/noir-contracts/contracts/app/lending_contract/src/main.nr#L231-L233)

Unlike the EVM however, private execution doesn't revert in the traditional way: in case of error (e.g. a failed assertion, a state changing operation in a static context, etc.) the proof generation simply fails and no transaction request is generated, spending no network gas or user funds.

#### Public Calls

Since public execution can only be performed by the sequencer, public functions cannot be executed in a private context. It is possible however to *enqueue* a public function call during private execution, requesting the sequencer to run it during inclusion of the transaction. It will be [executed in public](#public-execution) normally, including the possibility to enqueue static public calls.

Since the public call is made asynchronously, any return values or side effects are not available during private execution. If the public function fails once executed, the entire transaction is reverted including state changes caused by the private part, such as new notes or nullifiers. Note that this does result in gas being spent, like in the case of the EVM.

```rust
self.enqueue_self._deposit(AztecAddress::from_field(on_behalf_of), amount, collateral_asset);
```

> [Source code: noir-projects/noir-contracts/contracts/app/lending\_contract/src/main.nr#L117-L119](https://github.com/aztec-labs-eng/aztec-node/blob/v6.0.0/noir-projects/noir-contracts/contracts/app/lending_contract/src/main.nr#L117-L119)

It is also possible to create public functions that can *only* be invoked by privately enqueueing a call from the same contract, which can be very useful to update public state after private execution (e.g. update a token's supply after privately minting). This is achieved by annotating functions with `#[only_self]`.

A common pattern is to enqueue public calls to check some validity condition on public state, e.g. that a deadline has not expired or that some public value is set.

```rust
let selector = comptime { FunctionSelector::from_signature("check_block_number(u8,u32)") };
PublicStaticCall::<18, 2, ()>::new(
    STANDARD_PUBLIC_CHECKS_ADDRESS,
    selector,
    "check_block_number",
    [operation as Field, value as Field],
)
    .enqueue_view_incognito(context);
```

> [Source code: noir-projects/aztec-nr/aztec/src/public\_checks.nr#L29-L38](https://github.com/aztec-labs-eng/aztec-node/blob/v6.0.0/noir-projects/aztec-nr/aztec/src/public_checks.nr#L29-L38)

Note that this reveals what public function is being called on what contract, and perhaps more importantly which contract enqueued the call during private execution. To prevent this you can enqueue a call to a public function using `self.enqueue_incognito` that behaves the same as `self.enqueue` but conceals the message sender.

To address this, we have introduced a `PublicChecks` contract that can be used to perform common checks, such as verifying the timestamp or block number. By having these checks on a contract shared between apps the privacy set increases.

An example of how a deadline can be checked using the `PublicChecks` contract follows:

```rust
privately_check_timestamp(Comparator.LT, config.deadline, self.context);
```

> [Source code: noir-projects/noir-contracts/contracts/app/crowdfunding\_contract/src/main.nr#L61-L63](https://github.com/aztec-labs-eng/aztec-node/blob/v6.0.0/noir-projects/noir-contracts/contracts/app/crowdfunding_contract/src/main.nr#L61-L63)

`privately_check_timestamp` and `privately_check_block_number` are helper functions around the call to the `PublicChecks` contract:

```rust
/// Asserts that the current timestamp in the enqueued public call enqueued by `check_timestamp` satisfies
/// the `operation` with respect to the `value. Preserves privacy by performing the check via the public checks
/// contract.
/// This conceals an address of the calling contract by setting `context.msg_sender` to the public checks contract
/// address.
pub fn privately_check_timestamp(operation: u8, value: u64, context: &mut PrivateContext) {
    let selector = comptime { FunctionSelector::from_signature("check_timestamp(u8,u64)") };
    PublicStaticCall::<15, 2, ()>::new(
        STANDARD_PUBLIC_CHECKS_ADDRESS,
        selector,
        "check_timestamp",
        [operation as Field, value as Field],
    )
        .enqueue_view_incognito(context);
}

/// Asserts that the current block number in the enqueued public call enqueued by `check_block_number` satisfies
/// the `operation` with respect to the `value. Preserves privacy by performing the check via the public checks
/// contract.
/// This conceals an address of the calling contract by setting `context.msg_sender` to the public checks contract
/// address.
pub fn privately_check_block_number(operation: u8, value: u32, context: &mut PrivateContext) {
    let selector = comptime { FunctionSelector::from_signature("check_block_number(u8,u32)") };
    PublicStaticCall::<18, 2, ()>::new(
        STANDARD_PUBLIC_CHECKS_ADDRESS,
        selector,
        "check_block_number",
        [operation as Field, value as Field],
    )
        .enqueue_view_incognito(context);
}
```

> [Source code: noir-projects/aztec-nr/aztec/src/public\_checks.nr#L6-L40](https://github.com/aztec-labs-eng/aztec-node/blob/v6.0.0/noir-projects/aztec-nr/aztec/src/public_checks.nr#L6-L40)

This is what the implementation of the check timestamp functionality looks like:

```rust
/// Asserts that the current timestamp satisfies the `operation` with respect
/// to the `value.
/// NOTE: signature is hardcoded in `aztec-nr/aztec/src/public_checks.nr`; keep in sync.
#[external("public")]
#[view]
fn check_timestamp(operation: u8, value: u64) {
    let lhs_field = self.context.timestamp() as Field;
    let rhs_field = value as Field;
    assert(compare(lhs_field, operation, rhs_field), "Timestamp mismatch.");
}
```

> [Source code: noir-projects/noir-contracts/contracts/standard/public\_checks\_contract/src/main.nr#L14-L25](https://github.com/aztec-labs-eng/aztec-node/blob/v6.0.0/noir-projects/noir-contracts/contracts/standard/public_checks_contract/src/main.nr#L14-L25)

note

The `PublicChecks` contract is not part of the [aztec-nr repository](https://github.com/aztec-labs-eng/aztec-nr). To add it as a dependency, point to the aztec-packages repository:

```toml
[dependencies]
public_checks = { git = "https://github.com/aztec-labs-eng/aztec-node/", tag = "v6.0.0", directory = "noir-projects/noir-contracts/contracts/standard/public_checks_contract" }
```

Even with the public checks contract, achieving good privacy is hard. For example, if the value being checked against is unique and stored in the contract's public storage, it's then simple to find private transactions that are using that value in the enqueued public reads, and therefore link them to this contract. For this reason it is encouraged to try to avoid public function calls and instead privately read [Delayed Public Mutable](/developers/docs/aztec-nr/framework-description/state_variables.md#delayedpublicmutable) state when possible.

### Public Execution

Contract functions marked with `#[external("public")]` can only be called publicly, and are executed by the sequencer. The computation model is very similar to the EVM: all state, parameters, etc. are known to the entire network, and no data is private. Static execution like the EVM's `STATICCALL` is possible too, with similar semantics (state can be accessed but not modified, etc.).

note

The AVM supports a subset of Noir's cryptographic operations. Signature verification (ECDSA) is not available in public functions. See [AVM Cryptographic Compatibility](/developers/docs/foundational-topics/advanced/circuits/avm_compatibility.md) for details.

Since private calls are always run in a user's device, it is not possible to perform any private execution from a public context. A reasonably good mental model for public execution is that of an EVM in which some work has already been done privately, and all that is known about it is its correctness and side-effects (new notes and nullifiers, enqueued public calls, etc.). A reverted public execution will also revert the private side-effects.

Public functions in other contracts can be called both regularly and statically, just like on the EVM.

```rust
self.enqueue(Token::at(config.accepted_asset).transfer_in_public(
    self.msg_sender(),
    self.address,
    max_fee,
    authwit_nonce,
));
```

> [Source code: noir-projects/noir-contracts/contracts/fees/fpc\_contract/src/main.nr#L153-L160](https://github.com/aztec-labs-eng/aztec-node/blob/v6.0.0/noir-projects/noir-contracts/contracts/fees/fpc_contract/src/main.nr#L153-L160)

note

Public functions can be called either directly in a public context (as shown above), or asynchronously by enqueuing from a private context (as shown in the [Public Calls](#public-calls) section).

### Utility

Contract functions marked with `#[external("utility")]` are never proven as part of a transaction, even when called from a private function. They are invoked by applications that interact with contracts for:

* **State queries**: Reading from both private and public state via an offchain client
* **Local state management**: Modifying contract-related PXE state (e.g., processing logs in Aztec.nr)

Utility functions can also be called from other utility functions, and from private functions as unconstrained code. Calls that cross a contract boundary require wallet authorization. See [utility calls](/developers/docs/aztec-nr/framework-description/calling_contracts.md#utility-calls) for how to make them.

Since utility execution is unconstrained and relies heavily on oracle calls, no guarantees are made on the correctness of results. However, you can verify that the bytecode being executed is correct, since a contract's address includes a commitment to all of its utility functions.

### aztec.js

There are two main ways to execute an Aztec contract function using the `aztec.js` library, with close similarities to their [JSON-RPC counterparts](#json-rpc).

#### `simulate`

This is used to get a result out of an execution, either private or public. It creates no transaction and spends no gas. The mental model is fairly close to that of [`eth_call`](#eth_call), in that it can be used to call any type of function, simulate its execution and get a result out of it. `simulate` is also the only way to run [utility functions](#utility).

```typescript
const { result: balance } = await token.methods
  .balance_of_public(aliceAddress)
  .simulate({ from: aliceAddress });

console.log(`Alice's token balance: ${balance}`);
```

> [Source code: docs/examples/ts/aztecjs\_connection/index.ts#L151-L157](https://github.com/aztec-labs-eng/aztec-node/blob/v6.0.0/docs/examples/ts/aztecjs_connection/index.ts#L151-L157)

warning

No correctness is guaranteed on the result of `simulate`! Correct execution is entirely optional and left up to the client that handles this request.

#### `send`

This creates a transaction, generates proofs for private execution, broadcasts the transaction to the network, and returns a receipt. This is how transactions are sent, getting them to be included in blocks and spending gas. It is similar to [`eth_sendTransaction`](#eth_sendtransaction), except it also performs work on the user's device, namely the production of the proof for the private part of the transaction.

```typescript
await contract.methods.buy_pack(seed).send({ from: firstPlayer });
```

> [Source code: yarn-project/end-to-end/src/automine/card\_game.test.ts#L127-L129](https://github.com/aztec-labs-eng/aztec-node/blob/v6.0.0/yarn-project/end-to-end/src/automine/card_game.test.ts#L127-L129)

You can also use `send` to check for execution failures in testing contexts by expecting the transaction to throw:

```typescript
await expect(
  claimContract.methods.claim(anotherDonationNote, donorAddress).send({ from: unrelatedAddress }),
).rejects.toThrow('confirmed_note.owner == self.msg_sender()');
```

> [Source code: yarn-project/end-to-end/src/automine/token/crowdfunding\_and\_claim.test.ts#L221-L225](https://github.com/aztec-labs-eng/aztec-node/blob/v6.0.0/yarn-project/end-to-end/src/automine/token/crowdfunding_and_claim.test.ts#L221-L225)

## Next Steps

* [State Management](/developers/docs/foundational-topics/state_management.md) - Learn how private and public state works in Aztec
* [Transactions](/developers/docs/foundational-topics/transactions.md) - Understand the transaction lifecycle
* [Contract Creation](/developers/docs/foundational-topics/contract_creation.md) - Deploy and interact with contracts
* [Declaring Storage](/developers/docs/aztec-nr/framework-description/state_variables.md) - Define storage in your contracts
