RPC API Reference
Complete reference for all RPC methods available on Nexis Appchain, including standard Ethereum JSON-RPC, Optimism-specific methods, and custom Nexis AI agent methods.
Quick Start
# HTTP RPC endpoint
https://testnet-rpc.nex-t1.ai
# WebSocket endpoint
wss://testnet-ws.nex-t1.ai
# Example request
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1
}'
HTTP RPC https://testnet-rpc.nex-t1.aiRate Limit: 100 req/s
WebSocket wss://testnet-ws.nex-t1.aiReal-time subscriptions
Testnet Chain ID 84532 (0x14A34)Same as Base Sepolia
Block Time 2 secondsFast finality
Standard Ethereum JSON-RPC Methods
Block Methods
eth_blockNumber
Get the latest block number.
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_blockNumber",
"params": [],
"id": 1
}'
// ethers.js
const blockNumber = await provider . getBlockNumber ();
console . log ( 'Latest block:' , blockNumber );
# web3.py
block_number = w3.eth.block_number
print ( f "Latest block: { block_number } " )
Response:
{
"jsonrpc" : "2.0" ,
"id" : 1 ,
"result" : "0x1b4" // 436 in decimal
}
eth_getBlockByNumber
Get block information by block number.
Parameters:
QUANTITY|TAG - Block number or tag (“earliest”, “latest”, “pending”, “safe”, “finalized”)
Boolean - If true, returns full transaction objects; if false, only transaction hashes
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getBlockByNumber",
"params": ["latest", true],
"id": 1
}'
// ethers.js
const block = await provider . getBlock ( 'latest' );
console . log ( 'Block:' , {
number: block . number ,
hash: block . hash ,
timestamp: block . timestamp ,
transactions: block . transactions . length ,
gasUsed: block . gasUsed . toString (),
gasLimit: block . gasLimit . toString (),
});
// viem
import { createPublicClient , http } from 'viem' ;
import { nexisTestnet } from './chains' ;
const client = createPublicClient ({
chain: nexisTestnet ,
transport: http (),
});
const block = await client . getBlock ({
blockTag: 'latest' ,
includeTransactions: true ,
});
Response:
{
"jsonrpc" : "2.0" ,
"id" : 1 ,
"result" : {
"number" : "0x1b4" ,
"hash" : "0xabc123..." ,
"parentHash" : "0xdef456..." ,
"timestamp" : "0x6538b7e0" ,
"transactions" : [ "0x789..." , "0x012..." ],
"gasUsed" : "0x47e7c4" ,
"gasLimit" : "0x1c9c380" ,
"baseFeePerGas" : "0x3b9aca00" ,
"difficulty" : "0x0" ,
"extraData" : "0x" ,
"logsBloom" : "0x..." ,
"miner" : "0x4200000000000000000000000000000000000011" ,
"mixHash" : "0x..." ,
"nonce" : "0x0000000000000000" ,
"receiptsRoot" : "0x..." ,
"sha3Uncles" : "0x..." ,
"size" : "0x3e8" ,
"stateRoot" : "0x..." ,
"totalDifficulty" : "0x0" ,
"transactionsRoot" : "0x..." ,
"uncles" : []
}
}
eth_getBlockByHash
Get block information by block hash.
Parameters:
DATA - 32-byte block hash
Boolean - If true, returns full transaction objects
const block = await provider . getBlock ( '0xabc123...' );
eth_getBlockTransactionCountByNumber
Get transaction count in a block.
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getBlockTransactionCountByNumber",
"params": ["latest"],
"id": 1
}'
const txCount = await provider . send ( 'eth_getBlockTransactionCountByNumber' , [ 'latest' ]);
console . log ( 'Transactions in latest block:' , parseInt ( txCount , 16 ));
Transaction Methods
eth_getTransactionByHash
Get transaction details by hash.
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getTransactionByHash",
"params": ["0x123abc..."],
"id": 1
}'
const tx = await provider . getTransaction ( '0x123abc...' );
console . log ( 'Transaction:' , {
from: tx . from ,
to: tx . to ,
value: ethers . formatEther ( tx . value ),
gasPrice: ethers . formatUnits ( tx . gasPrice , 'gwei' ),
nonce: tx . nonce ,
data: tx . data ,
});
Response:
{
"jsonrpc" : "2.0" ,
"id" : 1 ,
"result" : {
"blockHash" : "0xabc..." ,
"blockNumber" : "0x1b4" ,
"from" : "0x1234..." ,
"to" : "0x5678..." ,
"gas" : "0x5208" ,
"gasPrice" : "0x3b9aca00" ,
"hash" : "0x123abc..." ,
"input" : "0x" ,
"nonce" : "0x0" ,
"r" : "0x..." ,
"s" : "0x..." ,
"v" : "0x25" ,
"transactionIndex" : "0x0" ,
"type" : "0x2" ,
"value" : "0xde0b6b3a7640000" ,
"maxFeePerGas" : "0x59682f00" ,
"maxPriorityFeePerGas" : "0x3b9aca00"
}
}
eth_getTransactionReceipt
Get transaction receipt (confirmation status, logs, gas used).
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getTransactionReceipt",
"params": ["0x123abc..."],
"id": 1
}'
const receipt = await provider . getTransactionReceipt ( '0x123abc...' );
console . log ( 'Receipt:' , {
status: receipt . status , // 1 = success, 0 = failed
blockNumber: receipt . blockNumber ,
gasUsed: receipt . gasUsed . toString (),
effectiveGasPrice: ethers . formatUnits ( receipt . effectiveGasPrice , 'gwei' ),
logs: receipt . logs . length ,
});
Response:
{
"jsonrpc" : "2.0" ,
"id" : 1 ,
"result" : {
"blockHash" : "0xabc..." ,
"blockNumber" : "0x1b4" ,
"contractAddress" : null ,
"cumulativeGasUsed" : "0x5208" ,
"effectiveGasPrice" : "0x3b9aca00" ,
"from" : "0x1234..." ,
"to" : "0x5678..." ,
"gasUsed" : "0x5208" ,
"logs" : [],
"logsBloom" : "0x..." ,
"status" : "0x1" ,
"transactionHash" : "0x123abc..." ,
"transactionIndex" : "0x0" ,
"type" : "0x2"
}
}
eth_sendRawTransaction
Send a signed transaction.
// ethers.js
const wallet = new ethers . Wallet ( privateKey , provider );
const tx = await wallet . sendTransaction ({
to: '0x5678...' ,
value: ethers . parseEther ( '0.1' ),
gasLimit: 21000 ,
});
console . log ( 'TX hash:' , tx . hash );
await tx . wait (); // Wait for confirmation
console . log ( 'Confirmed!' );
# Using cast (Foundry)
cast send 0x5678... \
--value 0.1ether \
--private-key $PRIVATE_KEY \
--rpc-url https://testnet-rpc.nex-t1.ai
# web3.py
from web3 import Web3
from eth_account import Account
w3 = Web3(Web3.HTTPProvider( 'https://testnet-rpc.nex-t1.ai' ))
account = Account.from_key( '0xYourPrivateKey' )
tx = {
'from' : account.address,
'to' : '0x5678...' ,
'value' : w3.to_wei( 0.1 , 'ether' ),
'gas' : 21000 ,
'gasPrice' : w3.eth.gas_price,
'nonce' : w3.eth.get_transaction_count(account.address),
'chainId' : 84532
}
signed_tx = account.sign_transaction(tx)
tx_hash = w3.eth.send_raw_transaction(signed_tx.rawTransaction)
print ( f "TX hash: { tx_hash.hex() } " )
# Wait for receipt
receipt = w3.eth.wait_for_transaction_receipt(tx_hash)
print ( f "Status: { 'Success' if receipt[ 'status' ] == 1 else 'Failed' } " )
eth_getTransactionCount
Get transaction count (nonce) for an address.
const nonce = await provider . getTransactionCount ( '0x1234...' , 'latest' );
console . log ( 'Nonce:' , nonce );
// Get pending nonce (includes pending transactions)
const pendingNonce = await provider . getTransactionCount ( '0x1234...' , 'pending' );
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getTransactionCount",
"params": ["0x1234...", "latest"],
"id": 1
}'
Account Methods
eth_getBalance
Get account balance.
const balance = await provider . getBalance ( '0x1234...' );
console . log ( 'Balance:' , ethers . formatEther ( balance ), 'NZT' );
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getBalance",
"params": ["0x1234...", "latest"],
"id": 1
}'
balance_wei = w3.eth.get_balance( '0x1234...' )
balance_eth = w3.from_wei(balance_wei, 'ether' )
print ( f "Balance: { balance_eth } NZT" )
Response:
{
"jsonrpc" : "2.0" ,
"id" : 1 ,
"result" : "0x8ac7230489e80000" // 10 NZT in hex wei
}
eth_getCode
Get contract bytecode at an address.
const code = await provider . getCode ( '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb' );
console . log ( 'Contract code length:' , code . length );
console . log ( 'Is contract:' , code !== '0x' );
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getCode",
"params": ["0x742d...", "latest"],
"id": 1
}'
eth_getStorageAt
Get storage value at a specific position.
// Get storage slot 0 of a contract
const storage = await provider . getStorage (
'0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb' ,
0 // storage slot
);
console . log ( 'Storage at slot 0:' , storage );
State Query Methods
eth_call
Execute a contract function without creating a transaction (read-only).
// Call a view function
const contract = new ethers . Contract ( address , abi , provider );
const result = await contract . getAgent ( agentId );
console . log ( 'Agent data:' , result );
// Or use provider.call directly
const data = contract . interface . encodeFunctionData ( 'getAgent' , [ agentId ]);
const result = await provider . call ({
to: address ,
data: data ,
});
# Using cast
cast call 0x742d... "getAgent(uint256)" 1 \
--rpc-url https://testnet-rpc.nex-t1.ai
# web3.py
contract = w3.eth.contract( address = contract_address, abi = abi)
result = contract.functions.getAgent(agent_id).call()
print ( f "Agent: { result } " )
eth_estimateGas
Estimate gas needed for a transaction.
const gasEstimate = await provider . estimateGas ({
to: '0x742d...' ,
data: contract . interface . encodeFunctionData ( 'createTask' , [ ... args ]),
value: ethers . parseEther ( '1.0' ),
});
console . log ( 'Estimated gas:' , gasEstimate . toString ());
// Add buffer (10%) for safety
const gasLimit = gasEstimate * 110 n / 100 n ;
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_estimateGas",
"params": [{
"from": "0x1234...",
"to": "0x742d...",
"data": "0xabcd..."
}],
"id": 1
}'
Gas & Fee Methods
eth_gasPrice
Get current gas price.
const gasPrice = await provider . getFeeData ();
console . log ( 'Gas price:' , ethers . formatUnits ( gasPrice . gasPrice , 'gwei' ), 'gwei' );
console . log ( 'Max fee:' , ethers . formatUnits ( gasPrice . maxFeePerGas , 'gwei' ), 'gwei' );
console . log ( 'Priority fee:' , ethers . formatUnits ( gasPrice . maxPriorityFeePerGas , 'gwei' ), 'gwei' );
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_gasPrice",
"params": [],
"id": 1
}'
eth_feeHistory
Get historical gas fee data.
Parameters:
QUANTITY - Number of blocks
QUANTITY|TAG - Newest block
Array - Reward percentiles (e.g., [25, 50, 75])
const feeHistory = await provider . send ( 'eth_feeHistory' , [
'0x5' , // Last 5 blocks
'latest' ,
[ 25 , 50 , 75 ] // 25th, 50th, 75th percentile
]);
console . log ( 'Fee history:' , feeHistory );
Response:
{
"jsonrpc" : "2.0" ,
"id" : 1 ,
"result" : {
"oldestBlock" : "0x1af" ,
"baseFeePerGas" : [
"0x3b9aca00" ,
"0x3b9aca00" ,
"0x3b9aca00"
],
"gasUsedRatio" : [ 0.5 , 0.6 , 0.4 ],
"reward" : [
[ "0x0" , "0x0" , "0x0" ],
[ "0x0" , "0x0" , "0x0" ]
]
}
}
Event & Log Methods
eth_getLogs
Get event logs matching filter criteria.
Parameters:
fromBlock: Starting block (number or “earliest”, “latest”)
toBlock: Ending block
address: Contract address(es) to filter
topics: Event signature and indexed parameters
// Get all TaskCreated events
const logs = await provider . getLogs ({
address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb' ,
fromBlock: 1000000 ,
toBlock: 'latest' ,
topics: [
ethers . id ( 'TaskCreated(uint256,address,string,uint256)' )
]
});
console . log ( 'Found' , logs . length , 'TaskCreated events' );
// Decode logs
const iface = new ethers . Interface ([
'event TaskCreated(uint256 indexed taskId, address indexed creator, string description, uint256 reward)'
]);
for ( const log of logs ) {
const decoded = iface . parseLog ( log );
console . log ( 'Task' , decoded . args . taskId . toString (), 'created by' , decoded . args . creator );
}
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_getLogs",
"params": [{
"address": "0x742d...",
"fromBlock": "0xf4240",
"toBlock": "latest",
"topics": ["0x..."]
}],
"id": 1
}'
Advanced filtering:
// Filter by multiple contracts
const logs = await provider . getLogs ({
address: [
'0x742d...' , // Tasks contract
'0x1234...' // Agents contract
],
fromBlock: 1000000 ,
toBlock: 'latest'
});
// Filter by indexed parameters
const agentAddress = '0xabcd...' ;
const logs = await provider . getLogs ({
address: '0x1234...' , // Agents contract
topics: [
ethers . id ( 'StakeIncreased(uint256,address,address,uint256,uint256)' ),
null , // agentId (not filtering)
null , // asset (not filtering)
ethers . zeroPadValue ( agentAddress , 32 ) // staker address
]
});
eth_newFilter
Create a log filter.
const filter = await provider . send ( 'eth_newFilter' , [{
address: '0x742d...' ,
topics: [ ethers . id ( 'TaskCreated(uint256,address,string,uint256)' )]
}]);
console . log ( 'Filter ID:' , filter );
// Get changes
const changes = await provider . send ( 'eth_getFilterChanges' , [ filter ]);
eth_newBlockFilter
Create a block filter for new blocks.
const filterId = await provider . send ( 'eth_newBlockFilter' , []);
// Poll for new blocks
setInterval ( async () => {
const newBlocks = await provider . send ( 'eth_getFilterChanges' , [ filterId ]);
if ( newBlocks . length > 0 ) {
console . log ( 'New blocks:' , newBlocks );
}
}, 2000 ); // Poll every 2 seconds
eth_chainId
Get chain ID.
const network = await provider . getNetwork ();
console . log ( 'Chain ID:' , network . chainId ); // 84532
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "eth_chainId",
"params": [],
"id": 1
}'
Response:
{
"jsonrpc" : "2.0" ,
"id" : 1 ,
"result" : "0x14A34" // 84532
}
net_version
Get network ID (same as chain ID).
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "net_version",
"params": [],
"id": 1
}'
web3_clientVersion
Get client version information.
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "web3_clientVersion",
"params": [],
"id": 1
}'
Response:
{
"jsonrpc" : "2.0" ,
"id" : 1 ,
"result" : "op-geth/v1.101315.1-stable/linux-amd64/go1.21.5"
}
Optimism-Specific Methods
Nexis is built on the OP Stack, so Optimism-specific methods are available.
optimism_syncStatus
Get L2 sync status relative to L1.
const syncStatus = await provider . send ( 'optimism_syncStatus' , []);
console . log ( 'Sync status:' , {
unsafeL2: syncStatus . unsafe_l2 . number ,
safeL2: syncStatus . safe_l2 . number ,
finalizedL2: syncStatus . finalized_l2 . number ,
currentL1: syncStatus . current_l1 . number ,
});
Response:
{
"unsafe_l2" : {
"hash" : "0xabc..." ,
"number" : 1234567 ,
"timestamp" : 1234567890 ,
"l1origin" : {
"hash" : "0xdef..." ,
"number" : 9876543
}
},
"safe_l2" : {
"hash" : "0x123..." ,
"number" : 1234560 ,
"timestamp" : 1234567880
},
"finalized_l2" : {
"hash" : "0x456..." ,
"number" : 1234550 ,
"timestamp" : 1234567860
},
"current_l1" : {
"hash" : "0x789..." ,
"number" : 9876545 ,
"timestamp" : 1234567892
}
}
Sync states explained:
Unsafe L2 : Latest block received from sequencer (may be reorged)
Safe L2 : Block derived from finalized L1 data (unlikely to reorg)
Finalized L2 : Block based on finalized L1 block (cannot reorg)
optimism_rollupConfig
Get rollup configuration.
curl -X POST https://testnet-rpc.nex-t1.ai \
-H "Content-Type: application/json" \
-d '{
"jsonrpc": "2.0",
"method": "optimism_rollupConfig",
"params": [],
"id": 1
}'
Response:
{
"genesis" : {
"l1" : {
"hash" : "0x..." ,
"number" : 1234567
},
"l2" : {
"hash" : "0x..." ,
"number" : 0
},
"l2_time" : 1234567890
},
"block_time" : 2 ,
"max_sequencer_drift" : 600 ,
"seq_window_size" : 3600 ,
"channel_timeout" : 300 ,
"l1_chain_id" : 84532 ,
"l2_chain_id" : 84532 ,
"batch_inbox_address" : "0xff..." ,
"deposit_contract_address" : "0x..." ,
"l1_system_config_address" : "0x..."
}
optimism_outputAtBlock
Get output root at a specific block (for withdrawals).
const outputRoot = await provider . send ( 'optimism_outputAtBlock' , [ 'latest' ]);
console . log ( 'Output root:' , outputRoot );
WebSocket Subscriptions
Connect via WebSocket for real-time events.
Subscribe to New Blocks
import { ethers } from 'ethers' ;
const wsProvider = new ethers . WebSocketProvider ( 'wss://testnet-ws.nex-t1.ai' );
// Subscribe to new blocks
wsProvider . on ( 'block' , ( blockNumber ) => {
console . log ( 'New block:' , blockNumber );
});
// Or use subscription API
const subscription = await wsProvider . send ( 'eth_subscribe' , [ 'newHeads' ]);
wsProvider . on ( subscription , ( block ) => {
console . log ( 'New block:' , block . number , block . hash );
});
Subscribe to Pending Transactions
const subscription = await wsProvider . send ( 'eth_subscribe' , [ 'newPendingTransactions' ]);
wsProvider . on ( subscription , ( txHash ) => {
console . log ( 'Pending TX:' , txHash );
// Get full transaction details
wsProvider . getTransaction ( txHash ). then ( tx => {
console . log ( 'TX details:' , tx );
});
});
Subscribe to Event Logs
// Subscribe to specific contract events
const subscription = await wsProvider . send ( 'eth_subscribe' , [
'logs' ,
{
address: '0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb' ,
topics: [ ethers . id ( 'TaskCreated(uint256,address,string,uint256)' )]
}
]);
wsProvider . on ( subscription , ( log ) => {
const iface = new ethers . Interface ([
'event TaskCreated(uint256 indexed taskId, address indexed creator, string description, uint256 reward)'
]);
const decoded = iface . parseLog ( log );
console . log ( 'Task created:' , decoded . args . taskId . toString ());
});
Python WebSocket Example
import asyncio
import websockets
import json
async def subscribe_blocks ():
uri = "wss://testnet-ws.nex-t1.ai"
async with websockets.connect(uri) as ws:
# Subscribe to new blocks
await ws.send(json.dumps({
"jsonrpc" : "2.0" ,
"id" : 1 ,
"method" : "eth_subscribe" ,
"params" : [ "newHeads" ]
}))
subscription_response = await ws.recv()
print ( f "Subscribed: { subscription_response } " )
# Listen for blocks
while True :
message = await ws.recv()
data = json.loads(message)
block = data[ 'params' ][ 'result' ]
print ( f "New block: { int (block[ 'number' ], 16 ) } " )
asyncio.run(subscribe_blocks())
Batch Requests
Send multiple RPC requests in a single HTTP call.
// Using ethers.js
const [ blockNumber , gasPrice , balance ] = await Promise . all ([
provider . getBlockNumber (),
provider . getFeeData (),
provider . getBalance ( '0x1234...' )
]);
// Or using raw batch request
const batchRequest = [
{
jsonrpc: '2.0' ,
id: 1 ,
method: 'eth_blockNumber' ,
params: []
},
{
jsonrpc: '2.0' ,
id: 2 ,
method: 'eth_gasPrice' ,
params: []
},
{
jsonrpc: '2.0' ,
id: 3 ,
method: 'eth_getBalance' ,
params: [ '0x1234...' , 'latest' ]
}
];
const response = await fetch ( 'https://testnet-rpc.nex-t1.ai' , {
method: 'POST' ,
headers: { 'Content-Type' : 'application/json' },
body: JSON . stringify ( batchRequest )
});
const results = await response . json ();
Rate Limiting
Tier Limit Burst Notes Free 100 req/s 150 req/s Public endpoint Developer 500 req/s 750 req/s Register on Discord Enterprise Custom Custom Contact team
Handling Rate Limits
class RateLimitedProvider extends ethers . JsonRpcProvider {
constructor ( url , maxRequestsPerSecond = 10 ) {
super ( url );
this . maxRPS = maxRequestsPerSecond ;
this . requestQueue = [];
this . processing = false ;
this . requestCount = 0 ;
// Reset counter every second
setInterval (() => {
this . requestCount = 0 ;
this . processQueue ();
}, 1000 );
}
async send ( method , params ) {
return new Promise (( resolve , reject ) => {
this . requestQueue . push ({ method , params , resolve , reject });
this . processQueue ();
});
}
async processQueue () {
if ( this . processing || this . requestQueue . length === 0 ) return ;
if ( this . requestCount >= this . maxRPS ) return ;
this . processing = true ;
while ( this . requestQueue . length > 0 && this . requestCount < this . maxRPS ) {
const request = this . requestQueue . shift ();
this . requestCount ++ ;
try {
const result = await super . send ( request . method , request . params );
request . resolve ( result );
} catch ( error ) {
if ( error . code === 429 ) {
// Rate limited - re-queue
this . requestQueue . unshift ( request );
break ;
}
request . reject ( error );
}
}
this . processing = false ;
}
}
// Usage
const provider = new RateLimitedProvider (
'https://testnet-rpc.nex-t1.ai' ,
10 // 10 requests per second
);
Error Handling
Common Error Codes
Code Message Meaning Solution -32700Parse error Invalid JSON Check JSON formatting -32600Invalid request Malformed RPC Verify method and params -32601Method not found Unknown method Check method name -32602Invalid params Wrong parameters Verify param types/count -32603Internal error Server error Retry or contact support -32000Server error Generic error Check error message 3Execution reverted TX failed Check contract logic 429Rate limited Too many requests Implement backoff
Error Handling Examples
async function safeRpcCall ( provider , method , params , maxRetries = 3 ) {
for ( let i = 0 ; i < maxRetries ; i ++ ) {
try {
return await provider . send ( method , params );
} catch ( error ) {
console . error ( `Attempt ${ i + 1 } failed:` , error . message );
if ( error . code === 429 ) {
// Rate limited - exponential backoff
const delay = Math . pow ( 2 , i ) * 1000 ;
console . log ( `Waiting ${ delay } ms before retry...` );
await new Promise ( r => setTimeout ( r , delay ));
} else if ( error . code === - 32603 ) {
// Internal error - retry
await new Promise ( r => setTimeout ( r , 1000 ));
} else {
// Other error - don't retry
throw error ;
}
}
}
throw new Error ( 'Max retries exceeded' );
}
// Usage
try {
const balance = await safeRpcCall (
provider ,
'eth_getBalance' ,
[ '0x1234...' , 'latest' ]
);
console . log ( 'Balance:' , balance );
} catch ( error ) {
console . error ( 'Failed to get balance:' , error );
}
1. Use Batch Requests
// ❌ Slow: Sequential requests
const block1 = await provider . getBlock ( 100 );
const block2 = await provider . getBlock ( 101 );
const block3 = await provider . getBlock ( 102 );
// ✅ Fast: Parallel batch
const [ block1 , block2 , block3 ] = await Promise . all ([
provider . getBlock ( 100 ),
provider . getBlock ( 101 ),
provider . getBlock ( 102 ),
]);
2. Cache Immutable Data
class CachingProvider extends ethers . JsonRpcProvider {
constructor ( url ) {
super ( url );
this . cache = new Map ();
}
async getBlock ( blockTag ) {
// Only cache by block number (immutable)
if ( typeof blockTag === 'number' ) {
const cacheKey = `block_ ${ blockTag } ` ;
if ( this . cache . has ( cacheKey )) {
return this . cache . get ( cacheKey );
}
const block = await super . getBlock ( blockTag );
this . cache . set ( cacheKey , block );
return block ;
}
// Don't cache 'latest', 'pending', etc.
return super . getBlock ( blockTag );
}
}
3. Use WebSockets for Real-time Data
// ❌ Polling (wasteful)
setInterval ( async () => {
const blockNumber = await provider . getBlockNumber ();
console . log ( 'Block:' , blockNumber );
}, 2000 );
// ✅ WebSocket subscription (efficient)
wsProvider . on ( 'block' , ( blockNumber ) => {
console . log ( 'Block:' , blockNumber );
});
Complete Integration Example
// complete-rpc-integration.ts
import { ethers } from 'ethers' ;
class NexisRPCClient {
private httpProvider : ethers . JsonRpcProvider ;
private wsProvider : ethers . WebSocketProvider ;
constructor () {
this . httpProvider = new ethers . JsonRpcProvider (
'https://testnet-rpc.nex-t1.ai'
);
this . wsProvider = new ethers . WebSocketProvider (
'wss://testnet-ws.nex-t1.ai'
);
}
// Block methods
async getLatestBlock () {
return await this . httpProvider . getBlock ( 'latest' );
}
async getBlockByNumber ( blockNumber : number ) {
return await this . httpProvider . getBlock ( blockNumber );
}
// Transaction methods
async getTransaction ( txHash : string ) {
return await this . httpProvider . getTransaction ( txHash );
}
async sendTransaction ( tx : ethers . TransactionRequest ) {
const signer = await this . httpProvider . getSigner ();
return await signer . sendTransaction ( tx );
}
// Account methods
async getBalance ( address : string ) {
return await this . httpProvider . getBalance ( address );
}
async getNonce ( address : string ) {
return await this . httpProvider . getTransactionCount ( address );
}
// Contract interaction
async callContract (
address : string ,
abi : any [],
method : string ,
args : any []
) {
const contract = new ethers . Contract ( address , abi , this . httpProvider );
return await contract [ method ]( ... args );
}
// Event listening
subscribeToBlocks ( callback : ( blockNumber : number ) => void ) {
this . wsProvider . on ( 'block' , callback );
}
subscribeToLogs (
address : string ,
topics : string [],
callback : ( log : ethers . Log ) => void
) {
const filter = {
address ,
topics ,
};
this . wsProvider . on ( filter , callback );
}
// Batch requests
async batchCall ( calls : Array <{
method : string ;
params : any [];
}>) {
return await Promise . all (
calls . map ( call => this . httpProvider . send ( call . method , call . params ))
);
}
// Cleanup
destroy () {
this . wsProvider . destroy ();
}
}
// Usage
const client = new NexisRPCClient ();
// Get latest block
const block = await client . getLatestBlock ();
console . log ( 'Latest block:' , block . number );
// Subscribe to new blocks
client . subscribeToBlocks (( blockNumber ) => {
console . log ( 'New block:' , blockNumber );
});
// Get balance
const balance = await client . getBalance ( '0x1234...' );
console . log ( 'Balance:' , ethers . formatEther ( balance ));
// Batch call
const results = await client . batchCall ([
{ method: 'eth_blockNumber' , params: [] },
{ method: 'eth_gasPrice' , params: [] },
{ method: 'eth_getBalance' , params: [ '0x1234...' , 'latest' ] },
]);
Resources
Pro Tip: Use WebSocket subscriptions for real-time updates and HTTP for one-off queries. This combination provides the best performance!
For production applications, consider running your own RPC node for better reliability and no rate limits.