Reference

RPC API

Vara.eth API — two interaction paths, injected transactions, state queries, and pre-confirmations.

RPC API

Endpoint Availability

The gear-tech.io and vara.network URLs in the examples below were operated by Gear for the launch campaigns and are no longer served. Swap the Ethereum RPC for any Hoodi or Mainnet provider, and the Vara.eth RPC for your own node — see Network Endpoints. The code itself is unchanged.

Vara.eth provides two interaction paths for communicating with programs:

  1. Ethereum-side — Send messages through Mirror contracts on Ethereum L1. Standard Ethereum transactions, settled on-chain.
  2. Vara.eth-side — Send messages directly to Vara.eth validator nodes, bypassing Ethereum. Faster, cheaper, used for pre-confirmations and state queries.

Both paths are unified in the @vara-eth/api TypeScript library.

SDK: @vara-eth/api

The primary client library for Vara.eth. Built on viem, it provides typed interfaces for both Ethereum-side and Vara.eth-side operations.

npm install @vara-eth/api viem@^2.39.0 kzg-wasm@1.0.0

Initialization

import { VaraEthApi, WsVaraEthProvider, EthereumClient } from "@vara-eth/api";
import { walletClientToSigner } from "@vara-eth/api/signer";
import { createPublicClient, createWalletClient, http } from "viem";
import { privateKeyToAccount } from "viem/accounts";

const publicClient = createPublicClient({
  transport: http("https://hoodi-reth-rpc.gear-tech.io"),
});
const account = privateKeyToAccount("0x...");
const walletClient = createWalletClient({
  account,
  transport: http("https://hoodi-reth-rpc.gear-tech.io"),
});

const signer = walletClientToSigner(walletClient);

const ethereumClient = new EthereumClient(
  publicClient,
  routerAddress,
  signer
);
await ethereumClient.waitForInitialization();

const api = new VaraEthApi(
  new WsVaraEthProvider("ws://localhost:9944"),
  ethereumClient
);

const router = ethereumClient.router; // RouterClient
const wvara = ethereumClient.wvara;   // WrappedVaraClient

Key Classes

ClassDescription
VaraEthApiMain API for Vara.eth-side operations (queries, injected tx, state reads)
EthereumClientEthereum-side wrapper (Router, Mirror, wVARA contracts)
RouterClientInterface to the Router contract (code validation, program creation)
MirrorClientInterface to Mirror contracts (send messages, top up balance, state hash)
WrappedVaraClientERC-20 wVARA token operations (approve, balanceOf, allowance)

Source contracts: Router.sol, Mirror.sol, WrappedVara.sol


Ethereum-Side Operations

Standard Ethereum transactions through Mirror contracts on L1.

Program Creation

const codeId = "0x..."; // From ethexe CLI upload

// Create program from validated code
const tx = await router.createProgram(codeId);
await tx.sendAndWaitForReceipt();
const programId = await tx.getProgramId();

// Or with Solidity ABI for Etherscan compatibility
const tx2 = await router.createProgramWithAbiInterface(codeId, abiAddress);
await tx2.sendAndWaitForReceipt();

Sending Messages

import { getMirrorClient } from "@vara-eth/api";

const mirror = getMirrorClient(programId, signer, publicClient);

// Send message (payload encoded via sails-js)
const tx = await mirror.sendMessage(payload, 0n);
await tx.send();

// Get message details
const message = await tx.getMessage();
console.log("Message ID:", message.id);

// Wait for reply
const { waitForReply } = await tx.setupReplyListener();
const { payload: replyPayload, replyCode, value } = await waitForReply;

Payload Encoding

Use sails-js to encode payloads and decode replies from your program's IDL. See Sails-JS documentation.

Managing Executable Balance

// Check wVARA balance
const balance = await wvara.balanceOf(ethereumClient.accountAddress);

// Approve program to spend wVARA
const approveTx = await wvara.approve(programId, BigInt(10 * 1e12));
await approveTx.sendAndWaitForReceipt();

// Top up program's Executable Balance
const topUpTx = await mirror.executableBalanceTopUp(BigInt(10 * 1e12));
const { status } = await topUpTx.sendAndWaitForReceipt();

Checking Program State (Ethereum-side)

// Code validation status
const codeState = await router.codeState(codeId);
// 'Validated' | 'Rejected' | 'Unknown'

// Program's code ID
const programCodeId = await router.programCodeId(programId);

// State hash from Mirror
const stateHash = await mirror.stateHash();

// Program nonce
const nonce = await mirror.nonce();

TxManager

All contract write methods return a TxManager that handles the transaction lifecycle:

const tx = await router.createProgram(codeId);

// Send and get hash
const response = await tx.send();
console.log("Tx hash:", response.hash);

// Send and wait for receipt
const receipt = await tx.sendAndWaitForReceipt();

// Estimate gas
const gasEstimate = await tx.estimateGas();

// Find events in receipt
const event = await tx.findEvent("ProgramCreated");

Vara.eth-Side Operations

Direct communication with Vara.eth validator nodes. This is the primary path for pre-confirmations and read-only state queries -- faster and cheaper than going through Ethereum.

Connecting to Vara.eth Nodes

import { VaraEthApi, WsVaraEthProvider, HttpVaraEthProvider } from "@vara-eth/api";

// WebSocket — for subscriptions and real-time updates
const api = new VaraEthApi(
  new WsVaraEthProvider("ws://localhost:9944"),
  ethereumClient
);

// HTTP — for one-time queries
const api = new VaraEthApi(
  new HttpVaraEthProvider("http://localhost:9944"),
  ethereumClient
);

