Are you an LLM? Read llms.txt for a summary of the docs, or llms-full.txt for the full context.
Skip to content

Simulating Calls

Brane exposes eth_simulateV1 via Brane.simulate(). This lets you dry-run one or more calls (in sequence), optionally applying state overrides (balance/nonce/code/storage) and collecting richer output such as per-call gas used, logs, and asset changes.

Basic Usage

1. Build a Simulation Request

import sh.brane.rpc.BlockTag;
import sh.brane.rpc.Brane;
import sh.brane.rpc.SimulateCall;
import sh.brane.rpc.SimulateRequest;
import sh.brane.core.types.Address;
import sh.brane.core.types.HexData;
 
Address from = new Address("0x1111111111111111111111111111111111111111");
Address to = new Address("0x2222222222222222222222222222222222222222");
 
SimulateRequest request = SimulateRequest.builder()
    .account(from) // default "from" if not provided per-call
    .call(SimulateCall.builder()
        .to(to)
        .data(new HexData("0x1234"))
        .build())
    .blockTag(BlockTag.LATEST)
    .traceAssetChanges(true)
    .build();

2. Execute

import sh.brane.rpc.SimulateResult;
 
Brane client = Brane.connect("https://eth-mainnet.example.com");
SimulateResult result = client.simulate(request);

Handling Results

Each call produces a CallResult in the same order as the input calls.

import sh.brane.rpc.CallResult;
 
for (var r : result.results()) {
    switch (r) {
        case CallResult.Success s -> {
            System.out.println("ok gasUsed=" + s.gasUsed());
            System.out.println("returnData=" + s.returnData());
        }
        case CallResult.Failure f -> {
            System.out.println("fail gasUsed=" + f.gasUsed());
            System.out.println("error=" + f.errorMessage());
            System.out.println("revertData=" + f.revertData());
        }
    }
}

Asset Changes

If you enabled traceAssetChanges(true), assetChanges() may contain token balance changes observed during the simulation:

var changes = result.assetChanges();
if (changes != null) {
    for (var c : changes) {
        System.out.println(c.token().symbol() + " diff=" + c.value().diff());
    }
}

State Overrides

State overrides allow you to simulate "what if" scenarios without touching real chain state.

import sh.brane.rpc.AccountOverride;
import sh.brane.core.types.Wei;
 
Address who = new Address("0x3333333333333333333333333333333333333333");
 
SimulateRequest requestWithOverride = SimulateRequest.builder()
    .account(from)
    .call(SimulateCall.builder().to(to).data(new HexData("0x1234")).build())
    .stateOverride(who, AccountOverride.builder()
        .balance(Wei.fromEther(new java.math.BigDecimal("1000")))
        .build())
    .build();

Error Handling

import sh.brane.core.error.RpcException;
import sh.brane.rpc.exception.SimulateNotSupportedException;
 
try {
    var result = client.simulate(request);
} catch (SimulateNotSupportedException e) {
    // Node/provider does not implement eth_simulateV1
    // Consider falling back to eth_call / createAccessList depending on your use case.
    System.err.println(e.getMessage());
} catch (RpcException e) {
    // Other RPC failures (timeouts, reverts surfaced by the node, etc.)
    System.err.println(e.getMessage());
}

Technical Notes

  • Block selection: SimulateRequest.blockTag() defaults to "latest" if not set.
  • Request format: Brane serializes the request payload as the first parameter and passes the block tag as the second parameter, per the eth_simulateV1 API shape.
  • Spec reference: See the eth_simulateV1 specification and supplementary notes.