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:
- Ethereum-side — Send messages through Mirror contracts on Ethereum L1. Standard Ethereum transactions, settled on-chain.
- 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.0Initialization
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; // WrappedVaraClientimport { 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://mainnet-reth-rpc.gear-tech.io"),
});
const account = privateKeyToAccount("0x...");
const walletClient = createWalletClient({
account,
transport: http("https://mainnet-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; // WrappedVaraClientKey Classes
| Class | Description |
|---|---|
VaraEthApi | Main API for Vara.eth-side operations (queries, injected tx, state reads) |
EthereumClient | Ethereum-side wrapper (Router, Mirror, wVARA contracts) |
RouterClient | Interface to the Router contract (code validation, program creation) |
MirrorClient | Interface to Mirror contracts (send messages, top up balance, state hash) |
WrappedVaraClient | ERC-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 validatorQuerying 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) │
└─────────────────────────┘| Path | Use Case | Cost | Latency |
|---|---|---|---|
| Ethereum-side (Mirror) | On-chain settlement, value transfers, Solidity integration | ETH gas + wVARA | ~12s (L1 block) |
| Vara.eth-side (Injected) | Pre-confirmations, fast UX, state queries | No ETH gas | Sub-second |
| Read-only (calculateReply) | State queries, testing payloads | Free | Instant |
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:9944Standard Ethereum Methods
Use Ethereum RPC for standard eth_* methods (eth_blockNumber, eth_call, eth_sendRawTransaction, eth_getLogs, etc.).
Vara.eth Extension Methods
| Method | Description |
|---|---|
program_ids | List known program IDs |
program_codeId | Get code ID for a program |
program_readState | Read full program state by state hash |
program_calculateReplyForHandle | Read-only simulation of a message call |
block_header | Get latest block header (or by hash) |
injected_sendTransaction | Send an injected transaction to validators |
injected_sendTransactionAndWatch / injected_sendTransactionAndWatchUnsubscribe | Subscribe to injected tx promise and unsubscribe |
Additional node methods (not wrapped by high-level SDK helpers):
| Method | Description |
|---|---|
program_readQueue / program_readWaitlist / program_readStash / program_readMailbox | Inspect internal program queues/storage fragments |
program_readFullState | Read extended full state snapshot |
program_readPages / program_readPageData | Inspect memory pages/page data |
block_events / block_outcome | Inspect block-level request events and transitions |
code_getOriginal / code_getInstrumented | Fetch uploaded/or instrumented code blobs |
Error Codes
| Code | Meaning |
|---|---|
-32600 | Invalid request |
-32601 | Method not found |
-32602 | Invalid params |
-32603 | Internal error |
8000 | Node runtime/database/internal error (ethexe RPC service-specific) |