// Disconnect when done
await api.provider.disconnect();

Injected Transactions

Injected transactions are Vara.eth-native transactions sent directly to validators, bypassing Ethereum entirely. They are:

  • Signed with your Ethereum private key but submitted off-chain
  • Cheaper (no Ethereum gas costs)
  • Faster (no L1 block time wait)
  • Reference an Ethereum block for security anchoring

This is the primary mechanism for pre-confirmations.

// Create injected transaction
const injected = await api.createInjectedTransaction({
  destination: programId,
  payload: encodedPayload,
  value: 0n,
  // Auto-populated if not provided:
  // recipient: validator address (auto-selected)
  // referenceBlock: recent Ethereum block hash
  // salt: random salt for uniqueness
});

// Send transaction
const result = await injected.send();

// Send and wait for promise (includes reply)
const promise = await injected.sendAndWaitForPromise();

// Validate the FROST threshold signature
await promise.validateSignature();

Configuring Injected Transactions

const injected = await api.createInjectedTransaction({
  destination: programId,
  payload: encodedPayload,
  value: 1000n,
  referenceBlock: blockHash,    // Specific Ethereum block
  salt: "0x030405",             // Custom salt
  recipient: validatorAddress,  // Specific validator
});

// Modify using fluent API
injected
  .setValue(2000n)
  .setSalt("0x060708");

// Access properties
const messageId = injected.messageId;

// Update fields
await injected.setReferenceBlock();     // Fetch latest block
await injected.setRecipient();          // Auto-select validator
await injected.setRecipient(address);   // Specific validator

Querying Program Data

// List all program IDs
const programIds = await api.query.program.getIds();

// Get program's code ID
const codeId = await api.query.program.codeId(programId);

// Read full program state by state hash
const stateHash = await mirror.stateHash();
const state = await api.query.program.readState(stateHash);

if ("Active" in state.program) {
  console.log("Program is active");
  console.log("Balance:", state.balance);
}

Reading State via calculateReplyForHandle

Perform read-only queries on program state without sending transactions or paying gas. This simulates a message and returns what the program would reply:

// Encode query payload using sails-js
const queryPayload = sails.services.Counter.queries.GetValue.encodePayload();

// Calculate reply (read-only, free)
const reply = await api.call.program.calculateReplyForHandle(
  await ethereumClient.getAccountAddress(), // Source address
  programId,                                 // Program to query
  queryPayload                               // Encoded query
);

// Decode result using sails-js
const value = sails.services.Counter.queries.GetValue.decodeResult(
  reply.payload
);
console.log("Current counter value:", value);

This is useful for:

  • Reading program state without modifying it
  • Testing message payloads before sending
  • Querying computed values from programs
  • Building responsive UIs with instant state reads

Interaction Flow Summary

                    ┌──────────────────────┐
                    │     Your dApp        │
                    └──────┬───────┬───────┘
                           │       │
              Ethereum-side│       │Vara.eth-side
                           │       │
                    ┌──────▼──┐ ┌──▼──────────┐
                    │ Mirror  │ │  VaraEthApi  │
                    │Contract │ │  (direct)    │
                    └──────┬──┘ └──┬───────────┘
                           │       │
                    ┌──────▼───────▼──────────┐
                    │   Vara.eth Validators   │
                    │   (WASM execution)      │
                    └─────────┬───────────────┘

                    ┌─────────▼───────────────┐
                    │   Ethereum L1           │
                    │   (batch settlement)    │
                    └─────────────────────────┘
PathUse CaseCostLatency
Ethereum-side (Mirror)On-chain settlement, value transfers, Solidity integrationETH gas + wVARA~12s (L1 block)
Vara.eth-side (Injected)Pre-confirmations, fast UX, state queriesNo ETH gasSub-second
Read-only (calculateReply)State queries, testing payloadsFreeInstant

JSON-RPC Methods (Low-Level)

For direct JSON-RPC access without the SDK:

Connection

# Mainnet
Ethereum RPC (L1): https://mainnet-reth-rpc.gear-tech.io

# Testnet (Hoodi)
Ethereum RPC (L1): https://hoodi-reth-rpc.gear-tech.io

# Vara.eth validator nodes (local or public)
Vara.eth RPC:      http://127.0.0.1:9944
Vara.eth WS:       ws://127.0.0.1:9944

Standard Ethereum Methods

Use Ethereum RPC for standard eth_* methods (eth_blockNumber, eth_call, eth_sendRawTransaction, eth_getLogs, etc.).

Vara.eth Extension Methods

MethodDescription
program_idsList known program IDs
program_codeIdGet code ID for a program
program_readStateRead full program state by state hash
program_calculateReplyForHandleRead-only simulation of a message call
block_headerGet latest block header (or by hash)
injected_sendTransactionSend an injected transaction to validators
injected_sendTransactionAndWatch / injected_sendTransactionAndWatchUnsubscribeSubscribe to injected tx promise and unsubscribe

Additional node methods (not wrapped by high-level SDK helpers):

MethodDescription
program_readQueue / program_readWaitlist / program_readStash / program_readMailboxInspect internal program queues/storage fragments
program_readFullStateRead extended full state snapshot
program_readPages / program_readPageDataInspect memory pages/page data
block_events / block_outcomeInspect block-level request events and transitions
code_getOriginal / code_getInstrumentedFetch uploaded/or instrumented code blobs

Error Codes

CodeMeaning
-32600Invalid request
-32601Method not found
-32602Invalid params
-32603Internal error
8000Node runtime/database/internal error (ethexe RPC service-specific)

On this page